2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include "alloc-util.h"
27 #include "glob-util.h"
28 #include "hexdecoct.h"
29 #include "path-util.h"
30 #include "string-util.h"
32 #include "unit-name.h"
34 /* Characters valid in a unit name. */
40 /* The same, but also permits the single @ character that may appear */
41 #define VALID_CHARS_WITH_AT \
45 /* All chars valid in a unit name glob */
46 #define VALID_CHARS_GLOB \
50 bool unit_name_is_valid(const char *n
, UnitNameFlags flags
) {
51 const char *e
, *i
, *at
;
53 assert((flags
& ~(UNIT_NAME_PLAIN
|UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
)) == 0);
55 if (_unlikely_(flags
== 0))
61 if (strlen(n
) >= UNIT_NAME_MAX
)
68 if (unit_type_from_string(e
+ 1) < 0)
71 for (i
= n
, at
= NULL
; i
< e
; i
++) {
76 if (!strchr("@" VALID_CHARS
, *i
))
83 if (flags
& UNIT_NAME_PLAIN
)
87 if (flags
& UNIT_NAME_INSTANCE
)
91 if (flags
& UNIT_NAME_TEMPLATE
)
92 if (at
&& e
== at
+ 1)
98 bool unit_prefix_is_valid(const char *p
) {
100 /* We don't allow additional @ in the prefix string */
105 return in_charset(p
, VALID_CHARS
);
108 bool unit_instance_is_valid(const char *i
) {
110 /* The max length depends on the length of the string, so we
111 * don't really check this here. */
116 /* We allow additional @ in the instance string, we do not
117 * allow them in the prefix! */
119 return in_charset(i
, "@" VALID_CHARS
);
122 bool unit_suffix_is_valid(const char *s
) {
129 if (unit_type_from_string(s
+ 1) < 0)
135 int unit_name_to_prefix(const char *n
, char **ret
) {
142 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
151 s
= strndup(n
, p
- n
);
159 int unit_name_to_instance(const char *n
, char **instance
) {
166 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
169 /* Everything past the first @ and before the last . is the instance */
190 int unit_name_to_prefix_and_instance(const char *n
, char **ret
) {
197 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
204 s
= strndup(n
, d
- n
);
212 UnitType
unit_name_to_type(const char *n
) {
217 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
218 return _UNIT_TYPE_INVALID
;
220 assert_se(e
= strrchr(n
, '.'));
222 return unit_type_from_string(e
+ 1);
225 int unit_name_change_suffix(const char *n
, const char *suffix
, char **ret
) {
233 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
236 if (!unit_suffix_is_valid(suffix
))
239 assert_se(e
= strrchr(n
, '.'));
244 s
= new(char, a
+ b
+ 1);
248 strcpy(mempcpy(s
, n
, a
), suffix
);
254 int unit_name_build(const char *prefix
, const char *instance
, const char *suffix
, char **ret
) {
261 if (!unit_prefix_is_valid(prefix
))
264 if (instance
&& !unit_instance_is_valid(instance
))
267 if (!unit_suffix_is_valid(suffix
))
271 s
= strappend(prefix
, suffix
);
273 s
= strjoin(prefix
, "@", instance
, suffix
);
281 static char *do_escape_char(char c
, char *t
) {
286 *(t
++) = hexchar(c
>> 4);
292 static char *do_escape(const char *f
, char *t
) {
296 /* do not create units with a leading '.', like for "/.dotdir" mount points */
298 t
= do_escape_char(*f
, t
);
305 else if (IN_SET(*f
, '-', '\\') || !strchr(VALID_CHARS
, *f
))
306 t
= do_escape_char(*f
, t
);
314 char *unit_name_escape(const char *f
) {
319 r
= new(char, strlen(f
)*4+1);
329 int unit_name_unescape(const char *f
, char **ret
) {
330 _cleanup_free_
char *r
= NULL
;
339 for (t
= r
; *f
; f
++) {
342 else if (*f
== '\\') {
356 *(t
++) = (char) (((uint8_t) a
<< 4U) | (uint8_t) b
);
370 int unit_name_path_escape(const char *f
, char **ret
) {
380 path_kill_slashes(p
);
382 if (STR_IN_SET(p
, "/", ""))
385 if (!path_is_normalized(p
))
388 /* Truncate trailing slashes */
389 delete_trailing_chars(p
, "/");
391 /* Truncate leading slashes */
392 p
= skip_leading_chars(p
, "/");
394 s
= unit_name_escape(p
);
403 int unit_name_path_unescape(const char *f
, char **ret
) {
419 r
= unit_name_unescape(f
, &w
);
423 /* Don't accept trailing or leading slashes */
424 if (startswith(w
, "/") || endswith(w
, "/")) {
429 /* Prefix a slash again */
430 s
= strappend("/", w
);
435 if (!path_is_normalized(s
)) {
449 int unit_name_replace_instance(const char *f
, const char *i
, char **ret
) {
458 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
460 if (!unit_instance_is_valid(i
))
463 assert_se(p
= strchr(f
, '@'));
464 assert_se(e
= strrchr(f
, '.'));
469 s
= new(char, a
+ 1 + b
+ strlen(e
) + 1);
473 strcpy(mempcpy(mempcpy(s
, f
, a
+ 1), i
, b
), e
);
479 int unit_name_template(const char *f
, char **ret
) {
487 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
490 assert_se(p
= strchr(f
, '@'));
491 assert_se(e
= strrchr(f
, '.'));
495 s
= new(char, a
+ 1 + strlen(e
) + 1);
499 strcpy(mempcpy(s
, f
, a
+ 1), e
);
505 int unit_name_from_path(const char *path
, const char *suffix
, char **ret
) {
506 _cleanup_free_
char *p
= NULL
;
514 if (!unit_suffix_is_valid(suffix
))
517 r
= unit_name_path_escape(path
, &p
);
521 s
= strappend(p
, suffix
);
529 int unit_name_from_path_instance(const char *prefix
, const char *path
, const char *suffix
, char **ret
) {
530 _cleanup_free_
char *p
= NULL
;
539 if (!unit_prefix_is_valid(prefix
))
542 if (!unit_suffix_is_valid(suffix
))
545 r
= unit_name_path_escape(path
, &p
);
549 s
= strjoin(prefix
, "@", p
, suffix
);
557 int unit_name_to_path(const char *name
, char **ret
) {
558 _cleanup_free_
char *prefix
= NULL
;
563 r
= unit_name_to_prefix(name
, &prefix
);
567 return unit_name_path_unescape(prefix
, ret
);
570 static char *do_escape_mangle(const char *f
, UnitNameMangle allow_globs
, char *t
) {
571 const char *valid_chars
;
574 assert(IN_SET(allow_globs
, UNIT_NAME_GLOB
, UNIT_NAME_NOGLOB
));
577 /* We'll only escape the obvious characters here, to play
580 valid_chars
= allow_globs
== UNIT_NAME_GLOB
? VALID_CHARS_GLOB
: VALID_CHARS_WITH_AT
;
585 else if (!strchr(valid_chars
, *f
))
586 t
= do_escape_char(*f
, t
);
595 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
596 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
597 * except that @suffix is appended if a valid unit suffix is not present.
599 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
601 int unit_name_mangle_with_suffix(const char *name
, UnitNameMangle allow_globs
, const char *suffix
, char **ret
) {
609 if (isempty(name
)) /* We cannot mangle empty unit names to become valid, sorry. */
612 if (!unit_suffix_is_valid(suffix
))
615 /* Already a fully valid unit name? If so, no mangling is necessary... */
616 if (unit_name_is_valid(name
, UNIT_NAME_ANY
))
619 /* Already a fully valid globbing expression? If so, no mangling is necessary either... */
620 if (allow_globs
== UNIT_NAME_GLOB
&&
621 string_is_glob(name
) &&
622 in_charset(name
, VALID_CHARS_GLOB
))
625 if (is_device_path(name
)) {
626 r
= unit_name_from_path(name
, ".device", ret
);
633 if (path_is_absolute(name
)) {
634 r
= unit_name_from_path(name
, ".mount", ret
);
641 s
= new(char, strlen(name
) * 4 + strlen(suffix
) + 1);
645 t
= do_escape_mangle(name
, allow_globs
, s
);
648 /* 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
650 if ((allow_globs
!= UNIT_NAME_GLOB
|| !string_is_glob(s
)) && unit_name_to_type(s
) < 0)
665 int slice_build_parent_slice(const char *slice
, char **ret
) {
672 if (!slice_name_is_valid(slice
))
675 if (streq(slice
, "-.slice")) {
684 dash
= strrchr(s
, '-');
686 strcpy(dash
, ".slice");
688 r
= free_and_strdup(&s
, "-.slice");
699 int slice_build_subslice(const char *slice
, const char*name
, char **ret
) {
706 if (!slice_name_is_valid(slice
))
709 if (!unit_prefix_is_valid(name
))
712 if (streq(slice
, "-.slice"))
713 subslice
= strappend(name
, ".slice");
717 assert_se(e
= endswith(slice
, ".slice"));
719 subslice
= new(char, (e
- slice
) + 1 + strlen(name
) + 6 + 1);
723 stpcpy(stpcpy(stpcpy(mempcpy(subslice
, slice
, e
- slice
), "-"), name
), ".slice");
730 bool slice_name_is_valid(const char *name
) {
734 if (!unit_name_is_valid(name
, UNIT_NAME_PLAIN
))
737 if (streq(name
, "-.slice"))
740 e
= endswith(name
, ".slice");
744 for (p
= name
; p
< e
; p
++) {
748 /* Don't allow initial dash */
752 /* Don't allow multiple dashes */
761 /* Don't allow trailing hash */