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 /// UNNEEDED by elogind
123 int unit_name_to_prefix(const char *n, char **ret) {
130 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
139 s = strndup(n, p - n);
147 int unit_name_to_instance(const char *n, char **instance) {
154 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
157 /* Everything past the first @ and before the last . is the instance */
178 int unit_name_to_prefix_and_instance(const char *n, char **ret) {
185 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
192 s = strndup(n, d - n);
200 UnitType unit_name_to_type(const char *n) {
205 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
206 return _UNIT_TYPE_INVALID;
208 assert_se(e = strrchr(n, '.'));
210 return unit_type_from_string(e + 1);
213 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
221 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
224 if (!unit_suffix_is_valid(suffix))
227 assert_se(e = strrchr(n, '.'));
232 s = new(char, a + b + 1);
236 strcpy(mempcpy(s, n, a), suffix);
243 int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
250 if (!unit_prefix_is_valid(prefix))
253 if (instance && !unit_instance_is_valid(instance))
256 if (!unit_suffix_is_valid(suffix))
260 s = strappend(prefix, suffix);
262 s = strjoin(prefix, "@", instance, suffix, NULL);
270 /// UNNEEDED by elogind
272 static char *do_escape_char(char c, char *t) {
277 *(t++) = hexchar(c >> 4);
283 static char *do_escape(const char *f, char *t) {
287 /* do not create units with a leading '.', like for "/.dotdir" mount points */
289 t = do_escape_char(*f, t);
296 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
297 t = do_escape_char(*f, t);
305 char *unit_name_escape(const char *f) {
310 r = new(char, strlen(f)*4+1);
320 int unit_name_unescape(const char *f, char **ret) {
321 _cleanup_free_ char *r = NULL;
330 for (t = r; *f; f++) {
333 else if (*f == '\\') {
347 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
361 int unit_name_path_escape(const char *f, char **ret) {
371 path_kill_slashes(p);
373 if (STR_IN_SET(p, "/", ""))
378 if (!path_is_safe(p))
381 /* Truncate trailing slashes */
382 e = endswith(p, "/");
386 /* Truncate leading slashes */
390 s = unit_name_escape(p);
399 int unit_name_path_unescape(const char *f, char **ret) {
415 r = unit_name_unescape(f, &w);
419 /* Don't accept trailing or leading slashes */
420 if (startswith(w, "/") || endswith(w, "/")) {
425 /* Prefix a slash again */
426 s = strappend("/", w);
431 if (!path_is_safe(s)) {
445 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
454 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
456 if (!unit_instance_is_valid(i))
459 assert_se(p = strchr(f, '@'));
460 assert_se(e = strrchr(f, '.'));
465 s = new(char, a + 1 + b + strlen(e) + 1);
469 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
475 int unit_name_template(const char *f, char **ret) {
483 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
486 assert_se(p = strchr(f, '@'));
487 assert_se(e = strrchr(f, '.'));
491 s = new(char, a + 1 + strlen(e) + 1);
495 strcpy(mempcpy(s, f, a + 1), e);
501 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
502 _cleanup_free_ char *p = NULL;
510 if (!unit_suffix_is_valid(suffix))
513 r = unit_name_path_escape(path, &p);
517 s = strappend(p, suffix);
525 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
526 _cleanup_free_ char *p = NULL;
535 if (!unit_prefix_is_valid(prefix))
538 if (!unit_suffix_is_valid(suffix))
541 r = unit_name_path_escape(path, &p);
545 s = strjoin(prefix, "@", p, suffix, NULL);
553 int unit_name_to_path(const char *name, char **ret) {
554 _cleanup_free_ char *prefix = NULL;
559 r = unit_name_to_prefix(name, &prefix);
563 return unit_name_path_unescape(prefix, ret);
566 char *unit_dbus_path_from_name(const char *name) {
567 _cleanup_free_ char *e = NULL;
571 e = bus_label_escape(name);
575 return strappend("/org/freedesktop/systemd1/unit/", e);
578 int unit_name_from_dbus_path(const char *path, char **name) {
582 e = startswith(path, "/org/freedesktop/systemd1/unit/");
586 n = bus_label_unescape(e);
594 const char* unit_dbus_interface_from_type(UnitType t) {
596 static const char *const table[_UNIT_TYPE_MAX] = {
597 [UNIT_SERVICE] = "org.freedesktop.systemd1.Service",
598 [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket",
599 [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName",
600 [UNIT_TARGET] = "org.freedesktop.systemd1.Target",
601 [UNIT_SNAPSHOT] = "org.freedesktop.systemd1.Snapshot",
602 [UNIT_DEVICE] = "org.freedesktop.systemd1.Device",
603 [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount",
604 [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount",
605 [UNIT_SWAP] = "org.freedesktop.systemd1.Swap",
606 [UNIT_TIMER] = "org.freedesktop.systemd1.Timer",
607 [UNIT_PATH] = "org.freedesktop.systemd1.Path",
608 [UNIT_SLICE] = "org.freedesktop.systemd1.Slice",
609 [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope",
614 if (t >= _UNIT_TYPE_MAX)
620 const char *unit_dbus_interface_from_name(const char *name) {
623 t = unit_name_to_type(name);
627 return unit_dbus_interface_from_type(t);
630 static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
631 const char *valid_chars;
634 assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
637 /* We'll only escape the obvious characters here, to play
640 valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
645 else if (!strchr(valid_chars, *f))
646 t = do_escape_char(*f, t);
655 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
656 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
657 * except that @suffix is appended if a valid unit suffix is not present.
659 * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
661 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
669 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
672 if (!unit_suffix_is_valid(suffix))
675 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
676 /* No mangling necessary... */
685 if (is_device_path(name)) {
686 r = unit_name_from_path(name, ".device", ret);
693 if (path_is_absolute(name)) {
694 r = unit_name_from_path(name, ".mount", ret);
701 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
705 t = do_escape_mangle(name, allow_globs, s);
708 if (unit_name_to_type(s) < 0)
715 int slice_build_parent_slice(const char *slice, char **ret) {
722 if (!slice_name_is_valid(slice))
725 if (streq(slice, "-.slice")) {
734 dash = strrchr(s, '-');
736 strcpy(dash, ".slice");
738 r = free_and_strdup(&s, "-.slice");
750 int slice_build_subslice(const char *slice, const char*name, char **ret) {
757 if (!slice_name_is_valid(slice))
760 if (!unit_prefix_is_valid(name))
763 if (streq(slice, "-.slice"))
764 subslice = strappend(name, ".slice");
768 assert_se(e = endswith(slice, ".slice"));
770 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
774 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
781 bool slice_name_is_valid(const char *name) {
785 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
788 if (streq(name, "-.slice"))
791 e = endswith(name, ".slice");
795 for (p = name; p < e; p++) {
799 /* Don't allow initial dash */
803 /* Don't allow multiple dashes */
812 /* Don't allow trailing hash */
819 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
820 [UNIT_SERVICE] = "service",
821 [UNIT_SOCKET] = "socket",
822 [UNIT_BUSNAME] = "busname",
823 [UNIT_TARGET] = "target",
824 [UNIT_SNAPSHOT] = "snapshot",
825 [UNIT_DEVICE] = "device",
826 [UNIT_MOUNT] = "mount",
827 [UNIT_AUTOMOUNT] = "automount",
828 [UNIT_SWAP] = "swap",
829 [UNIT_TIMER] = "timer",
830 [UNIT_PATH] = "path",
831 [UNIT_SLICE] = "slice",
832 [UNIT_SCOPE] = "scope",
835 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
837 /// UNNEEDED by elogind
839 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
840 [UNIT_STUB] = "stub",
841 [UNIT_LOADED] = "loaded",
842 [UNIT_NOT_FOUND] = "not-found",
843 [UNIT_ERROR] = "error",
844 [UNIT_MERGED] = "merged",
845 [UNIT_MASKED] = "masked"
848 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
850 static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
851 [UNIT_REQUIRES] = "Requires",
852 [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
853 [UNIT_REQUISITE] = "Requisite",
854 [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
855 [UNIT_WANTS] = "Wants",
856 [UNIT_BINDS_TO] = "BindsTo",
857 [UNIT_PART_OF] = "PartOf",
858 [UNIT_REQUIRED_BY] = "RequiredBy",
859 [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
860 [UNIT_REQUISITE_OF] = "RequisiteOf",
861 [UNIT_REQUISITE_OF_OVERRIDABLE] = "RequisiteOfOverridable",
862 [UNIT_WANTED_BY] = "WantedBy",
863 [UNIT_BOUND_BY] = "BoundBy",
864 [UNIT_CONSISTS_OF] = "ConsistsOf",
865 [UNIT_CONFLICTS] = "Conflicts",
866 [UNIT_CONFLICTED_BY] = "ConflictedBy",
867 [UNIT_BEFORE] = "Before",
868 [UNIT_AFTER] = "After",
869 [UNIT_ON_FAILURE] = "OnFailure",
870 [UNIT_TRIGGERS] = "Triggers",
871 [UNIT_TRIGGERED_BY] = "TriggeredBy",
872 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
873 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
874 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
875 [UNIT_REFERENCES] = "References",
876 [UNIT_REFERENCED_BY] = "ReferencedBy",
879 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);