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 automount_state_table
[_AUTOMOUNT_STATE_MAX
] = {
854 [AUTOMOUNT_DEAD
] = "dead",
855 [AUTOMOUNT_WAITING
] = "waiting",
856 [AUTOMOUNT_RUNNING
] = "running",
857 [AUTOMOUNT_FAILED
] = "failed"
860 DEFINE_STRING_TABLE_LOOKUP(automount_state
, AutomountState
);
862 static const char* const busname_state_table
[_BUSNAME_STATE_MAX
] = {
863 [BUSNAME_DEAD
] = "dead",
864 [BUSNAME_MAKING
] = "making",
865 [BUSNAME_REGISTERED
] = "registered",
866 [BUSNAME_LISTENING
] = "listening",
867 [BUSNAME_RUNNING
] = "running",
868 [BUSNAME_SIGTERM
] = "sigterm",
869 [BUSNAME_SIGKILL
] = "sigkill",
870 [BUSNAME_FAILED
] = "failed",
873 DEFINE_STRING_TABLE_LOOKUP(busname_state
, BusNameState
);
875 static const char* const device_state_table
[_DEVICE_STATE_MAX
] = {
876 [DEVICE_DEAD
] = "dead",
877 [DEVICE_TENTATIVE
] = "tentative",
878 [DEVICE_PLUGGED
] = "plugged",
881 DEFINE_STRING_TABLE_LOOKUP(device_state
, DeviceState
);
883 static const char* const mount_state_table
[_MOUNT_STATE_MAX
] = {
884 [MOUNT_DEAD
] = "dead",
885 [MOUNT_MOUNTING
] = "mounting",
886 [MOUNT_MOUNTING_DONE
] = "mounting-done",
887 [MOUNT_MOUNTED
] = "mounted",
888 [MOUNT_REMOUNTING
] = "remounting",
889 [MOUNT_UNMOUNTING
] = "unmounting",
890 [MOUNT_MOUNTING_SIGTERM
] = "mounting-sigterm",
891 [MOUNT_MOUNTING_SIGKILL
] = "mounting-sigkill",
892 [MOUNT_REMOUNTING_SIGTERM
] = "remounting-sigterm",
893 [MOUNT_REMOUNTING_SIGKILL
] = "remounting-sigkill",
894 [MOUNT_UNMOUNTING_SIGTERM
] = "unmounting-sigterm",
895 [MOUNT_UNMOUNTING_SIGKILL
] = "unmounting-sigkill",
896 [MOUNT_FAILED
] = "failed"
899 DEFINE_STRING_TABLE_LOOKUP(mount_state
, MountState
);
901 static const char* const path_state_table
[_PATH_STATE_MAX
] = {
902 [PATH_DEAD
] = "dead",
903 [PATH_WAITING
] = "waiting",
904 [PATH_RUNNING
] = "running",
905 [PATH_FAILED
] = "failed"
908 DEFINE_STRING_TABLE_LOOKUP(path_state
, PathState
);
910 static const char* const scope_state_table
[_SCOPE_STATE_MAX
] = {
911 [SCOPE_DEAD
] = "dead",
912 [SCOPE_RUNNING
] = "running",
913 [SCOPE_ABANDONED
] = "abandoned",
914 [SCOPE_STOP_SIGTERM
] = "stop-sigterm",
915 [SCOPE_STOP_SIGKILL
] = "stop-sigkill",
916 [SCOPE_FAILED
] = "failed",
919 DEFINE_STRING_TABLE_LOOKUP(scope_state
, ScopeState
);
921 static const char* const service_state_table
[_SERVICE_STATE_MAX
] = {
922 [SERVICE_DEAD
] = "dead",
923 [SERVICE_START_PRE
] = "start-pre",
924 [SERVICE_START
] = "start",
925 [SERVICE_START_POST
] = "start-post",
926 [SERVICE_RUNNING
] = "running",
927 [SERVICE_EXITED
] = "exited",
928 [SERVICE_RELOAD
] = "reload",
929 [SERVICE_STOP
] = "stop",
930 [SERVICE_STOP_SIGABRT
] = "stop-sigabrt",
931 [SERVICE_STOP_SIGTERM
] = "stop-sigterm",
932 [SERVICE_STOP_SIGKILL
] = "stop-sigkill",
933 [SERVICE_STOP_POST
] = "stop-post",
934 [SERVICE_FINAL_SIGTERM
] = "final-sigterm",
935 [SERVICE_FINAL_SIGKILL
] = "final-sigkill",
936 [SERVICE_FAILED
] = "failed",
937 [SERVICE_AUTO_RESTART
] = "auto-restart",
940 DEFINE_STRING_TABLE_LOOKUP(service_state
, ServiceState
);
942 static const char* const slice_state_table
[_SLICE_STATE_MAX
] = {
943 [SLICE_DEAD
] = "dead",
944 [SLICE_ACTIVE
] = "active"
947 DEFINE_STRING_TABLE_LOOKUP(slice_state
, SliceState
);
949 static const char* const snapshot_state_table
[_SNAPSHOT_STATE_MAX
] = {
950 [SNAPSHOT_DEAD
] = "dead",
951 [SNAPSHOT_ACTIVE
] = "active"
954 DEFINE_STRING_TABLE_LOOKUP(snapshot_state
, SnapshotState
);
956 static const char* const socket_state_table
[_SOCKET_STATE_MAX
] = {
957 [SOCKET_DEAD
] = "dead",
958 [SOCKET_START_PRE
] = "start-pre",
959 [SOCKET_START_CHOWN
] = "start-chown",
960 [SOCKET_START_POST
] = "start-post",
961 [SOCKET_LISTENING
] = "listening",
962 [SOCKET_RUNNING
] = "running",
963 [SOCKET_STOP_PRE
] = "stop-pre",
964 [SOCKET_STOP_PRE_SIGTERM
] = "stop-pre-sigterm",
965 [SOCKET_STOP_PRE_SIGKILL
] = "stop-pre-sigkill",
966 [SOCKET_STOP_POST
] = "stop-post",
967 [SOCKET_FINAL_SIGTERM
] = "final-sigterm",
968 [SOCKET_FINAL_SIGKILL
] = "final-sigkill",
969 [SOCKET_FAILED
] = "failed"
972 DEFINE_STRING_TABLE_LOOKUP(socket_state
, SocketState
);
974 static const char* const swap_state_table
[_SWAP_STATE_MAX
] = {
975 [SWAP_DEAD
] = "dead",
976 [SWAP_ACTIVATING
] = "activating",
977 [SWAP_ACTIVATING_DONE
] = "activating-done",
978 [SWAP_ACTIVE
] = "active",
979 [SWAP_DEACTIVATING
] = "deactivating",
980 [SWAP_ACTIVATING_SIGTERM
] = "activating-sigterm",
981 [SWAP_ACTIVATING_SIGKILL
] = "activating-sigkill",
982 [SWAP_DEACTIVATING_SIGTERM
] = "deactivating-sigterm",
983 [SWAP_DEACTIVATING_SIGKILL
] = "deactivating-sigkill",
984 [SWAP_FAILED
] = "failed"
987 DEFINE_STRING_TABLE_LOOKUP(swap_state
, SwapState
);
989 static const char* const target_state_table
[_TARGET_STATE_MAX
] = {
990 [TARGET_DEAD
] = "dead",
991 [TARGET_ACTIVE
] = "active"
994 DEFINE_STRING_TABLE_LOOKUP(target_state
, TargetState
);
996 static const char* const timer_state_table
[_TIMER_STATE_MAX
] = {
997 [TIMER_DEAD
] = "dead",
998 [TIMER_WAITING
] = "waiting",
999 [TIMER_RUNNING
] = "running",
1000 [TIMER_ELAPSED
] = "elapsed",
1001 [TIMER_FAILED
] = "failed"
1004 DEFINE_STRING_TABLE_LOOKUP(timer_state
, TimerState
);
1006 static const char* const unit_dependency_table
[_UNIT_DEPENDENCY_MAX
] = {
1007 [UNIT_REQUIRES
] = "Requires",
1008 [UNIT_REQUIRES_OVERRIDABLE
] = "RequiresOverridable",
1009 [UNIT_REQUISITE
] = "Requisite",
1010 [UNIT_REQUISITE_OVERRIDABLE
] = "RequisiteOverridable",
1011 [UNIT_WANTS
] = "Wants",
1012 [UNIT_BINDS_TO
] = "BindsTo",
1013 [UNIT_PART_OF
] = "PartOf",
1014 [UNIT_REQUIRED_BY
] = "RequiredBy",
1015 [UNIT_REQUIRED_BY_OVERRIDABLE
] = "RequiredByOverridable",
1016 [UNIT_REQUISITE_OF
] = "RequisiteOf",
1017 [UNIT_REQUISITE_OF_OVERRIDABLE
] = "RequisiteOfOverridable",
1018 [UNIT_WANTED_BY
] = "WantedBy",
1019 [UNIT_BOUND_BY
] = "BoundBy",
1020 [UNIT_CONSISTS_OF
] = "ConsistsOf",
1021 [UNIT_CONFLICTS
] = "Conflicts",
1022 [UNIT_CONFLICTED_BY
] = "ConflictedBy",
1023 [UNIT_BEFORE
] = "Before",
1024 [UNIT_AFTER
] = "After",
1025 [UNIT_ON_FAILURE
] = "OnFailure",
1026 [UNIT_TRIGGERS
] = "Triggers",
1027 [UNIT_TRIGGERED_BY
] = "TriggeredBy",
1028 [UNIT_PROPAGATES_RELOAD_TO
] = "PropagatesReloadTo",
1029 [UNIT_RELOAD_PROPAGATED_FROM
] = "ReloadPropagatedFrom",
1030 [UNIT_JOINS_NAMESPACE_OF
] = "JoinsNamespaceOf",
1031 [UNIT_REFERENCES
] = "References",
1032 [UNIT_REFERENCED_BY
] = "ReferencedBy",
1035 DEFINE_STRING_TABLE_LOOKUP(unit_dependency
, UnitDependency
);