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 "glob-util.h"
31 #include "hexdecoct.h"
33 #include "path-util.h"
34 #include "string-table.h"
35 #include "string-util.h"
37 #include "unit-name.h"
39 /* Characters valid in a unit name. */
45 /* The same, but also permits the single @ character that may appear */
46 #define VALID_CHARS_WITH_AT \
50 /* All chars valid in a unit name glob */
51 #define VALID_CHARS_GLOB \
55 bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
56 const char *e, *i, *at;
58 assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
60 if (_unlikely_(flags == 0))
66 if (strlen(n) >= UNIT_NAME_MAX)
73 if (unit_type_from_string(e + 1) < 0)
76 for (i = n, at = NULL; i < e; i++) {
81 if (!strchr("@" VALID_CHARS, *i))
88 if (flags & UNIT_NAME_PLAIN)
92 if (flags & UNIT_NAME_INSTANCE)
96 if (flags & UNIT_NAME_TEMPLATE)
97 if (at && e == at + 1)
103 bool unit_prefix_is_valid(const char *p) {
105 /* We don't allow additional @ in the prefix string */
110 return in_charset(p, VALID_CHARS);
113 bool unit_instance_is_valid(const char *i) {
115 /* The max length depends on the length of the string, so we
116 * don't really check this here. */
121 /* We allow additional @ in the instance string, we do not
122 * allow them in the prefix! */
124 return in_charset(i, "@" VALID_CHARS);
127 bool unit_suffix_is_valid(const char *s) {
134 if (unit_type_from_string(s + 1) < 0)
140 #if 0 /// UNNEEDED by elogind
141 int unit_name_to_prefix(const char *n, char **ret) {
148 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
157 s = strndup(n, p - n);
165 int unit_name_to_instance(const char *n, char **instance) {
172 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
175 /* Everything past the first @ and before the last . is the instance */
196 int unit_name_to_prefix_and_instance(const char *n, char **ret) {
203 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
210 s = strndup(n, d - n);
218 UnitType unit_name_to_type(const char *n) {
223 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
224 return _UNIT_TYPE_INVALID;
226 assert_se(e = strrchr(n, '.'));
228 return unit_type_from_string(e + 1);
231 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
239 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
242 if (!unit_suffix_is_valid(suffix))
245 assert_se(e = strrchr(n, '.'));
250 s = new(char, a + b + 1);
254 strcpy(mempcpy(s, n, a), suffix);
261 int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
268 if (!unit_prefix_is_valid(prefix))
271 if (instance && !unit_instance_is_valid(instance))
274 if (!unit_suffix_is_valid(suffix))
278 s = strappend(prefix, suffix);
280 s = strjoin(prefix, "@", instance, suffix, NULL);
288 #if 0 /// UNNEEDED by elogind
289 static char *do_escape_char(char c, char *t) {
294 *(t++) = hexchar(c >> 4);
300 static char *do_escape(const char *f, char *t) {
304 /* do not create units with a leading '.', like for "/.dotdir" mount points */
306 t = do_escape_char(*f, t);
313 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
314 t = do_escape_char(*f, t);
322 char *unit_name_escape(const char *f) {
327 r = new(char, strlen(f)*4+1);
337 int unit_name_unescape(const char *f, char **ret) {
338 _cleanup_free_ char *r = NULL;
347 for (t = r; *f; f++) {
350 else if (*f == '\\') {
364 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
378 int unit_name_path_escape(const char *f, char **ret) {
388 path_kill_slashes(p);
390 if (STR_IN_SET(p, "/", ""))
395 if (!path_is_safe(p))
398 /* Truncate trailing slashes */
399 e = endswith(p, "/");
403 /* Truncate leading slashes */
407 s = unit_name_escape(p);
416 int unit_name_path_unescape(const char *f, char **ret) {
432 r = unit_name_unescape(f, &w);
436 /* Don't accept trailing or leading slashes */
437 if (startswith(w, "/") || endswith(w, "/")) {
442 /* Prefix a slash again */
443 s = strappend("/", w);
448 if (!path_is_safe(s)) {
462 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
471 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
473 if (!unit_instance_is_valid(i))
476 assert_se(p = strchr(f, '@'));
477 assert_se(e = strrchr(f, '.'));
482 s = new(char, a + 1 + b + strlen(e) + 1);
486 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
492 int unit_name_template(const char *f, char **ret) {
500 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
503 assert_se(p = strchr(f, '@'));
504 assert_se(e = strrchr(f, '.'));
508 s = new(char, a + 1 + strlen(e) + 1);
512 strcpy(mempcpy(s, f, a + 1), e);
518 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
519 _cleanup_free_ char *p = NULL;
527 if (!unit_suffix_is_valid(suffix))
530 r = unit_name_path_escape(path, &p);
534 s = strappend(p, suffix);
542 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
543 _cleanup_free_ char *p = NULL;
552 if (!unit_prefix_is_valid(prefix))
555 if (!unit_suffix_is_valid(suffix))
558 r = unit_name_path_escape(path, &p);
562 s = strjoin(prefix, "@", p, suffix, NULL);
570 int unit_name_to_path(const char *name, char **ret) {
571 _cleanup_free_ char *prefix = NULL;
576 r = unit_name_to_prefix(name, &prefix);
580 return unit_name_path_unescape(prefix, ret);
583 char *unit_dbus_path_from_name(const char *name) {
584 _cleanup_free_ char *e = NULL;
588 e = bus_label_escape(name);
592 return strappend("/org/freedesktop/systemd1/unit/", e);
595 int unit_name_from_dbus_path(const char *path, char **name) {
599 e = startswith(path, "/org/freedesktop/systemd1/unit/");
603 n = bus_label_unescape(e);
611 const char* unit_dbus_interface_from_type(UnitType t) {
613 static const char *const table[_UNIT_TYPE_MAX] = {
614 [UNIT_SERVICE] = "org.freedesktop.systemd1.Service",
615 [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket",
616 [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName",
617 [UNIT_TARGET] = "org.freedesktop.systemd1.Target",
618 [UNIT_DEVICE] = "org.freedesktop.systemd1.Device",
619 [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount",
620 [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount",
621 [UNIT_SWAP] = "org.freedesktop.systemd1.Swap",
622 [UNIT_TIMER] = "org.freedesktop.systemd1.Timer",
623 [UNIT_PATH] = "org.freedesktop.systemd1.Path",
624 [UNIT_SLICE] = "org.freedesktop.systemd1.Slice",
625 [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope",
630 if (t >= _UNIT_TYPE_MAX)
636 const char *unit_dbus_interface_from_name(const char *name) {
639 t = unit_name_to_type(name);
643 return unit_dbus_interface_from_type(t);
646 static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
647 const char *valid_chars;
650 assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
653 /* We'll only escape the obvious characters here, to play
656 valid_chars = allow_globs == UNIT_NAME_GLOB ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT;
661 else if (!strchr(valid_chars, *f))
662 t = do_escape_char(*f, t);
671 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
672 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
673 * except that @suffix is appended if a valid unit suffix is not present.
675 * If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
677 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
685 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
688 if (!unit_suffix_is_valid(suffix))
691 /* Already a fully valid unit name? If so, no mangling is necessary... */
692 if (unit_name_is_valid(name, UNIT_NAME_ANY))
695 /* Already a fully valid globbing expression? If so, no mangling is necessary either... */
696 if (allow_globs == UNIT_NAME_GLOB &&
697 string_is_glob(name) &&
698 in_charset(name, VALID_CHARS_GLOB))
701 if (is_device_path(name)) {
702 r = unit_name_from_path(name, ".device", ret);
709 if (path_is_absolute(name)) {
710 r = unit_name_from_path(name, ".mount", ret);
717 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
721 t = do_escape_mangle(name, allow_globs, s);
724 /* Append a suffix if it doesn't have any, but only if this is not a glob, so that we can allow "foo.*" as a
726 if ((allow_globs != UNIT_NAME_GLOB || !string_is_glob(s)) && unit_name_to_type(s) < 0)
741 int slice_build_parent_slice(const char *slice, char **ret) {
748 if (!slice_name_is_valid(slice))
751 if (streq(slice, "-.slice")) {
760 dash = strrchr(s, '-');
762 strcpy(dash, ".slice");
764 r = free_and_strdup(&s, "-.slice");
776 int slice_build_subslice(const char *slice, const char*name, char **ret) {
783 if (!slice_name_is_valid(slice))
786 if (!unit_prefix_is_valid(name))
789 if (streq(slice, "-.slice"))
790 subslice = strappend(name, ".slice");
794 assert_se(e = endswith(slice, ".slice"));
796 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
800 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
807 bool slice_name_is_valid(const char *name) {
811 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
814 if (streq(name, "-.slice"))
817 e = endswith(name, ".slice");
821 for (p = name; p < e; p++) {
825 /* Don't allow initial dash */
829 /* Don't allow multiple dashes */
838 /* Don't allow trailing hash */
845 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
846 [UNIT_SERVICE] = "service",
847 [UNIT_SOCKET] = "socket",
848 [UNIT_BUSNAME] = "busname",
849 [UNIT_TARGET] = "target",
850 [UNIT_DEVICE] = "device",
851 [UNIT_MOUNT] = "mount",
852 [UNIT_AUTOMOUNT] = "automount",
853 [UNIT_SWAP] = "swap",
854 [UNIT_TIMER] = "timer",
855 [UNIT_PATH] = "path",
856 [UNIT_SLICE] = "slice",
857 [UNIT_SCOPE] = "scope",
860 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
862 #if 0 /// UNNEEDED by elogind
863 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
864 [UNIT_STUB] = "stub",
865 [UNIT_LOADED] = "loaded",
866 [UNIT_NOT_FOUND] = "not-found",
867 [UNIT_ERROR] = "error",
868 [UNIT_MERGED] = "merged",
869 [UNIT_MASKED] = "masked"
872 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
874 static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
875 [UNIT_ACTIVE] = "active",
876 [UNIT_RELOADING] = "reloading",
877 [UNIT_INACTIVE] = "inactive",
878 [UNIT_FAILED] = "failed",
879 [UNIT_ACTIVATING] = "activating",
880 [UNIT_DEACTIVATING] = "deactivating"
883 DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
885 static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
886 [AUTOMOUNT_DEAD] = "dead",
887 [AUTOMOUNT_WAITING] = "waiting",
888 [AUTOMOUNT_RUNNING] = "running",
889 [AUTOMOUNT_FAILED] = "failed"
892 DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
894 static const char* const busname_state_table[_BUSNAME_STATE_MAX] = {
895 [BUSNAME_DEAD] = "dead",
896 [BUSNAME_MAKING] = "making",
897 [BUSNAME_REGISTERED] = "registered",
898 [BUSNAME_LISTENING] = "listening",
899 [BUSNAME_RUNNING] = "running",
900 [BUSNAME_SIGTERM] = "sigterm",
901 [BUSNAME_SIGKILL] = "sigkill",
902 [BUSNAME_FAILED] = "failed",
905 DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState);
907 static const char* const device_state_table[_DEVICE_STATE_MAX] = {
908 [DEVICE_DEAD] = "dead",
909 [DEVICE_TENTATIVE] = "tentative",
910 [DEVICE_PLUGGED] = "plugged",
913 DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
915 static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
916 [MOUNT_DEAD] = "dead",
917 [MOUNT_MOUNTING] = "mounting",
918 [MOUNT_MOUNTING_DONE] = "mounting-done",
919 [MOUNT_MOUNTED] = "mounted",
920 [MOUNT_REMOUNTING] = "remounting",
921 [MOUNT_UNMOUNTING] = "unmounting",
922 [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
923 [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
924 [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
925 [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
926 [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
927 [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
928 [MOUNT_FAILED] = "failed"
931 DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
933 static const char* const path_state_table[_PATH_STATE_MAX] = {
934 [PATH_DEAD] = "dead",
935 [PATH_WAITING] = "waiting",
936 [PATH_RUNNING] = "running",
937 [PATH_FAILED] = "failed"
940 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
942 static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
943 [SCOPE_DEAD] = "dead",
944 [SCOPE_RUNNING] = "running",
945 [SCOPE_ABANDONED] = "abandoned",
946 [SCOPE_STOP_SIGTERM] = "stop-sigterm",
947 [SCOPE_STOP_SIGKILL] = "stop-sigkill",
948 [SCOPE_FAILED] = "failed",
951 DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
953 static const char* const service_state_table[_SERVICE_STATE_MAX] = {
954 [SERVICE_DEAD] = "dead",
955 [SERVICE_START_PRE] = "start-pre",
956 [SERVICE_START] = "start",
957 [SERVICE_START_POST] = "start-post",
958 [SERVICE_RUNNING] = "running",
959 [SERVICE_EXITED] = "exited",
960 [SERVICE_RELOAD] = "reload",
961 [SERVICE_STOP] = "stop",
962 [SERVICE_STOP_SIGABRT] = "stop-sigabrt",
963 [SERVICE_STOP_SIGTERM] = "stop-sigterm",
964 [SERVICE_STOP_SIGKILL] = "stop-sigkill",
965 [SERVICE_STOP_POST] = "stop-post",
966 [SERVICE_FINAL_SIGTERM] = "final-sigterm",
967 [SERVICE_FINAL_SIGKILL] = "final-sigkill",
968 [SERVICE_FAILED] = "failed",
969 [SERVICE_AUTO_RESTART] = "auto-restart",
972 DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
974 static const char* const slice_state_table[_SLICE_STATE_MAX] = {
975 [SLICE_DEAD] = "dead",
976 [SLICE_ACTIVE] = "active"
979 DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
981 static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
982 [SOCKET_DEAD] = "dead",
983 [SOCKET_START_PRE] = "start-pre",
984 [SOCKET_START_CHOWN] = "start-chown",
985 [SOCKET_START_POST] = "start-post",
986 [SOCKET_LISTENING] = "listening",
987 [SOCKET_RUNNING] = "running",
988 [SOCKET_STOP_PRE] = "stop-pre",
989 [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
990 [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
991 [SOCKET_STOP_POST] = "stop-post",
992 [SOCKET_FINAL_SIGTERM] = "final-sigterm",
993 [SOCKET_FINAL_SIGKILL] = "final-sigkill",
994 [SOCKET_FAILED] = "failed"
997 DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
999 static const char* const swap_state_table[_SWAP_STATE_MAX] = {
1000 [SWAP_DEAD] = "dead",
1001 [SWAP_ACTIVATING] = "activating",
1002 [SWAP_ACTIVATING_DONE] = "activating-done",
1003 [SWAP_ACTIVE] = "active",
1004 [SWAP_DEACTIVATING] = "deactivating",
1005 [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm",
1006 [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill",
1007 [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
1008 [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
1009 [SWAP_FAILED] = "failed"
1012 DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
1014 static const char* const target_state_table[_TARGET_STATE_MAX] = {
1015 [TARGET_DEAD] = "dead",
1016 [TARGET_ACTIVE] = "active"
1019 DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
1021 static const char* const timer_state_table[_TIMER_STATE_MAX] = {
1022 [TIMER_DEAD] = "dead",
1023 [TIMER_WAITING] = "waiting",
1024 [TIMER_RUNNING] = "running",
1025 [TIMER_ELAPSED] = "elapsed",
1026 [TIMER_FAILED] = "failed"
1029 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
1031 static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
1032 [UNIT_REQUIRES] = "Requires",
1033 [UNIT_REQUISITE] = "Requisite",
1034 [UNIT_WANTS] = "Wants",
1035 [UNIT_BINDS_TO] = "BindsTo",
1036 [UNIT_PART_OF] = "PartOf",
1037 [UNIT_REQUIRED_BY] = "RequiredBy",
1038 [UNIT_REQUISITE_OF] = "RequisiteOf",
1039 [UNIT_WANTED_BY] = "WantedBy",
1040 [UNIT_BOUND_BY] = "BoundBy",
1041 [UNIT_CONSISTS_OF] = "ConsistsOf",
1042 [UNIT_CONFLICTS] = "Conflicts",
1043 [UNIT_CONFLICTED_BY] = "ConflictedBy",
1044 [UNIT_BEFORE] = "Before",
1045 [UNIT_AFTER] = "After",
1046 [UNIT_ON_FAILURE] = "OnFailure",
1047 [UNIT_TRIGGERS] = "Triggers",
1048 [UNIT_TRIGGERED_BY] = "TriggeredBy",
1049 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
1050 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
1051 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
1052 [UNIT_REFERENCES] = "References",
1053 [UNIT_REFERENCED_BY] = "ReferencedBy",
1056 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);