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