1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
14 #include "alloc-util.h"
15 //#include "glob-util.h"
16 #include "hexdecoct.h"
17 #include "path-util.h"
19 #include "string-util.h"
21 #include "unit-name.h"
23 /* Characters valid in a unit name. */
29 /* The same, but also permits the single @ character that may appear */
30 #define VALID_CHARS_WITH_AT \
34 /* All chars valid in a unit name glob */
35 #define VALID_CHARS_GLOB \
39 bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
40 const char *e, *i, *at;
42 assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
44 if (_unlikely_(flags == 0))
50 if (strlen(n) >= UNIT_NAME_MAX)
57 if (unit_type_from_string(e + 1) < 0)
60 for (i = n, at = NULL; i < e; i++) {
65 if (!strchr("@" VALID_CHARS, *i))
72 if (flags & UNIT_NAME_PLAIN)
76 if (flags & UNIT_NAME_INSTANCE)
80 if (flags & UNIT_NAME_TEMPLATE)
81 if (at && e == at + 1)
87 bool unit_prefix_is_valid(const char *p) {
89 /* We don't allow additional @ in the prefix string */
94 return in_charset(p, VALID_CHARS);
97 bool unit_instance_is_valid(const char *i) {
99 /* The max length depends on the length of the string, so we
100 * don't really check this here. */
105 /* We allow additional @ in the instance string, we do not
106 * allow them in the prefix! */
108 return in_charset(i, "@" VALID_CHARS);
111 bool unit_suffix_is_valid(const char *s) {
118 if (unit_type_from_string(s + 1) < 0)
124 #if 0 /// UNNEEDED by elogind
125 int unit_name_to_prefix(const char *n, char **ret) {
132 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
141 s = strndup(n, p - n);
149 int unit_name_to_instance(const char *n, char **instance) {
156 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
159 /* Everything past the first @ and before the last . is the instance */
180 int unit_name_to_prefix_and_instance(const char *n, char **ret) {
187 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
194 s = strndup(n, d - n);
203 UnitType unit_name_to_type(const char *n) {
208 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
209 return _UNIT_TYPE_INVALID;
211 assert_se(e = strrchr(n, '.'));
213 return unit_type_from_string(e + 1);
216 #if 0 /// UNNEEDED by elogind
217 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
225 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
228 if (!unit_suffix_is_valid(suffix))
231 assert_se(e = strrchr(n, '.'));
236 s = new(char, a + b + 1);
240 strcpy(mempcpy(s, n, a), suffix);
247 int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
254 if (!unit_prefix_is_valid(prefix))
257 if (instance && !unit_instance_is_valid(instance))
260 if (!unit_suffix_is_valid(suffix))
264 s = strappend(prefix, suffix);
266 s = strjoin(prefix, "@", instance, suffix);
274 #if 0 /// UNNEEDED by elogind
275 static char *do_escape_char(char c, char *t) {
280 *(t++) = hexchar(c >> 4);
286 static char *do_escape(const char *f, char *t) {
290 /* do not create units with a leading '.', like for "/.dotdir" mount points */
292 t = do_escape_char(*f, t);
299 else if (IN_SET(*f, '-', '\\') || !strchr(VALID_CHARS, *f))
300 t = do_escape_char(*f, t);
308 char *unit_name_escape(const char *f) {
313 r = new(char, strlen(f)*4+1);
323 int unit_name_unescape(const char *f, char **ret) {
324 _cleanup_free_ char *r = NULL;
333 for (t = r; *f; f++) {
336 else if (*f == '\\') {
350 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
363 int unit_name_path_escape(const char *f, char **ret) {
373 path_kill_slashes(p);
375 if (STR_IN_SET(p, "/", ""))
378 if (!path_is_normalized(p))
381 /* Truncate trailing slashes */
382 delete_trailing_chars(p, "/");
384 /* Truncate leading slashes */
385 p = skip_leading_chars(p, "/");
387 s = unit_name_escape(p);
396 int unit_name_path_unescape(const char *f, char **ret) {
412 r = unit_name_unescape(f, &w);
416 /* Don't accept trailing or leading slashes */
417 if (startswith(w, "/") || endswith(w, "/")) {
422 /* Prefix a slash again */
423 s = strappend("/", w);
428 if (!path_is_normalized(s)) {
442 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
451 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
453 if (!unit_instance_is_valid(i))
456 assert_se(p = strchr(f, '@'));
457 assert_se(e = strrchr(f, '.'));
462 s = new(char, a + 1 + b + strlen(e) + 1);
466 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
472 int unit_name_template(const char *f, char **ret) {
480 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
483 assert_se(p = strchr(f, '@'));
484 assert_se(e = strrchr(f, '.'));
488 s = new(char, a + 1 + strlen(e) + 1);
492 strcpy(mempcpy(s, f, a + 1), e);
498 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
499 _cleanup_free_ char *p = NULL;
507 if (!unit_suffix_is_valid(suffix))
510 r = unit_name_path_escape(path, &p);
514 s = strappend(p, suffix);
522 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
523 _cleanup_free_ char *p = NULL;
532 if (!unit_prefix_is_valid(prefix))
535 if (!unit_suffix_is_valid(suffix))
538 r = unit_name_path_escape(path, &p);
542 s = strjoin(prefix, "@", p, suffix);
550 int unit_name_to_path(const char *name, char **ret) {
551 _cleanup_free_ char *prefix = NULL;
556 r = unit_name_to_prefix(name, &prefix);
560 return unit_name_path_unescape(prefix, ret);
563 static bool do_escape_mangle(const char *f, bool allow_globs, char *t) {
564 const char *valid_chars;
565 bool mangled = false;
570 /* We'll only escape the obvious characters here, to play safe.
572 * Returns true if any characters were mangled, false otherwise.
575 valid_chars = allow_globs ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT;
581 } else if (!strchr(valid_chars, *f)) {
582 t = do_escape_char(*f, t);
592 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
593 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
594 * except that @suffix is appended if a valid unit suffix is not present.
596 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
598 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle flags, const char *suffix, char **ret) {
607 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
610 if (!unit_suffix_is_valid(suffix))
613 /* Already a fully valid unit name? If so, no mangling is necessary... */
614 if (unit_name_is_valid(name, UNIT_NAME_ANY))
617 /* Already a fully valid globbing expression? If so, no mangling is necessary either... */
618 if ((flags & UNIT_NAME_MANGLE_GLOB) &&
619 string_is_glob(name) &&
620 in_charset(name, VALID_CHARS_GLOB))
623 if (is_device_path(name)) {
624 r = unit_name_from_path(name, ".device", ret);
631 if (path_is_absolute(name)) {
632 r = unit_name_from_path(name, ".mount", ret);
639 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
643 mangled = do_escape_mangle(name, flags & UNIT_NAME_MANGLE_GLOB, s);
645 log_full(flags & UNIT_NAME_MANGLE_WARN ? LOG_NOTICE : LOG_DEBUG,
646 "Invalid unit name \"%s\" was escaped as \"%s\" (maybe you should use systemd-escape?)",
649 /* 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
651 if ((!(flags & UNIT_NAME_MANGLE_GLOB) || !string_is_glob(s)) && unit_name_to_type(s) < 0)
666 int slice_build_parent_slice(const char *slice, char **ret) {
673 if (!slice_name_is_valid(slice))
676 if (streq(slice, SPECIAL_ROOT_SLICE)) {
685 dash = strrchr(s, '-');
687 strcpy(dash, ".slice");
689 r = free_and_strdup(&s, SPECIAL_ROOT_SLICE);
701 int slice_build_subslice(const char *slice, const char*name, char **ret) {
708 if (!slice_name_is_valid(slice))
711 if (!unit_prefix_is_valid(name))
714 if (streq(slice, SPECIAL_ROOT_SLICE))
715 subslice = strappend(name, ".slice");
719 assert_se(e = endswith(slice, ".slice"));
721 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
725 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
732 bool slice_name_is_valid(const char *name) {
736 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
739 if (streq(name, SPECIAL_ROOT_SLICE))
742 e = endswith(name, ".slice");
746 for (p = name; p < e; p++) {
750 /* Don't allow initial dash */
754 /* Don't allow multiple dashes */
763 /* Don't allow trailing hash */
769 #if 0 /// UNNEEDED by elogind