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 "hexdecoct.h"
32 #include "path-util.h"
33 #include "string-table.h"
34 #include "string-util.h"
36 #include "unit-name.h"
42 bool unit_name_is_valid(const char *n
, UnitNameFlags flags
) {
43 const char *e
, *i
, *at
;
45 assert((flags
& ~(UNIT_NAME_PLAIN
|UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
)) == 0);
47 if (_unlikely_(flags
== 0))
53 if (strlen(n
) >= UNIT_NAME_MAX
)
60 if (unit_type_from_string(e
+ 1) < 0)
63 for (i
= n
, at
= NULL
; i
< e
; i
++) {
68 if (!strchr("@" VALID_CHARS
, *i
))
75 if (flags
& UNIT_NAME_PLAIN
)
79 if (flags
& UNIT_NAME_INSTANCE
)
83 if (flags
& UNIT_NAME_TEMPLATE
)
84 if (at
&& e
== at
+ 1)
90 bool unit_prefix_is_valid(const char *p
) {
92 /* We don't allow additional @ in the prefix string */
97 return in_charset(p
, VALID_CHARS
);
100 bool unit_instance_is_valid(const char *i
) {
102 /* The max length depends on the length of the string, so we
103 * don't really check this here. */
108 /* We allow additional @ in the instance string, we do not
109 * allow them in the prefix! */
111 return in_charset(i
, "@" VALID_CHARS
);
114 bool unit_suffix_is_valid(const char *s
) {
121 if (unit_type_from_string(s
+ 1) < 0)
127 int unit_name_to_prefix(const char *n
, char **ret
) {
134 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
143 s
= strndup(n
, p
- n
);
151 int unit_name_to_instance(const char *n
, char **instance
) {
158 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
161 /* Everything past the first @ and before the last . is the instance */
182 int unit_name_to_prefix_and_instance(const char *n
, char **ret
) {
189 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
196 s
= strndup(n
, d
- n
);
204 UnitType
unit_name_to_type(const char *n
) {
209 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
210 return _UNIT_TYPE_INVALID
;
212 assert_se(e
= strrchr(n
, '.'));
214 return unit_type_from_string(e
+ 1);
217 int unit_name_change_suffix(const char *n
, const char *suffix
, char **ret
) {
225 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
228 if (!unit_suffix_is_valid(suffix
))
231 assert_se(e
= strrchr(n
, '.'));
236 s
= new(char, a
+ b
+ 1);
240 strcpy(mempcpy(s
, n
, a
), suffix
);
246 int unit_name_build(const char *prefix
, const char *instance
, const char *suffix
, char **ret
) {
253 if (!unit_prefix_is_valid(prefix
))
256 if (instance
&& !unit_instance_is_valid(instance
))
259 if (!unit_suffix_is_valid(suffix
))
263 s
= strappend(prefix
, suffix
);
265 s
= strjoin(prefix
, "@", instance
, suffix
, NULL
);
273 static char *do_escape_char(char c
, char *t
) {
278 *(t
++) = hexchar(c
>> 4);
284 static char *do_escape(const char *f
, char *t
) {
288 /* do not create units with a leading '.', like for "/.dotdir" mount points */
290 t
= do_escape_char(*f
, t
);
297 else if (*f
== '-' || *f
== '\\' || !strchr(VALID_CHARS
, *f
))
298 t
= do_escape_char(*f
, t
);
306 char *unit_name_escape(const char *f
) {
311 r
= new(char, strlen(f
)*4+1);
321 int unit_name_unescape(const char *f
, char **ret
) {
322 _cleanup_free_
char *r
= NULL
;
331 for (t
= r
; *f
; f
++) {
334 else if (*f
== '\\') {
348 *(t
++) = (char) (((uint8_t) a
<< 4U) | (uint8_t) b
);
362 int unit_name_path_escape(const char *f
, char **ret
) {
372 path_kill_slashes(p
);
374 if (STR_IN_SET(p
, "/", ""))
379 if (!path_is_safe(p
))
382 /* Truncate trailing slashes */
383 e
= endswith(p
, "/");
387 /* Truncate leading slashes */
391 s
= unit_name_escape(p
);
400 int unit_name_path_unescape(const char *f
, char **ret
) {
416 r
= unit_name_unescape(f
, &w
);
420 /* Don't accept trailing or leading slashes */
421 if (startswith(w
, "/") || endswith(w
, "/")) {
426 /* Prefix a slash again */
427 s
= strappend("/", w
);
432 if (!path_is_safe(s
)) {
446 int unit_name_replace_instance(const char *f
, const char *i
, char **ret
) {
455 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
457 if (!unit_instance_is_valid(i
))
460 assert_se(p
= strchr(f
, '@'));
461 assert_se(e
= strrchr(f
, '.'));
466 s
= new(char, a
+ 1 + b
+ strlen(e
) + 1);
470 strcpy(mempcpy(mempcpy(s
, f
, a
+ 1), i
, b
), e
);
476 int unit_name_template(const char *f
, char **ret
) {
484 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
487 assert_se(p
= strchr(f
, '@'));
488 assert_se(e
= strrchr(f
, '.'));
492 s
= new(char, a
+ 1 + strlen(e
) + 1);
496 strcpy(mempcpy(s
, f
, a
+ 1), e
);
502 int unit_name_from_path(const char *path
, const char *suffix
, char **ret
) {
503 _cleanup_free_
char *p
= NULL
;
511 if (!unit_suffix_is_valid(suffix
))
514 r
= unit_name_path_escape(path
, &p
);
518 s
= strappend(p
, suffix
);
526 int unit_name_from_path_instance(const char *prefix
, const char *path
, const char *suffix
, char **ret
) {
527 _cleanup_free_
char *p
= NULL
;
536 if (!unit_prefix_is_valid(prefix
))
539 if (!unit_suffix_is_valid(suffix
))
542 r
= unit_name_path_escape(path
, &p
);
546 s
= strjoin(prefix
, "@", p
, suffix
, NULL
);
554 int unit_name_to_path(const char *name
, char **ret
) {
555 _cleanup_free_
char *prefix
= NULL
;
560 r
= unit_name_to_prefix(name
, &prefix
);
564 return unit_name_path_unescape(prefix
, ret
);
567 char *unit_dbus_path_from_name(const char *name
) {
568 _cleanup_free_
char *e
= NULL
;
572 e
= bus_label_escape(name
);
576 return strappend("/org/freedesktop/systemd1/unit/", e
);
579 int unit_name_from_dbus_path(const char *path
, char **name
) {
583 e
= startswith(path
, "/org/freedesktop/systemd1/unit/");
587 n
= bus_label_unescape(e
);
595 const char* unit_dbus_interface_from_type(UnitType t
) {
597 static const char *const table
[_UNIT_TYPE_MAX
] = {
598 [UNIT_SERVICE
] = "org.freedesktop.systemd1.Service",
599 [UNIT_SOCKET
] = "org.freedesktop.systemd1.Socket",
600 [UNIT_BUSNAME
] = "org.freedesktop.systemd1.BusName",
601 [UNIT_TARGET
] = "org.freedesktop.systemd1.Target",
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");
749 int slice_build_subslice(const char *slice
, const char*name
, char **ret
) {
756 if (!slice_name_is_valid(slice
))
759 if (!unit_prefix_is_valid(name
))
762 if (streq(slice
, "-.slice"))
763 subslice
= strappend(name
, ".slice");
767 assert_se(e
= endswith(slice
, ".slice"));
769 subslice
= new(char, (e
- slice
) + 1 + strlen(name
) + 6 + 1);
773 stpcpy(stpcpy(stpcpy(mempcpy(subslice
, slice
, e
- slice
), "-"), name
), ".slice");
780 bool slice_name_is_valid(const char *name
) {
784 if (!unit_name_is_valid(name
, UNIT_NAME_PLAIN
))
787 if (streq(name
, "-.slice"))
790 e
= endswith(name
, ".slice");
794 for (p
= name
; p
< e
; p
++) {
798 /* Don't allow initial dash */
802 /* Don't allow multiple dashes */
811 /* Don't allow trailing hash */
818 static const char* const unit_type_table
[_UNIT_TYPE_MAX
] = {
819 [UNIT_SERVICE
] = "service",
820 [UNIT_SOCKET
] = "socket",
821 [UNIT_BUSNAME
] = "busname",
822 [UNIT_TARGET
] = "target",
823 [UNIT_DEVICE
] = "device",
824 [UNIT_MOUNT
] = "mount",
825 [UNIT_AUTOMOUNT
] = "automount",
826 [UNIT_SWAP
] = "swap",
827 [UNIT_TIMER
] = "timer",
828 [UNIT_PATH
] = "path",
829 [UNIT_SLICE
] = "slice",
830 [UNIT_SCOPE
] = "scope",
833 DEFINE_STRING_TABLE_LOOKUP(unit_type
, UnitType
);
835 static const char* const unit_load_state_table
[_UNIT_LOAD_STATE_MAX
] = {
836 [UNIT_STUB
] = "stub",
837 [UNIT_LOADED
] = "loaded",
838 [UNIT_NOT_FOUND
] = "not-found",
839 [UNIT_ERROR
] = "error",
840 [UNIT_MERGED
] = "merged",
841 [UNIT_MASKED
] = "masked"
844 DEFINE_STRING_TABLE_LOOKUP(unit_load_state
, UnitLoadState
);
846 static const char* const unit_active_state_table
[_UNIT_ACTIVE_STATE_MAX
] = {
847 [UNIT_ACTIVE
] = "active",
848 [UNIT_RELOADING
] = "reloading",
849 [UNIT_INACTIVE
] = "inactive",
850 [UNIT_FAILED
] = "failed",
851 [UNIT_ACTIVATING
] = "activating",
852 [UNIT_DEACTIVATING
] = "deactivating"
855 DEFINE_STRING_TABLE_LOOKUP(unit_active_state
, UnitActiveState
);
857 static const char* const automount_state_table
[_AUTOMOUNT_STATE_MAX
] = {
858 [AUTOMOUNT_DEAD
] = "dead",
859 [AUTOMOUNT_WAITING
] = "waiting",
860 [AUTOMOUNT_RUNNING
] = "running",
861 [AUTOMOUNT_FAILED
] = "failed"
864 DEFINE_STRING_TABLE_LOOKUP(automount_state
, AutomountState
);
866 static const char* const busname_state_table
[_BUSNAME_STATE_MAX
] = {
867 [BUSNAME_DEAD
] = "dead",
868 [BUSNAME_MAKING
] = "making",
869 [BUSNAME_REGISTERED
] = "registered",
870 [BUSNAME_LISTENING
] = "listening",
871 [BUSNAME_RUNNING
] = "running",
872 [BUSNAME_SIGTERM
] = "sigterm",
873 [BUSNAME_SIGKILL
] = "sigkill",
874 [BUSNAME_FAILED
] = "failed",
877 DEFINE_STRING_TABLE_LOOKUP(busname_state
, BusNameState
);
879 static const char* const device_state_table
[_DEVICE_STATE_MAX
] = {
880 [DEVICE_DEAD
] = "dead",
881 [DEVICE_TENTATIVE
] = "tentative",
882 [DEVICE_PLUGGED
] = "plugged",
885 DEFINE_STRING_TABLE_LOOKUP(device_state
, DeviceState
);
887 static const char* const mount_state_table
[_MOUNT_STATE_MAX
] = {
888 [MOUNT_DEAD
] = "dead",
889 [MOUNT_MOUNTING
] = "mounting",
890 [MOUNT_MOUNTING_DONE
] = "mounting-done",
891 [MOUNT_MOUNTED
] = "mounted",
892 [MOUNT_REMOUNTING
] = "remounting",
893 [MOUNT_UNMOUNTING
] = "unmounting",
894 [MOUNT_MOUNTING_SIGTERM
] = "mounting-sigterm",
895 [MOUNT_MOUNTING_SIGKILL
] = "mounting-sigkill",
896 [MOUNT_REMOUNTING_SIGTERM
] = "remounting-sigterm",
897 [MOUNT_REMOUNTING_SIGKILL
] = "remounting-sigkill",
898 [MOUNT_UNMOUNTING_SIGTERM
] = "unmounting-sigterm",
899 [MOUNT_UNMOUNTING_SIGKILL
] = "unmounting-sigkill",
900 [MOUNT_FAILED
] = "failed"
903 DEFINE_STRING_TABLE_LOOKUP(mount_state
, MountState
);
905 static const char* const path_state_table
[_PATH_STATE_MAX
] = {
906 [PATH_DEAD
] = "dead",
907 [PATH_WAITING
] = "waiting",
908 [PATH_RUNNING
] = "running",
909 [PATH_FAILED
] = "failed"
912 DEFINE_STRING_TABLE_LOOKUP(path_state
, PathState
);
914 static const char* const scope_state_table
[_SCOPE_STATE_MAX
] = {
915 [SCOPE_DEAD
] = "dead",
916 [SCOPE_RUNNING
] = "running",
917 [SCOPE_ABANDONED
] = "abandoned",
918 [SCOPE_STOP_SIGTERM
] = "stop-sigterm",
919 [SCOPE_STOP_SIGKILL
] = "stop-sigkill",
920 [SCOPE_FAILED
] = "failed",
923 DEFINE_STRING_TABLE_LOOKUP(scope_state
, ScopeState
);
925 static const char* const service_state_table
[_SERVICE_STATE_MAX
] = {
926 [SERVICE_DEAD
] = "dead",
927 [SERVICE_START_PRE
] = "start-pre",
928 [SERVICE_START
] = "start",
929 [SERVICE_START_POST
] = "start-post",
930 [SERVICE_RUNNING
] = "running",
931 [SERVICE_EXITED
] = "exited",
932 [SERVICE_RELOAD
] = "reload",
933 [SERVICE_STOP
] = "stop",
934 [SERVICE_STOP_SIGABRT
] = "stop-sigabrt",
935 [SERVICE_STOP_SIGTERM
] = "stop-sigterm",
936 [SERVICE_STOP_SIGKILL
] = "stop-sigkill",
937 [SERVICE_STOP_POST
] = "stop-post",
938 [SERVICE_FINAL_SIGTERM
] = "final-sigterm",
939 [SERVICE_FINAL_SIGKILL
] = "final-sigkill",
940 [SERVICE_FAILED
] = "failed",
941 [SERVICE_AUTO_RESTART
] = "auto-restart",
944 DEFINE_STRING_TABLE_LOOKUP(service_state
, ServiceState
);
946 static const char* const slice_state_table
[_SLICE_STATE_MAX
] = {
947 [SLICE_DEAD
] = "dead",
948 [SLICE_ACTIVE
] = "active"
951 DEFINE_STRING_TABLE_LOOKUP(slice_state
, SliceState
);
953 static const char* const socket_state_table
[_SOCKET_STATE_MAX
] = {
954 [SOCKET_DEAD
] = "dead",
955 [SOCKET_START_PRE
] = "start-pre",
956 [SOCKET_START_CHOWN
] = "start-chown",
957 [SOCKET_START_POST
] = "start-post",
958 [SOCKET_LISTENING
] = "listening",
959 [SOCKET_RUNNING
] = "running",
960 [SOCKET_STOP_PRE
] = "stop-pre",
961 [SOCKET_STOP_PRE_SIGTERM
] = "stop-pre-sigterm",
962 [SOCKET_STOP_PRE_SIGKILL
] = "stop-pre-sigkill",
963 [SOCKET_STOP_POST
] = "stop-post",
964 [SOCKET_FINAL_SIGTERM
] = "final-sigterm",
965 [SOCKET_FINAL_SIGKILL
] = "final-sigkill",
966 [SOCKET_FAILED
] = "failed"
969 DEFINE_STRING_TABLE_LOOKUP(socket_state
, SocketState
);
971 static const char* const swap_state_table
[_SWAP_STATE_MAX
] = {
972 [SWAP_DEAD
] = "dead",
973 [SWAP_ACTIVATING
] = "activating",
974 [SWAP_ACTIVATING_DONE
] = "activating-done",
975 [SWAP_ACTIVE
] = "active",
976 [SWAP_DEACTIVATING
] = "deactivating",
977 [SWAP_ACTIVATING_SIGTERM
] = "activating-sigterm",
978 [SWAP_ACTIVATING_SIGKILL
] = "activating-sigkill",
979 [SWAP_DEACTIVATING_SIGTERM
] = "deactivating-sigterm",
980 [SWAP_DEACTIVATING_SIGKILL
] = "deactivating-sigkill",
981 [SWAP_FAILED
] = "failed"
984 DEFINE_STRING_TABLE_LOOKUP(swap_state
, SwapState
);
986 static const char* const target_state_table
[_TARGET_STATE_MAX
] = {
987 [TARGET_DEAD
] = "dead",
988 [TARGET_ACTIVE
] = "active"
991 DEFINE_STRING_TABLE_LOOKUP(target_state
, TargetState
);
993 static const char* const timer_state_table
[_TIMER_STATE_MAX
] = {
994 [TIMER_DEAD
] = "dead",
995 [TIMER_WAITING
] = "waiting",
996 [TIMER_RUNNING
] = "running",
997 [TIMER_ELAPSED
] = "elapsed",
998 [TIMER_FAILED
] = "failed"
1001 DEFINE_STRING_TABLE_LOOKUP(timer_state
, TimerState
);
1003 static const char* const unit_dependency_table
[_UNIT_DEPENDENCY_MAX
] = {
1004 [UNIT_REQUIRES
] = "Requires",
1005 [UNIT_REQUISITE
] = "Requisite",
1006 [UNIT_WANTS
] = "Wants",
1007 [UNIT_BINDS_TO
] = "BindsTo",
1008 [UNIT_PART_OF
] = "PartOf",
1009 [UNIT_REQUIRED_BY
] = "RequiredBy",
1010 [UNIT_REQUISITE_OF
] = "RequisiteOf",
1011 [UNIT_WANTED_BY
] = "WantedBy",
1012 [UNIT_BOUND_BY
] = "BoundBy",
1013 [UNIT_CONSISTS_OF
] = "ConsistsOf",
1014 [UNIT_CONFLICTS
] = "Conflicts",
1015 [UNIT_CONFLICTED_BY
] = "ConflictedBy",
1016 [UNIT_BEFORE
] = "Before",
1017 [UNIT_AFTER
] = "After",
1018 [UNIT_ON_FAILURE
] = "OnFailure",
1019 [UNIT_TRIGGERS
] = "Triggers",
1020 [UNIT_TRIGGERED_BY
] = "TriggeredBy",
1021 [UNIT_PROPAGATES_RELOAD_TO
] = "PropagatesReloadTo",
1022 [UNIT_RELOAD_PROPAGATED_FROM
] = "ReloadPropagatedFrom",
1023 [UNIT_JOINS_NAMESPACE_OF
] = "JoinsNamespaceOf",
1024 [UNIT_REFERENCES
] = "References",
1025 [UNIT_REFERENCED_BY
] = "ReferencedBy",
1028 DEFINE_STRING_TABLE_LOOKUP(unit_dependency
, UnitDependency
);