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 "alloc-util.h"
26 #include "bus-label.h"
28 #include "hexdecoct.h"
29 #include "path-util.h"
30 #include "string-table.h"
31 #include "string-util.h"
33 #include "unit-name.h"
40 bool unit_name_is_valid(const char *n
, UnitNameFlags flags
) {
41 const char *e
, *i
, *at
;
43 assert((flags
& ~(UNIT_NAME_PLAIN
|UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
)) == 0);
45 if (_unlikely_(flags
== 0))
51 if (strlen(n
) >= UNIT_NAME_MAX
)
58 if (unit_type_from_string(e
+ 1) < 0)
61 for (i
= n
, at
= NULL
; i
< e
; i
++) {
66 if (!strchr("@" VALID_CHARS
, *i
))
73 if (flags
& UNIT_NAME_PLAIN
)
77 if (flags
& UNIT_NAME_INSTANCE
)
81 if (flags
& UNIT_NAME_TEMPLATE
)
82 if (at
&& e
== at
+ 1)
88 bool unit_prefix_is_valid(const char *p
) {
90 /* We don't allow additional @ in the prefix string */
95 return in_charset(p
, VALID_CHARS
);
98 bool unit_instance_is_valid(const char *i
) {
100 /* The max length depends on the length of the string, so we
101 * don't really check this here. */
106 /* We allow additional @ in the instance string, we do not
107 * allow them in the prefix! */
109 return in_charset(i
, "@" VALID_CHARS
);
112 bool unit_suffix_is_valid(const char *s
) {
119 if (unit_type_from_string(s
+ 1) < 0)
125 int unit_name_to_prefix(const char *n
, char **ret
) {
132 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
141 s
= strndup(n
, p
- n
);
149 int unit_name_to_instance(const char *n
, char **instance
) {
156 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
159 /* Everything past the first @ and before the last . is the instance */
180 int unit_name_to_prefix_and_instance(const char *n
, char **ret
) {
187 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
194 s
= strndup(n
, d
- n
);
202 UnitType
unit_name_to_type(const char *n
) {
207 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
208 return _UNIT_TYPE_INVALID
;
210 assert_se(e
= strrchr(n
, '.'));
212 return unit_type_from_string(e
+ 1);
215 int unit_name_change_suffix(const char *n
, const char *suffix
, char **ret
) {
223 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
226 if (!unit_suffix_is_valid(suffix
))
229 assert_se(e
= strrchr(n
, '.'));
234 s
= new(char, a
+ b
+ 1);
238 strcpy(mempcpy(s
, n
, a
), suffix
);
244 int unit_name_build(const char *prefix
, const char *instance
, const char *suffix
, char **ret
) {
251 if (!unit_prefix_is_valid(prefix
))
254 if (instance
&& !unit_instance_is_valid(instance
))
257 if (!unit_suffix_is_valid(suffix
))
261 s
= strappend(prefix
, suffix
);
263 s
= strjoin(prefix
, "@", instance
, suffix
, NULL
);
271 static char *do_escape_char(char c
, char *t
) {
276 *(t
++) = hexchar(c
>> 4);
282 static char *do_escape(const char *f
, char *t
) {
286 /* do not create units with a leading '.', like for "/.dotdir" mount points */
288 t
= do_escape_char(*f
, t
);
295 else if (*f
== '-' || *f
== '\\' || !strchr(VALID_CHARS
, *f
))
296 t
= do_escape_char(*f
, t
);
304 char *unit_name_escape(const char *f
) {
309 r
= new(char, strlen(f
)*4+1);
319 int unit_name_unescape(const char *f
, char **ret
) {
320 _cleanup_free_
char *r
= NULL
;
329 for (t
= r
; *f
; f
++) {
332 else if (*f
== '\\') {
346 *(t
++) = (char) (((uint8_t) a
<< 4U) | (uint8_t) b
);
360 int unit_name_path_escape(const char *f
, char **ret
) {
370 path_kill_slashes(p
);
372 if (STR_IN_SET(p
, "/", ""))
377 if (!path_is_safe(p
))
380 /* Truncate trailing slashes */
381 e
= endswith(p
, "/");
385 /* Truncate leading slashes */
389 s
= unit_name_escape(p
);
398 int unit_name_path_unescape(const char *f
, char **ret
) {
414 r
= unit_name_unescape(f
, &w
);
418 /* Don't accept trailing or leading slashes */
419 if (startswith(w
, "/") || endswith(w
, "/")) {
424 /* Prefix a slash again */
425 s
= strappend("/", w
);
430 if (!path_is_safe(s
)) {
444 int unit_name_replace_instance(const char *f
, const char *i
, char **ret
) {
453 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
455 if (!unit_instance_is_valid(i
))
458 assert_se(p
= strchr(f
, '@'));
459 assert_se(e
= strrchr(f
, '.'));
464 s
= new(char, a
+ 1 + b
+ strlen(e
) + 1);
468 strcpy(mempcpy(mempcpy(s
, f
, a
+ 1), i
, b
), e
);
474 int unit_name_template(const char *f
, char **ret
) {
482 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
485 assert_se(p
= strchr(f
, '@'));
486 assert_se(e
= strrchr(f
, '.'));
490 s
= new(char, a
+ 1 + strlen(e
) + 1);
494 strcpy(mempcpy(s
, f
, a
+ 1), e
);
500 int unit_name_from_path(const char *path
, const char *suffix
, char **ret
) {
501 _cleanup_free_
char *p
= NULL
;
509 if (!unit_suffix_is_valid(suffix
))
512 r
= unit_name_path_escape(path
, &p
);
516 s
= strappend(p
, suffix
);
524 int unit_name_from_path_instance(const char *prefix
, const char *path
, const char *suffix
, char **ret
) {
525 _cleanup_free_
char *p
= NULL
;
534 if (!unit_prefix_is_valid(prefix
))
537 if (!unit_suffix_is_valid(suffix
))
540 r
= unit_name_path_escape(path
, &p
);
544 s
= strjoin(prefix
, "@", p
, suffix
, NULL
);
552 int unit_name_to_path(const char *name
, char **ret
) {
553 _cleanup_free_
char *prefix
= NULL
;
558 r
= unit_name_to_prefix(name
, &prefix
);
562 return unit_name_path_unescape(prefix
, ret
);
565 char *unit_dbus_path_from_name(const char *name
) {
566 _cleanup_free_
char *e
= NULL
;
570 e
= bus_label_escape(name
);
574 return strappend("/org/freedesktop/systemd1/unit/", e
);
577 int unit_name_from_dbus_path(const char *path
, char **name
) {
581 e
= startswith(path
, "/org/freedesktop/systemd1/unit/");
585 n
= bus_label_unescape(e
);
593 const char* unit_dbus_interface_from_type(UnitType t
) {
595 static const char *const table
[_UNIT_TYPE_MAX
] = {
596 [UNIT_SERVICE
] = "org.freedesktop.systemd1.Service",
597 [UNIT_SOCKET
] = "org.freedesktop.systemd1.Socket",
598 [UNIT_BUSNAME
] = "org.freedesktop.systemd1.BusName",
599 [UNIT_TARGET
] = "org.freedesktop.systemd1.Target",
600 [UNIT_SNAPSHOT
] = "org.freedesktop.systemd1.Snapshot",
601 [UNIT_DEVICE
] = "org.freedesktop.systemd1.Device",
602 [UNIT_MOUNT
] = "org.freedesktop.systemd1.Mount",
603 [UNIT_AUTOMOUNT
] = "org.freedesktop.systemd1.Automount",
604 [UNIT_SWAP
] = "org.freedesktop.systemd1.Swap",
605 [UNIT_TIMER
] = "org.freedesktop.systemd1.Timer",
606 [UNIT_PATH
] = "org.freedesktop.systemd1.Path",
607 [UNIT_SLICE
] = "org.freedesktop.systemd1.Slice",
608 [UNIT_SCOPE
] = "org.freedesktop.systemd1.Scope",
613 if (t
>= _UNIT_TYPE_MAX
)
619 const char *unit_dbus_interface_from_name(const char *name
) {
622 t
= unit_name_to_type(name
);
626 return unit_dbus_interface_from_type(t
);
629 static char *do_escape_mangle(const char *f
, UnitNameMangle allow_globs
, char *t
) {
630 const char *valid_chars
;
633 assert(IN_SET(allow_globs
, UNIT_NAME_GLOB
, UNIT_NAME_NOGLOB
));
636 /* We'll only escape the obvious characters here, to play
639 valid_chars
= allow_globs
== UNIT_NAME_GLOB
? "@" VALID_CHARS
"[]!-*?" : "@" VALID_CHARS
;
644 else if (!strchr(valid_chars
, *f
))
645 t
= do_escape_char(*f
, t
);
654 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
655 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
656 * except that @suffix is appended if a valid unit suffix is not present.
658 * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
660 int unit_name_mangle_with_suffix(const char *name
, UnitNameMangle allow_globs
, const char *suffix
, char **ret
) {
668 if (isempty(name
)) /* We cannot mangle empty unit names to become valid, sorry. */
671 if (!unit_suffix_is_valid(suffix
))
674 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
675 /* No mangling necessary... */
684 if (is_device_path(name
)) {
685 r
= unit_name_from_path(name
, ".device", ret
);
692 if (path_is_absolute(name
)) {
693 r
= unit_name_from_path(name
, ".mount", ret
);
700 s
= new(char, strlen(name
) * 4 + strlen(suffix
) + 1);
704 t
= do_escape_mangle(name
, allow_globs
, s
);
707 if (unit_name_to_type(s
) < 0)
714 int slice_build_parent_slice(const char *slice
, char **ret
) {
721 if (!slice_name_is_valid(slice
))
724 if (streq(slice
, "-.slice")) {
733 dash
= strrchr(s
, '-');
735 strcpy(dash
, ".slice");
737 r
= free_and_strdup(&s
, "-.slice");
748 int slice_build_subslice(const char *slice
, const char*name
, char **ret
) {
755 if (!slice_name_is_valid(slice
))
758 if (!unit_prefix_is_valid(name
))
761 if (streq(slice
, "-.slice"))
762 subslice
= strappend(name
, ".slice");
766 assert_se(e
= endswith(slice
, ".slice"));
768 subslice
= new(char, (e
- slice
) + 1 + strlen(name
) + 6 + 1);
772 stpcpy(stpcpy(stpcpy(mempcpy(subslice
, slice
, e
- slice
), "-"), name
), ".slice");
779 bool slice_name_is_valid(const char *name
) {
783 if (!unit_name_is_valid(name
, UNIT_NAME_PLAIN
))
786 if (streq(name
, "-.slice"))
789 e
= endswith(name
, ".slice");
793 for (p
= name
; p
< e
; p
++) {
797 /* Don't allow initial dash */
801 /* Don't allow multiple dashes */
810 /* Don't allow trailing hash */
817 static const char* const unit_type_table
[_UNIT_TYPE_MAX
] = {
818 [UNIT_SERVICE
] = "service",
819 [UNIT_SOCKET
] = "socket",
820 [UNIT_BUSNAME
] = "busname",
821 [UNIT_TARGET
] = "target",
822 [UNIT_SNAPSHOT
] = "snapshot",
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 snapshot_state_table
[_SNAPSHOT_STATE_MAX
] = {
954 [SNAPSHOT_DEAD
] = "dead",
955 [SNAPSHOT_ACTIVE
] = "active"
958 DEFINE_STRING_TABLE_LOOKUP(snapshot_state
, SnapshotState
);
960 static const char* const socket_state_table
[_SOCKET_STATE_MAX
] = {
961 [SOCKET_DEAD
] = "dead",
962 [SOCKET_START_PRE
] = "start-pre",
963 [SOCKET_START_CHOWN
] = "start-chown",
964 [SOCKET_START_POST
] = "start-post",
965 [SOCKET_LISTENING
] = "listening",
966 [SOCKET_RUNNING
] = "running",
967 [SOCKET_STOP_PRE
] = "stop-pre",
968 [SOCKET_STOP_PRE_SIGTERM
] = "stop-pre-sigterm",
969 [SOCKET_STOP_PRE_SIGKILL
] = "stop-pre-sigkill",
970 [SOCKET_STOP_POST
] = "stop-post",
971 [SOCKET_FINAL_SIGTERM
] = "final-sigterm",
972 [SOCKET_FINAL_SIGKILL
] = "final-sigkill",
973 [SOCKET_FAILED
] = "failed"
976 DEFINE_STRING_TABLE_LOOKUP(socket_state
, SocketState
);
978 static const char* const swap_state_table
[_SWAP_STATE_MAX
] = {
979 [SWAP_DEAD
] = "dead",
980 [SWAP_ACTIVATING
] = "activating",
981 [SWAP_ACTIVATING_DONE
] = "activating-done",
982 [SWAP_ACTIVE
] = "active",
983 [SWAP_DEACTIVATING
] = "deactivating",
984 [SWAP_ACTIVATING_SIGTERM
] = "activating-sigterm",
985 [SWAP_ACTIVATING_SIGKILL
] = "activating-sigkill",
986 [SWAP_DEACTIVATING_SIGTERM
] = "deactivating-sigterm",
987 [SWAP_DEACTIVATING_SIGKILL
] = "deactivating-sigkill",
988 [SWAP_FAILED
] = "failed"
991 DEFINE_STRING_TABLE_LOOKUP(swap_state
, SwapState
);
993 static const char* const target_state_table
[_TARGET_STATE_MAX
] = {
994 [TARGET_DEAD
] = "dead",
995 [TARGET_ACTIVE
] = "active"
998 DEFINE_STRING_TABLE_LOOKUP(target_state
, TargetState
);
1000 static const char* const timer_state_table
[_TIMER_STATE_MAX
] = {
1001 [TIMER_DEAD
] = "dead",
1002 [TIMER_WAITING
] = "waiting",
1003 [TIMER_RUNNING
] = "running",
1004 [TIMER_ELAPSED
] = "elapsed",
1005 [TIMER_FAILED
] = "failed"
1008 DEFINE_STRING_TABLE_LOOKUP(timer_state
, TimerState
);
1010 static const char* const unit_dependency_table
[_UNIT_DEPENDENCY_MAX
] = {
1011 [UNIT_REQUIRES
] = "Requires",
1012 [UNIT_REQUIRES_OVERRIDABLE
] = "RequiresOverridable",
1013 [UNIT_REQUISITE
] = "Requisite",
1014 [UNIT_REQUISITE_OVERRIDABLE
] = "RequisiteOverridable",
1015 [UNIT_WANTS
] = "Wants",
1016 [UNIT_BINDS_TO
] = "BindsTo",
1017 [UNIT_PART_OF
] = "PartOf",
1018 [UNIT_REQUIRED_BY
] = "RequiredBy",
1019 [UNIT_REQUIRED_BY_OVERRIDABLE
] = "RequiredByOverridable",
1020 [UNIT_REQUISITE_OF
] = "RequisiteOf",
1021 [UNIT_REQUISITE_OF_OVERRIDABLE
] = "RequisiteOfOverridable",
1022 [UNIT_WANTED_BY
] = "WantedBy",
1023 [UNIT_BOUND_BY
] = "BoundBy",
1024 [UNIT_CONSISTS_OF
] = "ConsistsOf",
1025 [UNIT_CONFLICTS
] = "Conflicts",
1026 [UNIT_CONFLICTED_BY
] = "ConflictedBy",
1027 [UNIT_BEFORE
] = "Before",
1028 [UNIT_AFTER
] = "After",
1029 [UNIT_ON_FAILURE
] = "OnFailure",
1030 [UNIT_TRIGGERS
] = "Triggers",
1031 [UNIT_TRIGGERED_BY
] = "TriggeredBy",
1032 [UNIT_PROPAGATES_RELOAD_TO
] = "PropagatesReloadTo",
1033 [UNIT_RELOAD_PROPAGATED_FROM
] = "ReloadPropagatedFrom",
1034 [UNIT_JOINS_NAMESPACE_OF
] = "JoinsNamespaceOf",
1035 [UNIT_REFERENCES
] = "References",
1036 [UNIT_REFERENCED_BY
] = "ReferencedBy",
1039 DEFINE_STRING_TABLE_LOOKUP(unit_dependency
, UnitDependency
);