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 char *unit_name_escape(const char *f) {
251 r = new(char, strlen(f)*4+1);
261 char *unit_name_unescape(const char *f) {
270 for (t = r; *f; f++) {
273 else if (*f == '\\') {
277 (a = unhexchar(f[2])) < 0 ||
278 (b = unhexchar(f[3])) < 0) {
279 /* Invalid escape code, let's take it literal then */
282 *(t++) = (char) ((a << 4) | b);
294 char *unit_name_path_escape(const char *f) {
295 _cleanup_free_ char *p = NULL;
303 path_kill_slashes(p);
305 if (STR_IN_SET(p, "/", ""))
308 return unit_name_escape(p[0] == '/' ? p + 1 : p);
311 char *unit_name_path_unescape(const char *f) {
316 e = unit_name_unescape(f);
321 w = strappend("/", e);
329 bool unit_name_is_template(const char *n) {
338 e = strrchr(p+1, '.');
345 bool unit_name_is_instance(const char *n) {
354 e = strrchr(p+1, '.');
361 char *unit_name_replace_instance(const char *f, const char *i) {
380 r = new(char, a + 1 + b + strlen(e) + 1);
384 strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e);
388 char *unit_name_template(const char *f) {
405 r = new(char, a + 1 + strlen(e) + 1);
409 strcpy(mempcpy(r, f, a + 1), e);
413 char *unit_name_from_path(const char *path, const char *suffix) {
414 _cleanup_free_ char *p = NULL;
419 p = unit_name_path_escape(path);
423 return strappend(p, suffix);
426 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
427 _cleanup_free_ char *p = NULL;
433 p = unit_name_path_escape(path);
437 return strjoin(prefix, "@", p, suffix, NULL);
440 char *unit_name_to_path(const char *name) {
441 _cleanup_free_ char *w = NULL;
445 w = unit_name_to_prefix(name);
449 return unit_name_path_unescape(w);
452 char *unit_dbus_path_from_name(const char *name) {
453 _cleanup_free_ char *e = NULL;
457 e = bus_label_escape(name);
461 return strappend("/org/freedesktop/systemd1/unit/", e);
464 int unit_name_from_dbus_path(const char *path, char **name) {
468 e = startswith(path, "/org/freedesktop/systemd1/unit/");
472 n = bus_label_unescape(e);
481 * Try to turn a string that might not be a unit name into a
482 * sensible unit name.
484 char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
485 const char *valid_chars, *f;
489 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
491 if (is_device_path(name))
492 return unit_name_from_path(name, ".device");
494 if (path_is_absolute(name))
495 return unit_name_from_path(name, ".mount");
497 /* We'll only escape the obvious characters here, to play
500 valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
502 r = new(char, strlen(name) * 4 + strlen(".service") + 1);
506 for (f = name, t = r; *f; f++) {
509 else if (!strchr(valid_chars, *f))
510 t = do_escape_char(*f, t);
515 if (unit_name_to_type(name) < 0)
516 strcpy(t, ".service");
524 * Similar to unit_name_mangle(), but is called when we know
525 * that this is about a specific unit type.
527 char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
532 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
534 assert(suffix[0] == '.');
536 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
540 for (f = name, t = r; *f; f++) {
543 else if (!strchr(VALID_CHARS, *f))
544 t = do_escape_char(*f, t);
549 if (!endswith(name, suffix))
557 UnitType unit_name_to_type(const char *n) {
564 return _UNIT_TYPE_INVALID;
566 return unit_type_from_string(e + 1);
569 int build_subslice(const char *slice, const char*name, char **subslice) {
576 if (streq(slice, "-.slice"))
577 ret = strappend(name, ".slice");
581 e = endswith(slice, ".slice");
585 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
589 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");