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 "special.h"
32 #include "string-util.h"
34 #include "unit-name.h"
36 /* Characters valid in a unit name. */
42 /* The same, but also permits the single @ character that may appear */
43 #define VALID_CHARS_WITH_AT \
47 /* All chars valid in a unit name glob */
48 #define VALID_CHARS_GLOB \
52 bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
53 const char *e, *i, *at;
55 assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
57 if (_unlikely_(flags == 0))
63 if (strlen(n) >= UNIT_NAME_MAX)
70 if (unit_type_from_string(e + 1) < 0)
73 for (i = n, at = NULL; i < e; i++) {
78 if (!strchr("@" VALID_CHARS, *i))
85 if (flags & UNIT_NAME_PLAIN)
89 if (flags & UNIT_NAME_INSTANCE)
93 if (flags & UNIT_NAME_TEMPLATE)
94 if (at && e == at + 1)
100 bool unit_prefix_is_valid(const char *p) {
102 /* We don't allow additional @ in the prefix string */
107 return in_charset(p, VALID_CHARS);
110 bool unit_instance_is_valid(const char *i) {
112 /* The max length depends on the length of the string, so we
113 * don't really check this here. */
118 /* We allow additional @ in the instance string, we do not
119 * allow them in the prefix! */
121 return in_charset(i, "@" VALID_CHARS);
124 bool unit_suffix_is_valid(const char *s) {
131 if (unit_type_from_string(s + 1) < 0)
137 #if 0 /// UNNEEDED by elogind
138 int unit_name_to_prefix(const char *n, char **ret) {
145 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
154 s = strndup(n, p - n);
162 int unit_name_to_instance(const char *n, char **instance) {
169 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
172 /* Everything past the first @ and before the last . is the instance */
193 int unit_name_to_prefix_and_instance(const char *n, char **ret) {
200 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
207 s = strndup(n, d - n);
216 UnitType unit_name_to_type(const char *n) {
221 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
222 return _UNIT_TYPE_INVALID;
224 assert_se(e = strrchr(n, '.'));
226 return unit_type_from_string(e + 1);
229 #if 0 /// UNNEEDED by elogind
230 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
238 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
241 if (!unit_suffix_is_valid(suffix))
244 assert_se(e = strrchr(n, '.'));
249 s = new(char, a + b + 1);
253 strcpy(mempcpy(s, n, a), suffix);
260 int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
267 if (!unit_prefix_is_valid(prefix))
270 if (instance && !unit_instance_is_valid(instance))
273 if (!unit_suffix_is_valid(suffix))
277 s = strappend(prefix, suffix);
279 s = strjoin(prefix, "@", instance, suffix);
287 #if 0 /// UNNEEDED by elogind
288 static char *do_escape_char(char c, char *t) {
293 *(t++) = hexchar(c >> 4);
299 static char *do_escape(const char *f, char *t) {
303 /* do not create units with a leading '.', like for "/.dotdir" mount points */
305 t = do_escape_char(*f, t);
312 else if (IN_SET(*f, '-', '\\') || !strchr(VALID_CHARS, *f))
313 t = do_escape_char(*f, t);
321 char *unit_name_escape(const char *f) {
326 r = new(char, strlen(f)*4+1);
336 int unit_name_unescape(const char *f, char **ret) {
337 _cleanup_free_ char *r = NULL;
346 for (t = r; *f; f++) {
349 else if (*f == '\\') {
363 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
377 int unit_name_path_escape(const char *f, char **ret) {
387 path_kill_slashes(p);
389 if (STR_IN_SET(p, "/", ""))
392 if (!path_is_normalized(p))
395 /* Truncate trailing slashes */
396 delete_trailing_chars(p, "/");
398 /* Truncate leading slashes */
399 p = skip_leading_chars(p, "/");
401 s = unit_name_escape(p);
410 int unit_name_path_unescape(const char *f, char **ret) {
426 r = unit_name_unescape(f, &w);
430 /* Don't accept trailing or leading slashes */
431 if (startswith(w, "/") || endswith(w, "/")) {
436 /* Prefix a slash again */
437 s = strappend("/", w);
442 if (!path_is_normalized(s)) {
456 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
465 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
467 if (!unit_instance_is_valid(i))
470 assert_se(p = strchr(f, '@'));
471 assert_se(e = strrchr(f, '.'));
476 s = new(char, a + 1 + b + strlen(e) + 1);
480 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
486 int unit_name_template(const char *f, char **ret) {
494 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
497 assert_se(p = strchr(f, '@'));
498 assert_se(e = strrchr(f, '.'));
502 s = new(char, a + 1 + strlen(e) + 1);
506 strcpy(mempcpy(s, f, a + 1), e);
512 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
513 _cleanup_free_ char *p = NULL;
521 if (!unit_suffix_is_valid(suffix))
524 r = unit_name_path_escape(path, &p);
528 s = strappend(p, suffix);
536 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
537 _cleanup_free_ char *p = NULL;
546 if (!unit_prefix_is_valid(prefix))
549 if (!unit_suffix_is_valid(suffix))
552 r = unit_name_path_escape(path, &p);
556 s = strjoin(prefix, "@", p, suffix);
564 int unit_name_to_path(const char *name, char **ret) {
565 _cleanup_free_ char *prefix = NULL;
570 r = unit_name_to_prefix(name, &prefix);
574 return unit_name_path_unescape(prefix, ret);
577 static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
578 const char *valid_chars;
581 assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
584 /* We'll only escape the obvious characters here, to play
587 valid_chars = allow_globs == UNIT_NAME_GLOB ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT;
592 else if (!strchr(valid_chars, *f))
593 t = do_escape_char(*f, t);
602 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
603 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
604 * except that @suffix is appended if a valid unit suffix is not present.
606 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
608 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
616 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
619 if (!unit_suffix_is_valid(suffix))
622 /* Already a fully valid unit name? If so, no mangling is necessary... */
623 if (unit_name_is_valid(name, UNIT_NAME_ANY))
626 /* Already a fully valid globbing expression? If so, no mangling is necessary either... */
627 if (allow_globs == UNIT_NAME_GLOB &&
628 string_is_glob(name) &&
629 in_charset(name, VALID_CHARS_GLOB))
632 if (is_device_path(name)) {
633 r = unit_name_from_path(name, ".device", ret);
640 if (path_is_absolute(name)) {
641 r = unit_name_from_path(name, ".mount", ret);
648 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
652 t = do_escape_mangle(name, allow_globs, s);
655 /* 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
657 if ((allow_globs != UNIT_NAME_GLOB || !string_is_glob(s)) && unit_name_to_type(s) < 0)
672 int slice_build_parent_slice(const char *slice, char **ret) {
679 if (!slice_name_is_valid(slice))
682 if (streq(slice, SPECIAL_ROOT_SLICE)) {
691 dash = strrchr(s, '-');
693 strcpy(dash, ".slice");
695 r = free_and_strdup(&s, SPECIAL_ROOT_SLICE);
707 int slice_build_subslice(const char *slice, const char*name, char **ret) {
714 if (!slice_name_is_valid(slice))
717 if (!unit_prefix_is_valid(name))
720 if (streq(slice, SPECIAL_ROOT_SLICE))
721 subslice = strappend(name, ".slice");
725 assert_se(e = endswith(slice, ".slice"));
727 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
731 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
738 bool slice_name_is_valid(const char *name) {
742 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
745 if (streq(name, SPECIAL_ROOT_SLICE))
748 e = endswith(name, ".slice");
752 for (p = name; p < e; p++) {
756 /* Don't allow initial dash */
760 /* Don't allow multiple dashes */
769 /* Don't allow trailing hash */
775 #if 0 /// UNNEEDED by elogind