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 #if 0 /// UNNEEDED by elogind
126 int unit_name_to_prefix(const char *n, char **ret) {
133 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
142 s = strndup(n, p - n);
150 int unit_name_to_instance(const char *n, char **instance) {
157 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
160 /* Everything past the first @ and before the last . is the instance */
181 int unit_name_to_prefix_and_instance(const char *n, char **ret) {
188 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
195 s = strndup(n, d - n);
203 UnitType unit_name_to_type(const char *n) {
208 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
209 return _UNIT_TYPE_INVALID;
211 assert_se(e = strrchr(n, '.'));
213 return unit_type_from_string(e + 1);
216 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
224 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
227 if (!unit_suffix_is_valid(suffix))
230 assert_se(e = strrchr(n, '.'));
235 s = new(char, a + b + 1);
239 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 #if 0 /// UNNEEDED by elogind
274 static char *do_escape_char(char c, char *t) {
279 *(t++) = hexchar(c >> 4);
285 static char *do_escape(const char *f, char *t) {
289 /* do not create units with a leading '.', like for "/.dotdir" mount points */
291 t = do_escape_char(*f, t);
298 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
299 t = do_escape_char(*f, t);
307 char *unit_name_escape(const char *f) {
312 r = new(char, strlen(f)*4+1);
322 int unit_name_unescape(const char *f, char **ret) {
323 _cleanup_free_ char *r = NULL;
332 for (t = r; *f; f++) {
335 else if (*f == '\\') {
349 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
363 int unit_name_path_escape(const char *f, char **ret) {
373 path_kill_slashes(p);
375 if (STR_IN_SET(p, "/", ""))
380 if (!path_is_safe(p))
383 /* Truncate trailing slashes */
384 e = endswith(p, "/");
388 /* Truncate leading slashes */
392 s = unit_name_escape(p);
401 int unit_name_path_unescape(const char *f, char **ret) {
417 r = unit_name_unescape(f, &w);
421 /* Don't accept trailing or leading slashes */
422 if (startswith(w, "/") || endswith(w, "/")) {
427 /* Prefix a slash again */
428 s = strappend("/", w);
433 if (!path_is_safe(s)) {
447 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
456 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
458 if (!unit_instance_is_valid(i))
461 assert_se(p = strchr(f, '@'));
462 assert_se(e = strrchr(f, '.'));
467 s = new(char, a + 1 + b + strlen(e) + 1);
471 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
477 int unit_name_template(const char *f, char **ret) {
485 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
488 assert_se(p = strchr(f, '@'));
489 assert_se(e = strrchr(f, '.'));
493 s = new(char, a + 1 + strlen(e) + 1);
497 strcpy(mempcpy(s, f, a + 1), e);
503 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
504 _cleanup_free_ char *p = NULL;
512 if (!unit_suffix_is_valid(suffix))
515 r = unit_name_path_escape(path, &p);
519 s = strappend(p, suffix);
527 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
528 _cleanup_free_ char *p = NULL;
537 if (!unit_prefix_is_valid(prefix))
540 if (!unit_suffix_is_valid(suffix))
543 r = unit_name_path_escape(path, &p);
547 s = strjoin(prefix, "@", p, suffix, NULL);
555 int unit_name_to_path(const char *name, char **ret) {
556 _cleanup_free_ char *prefix = NULL;
561 r = unit_name_to_prefix(name, &prefix);
565 return unit_name_path_unescape(prefix, ret);
568 char *unit_dbus_path_from_name(const char *name) {
569 _cleanup_free_ char *e = NULL;
573 e = bus_label_escape(name);
577 return strappend("/org/freedesktop/systemd1/unit/", e);
580 int unit_name_from_dbus_path(const char *path, char **name) {
584 e = startswith(path, "/org/freedesktop/systemd1/unit/");
588 n = bus_label_unescape(e);
596 const char* unit_dbus_interface_from_type(UnitType t) {
598 static const char *const table[_UNIT_TYPE_MAX] = {
599 [UNIT_SERVICE] = "org.freedesktop.systemd1.Service",
600 [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket",
601 [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName",
602 [UNIT_TARGET] = "org.freedesktop.systemd1.Target",
603 [UNIT_DEVICE] = "org.freedesktop.systemd1.Device",
604 [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount",
605 [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount",
606 [UNIT_SWAP] = "org.freedesktop.systemd1.Swap",
607 [UNIT_TIMER] = "org.freedesktop.systemd1.Timer",
608 [UNIT_PATH] = "org.freedesktop.systemd1.Path",
609 [UNIT_SLICE] = "org.freedesktop.systemd1.Slice",
610 [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope",
615 if (t >= _UNIT_TYPE_MAX)
621 const char *unit_dbus_interface_from_name(const char *name) {
624 t = unit_name_to_type(name);
628 return unit_dbus_interface_from_type(t);
631 static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
632 const char *valid_chars;
635 assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
638 /* We'll only escape the obvious characters here, to play
641 valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
646 else if (!strchr(valid_chars, *f))
647 t = do_escape_char(*f, t);
656 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
657 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
658 * except that @suffix is appended if a valid unit suffix is not present.
660 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
662 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
670 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
673 if (!unit_suffix_is_valid(suffix))
676 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
677 /* No mangling necessary... */
686 if (is_device_path(name)) {
687 r = unit_name_from_path(name, ".device", ret);
694 if (path_is_absolute(name)) {
695 r = unit_name_from_path(name, ".mount", ret);
702 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
706 t = do_escape_mangle(name, allow_globs, s);
709 if (unit_name_to_type(s) < 0)
716 int slice_build_parent_slice(const char *slice, char **ret) {
723 if (!slice_name_is_valid(slice))
726 if (streq(slice, "-.slice")) {
735 dash = strrchr(s, '-');
737 strcpy(dash, ".slice");
739 r = free_and_strdup(&s, "-.slice");
751 int slice_build_subslice(const char *slice, const char*name, char **ret) {
758 if (!slice_name_is_valid(slice))
761 if (!unit_prefix_is_valid(name))
764 if (streq(slice, "-.slice"))
765 subslice = strappend(name, ".slice");
769 assert_se(e = endswith(slice, ".slice"));
771 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
775 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
782 bool slice_name_is_valid(const char *name) {
786 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
789 if (streq(name, "-.slice"))
792 e = endswith(name, ".slice");
796 for (p = name; p < e; p++) {
800 /* Don't allow initial dash */
804 /* Don't allow multiple dashes */
813 /* Don't allow trailing hash */
820 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
821 [UNIT_SERVICE] = "service",
822 [UNIT_SOCKET] = "socket",
823 [UNIT_BUSNAME] = "busname",
824 [UNIT_TARGET] = "target",
825 [UNIT_DEVICE] = "device",
826 [UNIT_MOUNT] = "mount",
827 [UNIT_AUTOMOUNT] = "automount",
828 [UNIT_SWAP] = "swap",
829 [UNIT_TIMER] = "timer",
830 [UNIT_PATH] = "path",
831 [UNIT_SLICE] = "slice",
832 [UNIT_SCOPE] = "scope",
835 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
837 #if 0 /// UNNEEDED by elogind
838 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
839 [UNIT_STUB] = "stub",
840 [UNIT_LOADED] = "loaded",
841 [UNIT_NOT_FOUND] = "not-found",
842 [UNIT_ERROR] = "error",
843 [UNIT_MERGED] = "merged",
844 [UNIT_MASKED] = "masked"
847 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
849 static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
850 [UNIT_ACTIVE] = "active",
851 [UNIT_RELOADING] = "reloading",
852 [UNIT_INACTIVE] = "inactive",
853 [UNIT_FAILED] = "failed",
854 [UNIT_ACTIVATING] = "activating",
855 [UNIT_DEACTIVATING] = "deactivating"
858 DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
860 static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
861 [AUTOMOUNT_DEAD] = "dead",
862 [AUTOMOUNT_WAITING] = "waiting",
863 [AUTOMOUNT_RUNNING] = "running",
864 [AUTOMOUNT_FAILED] = "failed"
867 DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
869 static const char* const busname_state_table[_BUSNAME_STATE_MAX] = {
870 [BUSNAME_DEAD] = "dead",
871 [BUSNAME_MAKING] = "making",
872 [BUSNAME_REGISTERED] = "registered",
873 [BUSNAME_LISTENING] = "listening",
874 [BUSNAME_RUNNING] = "running",
875 [BUSNAME_SIGTERM] = "sigterm",
876 [BUSNAME_SIGKILL] = "sigkill",
877 [BUSNAME_FAILED] = "failed",
880 DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState);
882 static const char* const device_state_table[_DEVICE_STATE_MAX] = {
883 [DEVICE_DEAD] = "dead",
884 [DEVICE_TENTATIVE] = "tentative",
885 [DEVICE_PLUGGED] = "plugged",
888 DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
890 static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
891 [MOUNT_DEAD] = "dead",
892 [MOUNT_MOUNTING] = "mounting",
893 [MOUNT_MOUNTING_DONE] = "mounting-done",
894 [MOUNT_MOUNTED] = "mounted",
895 [MOUNT_REMOUNTING] = "remounting",
896 [MOUNT_UNMOUNTING] = "unmounting",
897 [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
898 [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
899 [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
900 [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
901 [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
902 [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
903 [MOUNT_FAILED] = "failed"
906 DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
908 static const char* const path_state_table[_PATH_STATE_MAX] = {
909 [PATH_DEAD] = "dead",
910 [PATH_WAITING] = "waiting",
911 [PATH_RUNNING] = "running",
912 [PATH_FAILED] = "failed"
915 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
917 static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
918 [SCOPE_DEAD] = "dead",
919 [SCOPE_RUNNING] = "running",
920 [SCOPE_ABANDONED] = "abandoned",
921 [SCOPE_STOP_SIGTERM] = "stop-sigterm",
922 [SCOPE_STOP_SIGKILL] = "stop-sigkill",
923 [SCOPE_FAILED] = "failed",
926 DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
928 static const char* const service_state_table[_SERVICE_STATE_MAX] = {
929 [SERVICE_DEAD] = "dead",
930 [SERVICE_START_PRE] = "start-pre",
931 [SERVICE_START] = "start",
932 [SERVICE_START_POST] = "start-post",
933 [SERVICE_RUNNING] = "running",
934 [SERVICE_EXITED] = "exited",
935 [SERVICE_RELOAD] = "reload",
936 [SERVICE_STOP] = "stop",
937 [SERVICE_STOP_SIGABRT] = "stop-sigabrt",
938 [SERVICE_STOP_SIGTERM] = "stop-sigterm",
939 [SERVICE_STOP_SIGKILL] = "stop-sigkill",
940 [SERVICE_STOP_POST] = "stop-post",
941 [SERVICE_FINAL_SIGTERM] = "final-sigterm",
942 [SERVICE_FINAL_SIGKILL] = "final-sigkill",
943 [SERVICE_FAILED] = "failed",
944 [SERVICE_AUTO_RESTART] = "auto-restart",
947 DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
949 static const char* const slice_state_table[_SLICE_STATE_MAX] = {
950 [SLICE_DEAD] = "dead",
951 [SLICE_ACTIVE] = "active"
954 DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
956 static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
957 [SOCKET_DEAD] = "dead",
958 [SOCKET_START_PRE] = "start-pre",
959 [SOCKET_START_CHOWN] = "start-chown",
960 [SOCKET_START_POST] = "start-post",
961 [SOCKET_LISTENING] = "listening",
962 [SOCKET_RUNNING] = "running",
963 [SOCKET_STOP_PRE] = "stop-pre",
964 [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
965 [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
966 [SOCKET_STOP_POST] = "stop-post",
967 [SOCKET_FINAL_SIGTERM] = "final-sigterm",
968 [SOCKET_FINAL_SIGKILL] = "final-sigkill",
969 [SOCKET_FAILED] = "failed"
972 DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
974 static const char* const swap_state_table[_SWAP_STATE_MAX] = {
975 [SWAP_DEAD] = "dead",
976 [SWAP_ACTIVATING] = "activating",
977 [SWAP_ACTIVATING_DONE] = "activating-done",
978 [SWAP_ACTIVE] = "active",
979 [SWAP_DEACTIVATING] = "deactivating",
980 [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm",
981 [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill",
982 [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
983 [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
984 [SWAP_FAILED] = "failed"
987 DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
989 static const char* const target_state_table[_TARGET_STATE_MAX] = {
990 [TARGET_DEAD] = "dead",
991 [TARGET_ACTIVE] = "active"
994 DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
996 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
997 [TIMER_DEAD] = "dead",
998 [TIMER_WAITING] = "waiting",
999 [TIMER_RUNNING] = "running",
1000 [TIMER_ELAPSED] = "elapsed",
1001 [TIMER_FAILED] = "failed"
1004 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
1006 static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
1007 [UNIT_REQUIRES] = "Requires",
1008 [UNIT_REQUISITE] = "Requisite",
1009 [UNIT_WANTS] = "Wants",
1010 [UNIT_BINDS_TO] = "BindsTo",
1011 [UNIT_PART_OF] = "PartOf",
1012 [UNIT_REQUIRED_BY] = "RequiredBy",
1013 [UNIT_REQUISITE_OF] = "RequisiteOf",
1014 [UNIT_WANTED_BY] = "WantedBy",
1015 [UNIT_BOUND_BY] = "BoundBy",
1016 [UNIT_CONSISTS_OF] = "ConsistsOf",
1017 [UNIT_CONFLICTS] = "Conflicts",
1018 [UNIT_CONFLICTED_BY] = "ConflictedBy",
1019 [UNIT_BEFORE] = "Before",
1020 [UNIT_AFTER] = "After",
1021 [UNIT_ON_FAILURE] = "OnFailure",
1022 [UNIT_TRIGGERS] = "Triggers",
1023 [UNIT_TRIGGERED_BY] = "TriggeredBy",
1024 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
1025 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
1026 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
1027 [UNIT_REFERENCES] = "References",
1028 [UNIT_REFERENCED_BY] = "ReferencedBy",
1031 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);