]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/unit-name.c
macro: introduce TAKE_PTR() macro
[thirdparty/systemd.git] / src / basic / unit-name.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
9e2f7c11
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
9e2f7c11
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
9e2f7c11 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
9e2f7c11
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <errno.h>
11c3a366
TA
22#include <stddef.h>
23#include <stdint.h>
24#include <stdlib.h>
9e2f7c11
LP
25#include <string.h>
26
b5efdb8a 27#include "alloc-util.h"
2aaafcf5 28#include "glob-util.h"
8b43440b 29#include "hexdecoct.h"
93cc7779 30#include "path-util.h"
e5af6e0e 31#include "special.h"
07630cea 32#include "string-util.h"
b9a33026 33#include "strv.h"
07630cea 34#include "unit-name.h"
9e2f7c11 35
2aaafcf5 36/* Characters valid in a unit name. */
9e2f7c11 37#define VALID_CHARS \
2aaafcf5
LP
38 DIGITS \
39 LETTERS \
4f2d528d 40 ":-_.\\"
9e2f7c11 41
2aaafcf5
LP
42/* The same, but also permits the single @ character that may appear */
43#define VALID_CHARS_WITH_AT \
44 "@" \
45 VALID_CHARS
46
47/* All chars valid in a unit name glob */
48#define VALID_CHARS_GLOB \
49 VALID_CHARS_WITH_AT \
50 "[]!-*?"
51
7410616c 52bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
9e2f7c11
LP
53 const char *e, *i, *at;
54
7410616c 55 assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
9e2f7c11 56
7410616c
LP
57 if (_unlikely_(flags == 0))
58 return false;
9e2f7c11 59
b9a33026
LP
60 if (isempty(n))
61 return false;
62
9e2f7c11
LP
63 if (strlen(n) >= UNIT_NAME_MAX)
64 return false;
65
71fad675
LP
66 e = strrchr(n, '.');
67 if (!e || e == n)
9e2f7c11
LP
68 return false;
69
5f739699
LP
70 if (unit_type_from_string(e + 1) < 0)
71 return false;
72
9e2f7c11
LP
73 for (i = n, at = NULL; i < e; i++) {
74
75 if (*i == '@' && !at)
76 at = i;
77
78 if (!strchr("@" VALID_CHARS, *i))
79 return false;
80 }
81
7410616c
LP
82 if (at == n)
83 return false;
9e2f7c11 84
7410616c
LP
85 if (flags & UNIT_NAME_PLAIN)
86 if (!at)
87 return true;
9e2f7c11 88
7410616c
LP
89 if (flags & UNIT_NAME_INSTANCE)
90 if (at && e > at + 1)
91 return true;
92
93 if (flags & UNIT_NAME_TEMPLATE)
94 if (at && e == at + 1)
95 return true;
96
97 return false;
98}
99
100bool unit_prefix_is_valid(const char *p) {
101
102 /* We don't allow additional @ in the prefix string */
103
104 if (isempty(p))
105 return false;
106
107 return in_charset(p, VALID_CHARS);
9e2f7c11
LP
108}
109
110bool unit_instance_is_valid(const char *i) {
9e2f7c11
LP
111
112 /* The max length depends on the length of the string, so we
113 * don't really check this here. */
114
b9a33026 115 if (isempty(i))
9e2f7c11
LP
116 return false;
117
118 /* We allow additional @ in the instance string, we do not
119 * allow them in the prefix! */
120
b9a33026 121 return in_charset(i, "@" VALID_CHARS);
9e2f7c11
LP
122}
123
7410616c
LP
124bool unit_suffix_is_valid(const char *s) {
125 if (isempty(s))
126 return false;
9e2f7c11 127
7410616c
LP
128 if (s[0] != '.')
129 return false;
9e2f7c11 130
7410616c 131 if (unit_type_from_string(s + 1) < 0)
9e2f7c11
LP
132 return false;
133
7410616c
LP
134 return true;
135}
136
137int unit_name_to_prefix(const char *n, char **ret) {
138 const char *p;
139 char *s;
140
141 assert(n);
142 assert(ret);
143
144 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
145 return -EINVAL;
146
147 p = strchr(n, '@');
148 if (!p)
149 p = strrchr(n, '.');
150
151 assert_se(p);
152
153 s = strndup(n, p - n);
154 if (!s)
155 return -ENOMEM;
156
157 *ret = s;
158 return 0;
9e2f7c11
LP
159}
160
161int unit_name_to_instance(const char *n, char **instance) {
162 const char *p, *d;
163 char *i;
164
165 assert(n);
166 assert(instance);
167
7410616c
LP
168 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
169 return -EINVAL;
170
9e2f7c11 171 /* Everything past the first @ and before the last . is the instance */
35eb6b12
LP
172 p = strchr(n, '@');
173 if (!p) {
9e2f7c11
LP
174 *instance = NULL;
175 return 0;
176 }
177
7410616c
LP
178 p++;
179
180 d = strrchr(p, '.');
b9a33026
LP
181 if (!d)
182 return -EINVAL;
9e2f7c11 183
7410616c 184 i = strndup(p, d-p);
35eb6b12 185 if (!i)
9e2f7c11
LP
186 return -ENOMEM;
187
188 *instance = i;
b9a33026 189 return 1;
9e2f7c11
LP
190}
191
7410616c 192int unit_name_to_prefix_and_instance(const char *n, char **ret) {
9e2f7c11 193 const char *d;
7410616c 194 char *s;
9e2f7c11
LP
195
196 assert(n);
7410616c
LP
197 assert(ret);
198
199 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
200 return -EINVAL;
9e2f7c11 201
7410616c
LP
202 d = strrchr(n, '.');
203 if (!d)
204 return -EINVAL;
205
206 s = strndup(n, d - n);
207 if (!s)
208 return -ENOMEM;
209
210 *ret = s;
211 return 0;
9e2f7c11
LP
212}
213
7410616c
LP
214UnitType unit_name_to_type(const char *n) {
215 const char *e;
9e2f7c11 216
b9a33026
LP
217 assert(n);
218
7410616c
LP
219 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
220 return _UNIT_TYPE_INVALID;
9e2f7c11 221
7410616c
LP
222 assert_se(e = strrchr(n, '.'));
223
224 return unit_type_from_string(e + 1);
9e2f7c11
LP
225}
226
7410616c
LP
227int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
228 char *e, *s;
9e2f7c11
LP
229 size_t a, b;
230
231 assert(n);
9e2f7c11 232 assert(suffix);
7410616c
LP
233 assert(ret);
234
235 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
236 return -EINVAL;
237
238 if (!unit_suffix_is_valid(suffix))
239 return -EINVAL;
9e2f7c11
LP
240
241 assert_se(e = strrchr(n, '.'));
7410616c 242
9e2f7c11
LP
243 a = e - n;
244 b = strlen(suffix);
245
7410616c
LP
246 s = new(char, a + b + 1);
247 if (!s)
248 return -ENOMEM;
9e2f7c11 249
7410616c
LP
250 strcpy(mempcpy(s, n, a), suffix);
251 *ret = s;
252
253 return 0;
9e2f7c11
LP
254}
255
7410616c
LP
256int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
257 char *s;
258
9e2f7c11 259 assert(prefix);
9e2f7c11 260 assert(suffix);
7410616c
LP
261 assert(ret);
262
263 if (!unit_prefix_is_valid(prefix))
264 return -EINVAL;
265
266 if (instance && !unit_instance_is_valid(instance))
267 return -EINVAL;
268
269 if (!unit_suffix_is_valid(suffix))
270 return -EINVAL;
9e2f7c11
LP
271
272 if (!instance)
7410616c
LP
273 s = strappend(prefix, suffix);
274 else
605405c6 275 s = strjoin(prefix, "@", instance, suffix);
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
LP
380
381 path_kill_slashes(p);
382
b9a33026 383 if (STR_IN_SET(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
7410616c
LP
708int slice_build_subslice(const char *slice, const char*name, char **ret) {
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}