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