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) {
256 if (suffix[0] != '.')
259 type = unit_type_from_string(suffix + 1);
263 return unit_name_build_from_type(prefix, instance, type, ret);
266 int unit_name_build_from_type(const char *prefix, const char *instance, UnitType type, char **ret) {
272 assert(type < _UNIT_TYPE_MAX);
275 if (!unit_prefix_is_valid(prefix))
278 if (instance && !unit_instance_is_valid(instance))
281 ut = unit_type_to_string(type);
284 s = strjoin(prefix, ".", ut);
286 s = strjoin(prefix, "@", instance, ".", ut);
294 #if 0 /// UNNEEDED by elogind
295 static char *do_escape_char(char c, char *t) {
300 *(t++) = hexchar(c >> 4);
306 static char *do_escape(const char *f, char *t) {
310 /* do not create units with a leading '.', like for "/.dotdir" mount points */
312 t = do_escape_char(*f, t);
319 else if (IN_SET(*f, '-', '\\') || !strchr(VALID_CHARS, *f))
320 t = do_escape_char(*f, t);
328 char *unit_name_escape(const char *f) {
333 r = new(char, strlen(f)*4+1);
343 int unit_name_unescape(const char *f, char **ret) {
344 _cleanup_free_ char *r = NULL;
353 for (t = r; *f; f++) {
356 else if (*f == '\\') {
370 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
383 int unit_name_path_escape(const char *f, char **ret) {
393 path_kill_slashes(p);
395 if (STR_IN_SET(p, "/", ""))
398 if (!path_is_normalized(p))
401 /* Truncate trailing slashes */
402 delete_trailing_chars(p, "/");
404 /* Truncate leading slashes */
405 p = skip_leading_chars(p, "/");
407 s = unit_name_escape(p);
416 int unit_name_path_unescape(const char *f, char **ret) {
432 r = unit_name_unescape(f, &w);
436 /* Don't accept trailing or leading slashes */
437 if (startswith(w, "/") || endswith(w, "/")) {
442 /* Prefix a slash again */
443 s = strappend("/", w);
448 if (!path_is_normalized(s)) {
462 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
471 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
473 if (!unit_instance_is_valid(i))
476 assert_se(p = strchr(f, '@'));
477 assert_se(e = strrchr(f, '.'));
482 s = new(char, a + 1 + b + strlen(e) + 1);
486 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
492 int unit_name_template(const char *f, char **ret) {
500 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
503 assert_se(p = strchr(f, '@'));
504 assert_se(e = strrchr(f, '.'));
508 s = new(char, a + 1 + strlen(e) + 1);
512 strcpy(mempcpy(s, f, a + 1), e);
518 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
519 _cleanup_free_ char *p = NULL;
527 if (!unit_suffix_is_valid(suffix))
530 r = unit_name_path_escape(path, &p);
534 s = strappend(p, suffix);
542 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
543 _cleanup_free_ char *p = NULL;
552 if (!unit_prefix_is_valid(prefix))
555 if (!unit_suffix_is_valid(suffix))
558 r = unit_name_path_escape(path, &p);
562 s = strjoin(prefix, "@", p, suffix);
570 int unit_name_to_path(const char *name, char **ret) {
571 _cleanup_free_ char *prefix = NULL;
576 r = unit_name_to_prefix(name, &prefix);
580 return unit_name_path_unescape(prefix, ret);
583 static bool do_escape_mangle(const char *f, bool allow_globs, char *t) {
584 const char *valid_chars;
585 bool mangled = false;
590 /* We'll only escape the obvious characters here, to play safe.
592 * Returns true if any characters were mangled, false otherwise.
595 valid_chars = allow_globs ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT;
601 } else if (!strchr(valid_chars, *f)) {
602 t = do_escape_char(*f, t);
612 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
613 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
614 * except that @suffix is appended if a valid unit suffix is not present.
616 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
618 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle flags, const char *suffix, char **ret) {
627 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
630 if (!unit_suffix_is_valid(suffix))
633 /* Already a fully valid unit name? If so, no mangling is necessary... */
634 if (unit_name_is_valid(name, UNIT_NAME_ANY))
637 /* Already a fully valid globbing expression? If so, no mangling is necessary either... */
638 if ((flags & UNIT_NAME_MANGLE_GLOB) &&
639 string_is_glob(name) &&
640 in_charset(name, VALID_CHARS_GLOB))
643 if (is_device_path(name)) {
644 r = unit_name_from_path(name, ".device", ret);
651 if (path_is_absolute(name)) {
652 r = unit_name_from_path(name, ".mount", ret);
659 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
663 mangled = do_escape_mangle(name, flags & UNIT_NAME_MANGLE_GLOB, s);
665 log_full(flags & UNIT_NAME_MANGLE_WARN ? LOG_NOTICE : LOG_DEBUG,
666 "Invalid unit name \"%s\" was escaped as \"%s\" (maybe you should use systemd-escape?)",
669 /* 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
671 if ((!(flags & UNIT_NAME_MANGLE_GLOB) || !string_is_glob(s)) && unit_name_to_type(s) < 0)
686 int slice_build_parent_slice(const char *slice, char **ret) {
693 if (!slice_name_is_valid(slice))
696 if (streq(slice, SPECIAL_ROOT_SLICE)) {
705 dash = strrchr(s, '-');
707 strcpy(dash, ".slice");
709 r = free_and_strdup(&s, SPECIAL_ROOT_SLICE);
721 int slice_build_subslice(const char *slice, const char*name, char **ret) {
728 if (!slice_name_is_valid(slice))
731 if (!unit_prefix_is_valid(name))
734 if (streq(slice, SPECIAL_ROOT_SLICE))
735 subslice = strappend(name, ".slice");
739 assert_se(e = endswith(slice, ".slice"));
741 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
745 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
752 bool slice_name_is_valid(const char *name) {
756 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
759 if (streq(name, SPECIAL_ROOT_SLICE))
762 e = endswith(name, ".slice");
766 for (p = name; p < e; p++) {
770 /* Don't allow initial dash */
774 /* Don't allow multiple dashes */
783 /* Don't allow trailing hash */
789 #if 0 /// UNNEEDED by elogind