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 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
37 [UNIT_SERVICE] = "service",
38 [UNIT_SOCKET] = "socket",
39 [UNIT_BUSNAME] = "busname",
40 [UNIT_TARGET] = "target",
41 [UNIT_SNAPSHOT] = "snapshot",
42 [UNIT_DEVICE] = "device",
43 [UNIT_MOUNT] = "mount",
44 [UNIT_AUTOMOUNT] = "automount",
46 [UNIT_TIMER] = "timer",
48 [UNIT_SLICE] = "slice",
49 [UNIT_SCOPE] = "scope"
52 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
54 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
56 [UNIT_LOADED] = "loaded",
57 [UNIT_NOT_FOUND] = "not-found",
58 [UNIT_ERROR] = "error",
59 [UNIT_MERGED] = "merged",
60 [UNIT_MASKED] = "masked"
63 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
65 bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
66 const char *e, *i, *at;
70 * string@instance.suffix
74 assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID));
79 if (strlen(n) >= UNIT_NAME_MAX)
86 if (unit_type_from_string(e + 1) < 0)
89 for (i = n, at = NULL; i < e; i++) {
94 if (!strchr("@" VALID_CHARS, *i))
102 if (template_ok != TEMPLATE_VALID && at+1 == e)
109 bool unit_instance_is_valid(const char *i) {
111 /* The max length depends on the length of the string, so we
112 * don't really check this here. */
117 /* We allow additional @ in the instance string, we do not
118 * allow them in the prefix! */
120 return in_charset(i, "@" VALID_CHARS);
123 bool unit_prefix_is_valid(const char *p) {
125 /* We don't allow additional @ in the instance string */
130 return in_charset(p, VALID_CHARS);
133 int unit_name_to_instance(const char *n, char **instance) {
140 /* Everything past the first @ and before the last . is the instance */
153 i = strndup(p+1, d-p-1);
161 char *unit_name_to_prefix_and_instance(const char *n) {
166 assert_se(d = strrchr(n, '.'));
167 return strndup(n, d - n);
170 char *unit_name_to_prefix(const char *n) {
177 return strndup(n, p - n);
179 return unit_name_to_prefix_and_instance(n);
182 char *unit_name_change_suffix(const char *n, const char *suffix) {
188 assert(suffix[0] == '.');
190 assert_se(e = strrchr(n, '.'));
194 r = new(char, a + b + 1);
198 strcpy(mempcpy(r, n, a), suffix);
202 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
207 return strappend(prefix, suffix);
209 return strjoin(prefix, "@", instance, suffix, NULL);
212 static char *do_escape_char(char c, char *t) {
217 *(t++) = hexchar(c >> 4);
223 static char *do_escape(const char *f, char *t) {
227 /* do not create units with a leading '.', like for "/.dotdir" mount points */
229 t = do_escape_char(*f, t);
236 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
237 t = do_escape_char(*f, t);
245 static char *do_escape_mangle(const char *f, enum unit_name_mangle allow_globs, char *t) {
246 const char *valid_chars;
249 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
252 /* We'll only escape the obvious characters here, to play
255 valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
260 else if (!strchr(valid_chars, *f))
261 t = do_escape_char(*f, t);
269 char *unit_name_escape(const char *f) {
274 r = new(char, strlen(f)*4+1);
284 char *unit_name_unescape(const char *f) {
293 for (t = r; *f; f++) {
296 else if (*f == '\\') {
300 (a = unhexchar(f[2])) < 0 ||
301 (b = unhexchar(f[3])) < 0) {
302 /* Invalid escape code, let's take it literal then */
305 *(t++) = (char) ((a << 4) | b);
317 char *unit_name_path_escape(const char *f) {
318 _cleanup_free_ char *p = NULL;
326 path_kill_slashes(p);
328 if (STR_IN_SET(p, "/", ""))
331 return unit_name_escape(p[0] == '/' ? p + 1 : p);
334 char *unit_name_path_unescape(const char *f) {
339 e = unit_name_unescape(f);
344 w = strappend("/", e);
352 bool unit_name_is_template(const char *n) {
361 e = strrchr(p+1, '.');
368 bool unit_name_is_instance(const char *n) {
377 e = strrchr(p+1, '.');
384 char *unit_name_replace_instance(const char *f, const char *i) {
403 r = new(char, a + 1 + b + strlen(e) + 1);
407 strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e);
411 char *unit_name_template(const char *f) {
428 r = new(char, a + 1 + strlen(e) + 1);
432 strcpy(mempcpy(r, f, a + 1), e);
436 char *unit_name_from_path(const char *path, const char *suffix) {
437 _cleanup_free_ char *p = NULL;
442 p = unit_name_path_escape(path);
446 return strappend(p, suffix);
449 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
450 _cleanup_free_ char *p = NULL;
456 p = unit_name_path_escape(path);
460 return strjoin(prefix, "@", p, suffix, NULL);
463 char *unit_name_to_path(const char *name) {
464 _cleanup_free_ char *w = NULL;
468 w = unit_name_to_prefix(name);
472 return unit_name_path_unescape(w);
475 char *unit_dbus_path_from_name(const char *name) {
476 _cleanup_free_ char *e = NULL;
480 e = bus_label_escape(name);
484 return strappend("/org/freedesktop/systemd1/unit/", e);
487 int unit_name_from_dbus_path(const char *path, char **name) {
491 e = startswith(path, "/org/freedesktop/systemd1/unit/");
495 n = bus_label_unescape(e);
504 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
505 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
506 * except that @suffix is appended if a valid unit suffix is not present.
508 * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
510 char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
515 assert(suffix[0] == '.');
517 if (is_device_path(name))
518 return unit_name_from_path(name, ".device");
520 if (path_is_absolute(name))
521 return unit_name_from_path(name, ".mount");
523 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
527 t = do_escape_mangle(name, allow_globs, r);
529 if (unit_name_to_type(name) < 0)
537 UnitType unit_name_to_type(const char *n) {
544 return _UNIT_TYPE_INVALID;
546 return unit_type_from_string(e + 1);
549 int build_subslice(const char *slice, const char*name, char **subslice) {
556 if (streq(slice, "-.slice"))
557 ret = strappend(name, ".slice");
561 e = endswith(slice, ".slice");
565 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
569 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
576 static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
577 [UNIT_REQUIRES] = "Requires",
578 [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
579 [UNIT_REQUISITE] = "Requisite",
580 [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
581 [UNIT_WANTS] = "Wants",
582 [UNIT_BINDS_TO] = "BindsTo",
583 [UNIT_PART_OF] = "PartOf",
584 [UNIT_REQUIRED_BY] = "RequiredBy",
585 [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
586 [UNIT_WANTED_BY] = "WantedBy",
587 [UNIT_BOUND_BY] = "BoundBy",
588 [UNIT_CONSISTS_OF] = "ConsistsOf",
589 [UNIT_CONFLICTS] = "Conflicts",
590 [UNIT_CONFLICTED_BY] = "ConflictedBy",
591 [UNIT_BEFORE] = "Before",
592 [UNIT_AFTER] = "After",
593 [UNIT_ON_FAILURE] = "OnFailure",
594 [UNIT_TRIGGERS] = "Triggers",
595 [UNIT_TRIGGERED_BY] = "TriggeredBy",
596 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
597 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
598 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
599 [UNIT_REFERENCES] = "References",
600 [UNIT_REFERENCED_BY] = "ReferencedBy",
603 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);