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 "path-util.h"
26 #include "bus-label.h"
28 #include "unit-name.h"
36 bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
37 const char *e, *i, *at;
39 assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
41 if (_unlikely_(flags == 0))
47 if (strlen(n) >= UNIT_NAME_MAX)
54 if (unit_type_from_string(e + 1) < 0)
57 for (i = n, at = NULL; i < e; i++) {
62 if (!strchr("@" VALID_CHARS, *i))
69 if (flags & UNIT_NAME_PLAIN)
73 if (flags & UNIT_NAME_INSTANCE)
77 if (flags & UNIT_NAME_TEMPLATE)
78 if (at && e == at + 1)
84 bool unit_prefix_is_valid(const char *p) {
86 /* We don't allow additional @ in the prefix string */
91 return in_charset(p, VALID_CHARS);
94 bool unit_instance_is_valid(const char *i) {
96 /* The max length depends on the length of the string, so we
97 * don't really check this here. */
102 /* We allow additional @ in the instance string, we do not
103 * allow them in the prefix! */
105 return in_charset(i, "@" VALID_CHARS);
108 bool unit_suffix_is_valid(const char *s) {
115 if (unit_type_from_string(s + 1) < 0)
121 int unit_name_to_prefix(const char *n, char **ret) {
128 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
137 s = strndup(n, p - n);
145 int unit_name_to_instance(const char *n, char **instance) {
152 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
155 /* Everything past the first @ and before the last . is the instance */
176 int unit_name_to_prefix_and_instance(const char *n, char **ret) {
183 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
190 s = strndup(n, d - n);
198 UnitType unit_name_to_type(const char *n) {
203 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
204 return _UNIT_TYPE_INVALID;
206 assert_se(e = strrchr(n, '.'));
208 return unit_type_from_string(e + 1);
211 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
219 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
222 if (!unit_suffix_is_valid(suffix))
225 assert_se(e = strrchr(n, '.'));
230 s = new(char, a + b + 1);
234 strcpy(mempcpy(s, n, a), suffix);
240 int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
247 if (!unit_prefix_is_valid(prefix))
250 if (instance && !unit_instance_is_valid(instance))
253 if (!unit_suffix_is_valid(suffix))
257 s = strappend(prefix, suffix);
259 s = strjoin(prefix, "@", instance, suffix, NULL);
267 static char *do_escape_char(char c, char *t) {
272 *(t++) = hexchar(c >> 4);
278 static char *do_escape(const char *f, char *t) {
282 /* do not create units with a leading '.', like for "/.dotdir" mount points */
284 t = do_escape_char(*f, t);
291 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
292 t = do_escape_char(*f, t);
300 char *unit_name_escape(const char *f) {
305 r = new(char, strlen(f)*4+1);
315 int unit_name_unescape(const char *f, char **ret) {
316 _cleanup_free_ char *r = NULL;
325 for (t = r; *f; f++) {
328 else if (*f == '\\') {
342 *(t++) = (char) ((a << 4) | b);
356 int unit_name_path_escape(const char *f, char **ret) {
366 path_kill_slashes(p);
368 if (STR_IN_SET(p, "/", ""))
373 if (!path_is_safe(p))
376 /* Truncate trailing slashes */
377 e = endswith(p, "/");
381 /* Truncate leading slashes */
385 s = unit_name_escape(p);
394 int unit_name_path_unescape(const char *f, char **ret) {
409 r = unit_name_unescape(f, &s);
413 /* Don't accept trailing or leading slashes */
414 if (startswith(s, "/") || endswith(s, "/")) {
419 /* Prefix a slash again */
420 w = strappend("/", s);
425 if (!path_is_safe(w)) {
434 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
443 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
445 if (!unit_instance_is_valid(i))
448 assert_se(p = strchr(f, '@'));
449 assert_se(e = strrchr(f, '.'));
454 s = new(char, a + 1 + b + strlen(e) + 1);
458 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
464 int unit_name_template(const char *f, char **ret) {
472 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
475 assert_se(p = strchr(f, '@'));
476 assert_se(e = strrchr(f, '.'));
480 s = new(char, a + 1 + strlen(e) + 1);
484 strcpy(mempcpy(s, f, a + 1), e);
490 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
491 _cleanup_free_ char *p = NULL;
499 if (!unit_suffix_is_valid(suffix))
502 r = unit_name_path_escape(path, &p);
506 s = strappend(p, suffix);
514 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
515 _cleanup_free_ char *p = NULL;
524 if (!unit_prefix_is_valid(prefix))
527 if (!unit_suffix_is_valid(suffix))
530 r = unit_name_path_escape(path, &p);
534 s = strjoin(prefix, "@", p, suffix, NULL);
542 int unit_name_to_path(const char *name, char **ret) {
543 _cleanup_free_ char *prefix = NULL;
548 r = unit_name_to_prefix(name, &prefix);
552 return unit_name_path_unescape(prefix, ret);
555 char *unit_dbus_path_from_name(const char *name) {
556 _cleanup_free_ char *e = NULL;
560 e = bus_label_escape(name);
564 return strappend("/org/freedesktop/systemd1/unit/", e);
567 int unit_name_from_dbus_path(const char *path, char **name) {
571 e = startswith(path, "/org/freedesktop/systemd1/unit/");
575 n = bus_label_unescape(e);
583 static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
584 const char *valid_chars;
587 assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
590 /* We'll only escape the obvious characters here, to play
593 valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
598 else if (!strchr(valid_chars, *f))
599 t = do_escape_char(*f, t);
608 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
609 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
610 * except that @suffix is appended if a valid unit suffix is not present.
612 * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
614 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
622 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
625 if (!unit_suffix_is_valid(suffix))
628 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
629 /* No mangling necessary... */
638 if (is_device_path(name)) {
639 r = unit_name_from_path(name, ".device", ret);
646 if (path_is_absolute(name)) {
647 r = unit_name_from_path(name, ".mount", ret);
654 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
658 t = do_escape_mangle(name, allow_globs, s);
661 if (unit_name_to_type(s) < 0)
668 int slice_build_subslice(const char *slice, const char*name, char **ret) {
675 if (!unit_name_is_valid(slice, UNIT_NAME_PLAIN))
678 if (!unit_prefix_is_valid(name))
681 if (streq(slice, "-.slice"))
682 subslice = strappend(name, ".slice");
686 e = endswith(slice, ".slice");
690 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
694 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
701 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
702 [UNIT_SERVICE] = "service",
703 [UNIT_SOCKET] = "socket",
704 [UNIT_BUSNAME] = "busname",
705 [UNIT_TARGET] = "target",
706 [UNIT_SNAPSHOT] = "snapshot",
707 [UNIT_DEVICE] = "device",
708 [UNIT_MOUNT] = "mount",
709 [UNIT_AUTOMOUNT] = "automount",
710 [UNIT_SWAP] = "swap",
711 [UNIT_TIMER] = "timer",
712 [UNIT_PATH] = "path",
713 [UNIT_SLICE] = "slice",
714 [UNIT_SCOPE] = "scope"
717 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
719 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
720 [UNIT_STUB] = "stub",
721 [UNIT_LOADED] = "loaded",
722 [UNIT_NOT_FOUND] = "not-found",
723 [UNIT_ERROR] = "error",
724 [UNIT_MERGED] = "merged",
725 [UNIT_MASKED] = "masked"
728 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
730 static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
731 [UNIT_REQUIRES] = "Requires",
732 [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
733 [UNIT_REQUISITE] = "Requisite",
734 [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
735 [UNIT_WANTS] = "Wants",
736 [UNIT_BINDS_TO] = "BindsTo",
737 [UNIT_PART_OF] = "PartOf",
738 [UNIT_REQUIRED_BY] = "RequiredBy",
739 [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
740 [UNIT_WANTED_BY] = "WantedBy",
741 [UNIT_BOUND_BY] = "BoundBy",
742 [UNIT_CONSISTS_OF] = "ConsistsOf",
743 [UNIT_CONFLICTS] = "Conflicts",
744 [UNIT_CONFLICTED_BY] = "ConflictedBy",
745 [UNIT_BEFORE] = "Before",
746 [UNIT_AFTER] = "After",
747 [UNIT_ON_FAILURE] = "OnFailure",
748 [UNIT_TRIGGERS] = "Triggers",
749 [UNIT_TRIGGERED_BY] = "TriggeredBy",
750 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
751 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
752 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
753 [UNIT_REFERENCES] = "References",
754 [UNIT_REFERENCED_BY] = "ReferencedBy",
757 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);