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 "hexdecoct.h"
28 #include "path-util.h"
29 #include "string-table.h"
30 #include "string-util.h"
32 #include "unit-name.h"
39 bool unit_name_is_valid(const char *n
, UnitNameFlags flags
) {
40 const char *e
, *i
, *at
;
42 assert((flags
& ~(UNIT_NAME_PLAIN
|UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
)) == 0);
44 if (_unlikely_(flags
== 0))
50 if (strlen(n
) >= UNIT_NAME_MAX
)
57 if (unit_type_from_string(e
+ 1) < 0)
60 for (i
= n
, at
= NULL
; i
< e
; i
++) {
65 if (!strchr("@" VALID_CHARS
, *i
))
72 if (flags
& UNIT_NAME_PLAIN
)
76 if (flags
& UNIT_NAME_INSTANCE
)
80 if (flags
& UNIT_NAME_TEMPLATE
)
81 if (at
&& e
== at
+ 1)
87 bool unit_prefix_is_valid(const char *p
) {
89 /* We don't allow additional @ in the prefix string */
94 return in_charset(p
, VALID_CHARS
);
97 bool unit_instance_is_valid(const char *i
) {
99 /* The max length depends on the length of the string, so we
100 * don't really check this here. */
105 /* We allow additional @ in the instance string, we do not
106 * allow them in the prefix! */
108 return in_charset(i
, "@" VALID_CHARS
);
111 bool unit_suffix_is_valid(const char *s
) {
118 if (unit_type_from_string(s
+ 1) < 0)
124 int unit_name_to_prefix(const char *n
, char **ret
) {
131 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
140 s
= strndup(n
, p
- n
);
148 int unit_name_to_instance(const char *n
, char **instance
) {
155 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
158 /* Everything past the first @ and before the last . is the instance */
179 int unit_name_to_prefix_and_instance(const char *n
, char **ret
) {
186 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
193 s
= strndup(n
, d
- n
);
201 UnitType
unit_name_to_type(const char *n
) {
206 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
207 return _UNIT_TYPE_INVALID
;
209 assert_se(e
= strrchr(n
, '.'));
211 return unit_type_from_string(e
+ 1);
214 int unit_name_change_suffix(const char *n
, const char *suffix
, char **ret
) {
222 if (!unit_name_is_valid(n
, UNIT_NAME_ANY
))
225 if (!unit_suffix_is_valid(suffix
))
228 assert_se(e
= strrchr(n
, '.'));
233 s
= new(char, a
+ b
+ 1);
237 strcpy(mempcpy(s
, n
, a
), suffix
);
243 int unit_name_build(const char *prefix
, const char *instance
, const char *suffix
, char **ret
) {
250 if (!unit_prefix_is_valid(prefix
))
253 if (instance
&& !unit_instance_is_valid(instance
))
256 if (!unit_suffix_is_valid(suffix
))
260 s
= strappend(prefix
, suffix
);
262 s
= strjoin(prefix
, "@", instance
, suffix
, NULL
);
270 static char *do_escape_char(char c
, char *t
) {
275 *(t
++) = hexchar(c
>> 4);
281 static char *do_escape(const char *f
, char *t
) {
285 /* do not create units with a leading '.', like for "/.dotdir" mount points */
287 t
= do_escape_char(*f
, t
);
294 else if (*f
== '-' || *f
== '\\' || !strchr(VALID_CHARS
, *f
))
295 t
= do_escape_char(*f
, t
);
303 char *unit_name_escape(const char *f
) {
308 r
= new(char, strlen(f
)*4+1);
318 int unit_name_unescape(const char *f
, char **ret
) {
319 _cleanup_free_
char *r
= NULL
;
328 for (t
= r
; *f
; f
++) {
331 else if (*f
== '\\') {
345 *(t
++) = (char) (((uint8_t) a
<< 4U) | (uint8_t) b
);
359 int unit_name_path_escape(const char *f
, char **ret
) {
369 path_kill_slashes(p
);
371 if (STR_IN_SET(p
, "/", ""))
376 if (!path_is_safe(p
))
379 /* Truncate trailing slashes */
380 e
= endswith(p
, "/");
384 /* Truncate leading slashes */
388 s
= unit_name_escape(p
);
397 int unit_name_path_unescape(const char *f
, char **ret
) {
413 r
= unit_name_unescape(f
, &w
);
417 /* Don't accept trailing or leading slashes */
418 if (startswith(w
, "/") || endswith(w
, "/")) {
423 /* Prefix a slash again */
424 s
= strappend("/", w
);
429 if (!path_is_safe(s
)) {
443 int unit_name_replace_instance(const char *f
, const char *i
, char **ret
) {
452 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
454 if (!unit_instance_is_valid(i
))
457 assert_se(p
= strchr(f
, '@'));
458 assert_se(e
= strrchr(f
, '.'));
463 s
= new(char, a
+ 1 + b
+ strlen(e
) + 1);
467 strcpy(mempcpy(mempcpy(s
, f
, a
+ 1), i
, b
), e
);
473 int unit_name_template(const char *f
, char **ret
) {
481 if (!unit_name_is_valid(f
, UNIT_NAME_INSTANCE
|UNIT_NAME_TEMPLATE
))
484 assert_se(p
= strchr(f
, '@'));
485 assert_se(e
= strrchr(f
, '.'));
489 s
= new(char, a
+ 1 + strlen(e
) + 1);
493 strcpy(mempcpy(s
, f
, a
+ 1), e
);
499 int unit_name_from_path(const char *path
, const char *suffix
, char **ret
) {
500 _cleanup_free_
char *p
= NULL
;
508 if (!unit_suffix_is_valid(suffix
))
511 r
= unit_name_path_escape(path
, &p
);
515 s
= strappend(p
, suffix
);
523 int unit_name_from_path_instance(const char *prefix
, const char *path
, const char *suffix
, char **ret
) {
524 _cleanup_free_
char *p
= NULL
;
533 if (!unit_prefix_is_valid(prefix
))
536 if (!unit_suffix_is_valid(suffix
))
539 r
= unit_name_path_escape(path
, &p
);
543 s
= strjoin(prefix
, "@", p
, suffix
, NULL
);
551 int unit_name_to_path(const char *name
, char **ret
) {
552 _cleanup_free_
char *prefix
= NULL
;
557 r
= unit_name_to_prefix(name
, &prefix
);
561 return unit_name_path_unescape(prefix
, ret
);
564 char *unit_dbus_path_from_name(const char *name
) {
565 _cleanup_free_
char *e
= NULL
;
569 e
= bus_label_escape(name
);
573 return strappend("/org/freedesktop/systemd1/unit/", e
);
576 int unit_name_from_dbus_path(const char *path
, char **name
) {
580 e
= startswith(path
, "/org/freedesktop/systemd1/unit/");
584 n
= bus_label_unescape(e
);
592 const char* unit_dbus_interface_from_type(UnitType t
) {
594 static const char *const table
[_UNIT_TYPE_MAX
] = {
595 [UNIT_SERVICE
] = "org.freedesktop.systemd1.Service",
596 [UNIT_SOCKET
] = "org.freedesktop.systemd1.Socket",
597 [UNIT_BUSNAME
] = "org.freedesktop.systemd1.BusName",
598 [UNIT_TARGET
] = "org.freedesktop.systemd1.Target",
599 [UNIT_SNAPSHOT
] = "org.freedesktop.systemd1.Snapshot",
600 [UNIT_DEVICE
] = "org.freedesktop.systemd1.Device",
601 [UNIT_MOUNT
] = "org.freedesktop.systemd1.Mount",
602 [UNIT_AUTOMOUNT
] = "org.freedesktop.systemd1.Automount",
603 [UNIT_SWAP
] = "org.freedesktop.systemd1.Swap",
604 [UNIT_TIMER
] = "org.freedesktop.systemd1.Timer",
605 [UNIT_PATH
] = "org.freedesktop.systemd1.Path",
606 [UNIT_SLICE
] = "org.freedesktop.systemd1.Slice",
607 [UNIT_SCOPE
] = "org.freedesktop.systemd1.Scope",
612 if (t
>= _UNIT_TYPE_MAX
)
618 const char *unit_dbus_interface_from_name(const char *name
) {
621 t
= unit_name_to_type(name
);
625 return unit_dbus_interface_from_type(t
);
628 static char *do_escape_mangle(const char *f
, UnitNameMangle allow_globs
, char *t
) {
629 const char *valid_chars
;
632 assert(IN_SET(allow_globs
, UNIT_NAME_GLOB
, UNIT_NAME_NOGLOB
));
635 /* We'll only escape the obvious characters here, to play
638 valid_chars
= allow_globs
== UNIT_NAME_GLOB
? "@" VALID_CHARS
"[]!-*?" : "@" VALID_CHARS
;
643 else if (!strchr(valid_chars
, *f
))
644 t
= do_escape_char(*f
, t
);
653 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
654 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
655 * except that @suffix is appended if a valid unit suffix is not present.
657 * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
659 int unit_name_mangle_with_suffix(const char *name
, UnitNameMangle allow_globs
, const char *suffix
, char **ret
) {
667 if (isempty(name
)) /* We cannot mangle empty unit names to become valid, sorry. */
670 if (!unit_suffix_is_valid(suffix
))
673 if (unit_name_is_valid(name
, UNIT_NAME_ANY
)) {
674 /* No mangling necessary... */
683 if (is_device_path(name
)) {
684 r
= unit_name_from_path(name
, ".device", ret
);
691 if (path_is_absolute(name
)) {
692 r
= unit_name_from_path(name
, ".mount", ret
);
699 s
= new(char, strlen(name
) * 4 + strlen(suffix
) + 1);
703 t
= do_escape_mangle(name
, allow_globs
, s
);
706 if (unit_name_to_type(s
) < 0)
713 int slice_build_parent_slice(const char *slice
, char **ret
) {
720 if (!slice_name_is_valid(slice
))
723 if (streq(slice
, "-.slice")) {
732 dash
= strrchr(s
, '-');
734 strcpy(dash
, ".slice");
736 r
= free_and_strdup(&s
, "-.slice");
747 int slice_build_subslice(const char *slice
, const char*name
, char **ret
) {
754 if (!slice_name_is_valid(slice
))
757 if (!unit_prefix_is_valid(name
))
760 if (streq(slice
, "-.slice"))
761 subslice
= strappend(name
, ".slice");
765 assert_se(e
= endswith(slice
, ".slice"));
767 subslice
= new(char, (e
- slice
) + 1 + strlen(name
) + 6 + 1);
771 stpcpy(stpcpy(stpcpy(mempcpy(subslice
, slice
, e
- slice
), "-"), name
), ".slice");
778 bool slice_name_is_valid(const char *name
) {
782 if (!unit_name_is_valid(name
, UNIT_NAME_PLAIN
))
785 if (streq(name
, "-.slice"))
788 e
= endswith(name
, ".slice");
792 for (p
= name
; p
< e
; p
++) {
796 /* Don't allow initial dash */
800 /* Don't allow multiple dashes */
809 /* Don't allow trailing hash */
816 static const char* const unit_type_table
[_UNIT_TYPE_MAX
] = {
817 [UNIT_SERVICE
] = "service",
818 [UNIT_SOCKET
] = "socket",
819 [UNIT_BUSNAME
] = "busname",
820 [UNIT_TARGET
] = "target",
821 [UNIT_SNAPSHOT
] = "snapshot",
822 [UNIT_DEVICE
] = "device",
823 [UNIT_MOUNT
] = "mount",
824 [UNIT_AUTOMOUNT
] = "automount",
825 [UNIT_SWAP
] = "swap",
826 [UNIT_TIMER
] = "timer",
827 [UNIT_PATH
] = "path",
828 [UNIT_SLICE
] = "slice",
829 [UNIT_SCOPE
] = "scope",
832 DEFINE_STRING_TABLE_LOOKUP(unit_type
, UnitType
);
834 static const char* const unit_load_state_table
[_UNIT_LOAD_STATE_MAX
] = {
835 [UNIT_STUB
] = "stub",
836 [UNIT_LOADED
] = "loaded",
837 [UNIT_NOT_FOUND
] = "not-found",
838 [UNIT_ERROR
] = "error",
839 [UNIT_MERGED
] = "merged",
840 [UNIT_MASKED
] = "masked"
843 DEFINE_STRING_TABLE_LOOKUP(unit_load_state
, UnitLoadState
);
845 static const char* const unit_active_state_table
[_UNIT_ACTIVE_STATE_MAX
] = {
846 [UNIT_ACTIVE
] = "active",
847 [UNIT_RELOADING
] = "reloading",
848 [UNIT_INACTIVE
] = "inactive",
849 [UNIT_FAILED
] = "failed",
850 [UNIT_ACTIVATING
] = "activating",
851 [UNIT_DEACTIVATING
] = "deactivating"
854 DEFINE_STRING_TABLE_LOOKUP(unit_active_state
, UnitActiveState
);
856 static const char* const automount_state_table
[_AUTOMOUNT_STATE_MAX
] = {
857 [AUTOMOUNT_DEAD
] = "dead",
858 [AUTOMOUNT_WAITING
] = "waiting",
859 [AUTOMOUNT_RUNNING
] = "running",
860 [AUTOMOUNT_FAILED
] = "failed"
863 DEFINE_STRING_TABLE_LOOKUP(automount_state
, AutomountState
);
865 static const char* const busname_state_table
[_BUSNAME_STATE_MAX
] = {
866 [BUSNAME_DEAD
] = "dead",
867 [BUSNAME_MAKING
] = "making",
868 [BUSNAME_REGISTERED
] = "registered",
869 [BUSNAME_LISTENING
] = "listening",
870 [BUSNAME_RUNNING
] = "running",
871 [BUSNAME_SIGTERM
] = "sigterm",
872 [BUSNAME_SIGKILL
] = "sigkill",
873 [BUSNAME_FAILED
] = "failed",
876 DEFINE_STRING_TABLE_LOOKUP(busname_state
, BusNameState
);
878 static const char* const device_state_table
[_DEVICE_STATE_MAX
] = {
879 [DEVICE_DEAD
] = "dead",
880 [DEVICE_TENTATIVE
] = "tentative",
881 [DEVICE_PLUGGED
] = "plugged",
884 DEFINE_STRING_TABLE_LOOKUP(device_state
, DeviceState
);
886 static const char* const mount_state_table
[_MOUNT_STATE_MAX
] = {
887 [MOUNT_DEAD
] = "dead",
888 [MOUNT_MOUNTING
] = "mounting",
889 [MOUNT_MOUNTING_DONE
] = "mounting-done",
890 [MOUNT_MOUNTED
] = "mounted",
891 [MOUNT_REMOUNTING
] = "remounting",
892 [MOUNT_UNMOUNTING
] = "unmounting",
893 [MOUNT_MOUNTING_SIGTERM
] = "mounting-sigterm",
894 [MOUNT_MOUNTING_SIGKILL
] = "mounting-sigkill",
895 [MOUNT_REMOUNTING_SIGTERM
] = "remounting-sigterm",
896 [MOUNT_REMOUNTING_SIGKILL
] = "remounting-sigkill",
897 [MOUNT_UNMOUNTING_SIGTERM
] = "unmounting-sigterm",
898 [MOUNT_UNMOUNTING_SIGKILL
] = "unmounting-sigkill",
899 [MOUNT_FAILED
] = "failed"
902 DEFINE_STRING_TABLE_LOOKUP(mount_state
, MountState
);
904 static const char* const path_state_table
[_PATH_STATE_MAX
] = {
905 [PATH_DEAD
] = "dead",
906 [PATH_WAITING
] = "waiting",
907 [PATH_RUNNING
] = "running",
908 [PATH_FAILED
] = "failed"
911 DEFINE_STRING_TABLE_LOOKUP(path_state
, PathState
);
913 static const char* const scope_state_table
[_SCOPE_STATE_MAX
] = {
914 [SCOPE_DEAD
] = "dead",
915 [SCOPE_RUNNING
] = "running",
916 [SCOPE_ABANDONED
] = "abandoned",
917 [SCOPE_STOP_SIGTERM
] = "stop-sigterm",
918 [SCOPE_STOP_SIGKILL
] = "stop-sigkill",
919 [SCOPE_FAILED
] = "failed",
922 DEFINE_STRING_TABLE_LOOKUP(scope_state
, ScopeState
);
924 static const char* const service_state_table
[_SERVICE_STATE_MAX
] = {
925 [SERVICE_DEAD
] = "dead",
926 [SERVICE_START_PRE
] = "start-pre",
927 [SERVICE_START
] = "start",
928 [SERVICE_START_POST
] = "start-post",
929 [SERVICE_RUNNING
] = "running",
930 [SERVICE_EXITED
] = "exited",
931 [SERVICE_RELOAD
] = "reload",
932 [SERVICE_STOP
] = "stop",
933 [SERVICE_STOP_SIGABRT
] = "stop-sigabrt",
934 [SERVICE_STOP_SIGTERM
] = "stop-sigterm",
935 [SERVICE_STOP_SIGKILL
] = "stop-sigkill",
936 [SERVICE_STOP_POST
] = "stop-post",
937 [SERVICE_FINAL_SIGTERM
] = "final-sigterm",
938 [SERVICE_FINAL_SIGKILL
] = "final-sigkill",
939 [SERVICE_FAILED
] = "failed",
940 [SERVICE_AUTO_RESTART
] = "auto-restart",
943 DEFINE_STRING_TABLE_LOOKUP(service_state
, ServiceState
);
945 static const char* const slice_state_table
[_SLICE_STATE_MAX
] = {
946 [SLICE_DEAD
] = "dead",
947 [SLICE_ACTIVE
] = "active"
950 DEFINE_STRING_TABLE_LOOKUP(slice_state
, SliceState
);
952 static const char* const snapshot_state_table
[_SNAPSHOT_STATE_MAX
] = {
953 [SNAPSHOT_DEAD
] = "dead",
954 [SNAPSHOT_ACTIVE
] = "active"
957 DEFINE_STRING_TABLE_LOOKUP(snapshot_state
, SnapshotState
);
959 static const char* const socket_state_table
[_SOCKET_STATE_MAX
] = {
960 [SOCKET_DEAD
] = "dead",
961 [SOCKET_START_PRE
] = "start-pre",
962 [SOCKET_START_CHOWN
] = "start-chown",
963 [SOCKET_START_POST
] = "start-post",
964 [SOCKET_LISTENING
] = "listening",
965 [SOCKET_RUNNING
] = "running",
966 [SOCKET_STOP_PRE
] = "stop-pre",
967 [SOCKET_STOP_PRE_SIGTERM
] = "stop-pre-sigterm",
968 [SOCKET_STOP_PRE_SIGKILL
] = "stop-pre-sigkill",
969 [SOCKET_STOP_POST
] = "stop-post",
970 [SOCKET_FINAL_SIGTERM
] = "final-sigterm",
971 [SOCKET_FINAL_SIGKILL
] = "final-sigkill",
972 [SOCKET_FAILED
] = "failed"
975 DEFINE_STRING_TABLE_LOOKUP(socket_state
, SocketState
);
977 static const char* const swap_state_table
[_SWAP_STATE_MAX
] = {
978 [SWAP_DEAD
] = "dead",
979 [SWAP_ACTIVATING
] = "activating",
980 [SWAP_ACTIVATING_DONE
] = "activating-done",
981 [SWAP_ACTIVE
] = "active",
982 [SWAP_DEACTIVATING
] = "deactivating",
983 [SWAP_ACTIVATING_SIGTERM
] = "activating-sigterm",
984 [SWAP_ACTIVATING_SIGKILL
] = "activating-sigkill",
985 [SWAP_DEACTIVATING_SIGTERM
] = "deactivating-sigterm",
986 [SWAP_DEACTIVATING_SIGKILL
] = "deactivating-sigkill",
987 [SWAP_FAILED
] = "failed"
990 DEFINE_STRING_TABLE_LOOKUP(swap_state
, SwapState
);
992 static const char* const target_state_table
[_TARGET_STATE_MAX
] = {
993 [TARGET_DEAD
] = "dead",
994 [TARGET_ACTIVE
] = "active"
997 DEFINE_STRING_TABLE_LOOKUP(target_state
, TargetState
);
999 static const char* const timer_state_table
[_TIMER_STATE_MAX
] = {
1000 [TIMER_DEAD
] = "dead",
1001 [TIMER_WAITING
] = "waiting",
1002 [TIMER_RUNNING
] = "running",
1003 [TIMER_ELAPSED
] = "elapsed",
1004 [TIMER_FAILED
] = "failed"
1007 DEFINE_STRING_TABLE_LOOKUP(timer_state
, TimerState
);
1009 static const char* const unit_dependency_table
[_UNIT_DEPENDENCY_MAX
] = {
1010 [UNIT_REQUIRES
] = "Requires",
1011 [UNIT_REQUIRES_OVERRIDABLE
] = "RequiresOverridable",
1012 [UNIT_REQUISITE
] = "Requisite",
1013 [UNIT_REQUISITE_OVERRIDABLE
] = "RequisiteOverridable",
1014 [UNIT_WANTS
] = "Wants",
1015 [UNIT_BINDS_TO
] = "BindsTo",
1016 [UNIT_PART_OF
] = "PartOf",
1017 [UNIT_REQUIRED_BY
] = "RequiredBy",
1018 [UNIT_REQUIRED_BY_OVERRIDABLE
] = "RequiredByOverridable",
1019 [UNIT_REQUISITE_OF
] = "RequisiteOf",
1020 [UNIT_REQUISITE_OF_OVERRIDABLE
] = "RequisiteOfOverridable",
1021 [UNIT_WANTED_BY
] = "WantedBy",
1022 [UNIT_BOUND_BY
] = "BoundBy",
1023 [UNIT_CONSISTS_OF
] = "ConsistsOf",
1024 [UNIT_CONFLICTS
] = "Conflicts",
1025 [UNIT_CONFLICTED_BY
] = "ConflictedBy",
1026 [UNIT_BEFORE
] = "Before",
1027 [UNIT_AFTER
] = "After",
1028 [UNIT_ON_FAILURE
] = "OnFailure",
1029 [UNIT_TRIGGERS
] = "Triggers",
1030 [UNIT_TRIGGERED_BY
] = "TriggeredBy",
1031 [UNIT_PROPAGATES_RELOAD_TO
] = "PropagatesReloadTo",
1032 [UNIT_RELOAD_PROPAGATED_FROM
] = "ReloadPropagatedFrom",
1033 [UNIT_JOINS_NAMESPACE_OF
] = "JoinsNamespaceOf",
1034 [UNIT_REFERENCES
] = "References",
1035 [UNIT_REFERENCED_BY
] = "ReferencedBy",
1038 DEFINE_STRING_TABLE_LOOKUP(unit_dependency
, UnitDependency
);