1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2010 Lennart Poettering
12 #include "alloc-util.h"
13 //#include "glob-util.h"
14 #include "hexdecoct.h"
15 #include "path-util.h"
17 #include "string-util.h"
19 #include "unit-name.h"
21 /* Characters valid in a unit name. */
27 /* The same, but also permits the single @ character that may appear */
28 #define VALID_CHARS_WITH_AT \
32 /* All chars valid in a unit name glob */
33 #define VALID_CHARS_GLOB \
37 bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
38 const char *e, *i, *at;
40 assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
42 if (_unlikely_(flags == 0))
48 if (strlen(n) >= UNIT_NAME_MAX)
55 if (unit_type_from_string(e + 1) < 0)
58 for (i = n, at = NULL; i < e; i++) {
63 if (!strchr("@" VALID_CHARS, *i))
70 if (flags & UNIT_NAME_PLAIN)
74 if (flags & UNIT_NAME_INSTANCE)
78 if (flags & UNIT_NAME_TEMPLATE)
79 if (at && e == at + 1)
85 bool unit_prefix_is_valid(const char *p) {
87 /* We don't allow additional @ in the prefix string */
92 return in_charset(p, VALID_CHARS);
95 bool unit_instance_is_valid(const char *i) {
97 /* The max length depends on the length of the string, so we
98 * don't really check this here. */
103 /* We allow additional @ in the instance string, we do not
104 * allow them in the prefix! */
106 return in_charset(i, "@" VALID_CHARS);
109 bool unit_suffix_is_valid(const char *s) {
116 if (unit_type_from_string(s + 1) < 0)
122 #if 0 /// UNNEEDED by elogind
123 int unit_name_to_prefix(const char *n, char **ret) {
130 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
139 s = strndup(n, p - n);
147 int unit_name_to_instance(const char *n, char **instance) {
154 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
157 /* Everything past the first @ and before the last . is the instance */
178 int unit_name_to_prefix_and_instance(const char *n, char **ret) {
185 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
192 s = strndup(n, d - n);
201 UnitType unit_name_to_type(const char *n) {
206 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
207 return _UNIT_TYPE_INVALID;
209 assert_se(e = strrchr(n, '.'));
211 return unit_type_from_string(e + 1);
214 #if 0 /// UNNEEDED by elogind
215 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
223 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
226 if (!unit_suffix_is_valid(suffix))
229 assert_se(e = strrchr(n, '.'));
234 s = new(char, a + b + 1);
238 strcpy(mempcpy(s, n, a), suffix);
245 int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
252 if (suffix[0] != '.')
255 type = unit_type_from_string(suffix + 1);
259 return unit_name_build_from_type(prefix, instance, type, ret);
262 int unit_name_build_from_type(const char *prefix, const char *instance, UnitType type, char **ret) {
268 assert(type < _UNIT_TYPE_MAX);
271 if (!unit_prefix_is_valid(prefix))
274 if (instance && !unit_instance_is_valid(instance))
277 ut = unit_type_to_string(type);
280 s = strjoin(prefix, ".", ut);
282 s = strjoin(prefix, "@", instance, ".", ut);
290 #if 0 /// UNNEEDED by elogind
291 static char *do_escape_char(char c, char *t) {
296 *(t++) = hexchar(c >> 4);
302 static char *do_escape(const char *f, char *t) {
306 /* do not create units with a leading '.', like for "/.dotdir" mount points */
308 t = do_escape_char(*f, t);
315 else if (IN_SET(*f, '-', '\\') || !strchr(VALID_CHARS, *f))
316 t = do_escape_char(*f, t);
324 char *unit_name_escape(const char *f) {
329 r = new(char, strlen(f)*4+1);
339 int unit_name_unescape(const char *f, char **ret) {
340 _cleanup_free_ char *r = NULL;
349 for (t = r; *f; f++) {
352 else if (*f == '\\') {
366 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
379 int unit_name_path_escape(const char *f, char **ret) {
389 path_simplify(p, false);
391 if (empty_or_root(p))
394 if (!path_is_normalized(p))
397 /* Truncate trailing slashes */
398 delete_trailing_chars(p, "/");
400 /* Truncate leading slashes */
401 p = skip_leading_chars(p, "/");
403 s = unit_name_escape(p);
412 int unit_name_path_unescape(const char *f, char **ret) {
428 r = unit_name_unescape(f, &w);
432 /* Don't accept trailing or leading slashes */
433 if (startswith(w, "/") || endswith(w, "/")) {
438 /* Prefix a slash again */
439 s = strappend("/", w);
444 if (!path_is_normalized(s)) {
458 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
467 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
469 if (!unit_instance_is_valid(i))
472 assert_se(p = strchr(f, '@'));
473 assert_se(e = strrchr(f, '.'));
478 s = new(char, a + 1 + b + strlen(e) + 1);
482 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
488 int unit_name_template(const char *f, char **ret) {
496 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
499 assert_se(p = strchr(f, '@'));
500 assert_se(e = strrchr(f, '.'));
504 s = new(char, a + 1 + strlen(e) + 1);
508 strcpy(mempcpy(s, f, a + 1), e);
514 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
515 _cleanup_free_ char *p = NULL;
523 if (!unit_suffix_is_valid(suffix))
526 r = unit_name_path_escape(path, &p);
530 s = strappend(p, suffix);
538 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
539 _cleanup_free_ char *p = NULL;
548 if (!unit_prefix_is_valid(prefix))
551 if (!unit_suffix_is_valid(suffix))
554 r = unit_name_path_escape(path, &p);
558 s = strjoin(prefix, "@", p, suffix);
566 int unit_name_to_path(const char *name, char **ret) {
567 _cleanup_free_ char *prefix = NULL;
572 r = unit_name_to_prefix(name, &prefix);
576 return unit_name_path_unescape(prefix, ret);
579 static bool do_escape_mangle(const char *f, bool allow_globs, char *t) {
580 const char *valid_chars;
581 bool mangled = false;
586 /* We'll only escape the obvious characters here, to play safe.
588 * Returns true if any characters were mangled, false otherwise.
591 valid_chars = allow_globs ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT;
597 } else if (!strchr(valid_chars, *f)) {
598 t = do_escape_char(*f, t);
608 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
609 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
610 * except that @suffix is appended if a valid unit suffix is not present.
612 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
614 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle flags, const char *suffix, char **ret) {
623 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
626 if (!unit_suffix_is_valid(suffix))
629 /* Already a fully valid unit name? If so, no mangling is necessary... */
630 if (unit_name_is_valid(name, UNIT_NAME_ANY))
633 /* Already a fully valid globbing expression? If so, no mangling is necessary either... */
634 if ((flags & UNIT_NAME_MANGLE_GLOB) &&
635 string_is_glob(name) &&
636 in_charset(name, VALID_CHARS_GLOB))
639 if (is_device_path(name)) {
640 r = unit_name_from_path(name, ".device", ret);
647 if (path_is_absolute(name)) {
648 r = unit_name_from_path(name, ".mount", ret);
655 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
659 mangled = do_escape_mangle(name, flags & UNIT_NAME_MANGLE_GLOB, s);
661 log_full(flags & UNIT_NAME_MANGLE_WARN ? LOG_NOTICE : LOG_DEBUG,
662 "Invalid unit name \"%s\" was escaped as \"%s\" (maybe you should use systemd-escape?)",
665 /* 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
667 if ((!(flags & UNIT_NAME_MANGLE_GLOB) || !string_is_glob(s)) && unit_name_to_type(s) < 0)
682 int slice_build_parent_slice(const char *slice, char **ret) {
689 if (!slice_name_is_valid(slice))
692 if (streq(slice, SPECIAL_ROOT_SLICE)) {
701 dash = strrchr(s, '-');
703 strcpy(dash, ".slice");
705 r = free_and_strdup(&s, SPECIAL_ROOT_SLICE);
717 int slice_build_subslice(const char *slice, const char *name, char **ret) {
724 if (!slice_name_is_valid(slice))
727 if (!unit_prefix_is_valid(name))
730 if (streq(slice, SPECIAL_ROOT_SLICE))
731 subslice = strappend(name, ".slice");
735 assert_se(e = endswith(slice, ".slice"));
737 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
741 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
748 bool slice_name_is_valid(const char *name) {
752 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
755 if (streq(name, SPECIAL_ROOT_SLICE))
758 e = endswith(name, ".slice");
762 for (p = name; p < e; p++) {
766 /* Don't allow initial dash */
770 /* Don't allow multiple dashes */
779 /* Don't allow trailing hash */
785 #if 0 /// UNNEEDED by elogind