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/>.
28 #include "alloc-util.h"
29 #include "bus-label.h"
30 #include "glob-util.h"
31 #include "hexdecoct.h"
33 #include "path-util.h"
34 #include "string-table.h"
35 #include "string-util.h"
37 #include "unit-name.h"
39 /* Characters valid in a unit name. */
45 /* The same, but also permits the single @ character that may appear */
46 #define VALID_CHARS_WITH_AT \
50 /* All chars valid in a unit name glob */
51 #define VALID_CHARS_GLOB \
55 bool unit_name_is_valid(const char *n
, UnitNameFlags flags
) {
56 const char *e
, *i
, *at
;
58 assert((flags
& ~(UNIT_NAME_PLAIN
|UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
)) == 0);
60 if (_unlikely_(flags
== 0))
66 if (strlen(n
) >= UNIT_NAME_MAX
)
73 if (unit_type_from_string(e
+ 1) < 0)
76 for (i
= n
, at
= NULL
; i
< e
; i
++) {
81 if (!strchr("@" VALID_CHARS
, *i
))
88 if (flags
& UNIT_NAME_PLAIN
)
92 if (flags
& UNIT_NAME_INSTANCE
)
96 if (flags
& UNIT_NAME_TEMPLATE
)
97 if (at
&& e
== at
+ 1)
103 bool unit_prefix_is_valid(const char *p
) {
105 /* We don't allow additional @ in the prefix string */
110 return in_charset(p
, VALID_CHARS
);
113 bool unit_instance_is_valid(const char *i
) {
115 /* The max length depends on the length of the string, so we
116 * don't really check this here. */
121 /* We allow additional @ in the instance string, we do not
122 * allow them in the prefix! */
124 return in_charset(i
, "@" VALID_CHARS
);
127 bool unit_suffix_is_valid(const char *s
) {
134 if (unit_type_from_string(s
+ 1) < 0)
140 int unit_name_to_prefix(const char *n
, char **ret
) {
147 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
156 s
= strndup(n
, p
- n
);
164 int unit_name_to_instance(const char *n
, char **instance
) {
171 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
174 /* Everything past the first @ and before the last . is the instance */
195 int unit_name_to_prefix_and_instance(const char *n
, char **ret
) {
202 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
209 s
= strndup(n
, d
- n
);
217 UnitType
unit_name_to_type(const char *n
) {
222 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
223 return _UNIT_TYPE_INVALID
;
225 assert_se(e
= strrchr(n
, '.'));
227 return unit_type_from_string(e
+ 1);
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
);
259 int unit_name_build(const char *prefix
, const char *instance
, const char *suffix
, char **ret
) {
266 if (!unit_prefix_is_valid(prefix
))
269 if (instance
&& !unit_instance_is_valid(instance
))
272 if (!unit_suffix_is_valid(suffix
))
276 s
= strappend(prefix
, suffix
);
278 s
= strjoin(prefix
, "@", instance
, suffix
, NULL
);
286 static char *do_escape_char(char c
, char *t
) {
291 *(t
++) = hexchar(c
>> 4);
297 static char *do_escape(const char *f
, char *t
) {
301 /* do not create units with a leading '.', like for "/.dotdir" mount points */
303 t
= do_escape_char(*f
, t
);
310 else if (*f
== '-' || *f
== '\\' || !strchr(VALID_CHARS
, *f
))
311 t
= do_escape_char(*f
, t
);
319 char *unit_name_escape(const char *f
) {
324 r
= new(char, strlen(f
)*4+1);
334 int unit_name_unescape(const char *f
, char **ret
) {
335 _cleanup_free_
char *r
= NULL
;
344 for (t
= r
; *f
; f
++) {
347 else if (*f
== '\\') {
361 *(t
++) = (char) (((uint8_t) a
<< 4U) | (uint8_t) b
);
375 int unit_name_path_escape(const char *f
, char **ret
) {
385 path_kill_slashes(p
);
387 if (STR_IN_SET(p
, "/", ""))
392 if (!path_is_safe(p
))
395 /* Truncate trailing slashes */
396 e
= endswith(p
, "/");
400 /* Truncate leading slashes */
404 s
= unit_name_escape(p
);
413 int unit_name_path_unescape(const char *f
, char **ret
) {
429 r
= unit_name_unescape(f
, &w
);
433 /* Don't accept trailing or leading slashes */
434 if (startswith(w
, "/") || endswith(w
, "/")) {
439 /* Prefix a slash again */
440 s
= strappend("/", w
);
445 if (!path_is_safe(s
)) {
459 int unit_name_replace_instance(const char *f
, const char *i
, char **ret
) {
468 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
470 if (!unit_instance_is_valid(i
))
473 assert_se(p
= strchr(f
, '@'));
474 assert_se(e
= strrchr(f
, '.'));
479 s
= new(char, a
+ 1 + b
+ strlen(e
) + 1);
483 strcpy(mempcpy(mempcpy(s
, f
, a
+ 1), i
, b
), e
);
489 int unit_name_template(const char *f
, char **ret
) {
497 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
500 assert_se(p
= strchr(f
, '@'));
501 assert_se(e
= strrchr(f
, '.'));
505 s
= new(char, a
+ 1 + strlen(e
) + 1);
509 strcpy(mempcpy(s
, f
, a
+ 1), e
);
515 int unit_name_from_path(const char *path
, const char *suffix
, char **ret
) {
516 _cleanup_free_
char *p
= NULL
;
524 if (!unit_suffix_is_valid(suffix
))
527 r
= unit_name_path_escape(path
, &p
);
531 s
= strappend(p
, suffix
);
539 int unit_name_from_path_instance(const char *prefix
, const char *path
, const char *suffix
, char **ret
) {
540 _cleanup_free_
char *p
= NULL
;
549 if (!unit_prefix_is_valid(prefix
))
552 if (!unit_suffix_is_valid(suffix
))
555 r
= unit_name_path_escape(path
, &p
);
559 s
= strjoin(prefix
, "@", p
, suffix
, NULL
);
567 int unit_name_to_path(const char *name
, char **ret
) {
568 _cleanup_free_
char *prefix
= NULL
;
573 r
= unit_name_to_prefix(name
, &prefix
);
577 return unit_name_path_unescape(prefix
, ret
);
580 char *unit_dbus_path_from_name(const char *name
) {
581 _cleanup_free_
char *e
= NULL
;
585 e
= bus_label_escape(name
);
589 return strappend("/org/freedesktop/systemd1/unit/", e
);
592 int unit_name_from_dbus_path(const char *path
, char **name
) {
596 e
= startswith(path
, "/org/freedesktop/systemd1/unit/");
600 n
= bus_label_unescape(e
);
608 const char* unit_dbus_interface_from_type(UnitType t
) {
610 static const char *const table
[_UNIT_TYPE_MAX
] = {
611 [UNIT_SERVICE
] = "org.freedesktop.systemd1.Service",
612 [UNIT_SOCKET
] = "org.freedesktop.systemd1.Socket",
613 [UNIT_BUSNAME
] = "org.freedesktop.systemd1.BusName",
614 [UNIT_TARGET
] = "org.freedesktop.systemd1.Target",
615 [UNIT_DEVICE
] = "org.freedesktop.systemd1.Device",
616 [UNIT_MOUNT
] = "org.freedesktop.systemd1.Mount",
617 [UNIT_AUTOMOUNT
] = "org.freedesktop.systemd1.Automount",
618 [UNIT_SWAP
] = "org.freedesktop.systemd1.Swap",
619 [UNIT_TIMER
] = "org.freedesktop.systemd1.Timer",
620 [UNIT_PATH
] = "org.freedesktop.systemd1.Path",
621 [UNIT_SLICE
] = "org.freedesktop.systemd1.Slice",
622 [UNIT_SCOPE
] = "org.freedesktop.systemd1.Scope",
627 if (t
>= _UNIT_TYPE_MAX
)
633 const char *unit_dbus_interface_from_name(const char *name
) {
636 t
= unit_name_to_type(name
);
640 return unit_dbus_interface_from_type(t
);
643 static char *do_escape_mangle(const char *f
, UnitNameMangle allow_globs
, char *t
) {
644 const char *valid_chars
;
647 assert(IN_SET(allow_globs
, UNIT_NAME_GLOB
, UNIT_NAME_NOGLOB
));
650 /* We'll only escape the obvious characters here, to play
653 valid_chars
= allow_globs
== UNIT_NAME_GLOB
? VALID_CHARS_GLOB
: VALID_CHARS_WITH_AT
;
658 else if (!strchr(valid_chars
, *f
))
659 t
= do_escape_char(*f
, t
);
668 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
669 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
670 * except that @suffix is appended if a valid unit suffix is not present.
672 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
674 int unit_name_mangle_with_suffix(const char *name
, UnitNameMangle allow_globs
, const char *suffix
, char **ret
) {
682 if (isempty(name
)) /* We cannot mangle empty unit names to become valid, sorry. */
685 if (!unit_suffix_is_valid(suffix
))
688 /* Already a fully valid unit name? If so, no mangling is necessary... */
689 if (unit_name_is_valid(name
, UNIT_NAME_ANY
))
692 /* Already a fully valid globbing expression? If so, no mangling is necessary either... */
693 if (allow_globs
== UNIT_NAME_GLOB
&&
694 string_is_glob(name
) &&
695 in_charset(name
, VALID_CHARS_GLOB
))
698 if (is_device_path(name
)) {
699 r
= unit_name_from_path(name
, ".device", ret
);
706 if (path_is_absolute(name
)) {
707 r
= unit_name_from_path(name
, ".mount", ret
);
714 s
= new(char, strlen(name
) * 4 + strlen(suffix
) + 1);
718 t
= do_escape_mangle(name
, allow_globs
, s
);
721 /* 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
723 if ((allow_globs
!= UNIT_NAME_GLOB
|| !string_is_glob(s
)) && unit_name_to_type(s
) < 0)
738 int slice_build_parent_slice(const char *slice
, char **ret
) {
745 if (!slice_name_is_valid(slice
))
748 if (streq(slice
, "-.slice")) {
757 dash
= strrchr(s
, '-');
759 strcpy(dash
, ".slice");
761 r
= free_and_strdup(&s
, "-.slice");
772 int slice_build_subslice(const char *slice
, const char*name
, char **ret
) {
779 if (!slice_name_is_valid(slice
))
782 if (!unit_prefix_is_valid(name
))
785 if (streq(slice
, "-.slice"))
786 subslice
= strappend(name
, ".slice");
790 assert_se(e
= endswith(slice
, ".slice"));
792 subslice
= new(char, (e
- slice
) + 1 + strlen(name
) + 6 + 1);
796 stpcpy(stpcpy(stpcpy(mempcpy(subslice
, slice
, e
- slice
), "-"), name
), ".slice");
803 bool slice_name_is_valid(const char *name
) {
807 if (!unit_name_is_valid(name
, UNIT_NAME_PLAIN
))
810 if (streq(name
, "-.slice"))
813 e
= endswith(name
, ".slice");
817 for (p
= name
; p
< e
; p
++) {
821 /* Don't allow initial dash */
825 /* Don't allow multiple dashes */
834 /* Don't allow trailing hash */
841 static const char* const unit_type_table
[_UNIT_TYPE_MAX
] = {
842 [UNIT_SERVICE
] = "service",
843 [UNIT_SOCKET
] = "socket",
844 [UNIT_BUSNAME
] = "busname",
845 [UNIT_TARGET
] = "target",
846 [UNIT_DEVICE
] = "device",
847 [UNIT_MOUNT
] = "mount",
848 [UNIT_AUTOMOUNT
] = "automount",
849 [UNIT_SWAP
] = "swap",
850 [UNIT_TIMER
] = "timer",
851 [UNIT_PATH
] = "path",
852 [UNIT_SLICE
] = "slice",
853 [UNIT_SCOPE
] = "scope",
856 DEFINE_STRING_TABLE_LOOKUP(unit_type
, UnitType
);
858 static const char* const unit_load_state_table
[_UNIT_LOAD_STATE_MAX
] = {
859 [UNIT_STUB
] = "stub",
860 [UNIT_LOADED
] = "loaded",
861 [UNIT_NOT_FOUND
] = "not-found",
862 [UNIT_ERROR
] = "error",
863 [UNIT_MERGED
] = "merged",
864 [UNIT_MASKED
] = "masked"
867 DEFINE_STRING_TABLE_LOOKUP(unit_load_state
, UnitLoadState
);
869 static const char* const unit_active_state_table
[_UNIT_ACTIVE_STATE_MAX
] = {
870 [UNIT_ACTIVE
] = "active",
871 [UNIT_RELOADING
] = "reloading",
872 [UNIT_INACTIVE
] = "inactive",
873 [UNIT_FAILED
] = "failed",
874 [UNIT_ACTIVATING
] = "activating",
875 [UNIT_DEACTIVATING
] = "deactivating"
878 DEFINE_STRING_TABLE_LOOKUP(unit_active_state
, UnitActiveState
);
880 static const char* const automount_state_table
[_AUTOMOUNT_STATE_MAX
] = {
881 [AUTOMOUNT_DEAD
] = "dead",
882 [AUTOMOUNT_WAITING
] = "waiting",
883 [AUTOMOUNT_RUNNING
] = "running",
884 [AUTOMOUNT_FAILED
] = "failed"
887 DEFINE_STRING_TABLE_LOOKUP(automount_state
, AutomountState
);
889 static const char* const busname_state_table
[_BUSNAME_STATE_MAX
] = {
890 [BUSNAME_DEAD
] = "dead",
891 [BUSNAME_MAKING
] = "making",
892 [BUSNAME_REGISTERED
] = "registered",
893 [BUSNAME_LISTENING
] = "listening",
894 [BUSNAME_RUNNING
] = "running",
895 [BUSNAME_SIGTERM
] = "sigterm",
896 [BUSNAME_SIGKILL
] = "sigkill",
897 [BUSNAME_FAILED
] = "failed",
900 DEFINE_STRING_TABLE_LOOKUP(busname_state
, BusNameState
);
902 static const char* const device_state_table
[_DEVICE_STATE_MAX
] = {
903 [DEVICE_DEAD
] = "dead",
904 [DEVICE_TENTATIVE
] = "tentative",
905 [DEVICE_PLUGGED
] = "plugged",
908 DEFINE_STRING_TABLE_LOOKUP(device_state
, DeviceState
);
910 static const char* const mount_state_table
[_MOUNT_STATE_MAX
] = {
911 [MOUNT_DEAD
] = "dead",
912 [MOUNT_MOUNTING
] = "mounting",
913 [MOUNT_MOUNTING_DONE
] = "mounting-done",
914 [MOUNT_MOUNTED
] = "mounted",
915 [MOUNT_REMOUNTING
] = "remounting",
916 [MOUNT_UNMOUNTING
] = "unmounting",
917 [MOUNT_MOUNTING_SIGTERM
] = "mounting-sigterm",
918 [MOUNT_MOUNTING_SIGKILL
] = "mounting-sigkill",
919 [MOUNT_REMOUNTING_SIGTERM
] = "remounting-sigterm",
920 [MOUNT_REMOUNTING_SIGKILL
] = "remounting-sigkill",
921 [MOUNT_UNMOUNTING_SIGTERM
] = "unmounting-sigterm",
922 [MOUNT_UNMOUNTING_SIGKILL
] = "unmounting-sigkill",
923 [MOUNT_FAILED
] = "failed"
926 DEFINE_STRING_TABLE_LOOKUP(mount_state
, MountState
);
928 static const char* const path_state_table
[_PATH_STATE_MAX
] = {
929 [PATH_DEAD
] = "dead",
930 [PATH_WAITING
] = "waiting",
931 [PATH_RUNNING
] = "running",
932 [PATH_FAILED
] = "failed"
935 DEFINE_STRING_TABLE_LOOKUP(path_state
, PathState
);
937 static const char* const scope_state_table
[_SCOPE_STATE_MAX
] = {
938 [SCOPE_DEAD
] = "dead",
939 [SCOPE_RUNNING
] = "running",
940 [SCOPE_ABANDONED
] = "abandoned",
941 [SCOPE_STOP_SIGTERM
] = "stop-sigterm",
942 [SCOPE_STOP_SIGKILL
] = "stop-sigkill",
943 [SCOPE_FAILED
] = "failed",
946 DEFINE_STRING_TABLE_LOOKUP(scope_state
, ScopeState
);
948 static const char* const service_state_table
[_SERVICE_STATE_MAX
] = {
949 [SERVICE_DEAD
] = "dead",
950 [SERVICE_START_PRE
] = "start-pre",
951 [SERVICE_START
] = "start",
952 [SERVICE_START_POST
] = "start-post",
953 [SERVICE_RUNNING
] = "running",
954 [SERVICE_EXITED
] = "exited",
955 [SERVICE_RELOAD
] = "reload",
956 [SERVICE_STOP
] = "stop",
957 [SERVICE_STOP_SIGABRT
] = "stop-sigabrt",
958 [SERVICE_STOP_SIGTERM
] = "stop-sigterm",
959 [SERVICE_STOP_SIGKILL
] = "stop-sigkill",
960 [SERVICE_STOP_POST
] = "stop-post",
961 [SERVICE_FINAL_SIGTERM
] = "final-sigterm",
962 [SERVICE_FINAL_SIGKILL
] = "final-sigkill",
963 [SERVICE_FAILED
] = "failed",
964 [SERVICE_AUTO_RESTART
] = "auto-restart",
967 DEFINE_STRING_TABLE_LOOKUP(service_state
, ServiceState
);
969 static const char* const slice_state_table
[_SLICE_STATE_MAX
] = {
970 [SLICE_DEAD
] = "dead",
971 [SLICE_ACTIVE
] = "active"
974 DEFINE_STRING_TABLE_LOOKUP(slice_state
, SliceState
);
976 static const char* const socket_state_table
[_SOCKET_STATE_MAX
] = {
977 [SOCKET_DEAD
] = "dead",
978 [SOCKET_START_PRE
] = "start-pre",
979 [SOCKET_START_CHOWN
] = "start-chown",
980 [SOCKET_START_POST
] = "start-post",
981 [SOCKET_LISTENING
] = "listening",
982 [SOCKET_RUNNING
] = "running",
983 [SOCKET_STOP_PRE
] = "stop-pre",
984 [SOCKET_STOP_PRE_SIGTERM
] = "stop-pre-sigterm",
985 [SOCKET_STOP_PRE_SIGKILL
] = "stop-pre-sigkill",
986 [SOCKET_STOP_POST
] = "stop-post",
987 [SOCKET_FINAL_SIGTERM
] = "final-sigterm",
988 [SOCKET_FINAL_SIGKILL
] = "final-sigkill",
989 [SOCKET_FAILED
] = "failed"
992 DEFINE_STRING_TABLE_LOOKUP(socket_state
, SocketState
);
994 static const char* const swap_state_table
[_SWAP_STATE_MAX
] = {
995 [SWAP_DEAD
] = "dead",
996 [SWAP_ACTIVATING
] = "activating",
997 [SWAP_ACTIVATING_DONE
] = "activating-done",
998 [SWAP_ACTIVE
] = "active",
999 [SWAP_DEACTIVATING
] = "deactivating",
1000 [SWAP_ACTIVATING_SIGTERM
] = "activating-sigterm",
1001 [SWAP_ACTIVATING_SIGKILL
] = "activating-sigkill",
1002 [SWAP_DEACTIVATING_SIGTERM
] = "deactivating-sigterm",
1003 [SWAP_DEACTIVATING_SIGKILL
] = "deactivating-sigkill",
1004 [SWAP_FAILED
] = "failed"
1007 DEFINE_STRING_TABLE_LOOKUP(swap_state
, SwapState
);
1009 static const char* const target_state_table
[_TARGET_STATE_MAX
] = {
1010 [TARGET_DEAD
] = "dead",
1011 [TARGET_ACTIVE
] = "active"
1014 DEFINE_STRING_TABLE_LOOKUP(target_state
, TargetState
);
1016 static const char* const timer_state_table
[_TIMER_STATE_MAX
] = {
1017 [TIMER_DEAD
] = "dead",
1018 [TIMER_WAITING
] = "waiting",
1019 [TIMER_RUNNING
] = "running",
1020 [TIMER_ELAPSED
] = "elapsed",
1021 [TIMER_FAILED
] = "failed"
1024 DEFINE_STRING_TABLE_LOOKUP(timer_state
, TimerState
);
1026 static const char* const unit_dependency_table
[_UNIT_DEPENDENCY_MAX
] = {
1027 [UNIT_REQUIRES
] = "Requires",
1028 [UNIT_REQUISITE
] = "Requisite",
1029 [UNIT_WANTS
] = "Wants",
1030 [UNIT_BINDS_TO
] = "BindsTo",
1031 [UNIT_PART_OF
] = "PartOf",
1032 [UNIT_REQUIRED_BY
] = "RequiredBy",
1033 [UNIT_REQUISITE_OF
] = "RequisiteOf",
1034 [UNIT_WANTED_BY
] = "WantedBy",
1035 [UNIT_BOUND_BY
] = "BoundBy",
1036 [UNIT_CONSISTS_OF
] = "ConsistsOf",
1037 [UNIT_CONFLICTS
] = "Conflicts",
1038 [UNIT_CONFLICTED_BY
] = "ConflictedBy",
1039 [UNIT_BEFORE
] = "Before",
1040 [UNIT_AFTER
] = "After",
1041 [UNIT_ON_FAILURE
] = "OnFailure",
1042 [UNIT_TRIGGERS
] = "Triggers",
1043 [UNIT_TRIGGERED_BY
] = "TriggeredBy",
1044 [UNIT_PROPAGATES_RELOAD_TO
] = "PropagatesReloadTo",
1045 [UNIT_RELOAD_PROPAGATED_FROM
] = "ReloadPropagatedFrom",
1046 [UNIT_JOINS_NAMESPACE_OF
] = "JoinsNamespaceOf",
1047 [UNIT_REFERENCES
] = "References",
1048 [UNIT_REFERENCED_BY
] = "ReferencedBy",
1051 DEFINE_STRING_TABLE_LOOKUP(unit_dependency
, UnitDependency
);