]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/unit-name.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
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
10 (at your option) any later version.
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
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "alloc-util.h"
28 #include "glob-util.h"
29 #include "hexdecoct.h"
30 #include "path-util.h"
32 #include "string-util.h"
34 #include "unit-name.h"
36 /* Characters valid in a unit name. */
42 /* The same, but also permits the single @ character that may appear */
43 #define VALID_CHARS_WITH_AT \
47 /* All chars valid in a unit name glob */
48 #define VALID_CHARS_GLOB \
52 bool unit_name_is_valid(const char *n
, UnitNameFlags flags
) {
53 const char *e
, *i
, *at
;
55 assert((flags
& ~(UNIT_NAME_PLAIN
|UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
)) == 0);
57 if (_unlikely_(flags
== 0))
63 if (strlen(n
) >= UNIT_NAME_MAX
)
70 if (unit_type_from_string(e
+ 1) < 0)
73 for (i
= n
, at
= NULL
; i
< e
; i
++) {
78 if (!strchr("@" VALID_CHARS
, *i
))
85 if (flags
& UNIT_NAME_PLAIN
)
89 if (flags
& UNIT_NAME_INSTANCE
)
93 if (flags
& UNIT_NAME_TEMPLATE
)
94 if (at
&& e
== at
+ 1)
100 bool unit_prefix_is_valid(const char *p
) {
102 /* We don't allow additional @ in the prefix string */
107 return in_charset(p
, VALID_CHARS
);
110 bool unit_instance_is_valid(const char *i
) {
112 /* The max length depends on the length of the string, so we
113 * don't really check this here. */
118 /* We allow additional @ in the instance string, we do not
119 * allow them in the prefix! */
121 return in_charset(i
, "@" VALID_CHARS
);
124 bool unit_suffix_is_valid(const char *s
) {
131 if (unit_type_from_string(s
+ 1) < 0)
137 int unit_name_to_prefix(const char *n
, char **ret
) {
144 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
153 s
= strndup(n
, p
- n
);
161 int unit_name_to_instance(const char *n
, char **instance
) {
168 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
171 /* Everything past the first @ and before the last . is the instance */
192 int unit_name_to_prefix_and_instance(const char *n
, char **ret
) {
199 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
206 s
= strndup(n
, d
- n
);
214 UnitType
unit_name_to_type(const char *n
) {
219 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
220 return _UNIT_TYPE_INVALID
;
222 assert_se(e
= strrchr(n
, '.'));
224 return unit_type_from_string(e
+ 1);
227 int unit_name_change_suffix(const char *n
, const char *suffix
, char **ret
) {
235 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
238 if (!unit_suffix_is_valid(suffix
))
241 assert_se(e
= strrchr(n
, '.'));
246 s
= new(char, a
+ b
+ 1);
250 strcpy(mempcpy(s
, n
, a
), suffix
);
256 int unit_name_build(const char *prefix
, const char *instance
, const char *suffix
, char **ret
) {
263 if (!unit_prefix_is_valid(prefix
))
266 if (instance
&& !unit_instance_is_valid(instance
))
269 if (!unit_suffix_is_valid(suffix
))
273 s
= strappend(prefix
, suffix
);
275 s
= strjoin(prefix
, "@", instance
, suffix
);
283 static char *do_escape_char(char c
, char *t
) {
288 *(t
++) = hexchar(c
>> 4);
294 static char *do_escape(const char *f
, char *t
) {
298 /* do not create units with a leading '.', like for "/.dotdir" mount points */
300 t
= do_escape_char(*f
, t
);
307 else if (IN_SET(*f
, '-', '\\') || !strchr(VALID_CHARS
, *f
))
308 t
= do_escape_char(*f
, t
);
316 char *unit_name_escape(const char *f
) {
321 r
= new(char, strlen(f
)*4+1);
331 int unit_name_unescape(const char *f
, char **ret
) {
332 _cleanup_free_
char *r
= NULL
;
341 for (t
= r
; *f
; f
++) {
344 else if (*f
== '\\') {
358 *(t
++) = (char) (((uint8_t) a
<< 4U) | (uint8_t) b
);
371 int unit_name_path_escape(const char *f
, char **ret
) {
381 path_kill_slashes(p
);
383 if (STR_IN_SET(p
, "/", ""))
386 if (!path_is_normalized(p
))
389 /* Truncate trailing slashes */
390 delete_trailing_chars(p
, "/");
392 /* Truncate leading slashes */
393 p
= skip_leading_chars(p
, "/");
395 s
= unit_name_escape(p
);
404 int unit_name_path_unescape(const char *f
, char **ret
) {
420 r
= unit_name_unescape(f
, &w
);
424 /* Don't accept trailing or leading slashes */
425 if (startswith(w
, "/") || endswith(w
, "/")) {
430 /* Prefix a slash again */
431 s
= strappend("/", w
);
436 if (!path_is_normalized(s
)) {
450 int unit_name_replace_instance(const char *f
, const char *i
, char **ret
) {
459 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
461 if (!unit_instance_is_valid(i
))
464 assert_se(p
= strchr(f
, '@'));
465 assert_se(e
= strrchr(f
, '.'));
470 s
= new(char, a
+ 1 + b
+ strlen(e
) + 1);
474 strcpy(mempcpy(mempcpy(s
, f
, a
+ 1), i
, b
), e
);
480 int unit_name_template(const char *f
, char **ret
) {
488 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
491 assert_se(p
= strchr(f
, '@'));
492 assert_se(e
= strrchr(f
, '.'));
496 s
= new(char, a
+ 1 + strlen(e
) + 1);
500 strcpy(mempcpy(s
, f
, a
+ 1), e
);
506 int unit_name_from_path(const char *path
, const char *suffix
, char **ret
) {
507 _cleanup_free_
char *p
= NULL
;
515 if (!unit_suffix_is_valid(suffix
))
518 r
= unit_name_path_escape(path
, &p
);
522 s
= strappend(p
, suffix
);
530 int unit_name_from_path_instance(const char *prefix
, const char *path
, const char *suffix
, char **ret
) {
531 _cleanup_free_
char *p
= NULL
;
540 if (!unit_prefix_is_valid(prefix
))
543 if (!unit_suffix_is_valid(suffix
))
546 r
= unit_name_path_escape(path
, &p
);
550 s
= strjoin(prefix
, "@", p
, suffix
);
558 int unit_name_to_path(const char *name
, char **ret
) {
559 _cleanup_free_
char *prefix
= NULL
;
564 r
= unit_name_to_prefix(name
, &prefix
);
568 return unit_name_path_unescape(prefix
, ret
);
571 static bool do_escape_mangle(const char *f
, bool allow_globs
, char *t
) {
572 const char *valid_chars
;
573 bool mangled
= false;
578 /* We'll only escape the obvious characters here, to play safe.
580 * Returns true if any characters were mangled, false otherwise.
583 valid_chars
= allow_globs
? VALID_CHARS_GLOB
: VALID_CHARS_WITH_AT
;
589 } else if (!strchr(valid_chars
, *f
)) {
590 t
= do_escape_char(*f
, t
);
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.
604 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
606 int unit_name_mangle_with_suffix(const char *name
, UnitNameMangle flags
, const char *suffix
, char **ret
) {
615 if (isempty(name
)) /* We cannot mangle empty unit names to become valid, sorry. */
618 if (!unit_suffix_is_valid(suffix
))
621 /* Already a fully valid unit name? If so, no mangling is necessary... */
622 if (unit_name_is_valid(name
, UNIT_NAME_ANY
))
625 /* Already a fully valid globbing expression? If so, no mangling is necessary either... */
626 if ((flags
& UNIT_NAME_MANGLE_GLOB
) &&
627 string_is_glob(name
) &&
628 in_charset(name
, VALID_CHARS_GLOB
))
631 if (is_device_path(name
)) {
632 r
= unit_name_from_path(name
, ".device", ret
);
639 if (path_is_absolute(name
)) {
640 r
= unit_name_from_path(name
, ".mount", ret
);
647 s
= new(char, strlen(name
) * 4 + strlen(suffix
) + 1);
651 mangled
= do_escape_mangle(name
, flags
& UNIT_NAME_MANGLE_GLOB
, s
);
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?)",
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
659 if ((!(flags
& UNIT_NAME_MANGLE_GLOB
) || !string_is_glob(s
)) && unit_name_to_type(s
) < 0)
674 int slice_build_parent_slice(const char *slice
, char **ret
) {
681 if (!slice_name_is_valid(slice
))
684 if (streq(slice
, SPECIAL_ROOT_SLICE
)) {
693 dash
= strrchr(s
, '-');
695 strcpy(dash
, ".slice");
697 r
= free_and_strdup(&s
, SPECIAL_ROOT_SLICE
);
708 int slice_build_subslice(const char *slice
, const char*name
, char **ret
) {
715 if (!slice_name_is_valid(slice
))
718 if (!unit_prefix_is_valid(name
))
721 if (streq(slice
, SPECIAL_ROOT_SLICE
))
722 subslice
= strappend(name
, ".slice");
726 assert_se(e
= endswith(slice
, ".slice"));
728 subslice
= new(char, (e
- slice
) + 1 + strlen(name
) + 6 + 1);
732 stpcpy(stpcpy(stpcpy(mempcpy(subslice
, slice
, e
- slice
), "-"), name
), ".slice");
739 bool slice_name_is_valid(const char *name
) {
743 if (!unit_name_is_valid(name
, UNIT_NAME_PLAIN
))
746 if (streq(name
, SPECIAL_ROOT_SLICE
))
749 e
= endswith(name
, ".slice");
753 for (p
= name
; p
< e
; p
++) {
757 /* Don't allow initial dash */
761 /* Don't allow multiple dashes */
770 /* Don't allow trailing hash */