]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/unit-name.c
util-lib: move string table stuff into its own string-table.[ch]
[thirdparty/systemd.git] / src / basic / 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>
24
f01de965 25#include "bus-label.h"
4b549144 26#include "def.h"
8b43440b 27#include "hexdecoct.h"
07630cea 28#include "path-util.h"
8b43440b 29#include "string-table.h"
07630cea 30#include "string-util.h"
b9a33026 31#include "strv.h"
07630cea 32#include "unit-name.h"
8b43440b 33#include "util.h"
9e2f7c11
LP
34
35#define VALID_CHARS \
4b549144 36 DIGITS LETTERS \
4f2d528d 37 ":-_.\\"
9e2f7c11 38
7410616c 39bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
9e2f7c11
LP
40 const char *e, *i, *at;
41
7410616c 42 assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
9e2f7c11 43
7410616c
LP
44 if (_unlikely_(flags == 0))
45 return false;
9e2f7c11 46
b9a33026
LP
47 if (isempty(n))
48 return false;
49
9e2f7c11
LP
50 if (strlen(n) >= UNIT_NAME_MAX)
51 return false;
52
71fad675
LP
53 e = strrchr(n, '.');
54 if (!e || e == n)
9e2f7c11
LP
55 return false;
56
5f739699
LP
57 if (unit_type_from_string(e + 1) < 0)
58 return false;
59
9e2f7c11
LP
60 for (i = n, at = NULL; i < e; i++) {
61
62 if (*i == '@' && !at)
63 at = i;
64
65 if (!strchr("@" VALID_CHARS, *i))
66 return false;
67 }
68
7410616c
LP
69 if (at == n)
70 return false;
9e2f7c11 71
7410616c
LP
72 if (flags & UNIT_NAME_PLAIN)
73 if (!at)
74 return true;
9e2f7c11 75
7410616c
LP
76 if (flags & UNIT_NAME_INSTANCE)
77 if (at && e > at + 1)
78 return true;
79
80 if (flags & UNIT_NAME_TEMPLATE)
81 if (at && e == at + 1)
82 return true;
83
84 return false;
85}
86
87bool unit_prefix_is_valid(const char *p) {
88
89 /* We don't allow additional @ in the prefix string */
90
91 if (isempty(p))
92 return false;
93
94 return in_charset(p, VALID_CHARS);
9e2f7c11
LP
95}
96
97bool unit_instance_is_valid(const char *i) {
9e2f7c11
LP
98
99 /* The max length depends on the length of the string, so we
100 * don't really check this here. */
101
b9a33026 102 if (isempty(i))
9e2f7c11
LP
103 return false;
104
105 /* We allow additional @ in the instance string, we do not
106 * allow them in the prefix! */
107
b9a33026 108 return in_charset(i, "@" VALID_CHARS);
9e2f7c11
LP
109}
110
7410616c
LP
111bool unit_suffix_is_valid(const char *s) {
112 if (isempty(s))
113 return false;
9e2f7c11 114
7410616c
LP
115 if (s[0] != '.')
116 return false;
9e2f7c11 117
7410616c 118 if (unit_type_from_string(s + 1) < 0)
9e2f7c11
LP
119 return false;
120
7410616c
LP
121 return true;
122}
123
124int unit_name_to_prefix(const char *n, char **ret) {
125 const char *p;
126 char *s;
127
128 assert(n);
129 assert(ret);
130
131 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
132 return -EINVAL;
133
134 p = strchr(n, '@');
135 if (!p)
136 p = strrchr(n, '.');
137
138 assert_se(p);
139
140 s = strndup(n, p - n);
141 if (!s)
142 return -ENOMEM;
143
144 *ret = s;
145 return 0;
9e2f7c11
LP
146}
147
148int unit_name_to_instance(const char *n, char **instance) {
149 const char *p, *d;
150 char *i;
151
152 assert(n);
153 assert(instance);
154
7410616c
LP
155 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
156 return -EINVAL;
157
9e2f7c11 158 /* Everything past the first @ and before the last . is the instance */
35eb6b12
LP
159 p = strchr(n, '@');
160 if (!p) {
9e2f7c11
LP
161 *instance = NULL;
162 return 0;
163 }
164
7410616c
LP
165 p++;
166
167 d = strrchr(p, '.');
b9a33026
LP
168 if (!d)
169 return -EINVAL;
9e2f7c11 170
7410616c 171 i = strndup(p, d-p);
35eb6b12 172 if (!i)
9e2f7c11
LP
173 return -ENOMEM;
174
175 *instance = i;
b9a33026 176 return 1;
9e2f7c11
LP
177}
178
7410616c 179int unit_name_to_prefix_and_instance(const char *n, char **ret) {
9e2f7c11 180 const char *d;
7410616c 181 char *s;
9e2f7c11
LP
182
183 assert(n);
7410616c
LP
184 assert(ret);
185
186 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
187 return -EINVAL;
9e2f7c11 188
7410616c
LP
189 d = strrchr(n, '.');
190 if (!d)
191 return -EINVAL;
192
193 s = strndup(n, d - n);
194 if (!s)
195 return -ENOMEM;
196
197 *ret = s;
198 return 0;
9e2f7c11
LP
199}
200
7410616c
LP
201UnitType unit_name_to_type(const char *n) {
202 const char *e;
9e2f7c11 203
b9a33026
LP
204 assert(n);
205
7410616c
LP
206 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
207 return _UNIT_TYPE_INVALID;
9e2f7c11 208
7410616c
LP
209 assert_se(e = strrchr(n, '.'));
210
211 return unit_type_from_string(e + 1);
9e2f7c11
LP
212}
213
7410616c
LP
214int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
215 char *e, *s;
9e2f7c11
LP
216 size_t a, b;
217
218 assert(n);
9e2f7c11 219 assert(suffix);
7410616c
LP
220 assert(ret);
221
222 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
223 return -EINVAL;
224
225 if (!unit_suffix_is_valid(suffix))
226 return -EINVAL;
9e2f7c11
LP
227
228 assert_se(e = strrchr(n, '.'));
7410616c 229
9e2f7c11
LP
230 a = e - n;
231 b = strlen(suffix);
232
7410616c
LP
233 s = new(char, a + b + 1);
234 if (!s)
235 return -ENOMEM;
9e2f7c11 236
7410616c
LP
237 strcpy(mempcpy(s, n, a), suffix);
238 *ret = s;
239
240 return 0;
9e2f7c11
LP
241}
242
7410616c
LP
243int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
244 char *s;
245
9e2f7c11 246 assert(prefix);
9e2f7c11 247 assert(suffix);
7410616c
LP
248 assert(ret);
249
250 if (!unit_prefix_is_valid(prefix))
251 return -EINVAL;
252
253 if (instance && !unit_instance_is_valid(instance))
254 return -EINVAL;
255
256 if (!unit_suffix_is_valid(suffix))
257 return -EINVAL;
9e2f7c11
LP
258
259 if (!instance)
7410616c
LP
260 s = strappend(prefix, suffix);
261 else
262 s = strjoin(prefix, "@", instance, suffix, NULL);
263 if (!s)
264 return -ENOMEM;
9e2f7c11 265
7410616c
LP
266 *ret = s;
267 return 0;
9e2f7c11
LP
268}
269
4b712653 270static char *do_escape_char(char c, char *t) {
b9a33026
LP
271 assert(t);
272
4b712653
KS
273 *(t++) = '\\';
274 *(t++) = 'x';
275 *(t++) = hexchar(c >> 4);
276 *(t++) = hexchar(c);
b9a33026 277
4b712653
KS
278 return t;
279}
280
281static char *do_escape(const char *f, char *t) {
9e2f7c11
LP
282 assert(f);
283 assert(t);
284
4b712653
KS
285 /* do not create units with a leading '.', like for "/.dotdir" mount points */
286 if (*f == '.') {
287 t = do_escape_char(*f, t);
288 f++;
289 }
290
9e2f7c11
LP
291 for (; *f; f++) {
292 if (*f == '/')
8d567588 293 *(t++) = '-';
4b712653
KS
294 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
295 t = do_escape_char(*f, t);
296 else
9e2f7c11
LP
297 *(t++) = *f;
298 }
299
300 return t;
301}
302
9e2f7c11
LP
303char *unit_name_escape(const char *f) {
304 char *r, *t;
305
b9a33026
LP
306 assert(f);
307
b0193f1c
LP
308 r = new(char, strlen(f)*4+1);
309 if (!r)
9e2f7c11
LP
310 return NULL;
311
312 t = do_escape(f, r);
313 *t = 0;
314
315 return r;
9e2f7c11
LP
316}
317
7410616c
LP
318int unit_name_unescape(const char *f, char **ret) {
319 _cleanup_free_ char *r = NULL;
320 char *t;
9e2f7c11
LP
321
322 assert(f);
323
b0193f1c
LP
324 r = strdup(f);
325 if (!r)
7410616c 326 return -ENOMEM;
9e2f7c11
LP
327
328 for (t = r; *f; f++) {
8d567588 329 if (*f == '-')
9e2f7c11
LP
330 *(t++) = '/';
331 else if (*f == '\\') {
332 int a, b;
333
7410616c
LP
334 if (f[1] != 'x')
335 return -EINVAL;
336
337 a = unhexchar(f[2]);
338 if (a < 0)
339 return -EINVAL;
340
341 b = unhexchar(f[3]);
342 if (b < 0)
343 return -EINVAL;
344
93c47472 345 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
7410616c 346 f += 3;
9e2f7c11
LP
347 } else
348 *(t++) = *f;
349 }
350
351 *t = 0;
352
7410616c
LP
353 *ret = r;
354 r = NULL;
355
356 return 0;
9e2f7c11
LP
357}
358
7410616c
LP
359int unit_name_path_escape(const char *f, char **ret) {
360 char *p, *s;
35eb6b12
LP
361
362 assert(f);
7410616c 363 assert(ret);
35eb6b12 364
7410616c 365 p = strdupa(f);
35eb6b12 366 if (!p)
7410616c 367 return -ENOMEM;
35eb6b12
LP
368
369 path_kill_slashes(p);
370
b9a33026 371 if (STR_IN_SET(p, "/", ""))
7410616c
LP
372 s = strdup("-");
373 else {
374 char *e;
35eb6b12 375
7410616c
LP
376 if (!path_is_safe(p))
377 return -EINVAL;
35eb6b12 378
7410616c
LP
379 /* Truncate trailing slashes */
380 e = endswith(p, "/");
381 if (e)
382 *e = 0;
35eb6b12 383
7410616c
LP
384 /* Truncate leading slashes */
385 if (p[0] == '/')
386 p++;
35eb6b12 387
7410616c 388 s = unit_name_escape(p);
35eb6b12 389 }
7410616c
LP
390 if (!s)
391 return -ENOMEM;
35eb6b12 392
7410616c
LP
393 *ret = s;
394 return 0;
35eb6b12
LP
395}
396
7410616c 397int unit_name_path_unescape(const char *f, char **ret) {
93c47472 398 char *s;
7410616c 399 int r;
9e2f7c11 400
7410616c 401 assert(f);
9e2f7c11 402
93c47472
LP
403 if (isempty(f))
404 return -EINVAL;
405
7410616c
LP
406 if (streq(f, "-")) {
407 s = strdup("/");
408 if (!s)
409 return -ENOMEM;
93c47472
LP
410 } else {
411 char *w;
6ef9eeed 412
93c47472
LP
413 r = unit_name_unescape(f, &w);
414 if (r < 0)
415 return r;
29283ea4 416
93c47472
LP
417 /* Don't accept trailing or leading slashes */
418 if (startswith(w, "/") || endswith(w, "/")) {
419 free(w);
420 return -EINVAL;
421 }
29283ea4 422
93c47472
LP
423 /* Prefix a slash again */
424 s = strappend("/", w);
7410616c 425 free(w);
93c47472
LP
426 if (!s)
427 return -ENOMEM;
428
429 if (!path_is_safe(s)) {
430 free(s);
431 return -EINVAL;
432 }
7410616c 433 }
6ef9eeed 434
93c47472
LP
435 if (ret)
436 *ret = s;
437 else
438 free(s);
439
7410616c 440 return 0;
29283ea4
MS
441}
442
7410616c 443int unit_name_replace_instance(const char *f, const char *i, char **ret) {
9e2f7c11 444 const char *p, *e;
7410616c 445 char *s;
8556879e 446 size_t a, b;
9e2f7c11
LP
447
448 assert(f);
b9a33026 449 assert(i);
7410616c 450 assert(ret);
9e2f7c11 451
7410616c
LP
452 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
453 return -EINVAL;
454 if (!unit_instance_is_valid(i))
455 return -EINVAL;
9e2f7c11 456
7410616c
LP
457 assert_se(p = strchr(f, '@'));
458 assert_se(e = strrchr(f, '.'));
9e2f7c11 459
8556879e
LP
460 a = p - f;
461 b = strlen(i);
9e2f7c11 462
7410616c
LP
463 s = new(char, a + 1 + b + strlen(e) + 1);
464 if (!s)
465 return -ENOMEM;
9e2f7c11 466
7410616c
LP
467 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
468
469 *ret = s;
470 return 0;
9e2f7c11
LP
471}
472
7410616c 473int unit_name_template(const char *f, char **ret) {
9e2f7c11 474 const char *p, *e;
7410616c 475 char *s;
9e2f7c11
LP
476 size_t a;
477
b9a33026 478 assert(f);
7410616c 479 assert(ret);
b9a33026 480
7410616c
LP
481 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
482 return -EINVAL;
9e2f7c11 483
7410616c
LP
484 assert_se(p = strchr(f, '@'));
485 assert_se(e = strrchr(f, '.'));
b9a33026
LP
486
487 a = p - f;
9e2f7c11 488
7410616c
LP
489 s = new(char, a + 1 + strlen(e) + 1);
490 if (!s)
491 return -ENOMEM;
9e2f7c11 492
7410616c
LP
493 strcpy(mempcpy(s, f, a + 1), e);
494
495 *ret = s;
496 return 0;
9e2f7c11 497}
a16e1123 498
7410616c 499int unit_name_from_path(const char *path, const char *suffix, char **ret) {
58d08142 500 _cleanup_free_ char *p = NULL;
7410616c
LP
501 char *s = NULL;
502 int r;
5ffceb2f 503
a16e1123
LP
504 assert(path);
505 assert(suffix);
7410616c 506 assert(ret);
a16e1123 507
7410616c
LP
508 if (!unit_suffix_is_valid(suffix))
509 return -EINVAL;
5ffceb2f 510
7410616c
LP
511 r = unit_name_path_escape(path, &p);
512 if (r < 0)
513 return r;
514
515 s = strappend(p, suffix);
516 if (!s)
517 return -ENOMEM;
518
519 *ret = s;
520 return 0;
a16e1123
LP
521}
522
7410616c 523int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
58d08142 524 _cleanup_free_ char *p = NULL;
7410616c
LP
525 char *s;
526 int r;
9fff8a1f 527
35eb6b12 528 assert(prefix);
9fff8a1f
LP
529 assert(path);
530 assert(suffix);
7410616c 531 assert(ret);
9fff8a1f 532
7410616c
LP
533 if (!unit_prefix_is_valid(prefix))
534 return -EINVAL;
535
536 if (!unit_suffix_is_valid(suffix))
537 return -EINVAL;
538
539 r = unit_name_path_escape(path, &p);
540 if (r < 0)
541 return r;
542
543 s = strjoin(prefix, "@", p, suffix, NULL);
544 if (!s)
545 return -ENOMEM;
9fff8a1f 546
7410616c
LP
547 *ret = s;
548 return 0;
9fff8a1f
LP
549}
550
7410616c
LP
551int unit_name_to_path(const char *name, char **ret) {
552 _cleanup_free_ char *prefix = NULL;
553 int r;
a16e1123
LP
554
555 assert(name);
556
7410616c
LP
557 r = unit_name_to_prefix(name, &prefix);
558 if (r < 0)
559 return r;
a16e1123 560
7410616c 561 return unit_name_path_unescape(prefix, ret);
9fc50704 562}
48899192
MS
563
564char *unit_dbus_path_from_name(const char *name) {
9444b1f2 565 _cleanup_free_ char *e = NULL;
48899192 566
35eb6b12
LP
567 assert(name);
568
f01de965 569 e = bus_label_escape(name);
48899192
MS
570 if (!e)
571 return NULL;
572
9444b1f2 573 return strappend("/org/freedesktop/systemd1/unit/", e);
48899192 574}
b0193f1c 575
ede3a796
LP
576int unit_name_from_dbus_path(const char *path, char **name) {
577 const char *e;
578 char *n;
579
580 e = startswith(path, "/org/freedesktop/systemd1/unit/");
581 if (!e)
582 return -EINVAL;
583
f01de965 584 n = bus_label_unescape(e);
ede3a796
LP
585 if (!n)
586 return -ENOMEM;
587
588 *name = n;
589 return 0;
590}
591
21b735e7
LP
592const char* unit_dbus_interface_from_type(UnitType t) {
593
594 static const char *const table[_UNIT_TYPE_MAX] = {
595 [UNIT_SERVICE] = "org.freedesktop.systemd1.Service",
596 [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket",
597 [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName",
598 [UNIT_TARGET] = "org.freedesktop.systemd1.Target",
599 [UNIT_SNAPSHOT] = "org.freedesktop.systemd1.Snapshot",
600 [UNIT_DEVICE] = "org.freedesktop.systemd1.Device",
601 [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount",
602 [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount",
603 [UNIT_SWAP] = "org.freedesktop.systemd1.Swap",
604 [UNIT_TIMER] = "org.freedesktop.systemd1.Timer",
605 [UNIT_PATH] = "org.freedesktop.systemd1.Path",
606 [UNIT_SLICE] = "org.freedesktop.systemd1.Slice",
607 [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope",
608 };
609
610 if (t < 0)
611 return NULL;
612 if (t >= _UNIT_TYPE_MAX)
613 return NULL;
614
615 return table[t];
616}
617
618const char *unit_dbus_interface_from_name(const char *name) {
619 UnitType t;
620
621 t = unit_name_to_type(name);
622 if (t < 0)
623 return NULL;
624
625 return unit_dbus_interface_from_type(t);
626}
627
7410616c
LP
628static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
629 const char *valid_chars;
630
631 assert(f);
632 assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
633 assert(t);
634
635 /* We'll only escape the obvious characters here, to play
636 * safe. */
637
638 valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
639
640 for (; *f; f++) {
641 if (*f == '/')
642 *(t++) = '-';
643 else if (!strchr(valid_chars, *f))
644 t = do_escape_char(*f, t);
645 else
646 *(t++) = *f;
647 }
648
649 return t;
650}
651
e3e0314b 652/**
5e03c6e3
ZJS
653 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
654 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
655 * except that @suffix is appended if a valid unit suffix is not present.
656 *
657 * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
e3e0314b 658 */
7410616c
LP
659int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
660 char *s, *t;
661 int r;
b0193f1c
LP
662
663 assert(name);
5e03c6e3 664 assert(suffix);
7410616c 665 assert(ret);
b0193f1c 666
7410616c
LP
667 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
668 return -EINVAL;
b0193f1c 669
7410616c
LP
670 if (!unit_suffix_is_valid(suffix))
671 return -EINVAL;
b0193f1c 672
7410616c
LP
673 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
674 /* No mangling necessary... */
675 s = strdup(name);
676 if (!s)
677 return -ENOMEM;
1dcf6065 678
7410616c
LP
679 *ret = s;
680 return 0;
681 }
1dcf6065 682
7410616c
LP
683 if (is_device_path(name)) {
684 r = unit_name_from_path(name, ".device", ret);
685 if (r >= 0)
686 return 1;
687 if (r != -EINVAL)
688 return r;
689 }
1dcf6065 690
7410616c
LP
691 if (path_is_absolute(name)) {
692 r = unit_name_from_path(name, ".mount", ret);
693 if (r >= 0)
694 return 1;
695 if (r != -EINVAL)
696 return r;
697 }
1dcf6065 698
7410616c
LP
699 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
700 if (!s)
701 return -ENOMEM;
5f739699 702
7410616c
LP
703 t = do_escape_mangle(name, allow_globs, s);
704 *t = 0;
5f739699 705
7410616c
LP
706 if (unit_name_to_type(s) < 0)
707 strcpy(t, suffix);
5f739699 708
7410616c
LP
709 *ret = s;
710 return 1;
5f739699 711}
fb6becb4 712
93c47472
LP
713int slice_build_parent_slice(const char *slice, char **ret) {
714 char *s, *dash;
2fc09a9c 715 int r;
93c47472
LP
716
717 assert(slice);
718 assert(ret);
719
720 if (!slice_name_is_valid(slice))
721 return -EINVAL;
722
723 if (streq(slice, "-.slice")) {
724 *ret = NULL;
725 return 0;
726 }
727
728 s = strdup(slice);
729 if (!s)
730 return -ENOMEM;
731
732 dash = strrchr(s, '-');
733 if (dash)
734 strcpy(dash, ".slice");
735 else {
2fc09a9c 736 r = free_and_strdup(&s, "-.slice");
8e542fcd
DM
737 if (r < 0) {
738 free(s);
2fc09a9c 739 return r;
8e542fcd 740 }
93c47472
LP
741 }
742
743 *ret = s;
744 return 1;
745}
746
7410616c
LP
747int slice_build_subslice(const char *slice, const char*name, char **ret) {
748 char *subslice;
fb6becb4
LP
749
750 assert(slice);
751 assert(name);
7410616c
LP
752 assert(ret);
753
93c47472 754 if (!slice_name_is_valid(slice))
7410616c
LP
755 return -EINVAL;
756
757 if (!unit_prefix_is_valid(name))
758 return -EINVAL;
fb6becb4
LP
759
760 if (streq(slice, "-.slice"))
7410616c 761 subslice = strappend(name, ".slice");
fb6becb4
LP
762 else {
763 char *e;
764
93c47472 765 assert_se(e = endswith(slice, ".slice"));
fb6becb4 766
7410616c
LP
767 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
768 if (!subslice)
fb6becb4
LP
769 return -ENOMEM;
770
7410616c 771 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
fb6becb4
LP
772 }
773
7410616c 774 *ret = subslice;
fb6becb4
LP
775 return 0;
776}
cb87a73b 777
93c47472
LP
778bool slice_name_is_valid(const char *name) {
779 const char *p, *e;
780 bool dash = false;
781
782 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
783 return false;
784
785 if (streq(name, "-.slice"))
786 return true;
787
788 e = endswith(name, ".slice");
789 if (!e)
790 return false;
791
792 for (p = name; p < e; p++) {
793
794 if (*p == '-') {
795
796 /* Don't allow initial dash */
797 if (p == name)
798 return false;
799
800 /* Don't allow multiple dashes */
801 if (dash)
802 return false;
803
804 dash = true;
805 } else
806 dash = false;
807 }
808
809 /* Don't allow trailing hash */
810 if (dash)
811 return false;
812
813 return true;
814}
815
7410616c
LP
816static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
817 [UNIT_SERVICE] = "service",
818 [UNIT_SOCKET] = "socket",
819 [UNIT_BUSNAME] = "busname",
820 [UNIT_TARGET] = "target",
821 [UNIT_SNAPSHOT] = "snapshot",
822 [UNIT_DEVICE] = "device",
823 [UNIT_MOUNT] = "mount",
824 [UNIT_AUTOMOUNT] = "automount",
825 [UNIT_SWAP] = "swap",
826 [UNIT_TIMER] = "timer",
827 [UNIT_PATH] = "path",
828 [UNIT_SLICE] = "slice",
21b735e7 829 [UNIT_SCOPE] = "scope",
7410616c
LP
830};
831
832DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
833
834static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
835 [UNIT_STUB] = "stub",
836 [UNIT_LOADED] = "loaded",
837 [UNIT_NOT_FOUND] = "not-found",
838 [UNIT_ERROR] = "error",
839 [UNIT_MERGED] = "merged",
840 [UNIT_MASKED] = "masked"
841};
842
843DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
844
978c8b63
ZJS
845static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
846 [UNIT_ACTIVE] = "active",
847 [UNIT_RELOADING] = "reloading",
848 [UNIT_INACTIVE] = "inactive",
849 [UNIT_FAILED] = "failed",
850 [UNIT_ACTIVATING] = "activating",
851 [UNIT_DEACTIVATING] = "deactivating"
852};
853
854DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
855
7e55de3b
ZJS
856static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
857 [AUTOMOUNT_DEAD] = "dead",
858 [AUTOMOUNT_WAITING] = "waiting",
859 [AUTOMOUNT_RUNNING] = "running",
860 [AUTOMOUNT_FAILED] = "failed"
861};
862
863DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
864
865static const char* const busname_state_table[_BUSNAME_STATE_MAX] = {
866 [BUSNAME_DEAD] = "dead",
867 [BUSNAME_MAKING] = "making",
868 [BUSNAME_REGISTERED] = "registered",
869 [BUSNAME_LISTENING] = "listening",
870 [BUSNAME_RUNNING] = "running",
871 [BUSNAME_SIGTERM] = "sigterm",
872 [BUSNAME_SIGKILL] = "sigkill",
873 [BUSNAME_FAILED] = "failed",
874};
875
876DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState);
877
878static const char* const device_state_table[_DEVICE_STATE_MAX] = {
879 [DEVICE_DEAD] = "dead",
880 [DEVICE_TENTATIVE] = "tentative",
881 [DEVICE_PLUGGED] = "plugged",
882};
883
884DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
885
886static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
887 [MOUNT_DEAD] = "dead",
888 [MOUNT_MOUNTING] = "mounting",
889 [MOUNT_MOUNTING_DONE] = "mounting-done",
890 [MOUNT_MOUNTED] = "mounted",
891 [MOUNT_REMOUNTING] = "remounting",
892 [MOUNT_UNMOUNTING] = "unmounting",
893 [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
894 [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
895 [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
896 [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
897 [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
898 [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
899 [MOUNT_FAILED] = "failed"
900};
901
902DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
903
904static const char* const path_state_table[_PATH_STATE_MAX] = {
905 [PATH_DEAD] = "dead",
906 [PATH_WAITING] = "waiting",
907 [PATH_RUNNING] = "running",
908 [PATH_FAILED] = "failed"
909};
910
911DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
912
913static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
914 [SCOPE_DEAD] = "dead",
915 [SCOPE_RUNNING] = "running",
916 [SCOPE_ABANDONED] = "abandoned",
917 [SCOPE_STOP_SIGTERM] = "stop-sigterm",
918 [SCOPE_STOP_SIGKILL] = "stop-sigkill",
919 [SCOPE_FAILED] = "failed",
920};
921
922DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
923
924static const char* const service_state_table[_SERVICE_STATE_MAX] = {
925 [SERVICE_DEAD] = "dead",
926 [SERVICE_START_PRE] = "start-pre",
927 [SERVICE_START] = "start",
928 [SERVICE_START_POST] = "start-post",
929 [SERVICE_RUNNING] = "running",
930 [SERVICE_EXITED] = "exited",
931 [SERVICE_RELOAD] = "reload",
932 [SERVICE_STOP] = "stop",
933 [SERVICE_STOP_SIGABRT] = "stop-sigabrt",
934 [SERVICE_STOP_SIGTERM] = "stop-sigterm",
935 [SERVICE_STOP_SIGKILL] = "stop-sigkill",
936 [SERVICE_STOP_POST] = "stop-post",
937 [SERVICE_FINAL_SIGTERM] = "final-sigterm",
938 [SERVICE_FINAL_SIGKILL] = "final-sigkill",
939 [SERVICE_FAILED] = "failed",
940 [SERVICE_AUTO_RESTART] = "auto-restart",
941};
942
943DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
944
945static const char* const slice_state_table[_SLICE_STATE_MAX] = {
946 [SLICE_DEAD] = "dead",
947 [SLICE_ACTIVE] = "active"
948};
949
950DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
951
952static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = {
953 [SNAPSHOT_DEAD] = "dead",
954 [SNAPSHOT_ACTIVE] = "active"
955};
956
957DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState);
958
959static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
960 [SOCKET_DEAD] = "dead",
961 [SOCKET_START_PRE] = "start-pre",
962 [SOCKET_START_CHOWN] = "start-chown",
963 [SOCKET_START_POST] = "start-post",
964 [SOCKET_LISTENING] = "listening",
965 [SOCKET_RUNNING] = "running",
966 [SOCKET_STOP_PRE] = "stop-pre",
967 [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
968 [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
969 [SOCKET_STOP_POST] = "stop-post",
970 [SOCKET_FINAL_SIGTERM] = "final-sigterm",
971 [SOCKET_FINAL_SIGKILL] = "final-sigkill",
972 [SOCKET_FAILED] = "failed"
973};
974
975DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
976
977static const char* const swap_state_table[_SWAP_STATE_MAX] = {
978 [SWAP_DEAD] = "dead",
979 [SWAP_ACTIVATING] = "activating",
980 [SWAP_ACTIVATING_DONE] = "activating-done",
981 [SWAP_ACTIVE] = "active",
982 [SWAP_DEACTIVATING] = "deactivating",
983 [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm",
984 [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill",
985 [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
986 [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
987 [SWAP_FAILED] = "failed"
988};
989
990DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
991
992static const char* const target_state_table[_TARGET_STATE_MAX] = {
993 [TARGET_DEAD] = "dead",
994 [TARGET_ACTIVE] = "active"
995};
996
997DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
998
999static const char* const timer_state_table[_TIMER_STATE_MAX] = {
1000 [TIMER_DEAD] = "dead",
1001 [TIMER_WAITING] = "waiting",
1002 [TIMER_RUNNING] = "running",
1003 [TIMER_ELAPSED] = "elapsed",
1004 [TIMER_FAILED] = "failed"
1005};
1006
1007DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
1008
cb87a73b
LN
1009static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
1010 [UNIT_REQUIRES] = "Requires",
1011 [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
1012 [UNIT_REQUISITE] = "Requisite",
1013 [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
1014 [UNIT_WANTS] = "Wants",
1015 [UNIT_BINDS_TO] = "BindsTo",
1016 [UNIT_PART_OF] = "PartOf",
1017 [UNIT_REQUIRED_BY] = "RequiredBy",
1018 [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
be7d9ff7
LP
1019 [UNIT_REQUISITE_OF] = "RequisiteOf",
1020 [UNIT_REQUISITE_OF_OVERRIDABLE] = "RequisiteOfOverridable",
cb87a73b
LN
1021 [UNIT_WANTED_BY] = "WantedBy",
1022 [UNIT_BOUND_BY] = "BoundBy",
1023 [UNIT_CONSISTS_OF] = "ConsistsOf",
1024 [UNIT_CONFLICTS] = "Conflicts",
1025 [UNIT_CONFLICTED_BY] = "ConflictedBy",
1026 [UNIT_BEFORE] = "Before",
1027 [UNIT_AFTER] = "After",
1028 [UNIT_ON_FAILURE] = "OnFailure",
1029 [UNIT_TRIGGERS] = "Triggers",
1030 [UNIT_TRIGGERED_BY] = "TriggeredBy",
1031 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
1032 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
1033 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
1034 [UNIT_REFERENCES] = "References",
1035 [UNIT_REFERENCED_BY] = "ReferencedBy",
1036};
1037
1038DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);