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/>.
26 #include "path-util.h"
27 #include "bus-label.h"
29 #include "unit-name.h"
37 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
38 [UNIT_SERVICE] = "service",
39 [UNIT_SOCKET] = "socket",
40 [UNIT_BUSNAME] = "busname",
41 [UNIT_TARGET] = "target",
42 [UNIT_SNAPSHOT] = "snapshot",
43 [UNIT_DEVICE] = "device",
44 [UNIT_MOUNT] = "mount",
45 [UNIT_AUTOMOUNT] = "automount",
47 [UNIT_TIMER] = "timer",
49 [UNIT_SLICE] = "slice",
50 [UNIT_SCOPE] = "scope"
53 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
55 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
57 [UNIT_LOADED] = "loaded",
58 [UNIT_NOT_FOUND] = "not-found",
59 [UNIT_ERROR] = "error",
60 [UNIT_MERGED] = "merged",
61 [UNIT_MASKED] = "masked"
64 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
66 bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
67 const char *e, *i, *at;
71 * string@instance.suffix
75 assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID));
80 if (strlen(n) >= UNIT_NAME_MAX)
87 if (unit_type_from_string(e + 1) < 0)
90 for (i = n, at = NULL; i < e; i++) {
95 if (!strchr("@" VALID_CHARS, *i))
103 if (!template_ok == TEMPLATE_VALID && at+1 == e)
110 bool unit_instance_is_valid(const char *i) {
112 /* The max length depends on the length of the string, so we
113 * don't really check this here. */
118 /* We allow additional @ in the instance string, we do not
119 * allow them in the prefix! */
121 return in_charset(i, "@" VALID_CHARS);
124 bool unit_prefix_is_valid(const char *p) {
126 /* We don't allow additional @ in the instance string */
131 return in_charset(p, VALID_CHARS);
134 int unit_name_to_instance(const char *n, char **instance) {
141 /* Everything past the first @ and before the last . is the instance */
154 i = strndup(p+1, d-p-1);
162 char *unit_name_to_prefix_and_instance(const char *n) {
167 assert_se(d = strrchr(n, '.'));
168 return strndup(n, d - n);
171 char *unit_name_to_prefix(const char *n) {
178 return strndup(n, p - n);
180 return unit_name_to_prefix_and_instance(n);
183 char *unit_name_change_suffix(const char *n, const char *suffix) {
189 assert(suffix[0] == '.');
191 assert_se(e = strrchr(n, '.'));
195 r = new(char, a + b + 1);
199 strcpy(mempcpy(r, n, a), suffix);
203 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
208 return strappend(prefix, suffix);
210 return strjoin(prefix, "@", instance, suffix, NULL);
213 static char *do_escape_char(char c, char *t) {
218 *(t++) = hexchar(c >> 4);
224 static char *do_escape(const char *f, char *t) {
228 /* do not create units with a leading '.', like for "/.dotdir" mount points */
230 t = do_escape_char(*f, t);
237 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
238 t = do_escape_char(*f, t);
246 static char *do_escape_mangle(const char *f, enum unit_name_mangle allow_globs, char *t) {
247 const char *valid_chars;
250 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
253 /* We'll only escape the obvious characters here, to play
256 valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
261 else if (!strchr(valid_chars, *f))
262 t = do_escape_char(*f, t);
270 char *unit_name_escape(const char *f) {
275 r = new(char, strlen(f)*4+1);
285 char *unit_name_unescape(const char *f) {
294 for (t = r; *f; f++) {
297 else if (*f == '\\') {
301 (a = unhexchar(f[2])) < 0 ||
302 (b = unhexchar(f[3])) < 0) {
303 /* Invalid escape code, let's take it literal then */
306 *(t++) = (char) ((a << 4) | b);
318 char *unit_name_path_escape(const char *f) {
319 _cleanup_free_ char *p = NULL;
327 path_kill_slashes(p);
329 if (STR_IN_SET(p, "/", ""))
332 return unit_name_escape(p[0] == '/' ? p + 1 : p);
335 char *unit_name_path_unescape(const char *f) {
340 e = unit_name_unescape(f);
345 w = strappend("/", e);
353 bool unit_name_is_template(const char *n) {
362 e = strrchr(p+1, '.');
369 bool unit_name_is_instance(const char *n) {
378 e = strrchr(p+1, '.');
385 char *unit_name_replace_instance(const char *f, const char *i) {
404 r = new(char, a + 1 + b + strlen(e) + 1);
408 strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e);
412 char *unit_name_template(const char *f) {
429 r = new(char, a + 1 + strlen(e) + 1);
433 strcpy(mempcpy(r, f, a + 1), e);
437 char *unit_name_from_path(const char *path, const char *suffix) {
438 _cleanup_free_ char *p = NULL;
443 p = unit_name_path_escape(path);
447 return strappend(p, suffix);
450 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
451 _cleanup_free_ char *p = NULL;
457 p = unit_name_path_escape(path);
461 return strjoin(prefix, "@", p, suffix, NULL);
464 char *unit_name_to_path(const char *name) {
465 _cleanup_free_ char *w = NULL;
469 w = unit_name_to_prefix(name);
473 return unit_name_path_unescape(w);
476 char *unit_dbus_path_from_name(const char *name) {
477 _cleanup_free_ char *e = NULL;
481 e = bus_label_escape(name);
485 return strappend("/org/freedesktop/systemd1/unit/", e);
488 int unit_name_from_dbus_path(const char *path, char **name) {
492 e = startswith(path, "/org/freedesktop/systemd1/unit/");
496 n = bus_label_unescape(e);
505 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
506 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
507 * except that @suffix is appended if a valid unit suffix is not present.
509 * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
511 char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
516 assert(suffix[0] == '.');
518 if (is_device_path(name))
519 return unit_name_from_path(name, ".device");
521 if (path_is_absolute(name))
522 return unit_name_from_path(name, ".mount");
524 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
528 t = do_escape_mangle(name, allow_globs, r);
530 if (unit_name_to_type(name) < 0)
538 UnitType unit_name_to_type(const char *n) {
545 return _UNIT_TYPE_INVALID;
547 return unit_type_from_string(e + 1);
550 int build_subslice(const char *slice, const char*name, char **subslice) {
557 if (streq(slice, "-.slice"))
558 ret = strappend(name, ".slice");
562 e = endswith(slice, ".slice");
566 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
570 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
577 static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
578 [UNIT_REQUIRES] = "Requires",
579 [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
580 [UNIT_REQUISITE] = "Requisite",
581 [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
582 [UNIT_WANTS] = "Wants",
583 [UNIT_BINDS_TO] = "BindsTo",
584 [UNIT_PART_OF] = "PartOf",
585 [UNIT_REQUIRED_BY] = "RequiredBy",
586 [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
587 [UNIT_WANTED_BY] = "WantedBy",
588 [UNIT_BOUND_BY] = "BoundBy",
589 [UNIT_CONSISTS_OF] = "ConsistsOf",
590 [UNIT_CONFLICTS] = "Conflicts",
591 [UNIT_CONFLICTED_BY] = "ConflictedBy",
592 [UNIT_BEFORE] = "Before",
593 [UNIT_AFTER] = "After",
594 [UNIT_ON_FAILURE] = "OnFailure",
595 [UNIT_TRIGGERS] = "Triggers",
596 [UNIT_TRIGGERED_BY] = "TriggeredBy",
597 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
598 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
599 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
600 [UNIT_REFERENCES] = "References",
601 [UNIT_REFERENCED_BY] = "ReferencedBy",
604 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);