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 (suffix[0] != '.')
257 type = unit_type_from_string(suffix + 1);
261 return unit_name_build_from_type(prefix, instance, type, ret);
264 int unit_name_build_from_type(const char *prefix, const char *instance, UnitType type, char **ret) {
270 assert(type < _UNIT_TYPE_MAX);
273 if (!unit_prefix_is_valid(prefix))
276 if (instance && !unit_instance_is_valid(instance))
279 ut = unit_type_to_string(type);
282 s = strjoin(prefix, ".", ut);
284 s = strjoin(prefix, "@", instance, ".", ut);
292 #if 0 /// UNNEEDED by elogind
293 static char *do_escape_char(char c, char *t) {
298 *(t++) = hexchar(c >> 4);
304 static char *do_escape(const char *f, char *t) {
308 /* do not create units with a leading '.', like for "/.dotdir" mount points */
310 t = do_escape_char(*f, t);
317 else if (IN_SET(*f, '-', '\\') || !strchr(VALID_CHARS, *f))
318 t = do_escape_char(*f, t);
326 char *unit_name_escape(const char *f) {
331 r = new(char, strlen(f)*4+1);
341 int unit_name_unescape(const char *f, char **ret) {
342 _cleanup_free_ char *r = NULL;
351 for (t = r; *f; f++) {
354 else if (*f == '\\') {
368 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
381 int unit_name_path_escape(const char *f, char **ret) {
391 path_simplify(p, false);
393 if (empty_or_root(p))
396 if (!path_is_normalized(p))
399 /* Truncate trailing slashes */
400 delete_trailing_chars(p, "/");
402 /* Truncate leading slashes */
403 p = skip_leading_chars(p, "/");
405 s = unit_name_escape(p);
414 int unit_name_path_unescape(const char *f, char **ret) {
430 r = unit_name_unescape(f, &w);
434 /* Don't accept trailing or leading slashes */
435 if (startswith(w, "/") || endswith(w, "/")) {
440 /* Prefix a slash again */
441 s = strappend("/", w);
446 if (!path_is_normalized(s)) {
460 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
469 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
471 if (!unit_instance_is_valid(i))
474 assert_se(p = strchr(f, '@'));
475 assert_se(e = strrchr(f, '.'));
480 s = new(char, a + 1 + b + strlen(e) + 1);
484 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
490 int unit_name_template(const char *f, char **ret) {
498 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
501 assert_se(p = strchr(f, '@'));
502 assert_se(e = strrchr(f, '.'));
506 s = new(char, a + 1 + strlen(e) + 1);
510 strcpy(mempcpy(s, f, a + 1), e);
516 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
517 _cleanup_free_ char *p = NULL;
525 if (!unit_suffix_is_valid(suffix))
528 r = unit_name_path_escape(path, &p);
532 s = strappend(p, suffix);
540 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
541 _cleanup_free_ char *p = NULL;
550 if (!unit_prefix_is_valid(prefix))
553 if (!unit_suffix_is_valid(suffix))
556 r = unit_name_path_escape(path, &p);
560 s = strjoin(prefix, "@", p, suffix);
568 int unit_name_to_path(const char *name, char **ret) {
569 _cleanup_free_ char *prefix = NULL;
574 r = unit_name_to_prefix(name, &prefix);
578 return unit_name_path_unescape(prefix, ret);
581 static bool do_escape_mangle(const char *f, bool allow_globs, char *t) {
582 const char *valid_chars;
583 bool mangled = false;
588 /* We'll only escape the obvious characters here, to play safe.
590 * Returns true if any characters were mangled, false otherwise.
593 valid_chars = allow_globs ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT;
599 } else if (!strchr(valid_chars, *f)) {
600 t = do_escape_char(*f, t);
610 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
611 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
612 * except that @suffix is appended if a valid unit suffix is not present.
614 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
616 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle flags, const char *suffix, char **ret) {
625 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
628 if (!unit_suffix_is_valid(suffix))
631 /* Already a fully valid unit name? If so, no mangling is necessary... */
632 if (unit_name_is_valid(name, UNIT_NAME_ANY))
635 /* Already a fully valid globbing expression? If so, no mangling is necessary either... */
636 if ((flags & UNIT_NAME_MANGLE_GLOB) &&
637 string_is_glob(name) &&
638 in_charset(name, VALID_CHARS_GLOB))
641 if (is_device_path(name)) {
642 r = unit_name_from_path(name, ".device", ret);
649 if (path_is_absolute(name)) {
650 r = unit_name_from_path(name, ".mount", ret);
657 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
661 mangled = do_escape_mangle(name, flags & UNIT_NAME_MANGLE_GLOB, s);
663 log_full(flags & UNIT_NAME_MANGLE_WARN ? LOG_NOTICE : LOG_DEBUG,
664 "Invalid unit name \"%s\" was escaped as \"%s\" (maybe you should use systemd-escape?)",
667 /* 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
669 if ((!(flags & UNIT_NAME_MANGLE_GLOB) || !string_is_glob(s)) && unit_name_to_type(s) < 0)
684 int slice_build_parent_slice(const char *slice, char **ret) {
691 if (!slice_name_is_valid(slice))
694 if (streq(slice, SPECIAL_ROOT_SLICE)) {
703 dash = strrchr(s, '-');
705 strcpy(dash, ".slice");
707 r = free_and_strdup(&s, SPECIAL_ROOT_SLICE);
719 int slice_build_subslice(const char *slice, const char *name, char **ret) {
726 if (!slice_name_is_valid(slice))
729 if (!unit_prefix_is_valid(name))
732 if (streq(slice, SPECIAL_ROOT_SLICE))
733 subslice = strappend(name, ".slice");
737 assert_se(e = endswith(slice, ".slice"));
739 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
743 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
750 bool slice_name_is_valid(const char *name) {
754 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
757 if (streq(name, SPECIAL_ROOT_SLICE))
760 e = endswith(name, ".slice");
764 for (p = name; p < e; p++) {
768 /* Don't allow initial dash */
772 /* Don't allow multiple dashes */
781 /* Don't allow trailing hash */
787 #if 0 /// UNNEEDED by elogind