1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include "path-util.h"
26 #include "bus-label.h"
28 #include "unit-name.h"
36 bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
37 const char *e, *i, *at;
39 assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
41 if (_unlikely_(flags == 0))
47 if (strlen(n) >= UNIT_NAME_MAX)
54 if (unit_type_from_string(e + 1) < 0)
57 for (i = n, at = NULL; i < e; i++) {
62 if (!strchr("@" VALID_CHARS, *i))
69 if (flags & UNIT_NAME_PLAIN)
73 if (flags & UNIT_NAME_INSTANCE)
77 if (flags & UNIT_NAME_TEMPLATE)
78 if (at && e == at + 1)
84 bool unit_prefix_is_valid(const char *p) {
86 /* We don't allow additional @ in the prefix string */
91 return in_charset(p, VALID_CHARS);
94 bool unit_instance_is_valid(const char *i) {
96 /* The max length depends on the length of the string, so we
97 * don't really check this here. */
102 /* We allow additional @ in the instance string, we do not
103 * allow them in the prefix! */
105 return in_charset(i, "@" VALID_CHARS);
108 bool unit_suffix_is_valid(const char *s) {
115 if (unit_type_from_string(s + 1) < 0)
121 int unit_name_to_prefix(const char *n, char **ret) {
128 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
137 s = strndup(n, p - n);
145 int unit_name_to_instance(const char *n, char **instance) {
152 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
155 /* Everything past the first @ and before the last . is the instance */
176 int unit_name_to_prefix_and_instance(const char *n, char **ret) {
183 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
190 s = strndup(n, d - n);
198 UnitType unit_name_to_type(const char *n) {
203 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
204 return _UNIT_TYPE_INVALID;
206 assert_se(e = strrchr(n, '.'));
208 return unit_type_from_string(e + 1);
211 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
219 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
222 if (!unit_suffix_is_valid(suffix))
225 assert_se(e = strrchr(n, '.'));
230 s = new(char, a + b + 1);
234 strcpy(mempcpy(s, n, a), suffix);
240 int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
247 if (!unit_prefix_is_valid(prefix))
250 if (instance && !unit_instance_is_valid(instance))
253 if (!unit_suffix_is_valid(suffix))
257 s = strappend(prefix, suffix);
259 s = strjoin(prefix, "@", instance, suffix, NULL);
267 static char *do_escape_char(char c, char *t) {
272 *(t++) = hexchar(c >> 4);
278 static char *do_escape(const char *f, char *t) {
282 /* do not create units with a leading '.', like for "/.dotdir" mount points */
284 t = do_escape_char(*f, t);
291 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
292 t = do_escape_char(*f, t);
300 char *unit_name_escape(const char *f) {
305 r = new(char, strlen(f)*4+1);
315 int unit_name_unescape(const char *f, char **ret) {
316 _cleanup_free_ char *r = NULL;
325 for (t = r; *f; f++) {
328 else if (*f == '\\') {
342 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
356 int unit_name_path_escape(const char *f, char **ret) {
366 path_kill_slashes(p);
368 if (STR_IN_SET(p, "/", ""))
373 if (!path_is_safe(p))
376 /* Truncate trailing slashes */
377 e = endswith(p, "/");
381 /* Truncate leading slashes */
385 s = unit_name_escape(p);
394 int unit_name_path_unescape(const char *f, char **ret) {
410 r = unit_name_unescape(f, &w);
414 /* Don't accept trailing or leading slashes */
415 if (startswith(w, "/") || endswith(w, "/")) {
420 /* Prefix a slash again */
421 s = strappend("/", w);
426 if (!path_is_safe(s)) {
440 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
449 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
451 if (!unit_instance_is_valid(i))
454 assert_se(p = strchr(f, '@'));
455 assert_se(e = strrchr(f, '.'));
460 s = new(char, a + 1 + b + strlen(e) + 1);
464 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
470 int unit_name_template(const char *f, char **ret) {
478 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
481 assert_se(p = strchr(f, '@'));
482 assert_se(e = strrchr(f, '.'));
486 s = new(char, a + 1 + strlen(e) + 1);
490 strcpy(mempcpy(s, f, a + 1), e);
496 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
497 _cleanup_free_ char *p = NULL;
505 if (!unit_suffix_is_valid(suffix))
508 r = unit_name_path_escape(path, &p);
512 s = strappend(p, suffix);
520 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
521 _cleanup_free_ char *p = NULL;
530 if (!unit_prefix_is_valid(prefix))
533 if (!unit_suffix_is_valid(suffix))
536 r = unit_name_path_escape(path, &p);
540 s = strjoin(prefix, "@", p, suffix, NULL);
548 int unit_name_to_path(const char *name, char **ret) {
549 _cleanup_free_ char *prefix = NULL;
554 r = unit_name_to_prefix(name, &prefix);
558 return unit_name_path_unescape(prefix, ret);
561 char *unit_dbus_path_from_name(const char *name) {
562 _cleanup_free_ char *e = NULL;
566 e = bus_label_escape(name);
570 return strappend("/org/freedesktop/systemd1/unit/", e);
573 int unit_name_from_dbus_path(const char *path, char **name) {
577 e = startswith(path, "/org/freedesktop/systemd1/unit/");
581 n = bus_label_unescape(e);
589 static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
590 const char *valid_chars;
593 assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
596 /* We'll only escape the obvious characters here, to play
599 valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
604 else if (!strchr(valid_chars, *f))
605 t = do_escape_char(*f, t);
614 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
615 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
616 * except that @suffix is appended if a valid unit suffix is not present.
618 * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
620 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
628 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
631 if (!unit_suffix_is_valid(suffix))
634 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
635 /* No mangling necessary... */
644 if (is_device_path(name)) {
645 r = unit_name_from_path(name, ".device", ret);
652 if (path_is_absolute(name)) {
653 r = unit_name_from_path(name, ".mount", ret);
660 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
664 t = do_escape_mangle(name, allow_globs, s);
667 if (unit_name_to_type(s) < 0)
674 int slice_build_parent_slice(const char *slice, char **ret) {
680 if (!slice_name_is_valid(slice))
683 if (streq(slice, "-.slice")) {
692 dash = strrchr(s, '-');
694 strcpy(dash, ".slice");
698 s = strdup("-.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, "-.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, "-.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 */
776 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
777 [UNIT_SERVICE] = "service",
778 [UNIT_SOCKET] = "socket",
779 [UNIT_BUSNAME] = "busname",
780 [UNIT_TARGET] = "target",
781 [UNIT_SNAPSHOT] = "snapshot",
782 [UNIT_DEVICE] = "device",
783 [UNIT_MOUNT] = "mount",
784 [UNIT_AUTOMOUNT] = "automount",
785 [UNIT_SWAP] = "swap",
786 [UNIT_TIMER] = "timer",
787 [UNIT_PATH] = "path",
788 [UNIT_SLICE] = "slice",
789 [UNIT_SCOPE] = "scope"
792 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
794 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
795 [UNIT_STUB] = "stub",
796 [UNIT_LOADED] = "loaded",
797 [UNIT_NOT_FOUND] = "not-found",
798 [UNIT_ERROR] = "error",
799 [UNIT_MERGED] = "merged",
800 [UNIT_MASKED] = "masked"
803 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
805 static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
806 [UNIT_REQUIRES] = "Requires",
807 [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
808 [UNIT_REQUISITE] = "Requisite",
809 [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
810 [UNIT_WANTS] = "Wants",
811 [UNIT_BINDS_TO] = "BindsTo",
812 [UNIT_PART_OF] = "PartOf",
813 [UNIT_REQUIRED_BY] = "RequiredBy",
814 [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
815 [UNIT_REQUISITE_OF] = "RequisiteOf",
816 [UNIT_REQUISITE_OF_OVERRIDABLE] = "RequisiteOfOverridable",
817 [UNIT_WANTED_BY] = "WantedBy",
818 [UNIT_BOUND_BY] = "BoundBy",
819 [UNIT_CONSISTS_OF] = "ConsistsOf",
820 [UNIT_CONFLICTS] = "Conflicts",
821 [UNIT_CONFLICTED_BY] = "ConflictedBy",
822 [UNIT_BEFORE] = "Before",
823 [UNIT_AFTER] = "After",
824 [UNIT_ON_FAILURE] = "OnFailure",
825 [UNIT_TRIGGERS] = "Triggers",
826 [UNIT_TRIGGERED_BY] = "TriggeredBy",
827 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
828 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
829 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
830 [UNIT_REFERENCES] = "References",
831 [UNIT_REFERENCED_BY] = "ReferencedBy",
834 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);