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"
31 #include "string-util.h"
33 #include "unit-name.h"
35 /* Characters valid in a unit name. */
41 /* The same, but also permits the single @ character that may appear */
42 #define VALID_CHARS_WITH_AT \
46 /* All chars valid in a unit name glob */
47 #define VALID_CHARS_GLOB \
51 bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
52 const char *e, *i, *at;
54 assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
56 if (_unlikely_(flags == 0))
62 if (strlen(n) >= UNIT_NAME_MAX)
69 if (unit_type_from_string(e + 1) < 0)
72 for (i = n, at = NULL; i < e; i++) {
77 if (!strchr("@" VALID_CHARS, *i))
84 if (flags & UNIT_NAME_PLAIN)
88 if (flags & UNIT_NAME_INSTANCE)
92 if (flags & UNIT_NAME_TEMPLATE)
93 if (at && e == at + 1)
99 bool unit_prefix_is_valid(const char *p) {
101 /* We don't allow additional @ in the prefix string */
106 return in_charset(p, VALID_CHARS);
109 bool unit_instance_is_valid(const char *i) {
111 /* The max length depends on the length of the string, so we
112 * don't really check this here. */
117 /* We allow additional @ in the instance string, we do not
118 * allow them in the prefix! */
120 return in_charset(i, "@" VALID_CHARS);
123 bool unit_suffix_is_valid(const char *s) {
130 if (unit_type_from_string(s + 1) < 0)
136 #if 0 /// UNNEEDED by elogind
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);
215 UnitType unit_name_to_type(const char *n) {
220 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
221 return _UNIT_TYPE_INVALID;
223 assert_se(e = strrchr(n, '.'));
225 return unit_type_from_string(e + 1);
228 #if 0 /// UNNEEDED by elogind
229 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
237 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
240 if (!unit_suffix_is_valid(suffix))
243 assert_se(e = strrchr(n, '.'));
248 s = new(char, a + b + 1);
252 strcpy(mempcpy(s, n, a), suffix);
259 int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
266 if (!unit_prefix_is_valid(prefix))
269 if (instance && !unit_instance_is_valid(instance))
272 if (!unit_suffix_is_valid(suffix))
276 s = strappend(prefix, suffix);
278 s = strjoin(prefix, "@", instance, suffix);
286 #if 0 /// UNNEEDED by elogind
287 static char *do_escape_char(char c, char *t) {
292 *(t++) = hexchar(c >> 4);
298 static char *do_escape(const char *f, char *t) {
302 /* do not create units with a leading '.', like for "/.dotdir" mount points */
304 t = do_escape_char(*f, t);
311 else if (IN_SET(*f, '-', '\\') || !strchr(VALID_CHARS, *f))
312 t = do_escape_char(*f, t);
320 char *unit_name_escape(const char *f) {
325 r = new(char, strlen(f)*4+1);
335 int unit_name_unescape(const char *f, char **ret) {
336 _cleanup_free_ char *r = NULL;
345 for (t = r; *f; f++) {
348 else if (*f == '\\') {
362 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
376 int unit_name_path_escape(const char *f, char **ret) {
386 path_kill_slashes(p);
388 if (STR_IN_SET(p, "/", ""))
391 if (!path_is_normalized(p))
394 /* Truncate trailing slashes */
395 delete_trailing_chars(p, "/");
397 /* Truncate leading slashes */
398 p = skip_leading_chars(p, "/");
400 s = unit_name_escape(p);
409 int unit_name_path_unescape(const char *f, char **ret) {
425 r = unit_name_unescape(f, &w);
429 /* Don't accept trailing or leading slashes */
430 if (startswith(w, "/") || endswith(w, "/")) {
435 /* Prefix a slash again */
436 s = strappend("/", w);
441 if (!path_is_normalized(s)) {
455 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
464 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
466 if (!unit_instance_is_valid(i))
469 assert_se(p = strchr(f, '@'));
470 assert_se(e = strrchr(f, '.'));
475 s = new(char, a + 1 + b + strlen(e) + 1);
479 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
485 int unit_name_template(const char *f, char **ret) {
493 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
496 assert_se(p = strchr(f, '@'));
497 assert_se(e = strrchr(f, '.'));
501 s = new(char, a + 1 + strlen(e) + 1);
505 strcpy(mempcpy(s, f, a + 1), e);
511 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
512 _cleanup_free_ char *p = NULL;
520 if (!unit_suffix_is_valid(suffix))
523 r = unit_name_path_escape(path, &p);
527 s = strappend(p, suffix);
535 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
536 _cleanup_free_ char *p = NULL;
545 if (!unit_prefix_is_valid(prefix))
548 if (!unit_suffix_is_valid(suffix))
551 r = unit_name_path_escape(path, &p);
555 s = strjoin(prefix, "@", p, suffix);
563 int unit_name_to_path(const char *name, char **ret) {
564 _cleanup_free_ char *prefix = NULL;
569 r = unit_name_to_prefix(name, &prefix);
573 return unit_name_path_unescape(prefix, ret);
576 static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
577 const char *valid_chars;
580 assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
583 /* We'll only escape the obvious characters here, to play
586 valid_chars = allow_globs == UNIT_NAME_GLOB ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT;
591 else if (!strchr(valid_chars, *f))
592 t = do_escape_char(*f, t);
601 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
602 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
603 * except that @suffix is appended if a valid unit suffix is not present.
605 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
607 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, 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 (allow_globs == UNIT_NAME_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 t = do_escape_mangle(name, allow_globs, s);
654 /* 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
656 if ((allow_globs != UNIT_NAME_GLOB || !string_is_glob(s)) && unit_name_to_type(s) < 0)
671 int slice_build_parent_slice(const char *slice, char **ret) {
678 if (!slice_name_is_valid(slice))
681 if (streq(slice, "-.slice")) {
690 dash = strrchr(s, '-');
692 strcpy(dash, ".slice");
694 r = free_and_strdup(&s, "-.slice");
706 int slice_build_subslice(const char *slice, const char*name, char **ret) {
713 if (!slice_name_is_valid(slice))
716 if (!unit_prefix_is_valid(name))
719 if (streq(slice, "-.slice"))
720 subslice = strappend(name, ".slice");
724 assert_se(e = endswith(slice, ".slice"));
726 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
730 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
737 bool slice_name_is_valid(const char *name) {
741 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
744 if (streq(name, "-.slice"))
747 e = endswith(name, ".slice");
751 for (p = name; p < e; p++) {
755 /* Don't allow initial dash */
759 /* Don't allow multiple dashes */
768 /* Don't allow trailing hash */
774 #if 0 /// UNNEEDED by elogind