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 /// UNNEEDED by elogind
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);
247 int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
254 if (!unit_prefix_is_valid(prefix))
257 if (instance && !unit_instance_is_valid(instance))
260 if (!unit_suffix_is_valid(suffix))
264 s = strappend(prefix, suffix);
266 s = strjoin(prefix, "@", instance, suffix, NULL);
274 /// 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 /// UNNEEDED by elogind
841 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
842 [UNIT_STUB] = "stub",
843 [UNIT_LOADED] = "loaded",
844 [UNIT_NOT_FOUND] = "not-found",
845 [UNIT_ERROR] = "error",
846 [UNIT_MERGED] = "merged",
847 [UNIT_MASKED] = "masked"
850 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
852 static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
853 [UNIT_ACTIVE] = "active",
854 [UNIT_RELOADING] = "reloading",
855 [UNIT_INACTIVE] = "inactive",
856 [UNIT_FAILED] = "failed",
857 [UNIT_ACTIVATING] = "activating",
858 [UNIT_DEACTIVATING] = "deactivating"
861 DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
863 static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
864 [AUTOMOUNT_DEAD] = "dead",
865 [AUTOMOUNT_WAITING] = "waiting",
866 [AUTOMOUNT_RUNNING] = "running",
867 [AUTOMOUNT_FAILED] = "failed"
870 DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
872 static const char* const busname_state_table[_BUSNAME_STATE_MAX] = {
873 [BUSNAME_DEAD] = "dead",
874 [BUSNAME_MAKING] = "making",
875 [BUSNAME_REGISTERED] = "registered",
876 [BUSNAME_LISTENING] = "listening",
877 [BUSNAME_RUNNING] = "running",
878 [BUSNAME_SIGTERM] = "sigterm",
879 [BUSNAME_SIGKILL] = "sigkill",
880 [BUSNAME_FAILED] = "failed",
883 DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState);
885 static const char* const device_state_table[_DEVICE_STATE_MAX] = {
886 [DEVICE_DEAD] = "dead",
887 [DEVICE_TENTATIVE] = "tentative",
888 [DEVICE_PLUGGED] = "plugged",
891 DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
893 static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
894 [MOUNT_DEAD] = "dead",
895 [MOUNT_MOUNTING] = "mounting",
896 [MOUNT_MOUNTING_DONE] = "mounting-done",
897 [MOUNT_MOUNTED] = "mounted",
898 [MOUNT_REMOUNTING] = "remounting",
899 [MOUNT_UNMOUNTING] = "unmounting",
900 [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
901 [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
902 [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
903 [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
904 [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
905 [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
906 [MOUNT_FAILED] = "failed"
909 DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
911 static const char* const path_state_table[_PATH_STATE_MAX] = {
912 [PATH_DEAD] = "dead",
913 [PATH_WAITING] = "waiting",
914 [PATH_RUNNING] = "running",
915 [PATH_FAILED] = "failed"
918 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
920 static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
921 [SCOPE_DEAD] = "dead",
922 [SCOPE_RUNNING] = "running",
923 [SCOPE_ABANDONED] = "abandoned",
924 [SCOPE_STOP_SIGTERM] = "stop-sigterm",
925 [SCOPE_STOP_SIGKILL] = "stop-sigkill",
926 [SCOPE_FAILED] = "failed",
929 DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
931 static const char* const service_state_table[_SERVICE_STATE_MAX] = {
932 [SERVICE_DEAD] = "dead",
933 [SERVICE_START_PRE] = "start-pre",
934 [SERVICE_START] = "start",
935 [SERVICE_START_POST] = "start-post",
936 [SERVICE_RUNNING] = "running",
937 [SERVICE_EXITED] = "exited",
938 [SERVICE_RELOAD] = "reload",
939 [SERVICE_STOP] = "stop",
940 [SERVICE_STOP_SIGABRT] = "stop-sigabrt",
941 [SERVICE_STOP_SIGTERM] = "stop-sigterm",
942 [SERVICE_STOP_SIGKILL] = "stop-sigkill",
943 [SERVICE_STOP_POST] = "stop-post",
944 [SERVICE_FINAL_SIGTERM] = "final-sigterm",
945 [SERVICE_FINAL_SIGKILL] = "final-sigkill",
946 [SERVICE_FAILED] = "failed",
947 [SERVICE_AUTO_RESTART] = "auto-restart",
950 DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
952 static const char* const slice_state_table[_SLICE_STATE_MAX] = {
953 [SLICE_DEAD] = "dead",
954 [SLICE_ACTIVE] = "active"
957 DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
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_REQUISITE] = "Requisite",
1012 [UNIT_WANTS] = "Wants",
1013 [UNIT_BINDS_TO] = "BindsTo",
1014 [UNIT_PART_OF] = "PartOf",
1015 [UNIT_REQUIRED_BY] = "RequiredBy",
1016 [UNIT_REQUISITE_OF] = "RequisiteOf",
1017 [UNIT_WANTED_BY] = "WantedBy",
1018 [UNIT_BOUND_BY] = "BoundBy",
1019 [UNIT_CONSISTS_OF] = "ConsistsOf",
1020 [UNIT_CONFLICTS] = "Conflicts",
1021 [UNIT_CONFLICTED_BY] = "ConflictedBy",
1022 [UNIT_BEFORE] = "Before",
1023 [UNIT_AFTER] = "After",
1024 [UNIT_ON_FAILURE] = "OnFailure",
1025 [UNIT_TRIGGERS] = "Triggers",
1026 [UNIT_TRIGGERED_BY] = "TriggeredBy",
1027 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
1028 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
1029 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
1030 [UNIT_REFERENCES] = "References",
1031 [UNIT_REFERENCED_BY] = "ReferencedBy",
1034 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);