]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/unit-name.c
resolve: voidify sd_event_add_signal() and sd_event_set_watchdog()
[thirdparty/systemd.git] / src / basic / unit-name.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
9e2f7c11
LP
2
3#include <errno.h>
11c3a366
TA
4#include <stddef.h>
5#include <stdint.h>
6#include <stdlib.h>
9e2f7c11
LP
7#include <string.h>
8
b5efdb8a 9#include "alloc-util.h"
2aaafcf5 10#include "glob-util.h"
8b43440b 11#include "hexdecoct.h"
93cc7779 12#include "path-util.h"
e5af6e0e 13#include "special.h"
07630cea 14#include "string-util.h"
b9a33026 15#include "strv.h"
07630cea 16#include "unit-name.h"
9e2f7c11 17
2aaafcf5 18/* Characters valid in a unit name. */
9e2f7c11 19#define VALID_CHARS \
2aaafcf5
LP
20 DIGITS \
21 LETTERS \
4f2d528d 22 ":-_.\\"
9e2f7c11 23
2aaafcf5
LP
24/* The same, but also permits the single @ character that may appear */
25#define VALID_CHARS_WITH_AT \
26 "@" \
27 VALID_CHARS
28
29/* All chars valid in a unit name glob */
30#define VALID_CHARS_GLOB \
31 VALID_CHARS_WITH_AT \
32 "[]!-*?"
33
7410616c 34bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
9e2f7c11
LP
35 const char *e, *i, *at;
36
7410616c 37 assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
9e2f7c11 38
7410616c
LP
39 if (_unlikely_(flags == 0))
40 return false;
9e2f7c11 41
b9a33026
LP
42 if (isempty(n))
43 return false;
44
9e2f7c11
LP
45 if (strlen(n) >= UNIT_NAME_MAX)
46 return false;
47
71fad675
LP
48 e = strrchr(n, '.');
49 if (!e || e == n)
9e2f7c11
LP
50 return false;
51
5f739699
LP
52 if (unit_type_from_string(e + 1) < 0)
53 return false;
54
9e2f7c11
LP
55 for (i = n, at = NULL; i < e; i++) {
56
57 if (*i == '@' && !at)
58 at = i;
59
60 if (!strchr("@" VALID_CHARS, *i))
61 return false;
62 }
63
7410616c
LP
64 if (at == n)
65 return false;
9e2f7c11 66
7410616c
LP
67 if (flags & UNIT_NAME_PLAIN)
68 if (!at)
69 return true;
9e2f7c11 70
7410616c
LP
71 if (flags & UNIT_NAME_INSTANCE)
72 if (at && e > at + 1)
73 return true;
74
75 if (flags & UNIT_NAME_TEMPLATE)
76 if (at && e == at + 1)
77 return true;
78
79 return false;
80}
81
82bool unit_prefix_is_valid(const char *p) {
83
84 /* We don't allow additional @ in the prefix string */
85
86 if (isempty(p))
87 return false;
88
89 return in_charset(p, VALID_CHARS);
9e2f7c11
LP
90}
91
92bool unit_instance_is_valid(const char *i) {
9e2f7c11
LP
93
94 /* The max length depends on the length of the string, so we
95 * don't really check this here. */
96
b9a33026 97 if (isempty(i))
9e2f7c11
LP
98 return false;
99
100 /* We allow additional @ in the instance string, we do not
101 * allow them in the prefix! */
102
b9a33026 103 return in_charset(i, "@" VALID_CHARS);
9e2f7c11
LP
104}
105
7410616c
LP
106bool unit_suffix_is_valid(const char *s) {
107 if (isempty(s))
108 return false;
9e2f7c11 109
7410616c
LP
110 if (s[0] != '.')
111 return false;
9e2f7c11 112
7410616c 113 if (unit_type_from_string(s + 1) < 0)
9e2f7c11
LP
114 return false;
115
7410616c
LP
116 return true;
117}
118
119int unit_name_to_prefix(const char *n, char **ret) {
120 const char *p;
121 char *s;
122
123 assert(n);
124 assert(ret);
125
126 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
127 return -EINVAL;
128
129 p = strchr(n, '@');
130 if (!p)
131 p = strrchr(n, '.');
132
133 assert_se(p);
134
135 s = strndup(n, p - n);
136 if (!s)
137 return -ENOMEM;
138
139 *ret = s;
140 return 0;
9e2f7c11
LP
141}
142
143int unit_name_to_instance(const char *n, char **instance) {
144 const char *p, *d;
145 char *i;
146
147 assert(n);
148 assert(instance);
149
7410616c
LP
150 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
151 return -EINVAL;
152
9e2f7c11 153 /* Everything past the first @ and before the last . is the instance */
35eb6b12
LP
154 p = strchr(n, '@');
155 if (!p) {
9e2f7c11
LP
156 *instance = NULL;
157 return 0;
158 }
159
7410616c
LP
160 p++;
161
162 d = strrchr(p, '.');
b9a33026
LP
163 if (!d)
164 return -EINVAL;
9e2f7c11 165
7410616c 166 i = strndup(p, d-p);
35eb6b12 167 if (!i)
9e2f7c11
LP
168 return -ENOMEM;
169
170 *instance = i;
b9a33026 171 return 1;
9e2f7c11
LP
172}
173
7410616c 174int unit_name_to_prefix_and_instance(const char *n, char **ret) {
9e2f7c11 175 const char *d;
7410616c 176 char *s;
9e2f7c11
LP
177
178 assert(n);
7410616c
LP
179 assert(ret);
180
181 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
182 return -EINVAL;
9e2f7c11 183
7410616c
LP
184 d = strrchr(n, '.');
185 if (!d)
186 return -EINVAL;
187
188 s = strndup(n, d - n);
189 if (!s)
190 return -ENOMEM;
191
192 *ret = s;
193 return 0;
9e2f7c11
LP
194}
195
7410616c
LP
196UnitType unit_name_to_type(const char *n) {
197 const char *e;
9e2f7c11 198
b9a33026
LP
199 assert(n);
200
7410616c
LP
201 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
202 return _UNIT_TYPE_INVALID;
9e2f7c11 203
7410616c
LP
204 assert_se(e = strrchr(n, '.'));
205
206 return unit_type_from_string(e + 1);
9e2f7c11
LP
207}
208
7410616c
LP
209int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
210 char *e, *s;
9e2f7c11
LP
211 size_t a, b;
212
213 assert(n);
9e2f7c11 214 assert(suffix);
7410616c
LP
215 assert(ret);
216
217 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
218 return -EINVAL;
219
220 if (!unit_suffix_is_valid(suffix))
221 return -EINVAL;
9e2f7c11
LP
222
223 assert_se(e = strrchr(n, '.'));
7410616c 224
9e2f7c11
LP
225 a = e - n;
226 b = strlen(suffix);
227
7410616c
LP
228 s = new(char, a + b + 1);
229 if (!s)
230 return -ENOMEM;
9e2f7c11 231
7410616c
LP
232 strcpy(mempcpy(s, n, a), suffix);
233 *ret = s;
234
235 return 0;
9e2f7c11
LP
236}
237
7410616c 238int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
72406c2f 239 UnitType type;
7410616c 240
9e2f7c11 241 assert(prefix);
9e2f7c11 242 assert(suffix);
7410616c
LP
243 assert(ret);
244
72406c2f
LP
245 if (suffix[0] != '.')
246 return -EINVAL;
247
248 type = unit_type_from_string(suffix + 1);
249 if (type < 0)
250 return -EINVAL;
251
252 return unit_name_build_from_type(prefix, instance, type, ret);
253}
254
255int unit_name_build_from_type(const char *prefix, const char *instance, UnitType type, char **ret) {
256 const char *ut;
257 char *s;
258
259 assert(prefix);
260 assert(type >= 0);
261 assert(type < _UNIT_TYPE_MAX);
262 assert(ret);
263
7410616c
LP
264 if (!unit_prefix_is_valid(prefix))
265 return -EINVAL;
266
267 if (instance && !unit_instance_is_valid(instance))
268 return -EINVAL;
269
72406c2f 270 ut = unit_type_to_string(type);
9e2f7c11
LP
271
272 if (!instance)
72406c2f 273 s = strjoin(prefix, ".", ut);
7410616c 274 else
72406c2f 275 s = strjoin(prefix, "@", instance, ".", ut);
7410616c
LP
276 if (!s)
277 return -ENOMEM;
9e2f7c11 278
7410616c
LP
279 *ret = s;
280 return 0;
9e2f7c11
LP
281}
282
4b712653 283static char *do_escape_char(char c, char *t) {
b9a33026
LP
284 assert(t);
285
4b712653
KS
286 *(t++) = '\\';
287 *(t++) = 'x';
288 *(t++) = hexchar(c >> 4);
289 *(t++) = hexchar(c);
b9a33026 290
4b712653
KS
291 return t;
292}
293
294static char *do_escape(const char *f, char *t) {
9e2f7c11
LP
295 assert(f);
296 assert(t);
297
4b712653
KS
298 /* do not create units with a leading '.', like for "/.dotdir" mount points */
299 if (*f == '.') {
300 t = do_escape_char(*f, t);
301 f++;
302 }
303
9e2f7c11
LP
304 for (; *f; f++) {
305 if (*f == '/')
8d567588 306 *(t++) = '-';
4c701096 307 else if (IN_SET(*f, '-', '\\') || !strchr(VALID_CHARS, *f))
4b712653
KS
308 t = do_escape_char(*f, t);
309 else
9e2f7c11
LP
310 *(t++) = *f;
311 }
312
313 return t;
314}
315
9e2f7c11
LP
316char *unit_name_escape(const char *f) {
317 char *r, *t;
318
b9a33026
LP
319 assert(f);
320
b0193f1c
LP
321 r = new(char, strlen(f)*4+1);
322 if (!r)
9e2f7c11
LP
323 return NULL;
324
325 t = do_escape(f, r);
326 *t = 0;
327
328 return r;
9e2f7c11
LP
329}
330
7410616c
LP
331int unit_name_unescape(const char *f, char **ret) {
332 _cleanup_free_ char *r = NULL;
333 char *t;
9e2f7c11
LP
334
335 assert(f);
336
b0193f1c
LP
337 r = strdup(f);
338 if (!r)
7410616c 339 return -ENOMEM;
9e2f7c11
LP
340
341 for (t = r; *f; f++) {
8d567588 342 if (*f == '-')
9e2f7c11
LP
343 *(t++) = '/';
344 else if (*f == '\\') {
345 int a, b;
346
7410616c
LP
347 if (f[1] != 'x')
348 return -EINVAL;
349
350 a = unhexchar(f[2]);
351 if (a < 0)
352 return -EINVAL;
353
354 b = unhexchar(f[3]);
355 if (b < 0)
356 return -EINVAL;
357
93c47472 358 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
7410616c 359 f += 3;
9e2f7c11
LP
360 } else
361 *(t++) = *f;
362 }
363
364 *t = 0;
365
ae2a15bc 366 *ret = TAKE_PTR(r);
7410616c
LP
367
368 return 0;
9e2f7c11
LP
369}
370
7410616c
LP
371int unit_name_path_escape(const char *f, char **ret) {
372 char *p, *s;
35eb6b12
LP
373
374 assert(f);
7410616c 375 assert(ret);
35eb6b12 376
7410616c 377 p = strdupa(f);
35eb6b12 378 if (!p)
7410616c 379 return -ENOMEM;
35eb6b12 380
858d36c1 381 path_simplify(p, false);
35eb6b12 382
57ea45e1 383 if (empty_or_root(p))
7410616c
LP
384 s = strdup("-");
385 else {
99be45a4 386 if (!path_is_normalized(p))
7410616c 387 return -EINVAL;
35eb6b12 388
7410616c 389 /* Truncate trailing slashes */
7546145e 390 delete_trailing_chars(p, "/");
35eb6b12 391
7410616c 392 /* Truncate leading slashes */
7546145e 393 p = skip_leading_chars(p, "/");
35eb6b12 394
7410616c 395 s = unit_name_escape(p);
35eb6b12 396 }
7410616c
LP
397 if (!s)
398 return -ENOMEM;
35eb6b12 399
7410616c
LP
400 *ret = s;
401 return 0;
35eb6b12
LP
402}
403
7410616c 404int unit_name_path_unescape(const char *f, char **ret) {
93c47472 405 char *s;
7410616c 406 int r;
9e2f7c11 407
7410616c 408 assert(f);
9e2f7c11 409
93c47472
LP
410 if (isempty(f))
411 return -EINVAL;
412
7410616c
LP
413 if (streq(f, "-")) {
414 s = strdup("/");
415 if (!s)
416 return -ENOMEM;
93c47472
LP
417 } else {
418 char *w;
6ef9eeed 419
93c47472
LP
420 r = unit_name_unescape(f, &w);
421 if (r < 0)
422 return r;
29283ea4 423
93c47472
LP
424 /* Don't accept trailing or leading slashes */
425 if (startswith(w, "/") || endswith(w, "/")) {
426 free(w);
427 return -EINVAL;
428 }
29283ea4 429
93c47472
LP
430 /* Prefix a slash again */
431 s = strappend("/", w);
7410616c 432 free(w);
93c47472
LP
433 if (!s)
434 return -ENOMEM;
435
99be45a4 436 if (!path_is_normalized(s)) {
93c47472
LP
437 free(s);
438 return -EINVAL;
439 }
7410616c 440 }
6ef9eeed 441
93c47472
LP
442 if (ret)
443 *ret = s;
444 else
445 free(s);
446
7410616c 447 return 0;
29283ea4
MS
448}
449
7410616c 450int unit_name_replace_instance(const char *f, const char *i, char **ret) {
9e2f7c11 451 const char *p, *e;
7410616c 452 char *s;
8556879e 453 size_t a, b;
9e2f7c11
LP
454
455 assert(f);
b9a33026 456 assert(i);
7410616c 457 assert(ret);
9e2f7c11 458
7410616c
LP
459 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
460 return -EINVAL;
461 if (!unit_instance_is_valid(i))
462 return -EINVAL;
9e2f7c11 463
7410616c
LP
464 assert_se(p = strchr(f, '@'));
465 assert_se(e = strrchr(f, '.'));
9e2f7c11 466
8556879e
LP
467 a = p - f;
468 b = strlen(i);
9e2f7c11 469
7410616c
LP
470 s = new(char, a + 1 + b + strlen(e) + 1);
471 if (!s)
472 return -ENOMEM;
9e2f7c11 473
7410616c
LP
474 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
475
476 *ret = s;
477 return 0;
9e2f7c11
LP
478}
479
7410616c 480int unit_name_template(const char *f, char **ret) {
9e2f7c11 481 const char *p, *e;
7410616c 482 char *s;
9e2f7c11
LP
483 size_t a;
484
b9a33026 485 assert(f);
7410616c 486 assert(ret);
b9a33026 487
7410616c
LP
488 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
489 return -EINVAL;
9e2f7c11 490
7410616c
LP
491 assert_se(p = strchr(f, '@'));
492 assert_se(e = strrchr(f, '.'));
b9a33026
LP
493
494 a = p - f;
9e2f7c11 495
7410616c
LP
496 s = new(char, a + 1 + strlen(e) + 1);
497 if (!s)
498 return -ENOMEM;
9e2f7c11 499
7410616c
LP
500 strcpy(mempcpy(s, f, a + 1), e);
501
502 *ret = s;
503 return 0;
9e2f7c11 504}
a16e1123 505
7410616c 506int unit_name_from_path(const char *path, const char *suffix, char **ret) {
58d08142 507 _cleanup_free_ char *p = NULL;
7410616c
LP
508 char *s = NULL;
509 int r;
5ffceb2f 510
a16e1123
LP
511 assert(path);
512 assert(suffix);
7410616c 513 assert(ret);
a16e1123 514
7410616c
LP
515 if (!unit_suffix_is_valid(suffix))
516 return -EINVAL;
5ffceb2f 517
7410616c
LP
518 r = unit_name_path_escape(path, &p);
519 if (r < 0)
520 return r;
521
522 s = strappend(p, suffix);
523 if (!s)
524 return -ENOMEM;
525
526 *ret = s;
527 return 0;
a16e1123
LP
528}
529
7410616c 530int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
58d08142 531 _cleanup_free_ char *p = NULL;
7410616c
LP
532 char *s;
533 int r;
9fff8a1f 534
35eb6b12 535 assert(prefix);
9fff8a1f
LP
536 assert(path);
537 assert(suffix);
7410616c 538 assert(ret);
9fff8a1f 539
7410616c
LP
540 if (!unit_prefix_is_valid(prefix))
541 return -EINVAL;
542
543 if (!unit_suffix_is_valid(suffix))
544 return -EINVAL;
545
546 r = unit_name_path_escape(path, &p);
547 if (r < 0)
548 return r;
549
605405c6 550 s = strjoin(prefix, "@", p, suffix);
7410616c
LP
551 if (!s)
552 return -ENOMEM;
9fff8a1f 553
7410616c
LP
554 *ret = s;
555 return 0;
9fff8a1f
LP
556}
557
7410616c
LP
558int unit_name_to_path(const char *name, char **ret) {
559 _cleanup_free_ char *prefix = NULL;
560 int r;
a16e1123
LP
561
562 assert(name);
563
7410616c
LP
564 r = unit_name_to_prefix(name, &prefix);
565 if (r < 0)
566 return r;
a16e1123 567
7410616c 568 return unit_name_path_unescape(prefix, ret);
9fc50704 569}
48899192 570
37cbc1d5 571static bool do_escape_mangle(const char *f, bool allow_globs, char *t) {
7410616c 572 const char *valid_chars;
37cbc1d5 573 bool mangled = false;
7410616c
LP
574
575 assert(f);
7410616c
LP
576 assert(t);
577
37cbc1d5
ZJS
578 /* We'll only escape the obvious characters here, to play safe.
579 *
580 * Returns true if any characters were mangled, false otherwise.
581 */
7410616c 582
37cbc1d5 583 valid_chars = allow_globs ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT;
7410616c 584
37cbc1d5
ZJS
585 for (; *f; f++)
586 if (*f == '/') {
7410616c 587 *(t++) = '-';
37cbc1d5
ZJS
588 mangled = true;
589 } else if (!strchr(valid_chars, *f)) {
7410616c 590 t = do_escape_char(*f, t);
37cbc1d5
ZJS
591 mangled = true;
592 } else
7410616c 593 *(t++) = *f;
37cbc1d5 594 *t = 0;
7410616c 595
37cbc1d5 596 return mangled;
7410616c
LP
597}
598
e3e0314b 599/**
5e03c6e3
ZJS
600 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
601 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
602 * except that @suffix is appended if a valid unit suffix is not present.
603 *
b938cb90 604 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
e3e0314b 605 */
37cbc1d5
ZJS
606int unit_name_mangle_with_suffix(const char *name, UnitNameMangle flags, const char *suffix, char **ret) {
607 char *s;
7410616c 608 int r;
37cbc1d5 609 bool mangled;
b0193f1c
LP
610
611 assert(name);
5e03c6e3 612 assert(suffix);
7410616c 613 assert(ret);
b0193f1c 614
7410616c
LP
615 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
616 return -EINVAL;
b0193f1c 617
7410616c
LP
618 if (!unit_suffix_is_valid(suffix))
619 return -EINVAL;
b0193f1c 620
2aaafcf5
LP
621 /* Already a fully valid unit name? If so, no mangling is necessary... */
622 if (unit_name_is_valid(name, UNIT_NAME_ANY))
623 goto good;
1dcf6065 624
2aaafcf5 625 /* Already a fully valid globbing expression? If so, no mangling is necessary either... */
37cbc1d5 626 if ((flags & UNIT_NAME_MANGLE_GLOB) &&
2aaafcf5
LP
627 string_is_glob(name) &&
628 in_charset(name, VALID_CHARS_GLOB))
629 goto good;
1dcf6065 630
7410616c
LP
631 if (is_device_path(name)) {
632 r = unit_name_from_path(name, ".device", ret);
633 if (r >= 0)
634 return 1;
635 if (r != -EINVAL)
636 return r;
637 }
1dcf6065 638
7410616c
LP
639 if (path_is_absolute(name)) {
640 r = unit_name_from_path(name, ".mount", ret);
641 if (r >= 0)
642 return 1;
643 if (r != -EINVAL)
644 return r;
645 }
1dcf6065 646
7410616c
LP
647 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
648 if (!s)
649 return -ENOMEM;
5f739699 650
37cbc1d5
ZJS
651 mangled = do_escape_mangle(name, flags & UNIT_NAME_MANGLE_GLOB, s);
652 if (mangled)
653 log_full(flags & UNIT_NAME_MANGLE_WARN ? LOG_NOTICE : LOG_DEBUG,
654 "Invalid unit name \"%s\" was escaped as \"%s\" (maybe you should use systemd-escape?)",
655 name, s);
5f739699 656
2aaafcf5
LP
657 /* Append a suffix if it doesn't have any, but only if this is not a glob, so that we can allow "foo.*" as a
658 * valid glob. */
37cbc1d5
ZJS
659 if ((!(flags & UNIT_NAME_MANGLE_GLOB) || !string_is_glob(s)) && unit_name_to_type(s) < 0)
660 strcat(s, suffix);
5f739699 661
7410616c
LP
662 *ret = s;
663 return 1;
2aaafcf5
LP
664
665good:
666 s = strdup(name);
667 if (!s)
668 return -ENOMEM;
669
670 *ret = s;
671 return 0;
5f739699 672}
fb6becb4 673
93c47472
LP
674int slice_build_parent_slice(const char *slice, char **ret) {
675 char *s, *dash;
2fc09a9c 676 int r;
93c47472
LP
677
678 assert(slice);
679 assert(ret);
680
681 if (!slice_name_is_valid(slice))
682 return -EINVAL;
683
e5af6e0e 684 if (streq(slice, SPECIAL_ROOT_SLICE)) {
93c47472
LP
685 *ret = NULL;
686 return 0;
687 }
688
689 s = strdup(slice);
690 if (!s)
691 return -ENOMEM;
692
693 dash = strrchr(s, '-');
694 if (dash)
695 strcpy(dash, ".slice");
696 else {
e5af6e0e 697 r = free_and_strdup(&s, SPECIAL_ROOT_SLICE);
8e542fcd
DM
698 if (r < 0) {
699 free(s);
2fc09a9c 700 return r;
8e542fcd 701 }
93c47472
LP
702 }
703
704 *ret = s;
705 return 1;
706}
707
0682ed5c 708int slice_build_subslice(const char *slice, const char *name, char **ret) {
7410616c 709 char *subslice;
fb6becb4
LP
710
711 assert(slice);
712 assert(name);
7410616c
LP
713 assert(ret);
714
93c47472 715 if (!slice_name_is_valid(slice))
7410616c
LP
716 return -EINVAL;
717
718 if (!unit_prefix_is_valid(name))
719 return -EINVAL;
fb6becb4 720
e5af6e0e 721 if (streq(slice, SPECIAL_ROOT_SLICE))
7410616c 722 subslice = strappend(name, ".slice");
fb6becb4
LP
723 else {
724 char *e;
725
93c47472 726 assert_se(e = endswith(slice, ".slice"));
fb6becb4 727
7410616c
LP
728 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
729 if (!subslice)
fb6becb4
LP
730 return -ENOMEM;
731
7410616c 732 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
fb6becb4
LP
733 }
734
7410616c 735 *ret = subslice;
fb6becb4
LP
736 return 0;
737}
cb87a73b 738
93c47472
LP
739bool slice_name_is_valid(const char *name) {
740 const char *p, *e;
741 bool dash = false;
742
743 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
744 return false;
745
e5af6e0e 746 if (streq(name, SPECIAL_ROOT_SLICE))
93c47472
LP
747 return true;
748
749 e = endswith(name, ".slice");
750 if (!e)
751 return false;
752
753 for (p = name; p < e; p++) {
754
755 if (*p == '-') {
756
757 /* Don't allow initial dash */
758 if (p == name)
759 return false;
760
761 /* Don't allow multiple dashes */
762 if (dash)
763 return false;
764
765 dash = true;
766 } else
767 dash = false;
768 }
769
770 /* Don't allow trailing hash */
771 if (dash)
772 return false;
773
774 return true;
775}