]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/unit-name.c
unit-name: modernizations
[thirdparty/systemd.git] / src / shared / unit-name.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
9e2f7c11
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
9e2f7c11
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
9e2f7c11 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
9e2f7c11
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <string.h>
71fad675 24#include <assert.h>
9e2f7c11 25
9eb977db 26#include "path-util.h"
f01de965 27#include "bus-label.h"
71fad675 28#include "util.h"
9e2f7c11 29#include "unit-name.h"
4b549144 30#include "def.h"
9e2f7c11
LP
31
32#define VALID_CHARS \
4b549144 33 DIGITS LETTERS \
4f2d528d 34 ":-_.\\"
9e2f7c11 35
830f01f0 36static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
0a9f8ed0
ZJS
37 [UNIT_SERVICE] = "service",
38 [UNIT_SOCKET] = "socket",
e821075a 39 [UNIT_BUSNAME] = "busname",
0a9f8ed0 40 [UNIT_TARGET] = "target",
e821075a 41 [UNIT_SNAPSHOT] = "snapshot",
0a9f8ed0
ZJS
42 [UNIT_DEVICE] = "device",
43 [UNIT_MOUNT] = "mount",
44 [UNIT_AUTOMOUNT] = "automount",
0a9f8ed0 45 [UNIT_SWAP] = "swap",
e821075a 46 [UNIT_TIMER] = "timer",
0a9f8ed0 47 [UNIT_PATH] = "path",
6c12b52e
LP
48 [UNIT_SLICE] = "slice",
49 [UNIT_SCOPE] = "scope"
0a9f8ed0
ZJS
50};
51
52DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
53
830f01f0 54static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
f69614f8
ZJS
55 [UNIT_STUB] = "stub",
56 [UNIT_LOADED] = "loaded",
c2756a68 57 [UNIT_NOT_FOUND] = "not-found",
f69614f8
ZJS
58 [UNIT_ERROR] = "error",
59 [UNIT_MERGED] = "merged",
60 [UNIT_MASKED] = "masked"
61};
62
63DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
64
f78e6385 65bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
9e2f7c11
LP
66 const char *e, *i, *at;
67
68 /* Valid formats:
69 *
70 * string@instance.suffix
71 * string.suffix
72 */
73
74 assert(n);
f78e6385 75 assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID));
9e2f7c11
LP
76
77 if (strlen(n) >= UNIT_NAME_MAX)
78 return false;
79
71fad675
LP
80 e = strrchr(n, '.');
81 if (!e || e == n)
9e2f7c11
LP
82 return false;
83
5f739699
LP
84 if (unit_type_from_string(e + 1) < 0)
85 return false;
86
9e2f7c11
LP
87 for (i = n, at = NULL; i < e; i++) {
88
89 if (*i == '@' && !at)
90 at = i;
91
92 if (!strchr("@" VALID_CHARS, *i))
93 return false;
94 }
95
96 if (at) {
97 if (at == n)
98 return false;
99
f78e6385 100 if (!template_ok == TEMPLATE_VALID && at+1 == e)
9e2f7c11
LP
101 return false;
102 }
103
104 return true;
105}
106
107bool unit_instance_is_valid(const char *i) {
108 assert(i);
109
110 /* The max length depends on the length of the string, so we
111 * don't really check this here. */
112
113 if (i[0] == 0)
114 return false;
115
116 /* We allow additional @ in the instance string, we do not
117 * allow them in the prefix! */
118
119 for (; *i; i++)
120 if (!strchr("@" VALID_CHARS, *i))
121 return false;
122
123 return true;
124}
125
126bool unit_prefix_is_valid(const char *p) {
127
128 /* We don't allow additional @ in the instance string */
129
130 if (p[0] == 0)
131 return false;
132
133 for (; *p; p++)
134 if (!strchr(VALID_CHARS, *p))
135 return false;
136
137 return true;
138}
139
140int unit_name_to_instance(const char *n, char **instance) {
141 const char *p, *d;
142 char *i;
143
144 assert(n);
145 assert(instance);
146
147 /* Everything past the first @ and before the last . is the instance */
35eb6b12
LP
148 p = strchr(n, '@');
149 if (!p) {
9e2f7c11
LP
150 *instance = NULL;
151 return 0;
152 }
153
154 assert_se(d = strrchr(n, '.'));
155 assert(p < d);
156
35eb6b12
LP
157 i = strndup(p+1, d-p-1);
158 if (!i)
9e2f7c11
LP
159 return -ENOMEM;
160
161 *instance = i;
162 return 0;
163}
164
165char *unit_name_to_prefix_and_instance(const char *n) {
166 const char *d;
167
168 assert(n);
169
170 assert_se(d = strrchr(n, '.'));
171
172 return strndup(n, d - n);
173}
174
175char *unit_name_to_prefix(const char *n) {
176 const char *p;
177
b0193f1c
LP
178 p = strchr(n, '@');
179 if (p)
9e2f7c11
LP
180 return strndup(n, p - n);
181
182 return unit_name_to_prefix_and_instance(n);
183}
184
185char *unit_name_change_suffix(const char *n, const char *suffix) {
186 char *e, *r;
187 size_t a, b;
188
189 assert(n);
f78e6385 190 assert(unit_name_is_valid(n, TEMPLATE_VALID));
9e2f7c11 191 assert(suffix);
a016b922 192 assert(suffix[0] == '.');
9e2f7c11
LP
193
194 assert_se(e = strrchr(n, '.'));
195 a = e - n;
196 b = strlen(suffix);
197
b0193f1c
LP
198 r = new(char, a + b + 1);
199 if (!r)
9e2f7c11
LP
200 return NULL;
201
202 memcpy(r, n, a);
203 memcpy(r+a, suffix, b+1);
204
205 return r;
206}
207
208char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
9e2f7c11
LP
209 assert(prefix);
210 assert(unit_prefix_is_valid(prefix));
211 assert(!instance || unit_instance_is_valid(instance));
212 assert(suffix);
9e2f7c11
LP
213
214 if (!instance)
215 return strappend(prefix, suffix);
216
b7def684 217 return strjoin(prefix, "@", instance, suffix, NULL);
9e2f7c11
LP
218}
219
4b712653
KS
220static char *do_escape_char(char c, char *t) {
221 *(t++) = '\\';
222 *(t++) = 'x';
223 *(t++) = hexchar(c >> 4);
224 *(t++) = hexchar(c);
225 return t;
226}
227
228static char *do_escape(const char *f, char *t) {
9e2f7c11
LP
229 assert(f);
230 assert(t);
231
4b712653
KS
232 /* do not create units with a leading '.', like for "/.dotdir" mount points */
233 if (*f == '.') {
234 t = do_escape_char(*f, t);
235 f++;
236 }
237
9e2f7c11
LP
238 for (; *f; f++) {
239 if (*f == '/')
8d567588 240 *(t++) = '-';
4b712653
KS
241 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
242 t = do_escape_char(*f, t);
243 else
9e2f7c11
LP
244 *(t++) = *f;
245 }
246
247 return t;
248}
249
9e2f7c11
LP
250char *unit_name_escape(const char *f) {
251 char *r, *t;
252
b0193f1c
LP
253 r = new(char, strlen(f)*4+1);
254 if (!r)
9e2f7c11
LP
255 return NULL;
256
257 t = do_escape(f, r);
258 *t = 0;
259
260 return r;
9e2f7c11
LP
261}
262
263char *unit_name_unescape(const char *f) {
264 char *r, *t;
265
266 assert(f);
267
b0193f1c
LP
268 r = strdup(f);
269 if (!r)
9e2f7c11
LP
270 return NULL;
271
272 for (t = r; *f; f++) {
8d567588 273 if (*f == '-')
9e2f7c11
LP
274 *(t++) = '/';
275 else if (*f == '\\') {
276 int a, b;
277
15e11d81
LP
278 if (f[1] != 'x' ||
279 (a = unhexchar(f[2])) < 0 ||
280 (b = unhexchar(f[3])) < 0) {
281 /* Invalid escape code, let's take it literal then */
9e2f7c11
LP
282 *(t++) = '\\';
283 } else {
284 *(t++) = (char) ((a << 4) | b);
95e501f8 285 f += 3;
9e2f7c11
LP
286 }
287 } else
288 *(t++) = *f;
289 }
290
291 *t = 0;
292
293 return r;
294}
295
35eb6b12 296char *unit_name_path_escape(const char *f) {
58d08142 297 _cleanup_free_ char *p;
35eb6b12
LP
298
299 assert(f);
300
301 p = strdup(f);
302 if (!p)
303 return NULL;
304
305 path_kill_slashes(p);
306
58d08142 307 if (streq(p, "/") || streq(p, ""))
35eb6b12 308 return strdup("-");
35eb6b12 309
58d08142 310 return unit_name_escape(p[0] == '/' ? p + 1 : p);
35eb6b12
LP
311}
312
313char *unit_name_path_unescape(const char *f) {
314 char *e;
315
316 assert(f);
317
318 e = unit_name_unescape(f);
319 if (!e)
320 return NULL;
321
322 if (e[0] != '/') {
323 char *w;
324
325 w = strappend("/", e);
326 free(e);
327
328 return w;
329 }
330
331 return e;
332}
333
9e2f7c11
LP
334bool unit_name_is_template(const char *n) {
335 const char *p;
336
337 assert(n);
338
a7b9ecf9
MS
339 p = strchr(n, '@');
340 if (!p)
9e2f7c11
LP
341 return false;
342
343 return p[1] == '.';
344}
345
29283ea4
MS
346bool unit_name_is_instance(const char *n) {
347 const char *p;
348
349 assert(n);
350
351 p = strchr(n, '@');
352 if (!p)
353 return false;
354
355 return p[1] != '.';
356}
357
9e2f7c11
LP
358char *unit_name_replace_instance(const char *f, const char *i) {
359 const char *p, *e;
360 char *r, *k;
8556879e 361 size_t a, b;
9e2f7c11
LP
362
363 assert(f);
364
365 p = strchr(f, '@');
8556879e
LP
366 if (!p)
367 return strdup(f);
9e2f7c11 368
8556879e
LP
369 e = strrchr(f, '.');
370 if (!e)
371 assert_se(e = strchr(f, 0));
9e2f7c11 372
8556879e
LP
373 a = p - f;
374 b = strlen(i);
9e2f7c11 375
8556879e
LP
376 r = new(char, a + 1 + b + strlen(e) + 1);
377 if (!r)
378 return NULL;
9e2f7c11 379
8556879e
LP
380 k = mempcpy(r, f, a + 1);
381 k = mempcpy(k, i, b);
9e2f7c11 382 strcpy(k, e);
8556879e 383
9e2f7c11
LP
384 return r;
385}
386
387char *unit_name_template(const char *f) {
388 const char *p, *e;
389 char *r;
390 size_t a;
391
b0193f1c
LP
392 p = strchr(f, '@');
393 if (!p)
9e2f7c11
LP
394 return strdup(f);
395
396 assert_se(e = strrchr(f, '.'));
397 a = p - f + 1;
398
b0193f1c
LP
399 r = new(char, a + strlen(e) + 1);
400 if (!r)
9e2f7c11
LP
401 return NULL;
402
403 strcpy(mempcpy(r, f, a), e);
404 return r;
9e2f7c11 405}
a16e1123
LP
406
407char *unit_name_from_path(const char *path, const char *suffix) {
58d08142 408 _cleanup_free_ char *p = NULL;
5ffceb2f 409
a16e1123
LP
410 assert(path);
411 assert(suffix);
412
35eb6b12 413 p = unit_name_path_escape(path);
b0193f1c 414 if (!p)
5ffceb2f
LP
415 return NULL;
416
58d08142 417 return strappend(p, suffix);
a16e1123
LP
418}
419
9fff8a1f 420char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
58d08142 421 _cleanup_free_ char *p = NULL;
9fff8a1f 422
35eb6b12 423 assert(prefix);
9fff8a1f
LP
424 assert(path);
425 assert(suffix);
426
35eb6b12
LP
427 p = unit_name_path_escape(path);
428 if (!p)
9fff8a1f
LP
429 return NULL;
430
58d08142 431 return strjoin(prefix, "@", p, suffix, NULL);
9fff8a1f
LP
432}
433
a16e1123 434char *unit_name_to_path(const char *name) {
e821075a 435 _cleanup_free_ char *w = NULL;
a16e1123
LP
436
437 assert(name);
438
b0193f1c
LP
439 w = unit_name_to_prefix(name);
440 if (!w)
a16e1123
LP
441 return NULL;
442
e821075a 443 return unit_name_path_unescape(w);
9fc50704 444}
48899192
MS
445
446char *unit_dbus_path_from_name(const char *name) {
9444b1f2 447 _cleanup_free_ char *e = NULL;
48899192 448
35eb6b12
LP
449 assert(name);
450
f01de965 451 e = bus_label_escape(name);
48899192
MS
452 if (!e)
453 return NULL;
454
9444b1f2 455 return strappend("/org/freedesktop/systemd1/unit/", e);
48899192 456}
b0193f1c 457
ede3a796
LP
458int unit_name_from_dbus_path(const char *path, char **name) {
459 const char *e;
460 char *n;
461
462 e = startswith(path, "/org/freedesktop/systemd1/unit/");
463 if (!e)
464 return -EINVAL;
465
f01de965 466 n = bus_label_unescape(e);
ede3a796
LP
467 if (!n)
468 return -ENOMEM;
469
470 *name = n;
471 return 0;
472}
473
e3e0314b
ZJS
474
475/**
476 * Try to turn a string that might not be a unit name into a
477 * sensible unit name.
478 */
f78e6385 479char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
b0193f1c
LP
480 char *r, *t;
481 const char *f;
f78e6385 482 const char* valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
b0193f1c
LP
483
484 assert(name);
f78e6385 485 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
b0193f1c 486
a05f97b3 487 if (is_device_path(name))
b0193f1c
LP
488 return unit_name_from_path(name, ".device");
489
490 if (path_is_absolute(name))
491 return unit_name_from_path(name, ".mount");
492
493 /* We'll only escape the obvious characters here, to play
494 * safe. */
495
56d4fbf9 496 r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
b0193f1c
LP
497 if (!r)
498 return NULL;
499
500 for (f = name, t = r; *f; f++) {
b0193f1c
LP
501 if (*f == '/')
502 *(t++) = '-';
e3e0314b 503 else if (!strchr(valid_chars, *f))
b0193f1c
LP
504 t = do_escape_char(*f, t);
505 else
506 *(t++) = *f;
507 }
508
696c245a 509 if (unit_name_to_type(name) < 0)
56d4fbf9
LP
510 strcpy(t, ".service");
511 else
512 *t = 0;
b0193f1c
LP
513
514 return r;
515}
5f739699 516
e3e0314b
ZJS
517
518/**
519 * Similar to unit_name_mangle(), but is called when we know
520 * that this is about a specific unit type.
521 */
f78e6385 522char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
1dcf6065
LP
523 char *r, *t;
524 const char *f;
525
526 assert(name);
a016b922
LP
527 assert(suffix);
528 assert(suffix[0] == '.');
1dcf6065 529
a016b922 530 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
1dcf6065
LP
531 if (!r)
532 return NULL;
533
534 for (f = name, t = r; *f; f++) {
535 if (*f == '/')
536 *(t++) = '-';
537 else if (!strchr(VALID_CHARS, *f))
538 t = do_escape_char(*f, t);
539 else
540 *(t++) = *f;
541 }
542
a016b922
LP
543 if (!endswith(name, suffix))
544 strcpy(t, suffix);
1dcf6065
LP
545 else
546 *t = 0;
547
548 return r;
549}
550
5f739699
LP
551UnitType unit_name_to_type(const char *n) {
552 const char *e;
553
554 assert(n);
555
556 e = strrchr(n, '.');
557 if (!e)
558 return _UNIT_TYPE_INVALID;
559
560 return unit_type_from_string(e + 1);
561}
fb6becb4
LP
562
563int build_subslice(const char *slice, const char*name, char **subslice) {
564 char *ret;
565
566 assert(slice);
567 assert(name);
568 assert(subslice);
569
570 if (streq(slice, "-.slice"))
571 ret = strappend(name, ".slice");
572 else {
573 char *e;
574
575 e = endswith(slice, ".slice");
576 if (!e)
577 return -EINVAL;
578
579 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
580 if (!ret)
581 return -ENOMEM;
582
583 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
584 }
585
586 *subslice = ret;
587 return 0;
588}