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 #if 0 /// UNNEEDED by elogind
128 int unit_name_to_prefix(const char *n, char **ret) {
135 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
144 s = strndup(n, p - n);
152 int unit_name_to_instance(const char *n, char **instance) {
159 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
162 /* Everything past the first @ and before the last . is the instance */
183 int unit_name_to_prefix_and_instance(const char *n, char **ret) {
190 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
197 s = strndup(n, d - n);
205 UnitType unit_name_to_type(const char *n) {
210 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
211 return _UNIT_TYPE_INVALID;
213 assert_se(e = strrchr(n, '.'));
215 return unit_type_from_string(e + 1);
218 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
226 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
229 if (!unit_suffix_is_valid(suffix))
232 assert_se(e = strrchr(n, '.'));
237 s = new(char, a + b + 1);
241 strcpy(mempcpy(s, n, a), suffix);
248 int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
255 if (!unit_prefix_is_valid(prefix))
258 if (instance && !unit_instance_is_valid(instance))
261 if (!unit_suffix_is_valid(suffix))
265 s = strappend(prefix, suffix);
267 s = strjoin(prefix, "@", instance, suffix, NULL);
275 #if 0 /// UNNEEDED by elogind
276 static char *do_escape_char(char c, char *t) {
281 *(t++) = hexchar(c >> 4);
287 static char *do_escape(const char *f, char *t) {
291 /* do not create units with a leading '.', like for "/.dotdir" mount points */
293 t = do_escape_char(*f, t);
300 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
301 t = do_escape_char(*f, t);
309 char *unit_name_escape(const char *f) {
314 r = new(char, strlen(f)*4+1);
324 int unit_name_unescape(const char *f, char **ret) {
325 _cleanup_free_ char *r = NULL;
334 for (t = r; *f; f++) {
337 else if (*f == '\\') {
351 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
365 int unit_name_path_escape(const char *f, char **ret) {
375 path_kill_slashes(p);
377 if (STR_IN_SET(p, "/", ""))
382 if (!path_is_safe(p))
385 /* Truncate trailing slashes */
386 e = endswith(p, "/");
390 /* Truncate leading slashes */
394 s = unit_name_escape(p);
403 int unit_name_path_unescape(const char *f, char **ret) {
419 r = unit_name_unescape(f, &w);
423 /* Don't accept trailing or leading slashes */
424 if (startswith(w, "/") || endswith(w, "/")) {
429 /* Prefix a slash again */
430 s = strappend("/", w);
435 if (!path_is_safe(s)) {
449 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
458 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
460 if (!unit_instance_is_valid(i))
463 assert_se(p = strchr(f, '@'));
464 assert_se(e = strrchr(f, '.'));
469 s = new(char, a + 1 + b + strlen(e) + 1);
473 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
479 int unit_name_template(const char *f, char **ret) {
487 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
490 assert_se(p = strchr(f, '@'));
491 assert_se(e = strrchr(f, '.'));
495 s = new(char, a + 1 + strlen(e) + 1);
499 strcpy(mempcpy(s, f, a + 1), e);
505 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
506 _cleanup_free_ char *p = NULL;
514 if (!unit_suffix_is_valid(suffix))
517 r = unit_name_path_escape(path, &p);
521 s = strappend(p, suffix);
529 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
530 _cleanup_free_ char *p = NULL;
539 if (!unit_prefix_is_valid(prefix))
542 if (!unit_suffix_is_valid(suffix))
545 r = unit_name_path_escape(path, &p);
549 s = strjoin(prefix, "@", p, suffix, NULL);
557 int unit_name_to_path(const char *name, char **ret) {
558 _cleanup_free_ char *prefix = NULL;
563 r = unit_name_to_prefix(name, &prefix);
567 return unit_name_path_unescape(prefix, ret);
570 char *unit_dbus_path_from_name(const char *name) {
571 _cleanup_free_ char *e = NULL;
575 e = bus_label_escape(name);
579 return strappend("/org/freedesktop/systemd1/unit/", e);
582 int unit_name_from_dbus_path(const char *path, char **name) {
586 e = startswith(path, "/org/freedesktop/systemd1/unit/");
590 n = bus_label_unescape(e);
598 const char* unit_dbus_interface_from_type(UnitType t) {
600 static const char *const table[_UNIT_TYPE_MAX] = {
601 [UNIT_SERVICE] = "org.freedesktop.systemd1.Service",
602 [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket",
603 [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName",
604 [UNIT_TARGET] = "org.freedesktop.systemd1.Target",
605 [UNIT_DEVICE] = "org.freedesktop.systemd1.Device",
606 [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount",
607 [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount",
608 [UNIT_SWAP] = "org.freedesktop.systemd1.Swap",
609 [UNIT_TIMER] = "org.freedesktop.systemd1.Timer",
610 [UNIT_PATH] = "org.freedesktop.systemd1.Path",
611 [UNIT_SLICE] = "org.freedesktop.systemd1.Slice",
612 [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope",
617 if (t >= _UNIT_TYPE_MAX)
623 const char *unit_dbus_interface_from_name(const char *name) {
626 t = unit_name_to_type(name);
630 return unit_dbus_interface_from_type(t);
633 static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
634 const char *valid_chars;
637 assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
640 /* We'll only escape the obvious characters here, to play
643 valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
648 else if (!strchr(valid_chars, *f))
649 t = do_escape_char(*f, t);
658 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
659 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
660 * except that @suffix is appended if a valid unit suffix is not present.
662 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
664 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
672 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
675 if (!unit_suffix_is_valid(suffix))
678 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
679 /* No mangling necessary... */
688 if (is_device_path(name)) {
689 r = unit_name_from_path(name, ".device", ret);
696 if (path_is_absolute(name)) {
697 r = unit_name_from_path(name, ".mount", ret);
704 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
708 t = do_escape_mangle(name, allow_globs, s);
711 if (unit_name_to_type(s) < 0)
718 int slice_build_parent_slice(const char *slice, char **ret) {
725 if (!slice_name_is_valid(slice))
728 if (streq(slice, "-.slice")) {
737 dash = strrchr(s, '-');
739 strcpy(dash, ".slice");
741 r = free_and_strdup(&s, "-.slice");
753 int slice_build_subslice(const char *slice, const char*name, char **ret) {
760 if (!slice_name_is_valid(slice))
763 if (!unit_prefix_is_valid(name))
766 if (streq(slice, "-.slice"))
767 subslice = strappend(name, ".slice");
771 assert_se(e = endswith(slice, ".slice"));
773 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
777 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
784 bool slice_name_is_valid(const char *name) {
788 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
791 if (streq(name, "-.slice"))
794 e = endswith(name, ".slice");
798 for (p = name; p < e; p++) {
802 /* Don't allow initial dash */
806 /* Don't allow multiple dashes */
815 /* Don't allow trailing hash */
822 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
823 [UNIT_SERVICE] = "service",
824 [UNIT_SOCKET] = "socket",
825 [UNIT_BUSNAME] = "busname",
826 [UNIT_TARGET] = "target",
827 [UNIT_DEVICE] = "device",
828 [UNIT_MOUNT] = "mount",
829 [UNIT_AUTOMOUNT] = "automount",
830 [UNIT_SWAP] = "swap",
831 [UNIT_TIMER] = "timer",
832 [UNIT_PATH] = "path",
833 [UNIT_SLICE] = "slice",
834 [UNIT_SCOPE] = "scope",
837 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
839 #if 0 /// UNNEEDED by elogind
840 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
841 [UNIT_STUB] = "stub",
842 [UNIT_LOADED] = "loaded",
843 [UNIT_NOT_FOUND] = "not-found",
844 [UNIT_ERROR] = "error",
845 [UNIT_MERGED] = "merged",
846 [UNIT_MASKED] = "masked"
849 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
851 static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
852 [UNIT_ACTIVE] = "active",
853 [UNIT_RELOADING] = "reloading",
854 [UNIT_INACTIVE] = "inactive",
855 [UNIT_FAILED] = "failed",
856 [UNIT_ACTIVATING] = "activating",
857 [UNIT_DEACTIVATING] = "deactivating"
860 DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
862 static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
863 [AUTOMOUNT_DEAD] = "dead",
864 [AUTOMOUNT_WAITING] = "waiting",
865 [AUTOMOUNT_RUNNING] = "running",
866 [AUTOMOUNT_FAILED] = "failed"
869 DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
871 static const char* const busname_state_table[_BUSNAME_STATE_MAX] = {
872 [BUSNAME_DEAD] = "dead",
873 [BUSNAME_MAKING] = "making",
874 [BUSNAME_REGISTERED] = "registered",
875 [BUSNAME_LISTENING] = "listening",
876 [BUSNAME_RUNNING] = "running",
877 [BUSNAME_SIGTERM] = "sigterm",
878 [BUSNAME_SIGKILL] = "sigkill",
879 [BUSNAME_FAILED] = "failed",
882 DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState);
884 static const char* const device_state_table[_DEVICE_STATE_MAX] = {
885 [DEVICE_DEAD] = "dead",
886 [DEVICE_TENTATIVE] = "tentative",
887 [DEVICE_PLUGGED] = "plugged",
890 DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
892 static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
893 [MOUNT_DEAD] = "dead",
894 [MOUNT_MOUNTING] = "mounting",
895 [MOUNT_MOUNTING_DONE] = "mounting-done",
896 [MOUNT_MOUNTED] = "mounted",
897 [MOUNT_REMOUNTING] = "remounting",
898 [MOUNT_UNMOUNTING] = "unmounting",
899 [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
900 [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
901 [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
902 [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
903 [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
904 [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
905 [MOUNT_FAILED] = "failed"
908 DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
910 static const char* const path_state_table[_PATH_STATE_MAX] = {
911 [PATH_DEAD] = "dead",
912 [PATH_WAITING] = "waiting",
913 [PATH_RUNNING] = "running",
914 [PATH_FAILED] = "failed"
917 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
919 static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
920 [SCOPE_DEAD] = "dead",
921 [SCOPE_RUNNING] = "running",
922 [SCOPE_ABANDONED] = "abandoned",
923 [SCOPE_STOP_SIGTERM] = "stop-sigterm",
924 [SCOPE_STOP_SIGKILL] = "stop-sigkill",
925 [SCOPE_FAILED] = "failed",
928 DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
930 static const char* const service_state_table[_SERVICE_STATE_MAX] = {
931 [SERVICE_DEAD] = "dead",
932 [SERVICE_START_PRE] = "start-pre",
933 [SERVICE_START] = "start",
934 [SERVICE_START_POST] = "start-post",
935 [SERVICE_RUNNING] = "running",
936 [SERVICE_EXITED] = "exited",
937 [SERVICE_RELOAD] = "reload",
938 [SERVICE_STOP] = "stop",
939 [SERVICE_STOP_SIGABRT] = "stop-sigabrt",
940 [SERVICE_STOP_SIGTERM] = "stop-sigterm",
941 [SERVICE_STOP_SIGKILL] = "stop-sigkill",
942 [SERVICE_STOP_POST] = "stop-post",
943 [SERVICE_FINAL_SIGTERM] = "final-sigterm",
944 [SERVICE_FINAL_SIGKILL] = "final-sigkill",
945 [SERVICE_FAILED] = "failed",
946 [SERVICE_AUTO_RESTART] = "auto-restart",
949 DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
951 static const char* const slice_state_table[_SLICE_STATE_MAX] = {
952 [SLICE_DEAD] = "dead",
953 [SLICE_ACTIVE] = "active"
956 DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
958 static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
959 [SOCKET_DEAD] = "dead",
960 [SOCKET_START_PRE] = "start-pre",
961 [SOCKET_START_CHOWN] = "start-chown",
962 [SOCKET_START_POST] = "start-post",
963 [SOCKET_LISTENING] = "listening",
964 [SOCKET_RUNNING] = "running",
965 [SOCKET_STOP_PRE] = "stop-pre",
966 [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
967 [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
968 [SOCKET_STOP_POST] = "stop-post",
969 [SOCKET_FINAL_SIGTERM] = "final-sigterm",
970 [SOCKET_FINAL_SIGKILL] = "final-sigkill",
971 [SOCKET_FAILED] = "failed"
974 DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
976 static const char* const swap_state_table[_SWAP_STATE_MAX] = {
977 [SWAP_DEAD] = "dead",
978 [SWAP_ACTIVATING] = "activating",
979 [SWAP_ACTIVATING_DONE] = "activating-done",
980 [SWAP_ACTIVE] = "active",
981 [SWAP_DEACTIVATING] = "deactivating",
982 [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm",
983 [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill",
984 [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
985 [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
986 [SWAP_FAILED] = "failed"
989 DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
991 static const char* const target_state_table[_TARGET_STATE_MAX] = {
992 [TARGET_DEAD] = "dead",
993 [TARGET_ACTIVE] = "active"
996 DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
998 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
999 [TIMER_DEAD] = "dead",
1000 [TIMER_WAITING] = "waiting",
1001 [TIMER_RUNNING] = "running",
1002 [TIMER_ELAPSED] = "elapsed",
1003 [TIMER_FAILED] = "failed"
1006 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
1008 static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
1009 [UNIT_REQUIRES] = "Requires",
1010 [UNIT_REQUISITE] = "Requisite",
1011 [UNIT_WANTS] = "Wants",
1012 [UNIT_BINDS_TO] = "BindsTo",
1013 [UNIT_PART_OF] = "PartOf",
1014 [UNIT_REQUIRED_BY] = "RequiredBy",
1015 [UNIT_REQUISITE_OF] = "RequisiteOf",
1016 [UNIT_WANTED_BY] = "WantedBy",
1017 [UNIT_BOUND_BY] = "BoundBy",
1018 [UNIT_CONSISTS_OF] = "ConsistsOf",
1019 [UNIT_CONFLICTS] = "Conflicts",
1020 [UNIT_CONFLICTED_BY] = "ConflictedBy",
1021 [UNIT_BEFORE] = "Before",
1022 [UNIT_AFTER] = "After",
1023 [UNIT_ON_FAILURE] = "OnFailure",
1024 [UNIT_TRIGGERS] = "Triggers",
1025 [UNIT_TRIGGERED_BY] = "TriggeredBy",
1026 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
1027 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
1028 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
1029 [UNIT_REFERENCES] = "References",
1030 [UNIT_REFERENCED_BY] = "ReferencedBy",
1033 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);