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 const char* unit_dbus_interface_from_type(UnitType t
) {
591 static const char *const table
[_UNIT_TYPE_MAX
] = {
592 [UNIT_SERVICE
] = "org.freedesktop.systemd1.Service",
593 [UNIT_SOCKET
] = "org.freedesktop.systemd1.Socket",
594 [UNIT_BUSNAME
] = "org.freedesktop.systemd1.BusName",
595 [UNIT_TARGET
] = "org.freedesktop.systemd1.Target",
596 [UNIT_SNAPSHOT
] = "org.freedesktop.systemd1.Snapshot",
597 [UNIT_DEVICE
] = "org.freedesktop.systemd1.Device",
598 [UNIT_MOUNT
] = "org.freedesktop.systemd1.Mount",
599 [UNIT_AUTOMOUNT
] = "org.freedesktop.systemd1.Automount",
600 [UNIT_SWAP
] = "org.freedesktop.systemd1.Swap",
601 [UNIT_TIMER
] = "org.freedesktop.systemd1.Timer",
602 [UNIT_PATH
] = "org.freedesktop.systemd1.Path",
603 [UNIT_SLICE
] = "org.freedesktop.systemd1.Slice",
604 [UNIT_SCOPE
] = "org.freedesktop.systemd1.Scope",
609 if (t
>= _UNIT_TYPE_MAX
)
615 const char *unit_dbus_interface_from_name(const char *name
) {
618 t
= unit_name_to_type(name
);
622 return unit_dbus_interface_from_type(t
);
625 static char *do_escape_mangle(const char *f
, UnitNameMangle allow_globs
, char *t
) {
626 const char *valid_chars
;
629 assert(IN_SET(allow_globs
, UNIT_NAME_GLOB
, UNIT_NAME_NOGLOB
));
632 /* We'll only escape the obvious characters here, to play
635 valid_chars
= allow_globs
== UNIT_NAME_GLOB
? "@" VALID_CHARS
"[]!-*?" : "@" VALID_CHARS
;
640 else if (!strchr(valid_chars
, *f
))
641 t
= do_escape_char(*f
, t
);
650 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
651 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
652 * except that @suffix is appended if a valid unit suffix is not present.
654 * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
656 int unit_name_mangle_with_suffix(const char *name
, UnitNameMangle allow_globs
, const char *suffix
, char **ret
) {
664 if (isempty(name
)) /* We cannot mangle empty unit names to become valid, sorry. */
667 if (!unit_suffix_is_valid(suffix
))
670 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
671 /* No mangling necessary... */
680 if (is_device_path(name
)) {
681 r
= unit_name_from_path(name
, ".device", ret
);
688 if (path_is_absolute(name
)) {
689 r
= unit_name_from_path(name
, ".mount", ret
);
696 s
= new(char, strlen(name
) * 4 + strlen(suffix
) + 1);
700 t
= do_escape_mangle(name
, allow_globs
, s
);
703 if (unit_name_to_type(s
) < 0)
710 int slice_build_parent_slice(const char *slice
, char **ret
) {
717 if (!slice_name_is_valid(slice
))
720 if (streq(slice
, "-.slice")) {
729 dash
= strrchr(s
, '-');
731 strcpy(dash
, ".slice");
733 r
= free_and_strdup(&s
, "-.slice");
744 int slice_build_subslice(const char *slice
, const char*name
, char **ret
) {
751 if (!slice_name_is_valid(slice
))
754 if (!unit_prefix_is_valid(name
))
757 if (streq(slice
, "-.slice"))
758 subslice
= strappend(name
, ".slice");
762 assert_se(e
= endswith(slice
, ".slice"));
764 subslice
= new(char, (e
- slice
) + 1 + strlen(name
) + 6 + 1);
768 stpcpy(stpcpy(stpcpy(mempcpy(subslice
, slice
, e
- slice
), "-"), name
), ".slice");
775 bool slice_name_is_valid(const char *name
) {
779 if (!unit_name_is_valid(name
, UNIT_NAME_PLAIN
))
782 if (streq(name
, "-.slice"))
785 e
= endswith(name
, ".slice");
789 for (p
= name
; p
< e
; p
++) {
793 /* Don't allow initial dash */
797 /* Don't allow multiple dashes */
806 /* Don't allow trailing hash */
813 static const char* const unit_type_table
[_UNIT_TYPE_MAX
] = {
814 [UNIT_SERVICE
] = "service",
815 [UNIT_SOCKET
] = "socket",
816 [UNIT_BUSNAME
] = "busname",
817 [UNIT_TARGET
] = "target",
818 [UNIT_SNAPSHOT
] = "snapshot",
819 [UNIT_DEVICE
] = "device",
820 [UNIT_MOUNT
] = "mount",
821 [UNIT_AUTOMOUNT
] = "automount",
822 [UNIT_SWAP
] = "swap",
823 [UNIT_TIMER
] = "timer",
824 [UNIT_PATH
] = "path",
825 [UNIT_SLICE
] = "slice",
826 [UNIT_SCOPE
] = "scope",
829 DEFINE_STRING_TABLE_LOOKUP(unit_type
, UnitType
);
831 static const char* const unit_load_state_table
[_UNIT_LOAD_STATE_MAX
] = {
832 [UNIT_STUB
] = "stub",
833 [UNIT_LOADED
] = "loaded",
834 [UNIT_NOT_FOUND
] = "not-found",
835 [UNIT_ERROR
] = "error",
836 [UNIT_MERGED
] = "merged",
837 [UNIT_MASKED
] = "masked"
840 DEFINE_STRING_TABLE_LOOKUP(unit_load_state
, UnitLoadState
);
842 static const char* const unit_active_state_table
[_UNIT_ACTIVE_STATE_MAX
] = {
843 [UNIT_ACTIVE
] = "active",
844 [UNIT_RELOADING
] = "reloading",
845 [UNIT_INACTIVE
] = "inactive",
846 [UNIT_FAILED
] = "failed",
847 [UNIT_ACTIVATING
] = "activating",
848 [UNIT_DEACTIVATING
] = "deactivating"
851 DEFINE_STRING_TABLE_LOOKUP(unit_active_state
, UnitActiveState
);
853 static const char* const unit_dependency_table
[_UNIT_DEPENDENCY_MAX
] = {
854 [UNIT_REQUIRES
] = "Requires",
855 [UNIT_REQUIRES_OVERRIDABLE
] = "RequiresOverridable",
856 [UNIT_REQUISITE
] = "Requisite",
857 [UNIT_REQUISITE_OVERRIDABLE
] = "RequisiteOverridable",
858 [UNIT_WANTS
] = "Wants",
859 [UNIT_BINDS_TO
] = "BindsTo",
860 [UNIT_PART_OF
] = "PartOf",
861 [UNIT_REQUIRED_BY
] = "RequiredBy",
862 [UNIT_REQUIRED_BY_OVERRIDABLE
] = "RequiredByOverridable",
863 [UNIT_REQUISITE_OF
] = "RequisiteOf",
864 [UNIT_REQUISITE_OF_OVERRIDABLE
] = "RequisiteOfOverridable",
865 [UNIT_WANTED_BY
] = "WantedBy",
866 [UNIT_BOUND_BY
] = "BoundBy",
867 [UNIT_CONSISTS_OF
] = "ConsistsOf",
868 [UNIT_CONFLICTS
] = "Conflicts",
869 [UNIT_CONFLICTED_BY
] = "ConflictedBy",
870 [UNIT_BEFORE
] = "Before",
871 [UNIT_AFTER
] = "After",
872 [UNIT_ON_FAILURE
] = "OnFailure",
873 [UNIT_TRIGGERS
] = "Triggers",
874 [UNIT_TRIGGERED_BY
] = "TriggeredBy",
875 [UNIT_PROPAGATES_RELOAD_TO
] = "PropagatesReloadTo",
876 [UNIT_RELOAD_PROPAGATED_FROM
] = "ReloadPropagatedFrom",
877 [UNIT_JOINS_NAMESPACE_OF
] = "JoinsNamespaceOf",
878 [UNIT_REFERENCES
] = "References",
879 [UNIT_REFERENCED_BY
] = "ReferencedBy",
882 DEFINE_STRING_TABLE_LOOKUP(unit_dependency
, UnitDependency
);