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 "bus-label.h"
27 #include "path-util.h"
28 #include "string-util.h"
31 #include "unit-name.h"
37 bool unit_name_is_valid(const char *n
, UnitNameFlags flags
) {
38 const char *e
, *i
, *at
;
40 assert((flags
& ~(UNIT_NAME_PLAIN
|UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
)) == 0);
42 if (_unlikely_(flags
== 0))
48 if (strlen(n
) >= UNIT_NAME_MAX
)
55 if (unit_type_from_string(e
+ 1) < 0)
58 for (i
= n
, at
= NULL
; i
< e
; i
++) {
63 if (!strchr("@" VALID_CHARS
, *i
))
70 if (flags
& UNIT_NAME_PLAIN
)
74 if (flags
& UNIT_NAME_INSTANCE
)
78 if (flags
& UNIT_NAME_TEMPLATE
)
79 if (at
&& e
== at
+ 1)
85 bool unit_prefix_is_valid(const char *p
) {
87 /* We don't allow additional @ in the prefix string */
92 return in_charset(p
, VALID_CHARS
);
95 bool unit_instance_is_valid(const char *i
) {
97 /* The max length depends on the length of the string, so we
98 * don't really check this here. */
103 /* We allow additional @ in the instance string, we do not
104 * allow them in the prefix! */
106 return in_charset(i
, "@" VALID_CHARS
);
109 bool unit_suffix_is_valid(const char *s
) {
116 if (unit_type_from_string(s
+ 1) < 0)
122 int unit_name_to_prefix(const char *n
, char **ret
) {
129 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
138 s
= strndup(n
, p
- n
);
146 int unit_name_to_instance(const char *n
, char **instance
) {
153 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
156 /* Everything past the first @ and before the last . is the instance */
177 int unit_name_to_prefix_and_instance(const char *n
, char **ret
) {
184 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
191 s
= strndup(n
, d
- n
);
199 UnitType
unit_name_to_type(const char *n
) {
204 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
205 return _UNIT_TYPE_INVALID
;
207 assert_se(e
= strrchr(n
, '.'));
209 return unit_type_from_string(e
+ 1);
212 int unit_name_change_suffix(const char *n
, const char *suffix
, char **ret
) {
220 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
223 if (!unit_suffix_is_valid(suffix
))
226 assert_se(e
= strrchr(n
, '.'));
231 s
= new(char, a
+ b
+ 1);
235 strcpy(mempcpy(s
, n
, a
), suffix
);
241 int unit_name_build(const char *prefix
, const char *instance
, const char *suffix
, char **ret
) {
248 if (!unit_prefix_is_valid(prefix
))
251 if (instance
&& !unit_instance_is_valid(instance
))
254 if (!unit_suffix_is_valid(suffix
))
258 s
= strappend(prefix
, suffix
);
260 s
= strjoin(prefix
, "@", instance
, suffix
, NULL
);
268 static char *do_escape_char(char c
, char *t
) {
273 *(t
++) = hexchar(c
>> 4);
279 static char *do_escape(const char *f
, char *t
) {
283 /* do not create units with a leading '.', like for "/.dotdir" mount points */
285 t
= do_escape_char(*f
, t
);
292 else if (*f
== '-' || *f
== '\\' || !strchr(VALID_CHARS
, *f
))
293 t
= do_escape_char(*f
, t
);
301 char *unit_name_escape(const char *f
) {
306 r
= new(char, strlen(f
)*4+1);
316 int unit_name_unescape(const char *f
, char **ret
) {
317 _cleanup_free_
char *r
= NULL
;
326 for (t
= r
; *f
; f
++) {
329 else if (*f
== '\\') {
343 *(t
++) = (char) (((uint8_t) a
<< 4U) | (uint8_t) b
);
357 int unit_name_path_escape(const char *f
, char **ret
) {
367 path_kill_slashes(p
);
369 if (STR_IN_SET(p
, "/", ""))
374 if (!path_is_safe(p
))
377 /* Truncate trailing slashes */
378 e
= endswith(p
, "/");
382 /* Truncate leading slashes */
386 s
= unit_name_escape(p
);
395 int unit_name_path_unescape(const char *f
, char **ret
) {
411 r
= unit_name_unescape(f
, &w
);
415 /* Don't accept trailing or leading slashes */
416 if (startswith(w
, "/") || endswith(w
, "/")) {
421 /* Prefix a slash again */
422 s
= strappend("/", w
);
427 if (!path_is_safe(s
)) {
441 int unit_name_replace_instance(const char *f
, const char *i
, char **ret
) {
450 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
452 if (!unit_instance_is_valid(i
))
455 assert_se(p
= strchr(f
, '@'));
456 assert_se(e
= strrchr(f
, '.'));
461 s
= new(char, a
+ 1 + b
+ strlen(e
) + 1);
465 strcpy(mempcpy(mempcpy(s
, f
, a
+ 1), i
, b
), e
);
471 int unit_name_template(const char *f
, char **ret
) {
479 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
482 assert_se(p
= strchr(f
, '@'));
483 assert_se(e
= strrchr(f
, '.'));
487 s
= new(char, a
+ 1 + strlen(e
) + 1);
491 strcpy(mempcpy(s
, f
, a
+ 1), e
);
497 int unit_name_from_path(const char *path
, const char *suffix
, char **ret
) {
498 _cleanup_free_
char *p
= NULL
;
506 if (!unit_suffix_is_valid(suffix
))
509 r
= unit_name_path_escape(path
, &p
);
513 s
= strappend(p
, suffix
);
521 int unit_name_from_path_instance(const char *prefix
, const char *path
, const char *suffix
, char **ret
) {
522 _cleanup_free_
char *p
= NULL
;
531 if (!unit_prefix_is_valid(prefix
))
534 if (!unit_suffix_is_valid(suffix
))
537 r
= unit_name_path_escape(path
, &p
);
541 s
= strjoin(prefix
, "@", p
, suffix
, NULL
);
549 int unit_name_to_path(const char *name
, char **ret
) {
550 _cleanup_free_
char *prefix
= NULL
;
555 r
= unit_name_to_prefix(name
, &prefix
);
559 return unit_name_path_unescape(prefix
, ret
);
562 char *unit_dbus_path_from_name(const char *name
) {
563 _cleanup_free_
char *e
= NULL
;
567 e
= bus_label_escape(name
);
571 return strappend("/org/freedesktop/systemd1/unit/", e
);
574 int unit_name_from_dbus_path(const char *path
, char **name
) {
578 e
= startswith(path
, "/org/freedesktop/systemd1/unit/");
582 n
= bus_label_unescape(e
);
590 const char* unit_dbus_interface_from_type(UnitType t
) {
592 static const char *const table
[_UNIT_TYPE_MAX
] = {
593 [UNIT_SERVICE
] = "org.freedesktop.systemd1.Service",
594 [UNIT_SOCKET
] = "org.freedesktop.systemd1.Socket",
595 [UNIT_BUSNAME
] = "org.freedesktop.systemd1.BusName",
596 [UNIT_TARGET
] = "org.freedesktop.systemd1.Target",
597 [UNIT_SNAPSHOT
] = "org.freedesktop.systemd1.Snapshot",
598 [UNIT_DEVICE
] = "org.freedesktop.systemd1.Device",
599 [UNIT_MOUNT
] = "org.freedesktop.systemd1.Mount",
600 [UNIT_AUTOMOUNT
] = "org.freedesktop.systemd1.Automount",
601 [UNIT_SWAP
] = "org.freedesktop.systemd1.Swap",
602 [UNIT_TIMER
] = "org.freedesktop.systemd1.Timer",
603 [UNIT_PATH
] = "org.freedesktop.systemd1.Path",
604 [UNIT_SLICE
] = "org.freedesktop.systemd1.Slice",
605 [UNIT_SCOPE
] = "org.freedesktop.systemd1.Scope",
610 if (t
>= _UNIT_TYPE_MAX
)
616 const char *unit_dbus_interface_from_name(const char *name
) {
619 t
= unit_name_to_type(name
);
623 return unit_dbus_interface_from_type(t
);
626 static char *do_escape_mangle(const char *f
, UnitNameMangle allow_globs
, char *t
) {
627 const char *valid_chars
;
630 assert(IN_SET(allow_globs
, UNIT_NAME_GLOB
, UNIT_NAME_NOGLOB
));
633 /* We'll only escape the obvious characters here, to play
636 valid_chars
= allow_globs
== UNIT_NAME_GLOB
? "@" VALID_CHARS
"[]!-*?" : "@" VALID_CHARS
;
641 else if (!strchr(valid_chars
, *f
))
642 t
= do_escape_char(*f
, t
);
651 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
652 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
653 * except that @suffix is appended if a valid unit suffix is not present.
655 * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
657 int unit_name_mangle_with_suffix(const char *name
, UnitNameMangle allow_globs
, const char *suffix
, char **ret
) {
665 if (isempty(name
)) /* We cannot mangle empty unit names to become valid, sorry. */
668 if (!unit_suffix_is_valid(suffix
))
671 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
672 /* No mangling necessary... */
681 if (is_device_path(name
)) {
682 r
= unit_name_from_path(name
, ".device", ret
);
689 if (path_is_absolute(name
)) {
690 r
= unit_name_from_path(name
, ".mount", ret
);
697 s
= new(char, strlen(name
) * 4 + strlen(suffix
) + 1);
701 t
= do_escape_mangle(name
, allow_globs
, s
);
704 if (unit_name_to_type(s
) < 0)
711 int slice_build_parent_slice(const char *slice
, char **ret
) {
718 if (!slice_name_is_valid(slice
))
721 if (streq(slice
, "-.slice")) {
730 dash
= strrchr(s
, '-');
732 strcpy(dash
, ".slice");
734 r
= free_and_strdup(&s
, "-.slice");
745 int slice_build_subslice(const char *slice
, const char*name
, char **ret
) {
752 if (!slice_name_is_valid(slice
))
755 if (!unit_prefix_is_valid(name
))
758 if (streq(slice
, "-.slice"))
759 subslice
= strappend(name
, ".slice");
763 assert_se(e
= endswith(slice
, ".slice"));
765 subslice
= new(char, (e
- slice
) + 1 + strlen(name
) + 6 + 1);
769 stpcpy(stpcpy(stpcpy(mempcpy(subslice
, slice
, e
- slice
), "-"), name
), ".slice");
776 bool slice_name_is_valid(const char *name
) {
780 if (!unit_name_is_valid(name
, UNIT_NAME_PLAIN
))
783 if (streq(name
, "-.slice"))
786 e
= endswith(name
, ".slice");
790 for (p
= name
; p
< e
; p
++) {
794 /* Don't allow initial dash */
798 /* Don't allow multiple dashes */
807 /* Don't allow trailing hash */
814 static const char* const unit_type_table
[_UNIT_TYPE_MAX
] = {
815 [UNIT_SERVICE
] = "service",
816 [UNIT_SOCKET
] = "socket",
817 [UNIT_BUSNAME
] = "busname",
818 [UNIT_TARGET
] = "target",
819 [UNIT_SNAPSHOT
] = "snapshot",
820 [UNIT_DEVICE
] = "device",
821 [UNIT_MOUNT
] = "mount",
822 [UNIT_AUTOMOUNT
] = "automount",
823 [UNIT_SWAP
] = "swap",
824 [UNIT_TIMER
] = "timer",
825 [UNIT_PATH
] = "path",
826 [UNIT_SLICE
] = "slice",
827 [UNIT_SCOPE
] = "scope",
830 DEFINE_STRING_TABLE_LOOKUP(unit_type
, UnitType
);
832 static const char* const unit_load_state_table
[_UNIT_LOAD_STATE_MAX
] = {
833 [UNIT_STUB
] = "stub",
834 [UNIT_LOADED
] = "loaded",
835 [UNIT_NOT_FOUND
] = "not-found",
836 [UNIT_ERROR
] = "error",
837 [UNIT_MERGED
] = "merged",
838 [UNIT_MASKED
] = "masked"
841 DEFINE_STRING_TABLE_LOOKUP(unit_load_state
, UnitLoadState
);
843 static const char* const unit_active_state_table
[_UNIT_ACTIVE_STATE_MAX
] = {
844 [UNIT_ACTIVE
] = "active",
845 [UNIT_RELOADING
] = "reloading",
846 [UNIT_INACTIVE
] = "inactive",
847 [UNIT_FAILED
] = "failed",
848 [UNIT_ACTIVATING
] = "activating",
849 [UNIT_DEACTIVATING
] = "deactivating"
852 DEFINE_STRING_TABLE_LOOKUP(unit_active_state
, UnitActiveState
);
854 static const char* const automount_state_table
[_AUTOMOUNT_STATE_MAX
] = {
855 [AUTOMOUNT_DEAD
] = "dead",
856 [AUTOMOUNT_WAITING
] = "waiting",
857 [AUTOMOUNT_RUNNING
] = "running",
858 [AUTOMOUNT_FAILED
] = "failed"
861 DEFINE_STRING_TABLE_LOOKUP(automount_state
, AutomountState
);
863 static const char* const busname_state_table
[_BUSNAME_STATE_MAX
] = {
864 [BUSNAME_DEAD
] = "dead",
865 [BUSNAME_MAKING
] = "making",
866 [BUSNAME_REGISTERED
] = "registered",
867 [BUSNAME_LISTENING
] = "listening",
868 [BUSNAME_RUNNING
] = "running",
869 [BUSNAME_SIGTERM
] = "sigterm",
870 [BUSNAME_SIGKILL
] = "sigkill",
871 [BUSNAME_FAILED
] = "failed",
874 DEFINE_STRING_TABLE_LOOKUP(busname_state
, BusNameState
);
876 static const char* const device_state_table
[_DEVICE_STATE_MAX
] = {
877 [DEVICE_DEAD
] = "dead",
878 [DEVICE_TENTATIVE
] = "tentative",
879 [DEVICE_PLUGGED
] = "plugged",
882 DEFINE_STRING_TABLE_LOOKUP(device_state
, DeviceState
);
884 static const char* const mount_state_table
[_MOUNT_STATE_MAX
] = {
885 [MOUNT_DEAD
] = "dead",
886 [MOUNT_MOUNTING
] = "mounting",
887 [MOUNT_MOUNTING_DONE
] = "mounting-done",
888 [MOUNT_MOUNTED
] = "mounted",
889 [MOUNT_REMOUNTING
] = "remounting",
890 [MOUNT_UNMOUNTING
] = "unmounting",
891 [MOUNT_MOUNTING_SIGTERM
] = "mounting-sigterm",
892 [MOUNT_MOUNTING_SIGKILL
] = "mounting-sigkill",
893 [MOUNT_REMOUNTING_SIGTERM
] = "remounting-sigterm",
894 [MOUNT_REMOUNTING_SIGKILL
] = "remounting-sigkill",
895 [MOUNT_UNMOUNTING_SIGTERM
] = "unmounting-sigterm",
896 [MOUNT_UNMOUNTING_SIGKILL
] = "unmounting-sigkill",
897 [MOUNT_FAILED
] = "failed"
900 DEFINE_STRING_TABLE_LOOKUP(mount_state
, MountState
);
902 static const char* const path_state_table
[_PATH_STATE_MAX
] = {
903 [PATH_DEAD
] = "dead",
904 [PATH_WAITING
] = "waiting",
905 [PATH_RUNNING
] = "running",
906 [PATH_FAILED
] = "failed"
909 DEFINE_STRING_TABLE_LOOKUP(path_state
, PathState
);
911 static const char* const scope_state_table
[_SCOPE_STATE_MAX
] = {
912 [SCOPE_DEAD
] = "dead",
913 [SCOPE_RUNNING
] = "running",
914 [SCOPE_ABANDONED
] = "abandoned",
915 [SCOPE_STOP_SIGTERM
] = "stop-sigterm",
916 [SCOPE_STOP_SIGKILL
] = "stop-sigkill",
917 [SCOPE_FAILED
] = "failed",
920 DEFINE_STRING_TABLE_LOOKUP(scope_state
, ScopeState
);
922 static const char* const service_state_table
[_SERVICE_STATE_MAX
] = {
923 [SERVICE_DEAD
] = "dead",
924 [SERVICE_START_PRE
] = "start-pre",
925 [SERVICE_START
] = "start",
926 [SERVICE_START_POST
] = "start-post",
927 [SERVICE_RUNNING
] = "running",
928 [SERVICE_EXITED
] = "exited",
929 [SERVICE_RELOAD
] = "reload",
930 [SERVICE_STOP
] = "stop",
931 [SERVICE_STOP_SIGABRT
] = "stop-sigabrt",
932 [SERVICE_STOP_SIGTERM
] = "stop-sigterm",
933 [SERVICE_STOP_SIGKILL
] = "stop-sigkill",
934 [SERVICE_STOP_POST
] = "stop-post",
935 [SERVICE_FINAL_SIGTERM
] = "final-sigterm",
936 [SERVICE_FINAL_SIGKILL
] = "final-sigkill",
937 [SERVICE_FAILED
] = "failed",
938 [SERVICE_AUTO_RESTART
] = "auto-restart",
941 DEFINE_STRING_TABLE_LOOKUP(service_state
, ServiceState
);
943 static const char* const slice_state_table
[_SLICE_STATE_MAX
] = {
944 [SLICE_DEAD
] = "dead",
945 [SLICE_ACTIVE
] = "active"
948 DEFINE_STRING_TABLE_LOOKUP(slice_state
, SliceState
);
950 static const char* const snapshot_state_table
[_SNAPSHOT_STATE_MAX
] = {
951 [SNAPSHOT_DEAD
] = "dead",
952 [SNAPSHOT_ACTIVE
] = "active"
955 DEFINE_STRING_TABLE_LOOKUP(snapshot_state
, SnapshotState
);
957 static const char* const socket_state_table
[_SOCKET_STATE_MAX
] = {
958 [SOCKET_DEAD
] = "dead",
959 [SOCKET_START_PRE
] = "start-pre",
960 [SOCKET_START_CHOWN
] = "start-chown",
961 [SOCKET_START_POST
] = "start-post",
962 [SOCKET_LISTENING
] = "listening",
963 [SOCKET_RUNNING
] = "running",
964 [SOCKET_STOP_PRE
] = "stop-pre",
965 [SOCKET_STOP_PRE_SIGTERM
] = "stop-pre-sigterm",
966 [SOCKET_STOP_PRE_SIGKILL
] = "stop-pre-sigkill",
967 [SOCKET_STOP_POST
] = "stop-post",
968 [SOCKET_FINAL_SIGTERM
] = "final-sigterm",
969 [SOCKET_FINAL_SIGKILL
] = "final-sigkill",
970 [SOCKET_FAILED
] = "failed"
973 DEFINE_STRING_TABLE_LOOKUP(socket_state
, SocketState
);
975 static const char* const swap_state_table
[_SWAP_STATE_MAX
] = {
976 [SWAP_DEAD
] = "dead",
977 [SWAP_ACTIVATING
] = "activating",
978 [SWAP_ACTIVATING_DONE
] = "activating-done",
979 [SWAP_ACTIVE
] = "active",
980 [SWAP_DEACTIVATING
] = "deactivating",
981 [SWAP_ACTIVATING_SIGTERM
] = "activating-sigterm",
982 [SWAP_ACTIVATING_SIGKILL
] = "activating-sigkill",
983 [SWAP_DEACTIVATING_SIGTERM
] = "deactivating-sigterm",
984 [SWAP_DEACTIVATING_SIGKILL
] = "deactivating-sigkill",
985 [SWAP_FAILED
] = "failed"
988 DEFINE_STRING_TABLE_LOOKUP(swap_state
, SwapState
);
990 static const char* const target_state_table
[_TARGET_STATE_MAX
] = {
991 [TARGET_DEAD
] = "dead",
992 [TARGET_ACTIVE
] = "active"
995 DEFINE_STRING_TABLE_LOOKUP(target_state
, TargetState
);
997 static const char* const timer_state_table
[_TIMER_STATE_MAX
] = {
998 [TIMER_DEAD
] = "dead",
999 [TIMER_WAITING
] = "waiting",
1000 [TIMER_RUNNING
] = "running",
1001 [TIMER_ELAPSED
] = "elapsed",
1002 [TIMER_FAILED
] = "failed"
1005 DEFINE_STRING_TABLE_LOOKUP(timer_state
, TimerState
);
1007 static const char* const unit_dependency_table
[_UNIT_DEPENDENCY_MAX
] = {
1008 [UNIT_REQUIRES
] = "Requires",
1009 [UNIT_REQUIRES_OVERRIDABLE
] = "RequiresOverridable",
1010 [UNIT_REQUISITE
] = "Requisite",
1011 [UNIT_REQUISITE_OVERRIDABLE
] = "RequisiteOverridable",
1012 [UNIT_WANTS
] = "Wants",
1013 [UNIT_BINDS_TO
] = "BindsTo",
1014 [UNIT_PART_OF
] = "PartOf",
1015 [UNIT_REQUIRED_BY
] = "RequiredBy",
1016 [UNIT_REQUIRED_BY_OVERRIDABLE
] = "RequiredByOverridable",
1017 [UNIT_REQUISITE_OF
] = "RequisiteOf",
1018 [UNIT_REQUISITE_OF_OVERRIDABLE
] = "RequisiteOfOverridable",
1019 [UNIT_WANTED_BY
] = "WantedBy",
1020 [UNIT_BOUND_BY
] = "BoundBy",
1021 [UNIT_CONSISTS_OF
] = "ConsistsOf",
1022 [UNIT_CONFLICTS
] = "Conflicts",
1023 [UNIT_CONFLICTED_BY
] = "ConflictedBy",
1024 [UNIT_BEFORE
] = "Before",
1025 [UNIT_AFTER
] = "After",
1026 [UNIT_ON_FAILURE
] = "OnFailure",
1027 [UNIT_TRIGGERS
] = "Triggers",
1028 [UNIT_TRIGGERED_BY
] = "TriggeredBy",
1029 [UNIT_PROPAGATES_RELOAD_TO
] = "PropagatesReloadTo",
1030 [UNIT_RELOAD_PROPAGATED_FROM
] = "ReloadPropagatedFrom",
1031 [UNIT_JOINS_NAMESPACE_OF
] = "JoinsNamespaceOf",
1032 [UNIT_REFERENCES
] = "References",
1033 [UNIT_REFERENCED_BY
] = "ReferencedBy",
1036 DEFINE_STRING_TABLE_LOOKUP(unit_dependency
, UnitDependency
);