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"
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
75 assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID));
77 if (strlen(n) >= UNIT_NAME_MAX)
84 if (unit_type_from_string(e + 1) < 0)
87 for (i = n, at = NULL; i < e; i++) {
92 if (!strchr("@" VALID_CHARS, *i))
100 if (!template_ok == TEMPLATE_VALID && at+1 == e)
107 bool unit_instance_is_valid(const char *i) {
110 /* The max length depends on the length of the string, so we
111 * don't really check this here. */
116 /* We allow additional @ in the instance string, we do not
117 * allow them in the prefix! */
120 if (!strchr("@" VALID_CHARS, *i))
126 bool unit_prefix_is_valid(const char *p) {
128 /* We don't allow additional @ in the instance string */
134 if (!strchr(VALID_CHARS, *p))
140 int unit_name_to_instance(const char *n, char **instance) {
147 /* Everything past the first @ and before the last . is the instance */
154 assert_se(d = strrchr(n, '.'));
157 i = strndup(p+1, d-p-1);
165 char *unit_name_to_prefix_and_instance(const char *n) {
170 assert_se(d = strrchr(n, '.'));
172 return strndup(n, d - n);
175 char *unit_name_to_prefix(const char *n) {
180 return strndup(n, p - n);
182 return unit_name_to_prefix_and_instance(n);
185 char *unit_name_change_suffix(const char *n, const char *suffix) {
190 assert(unit_name_is_valid(n, TEMPLATE_VALID));
192 assert(suffix[0] == '.');
194 assert_se(e = strrchr(n, '.'));
198 r = new(char, a + b + 1);
203 memcpy(r+a, suffix, b+1);
208 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
210 assert(unit_prefix_is_valid(prefix));
211 assert(!instance || unit_instance_is_valid(instance));
215 return strappend(prefix, suffix);
217 return strjoin(prefix, "@", instance, suffix, NULL);
220 static char *do_escape_char(char c, char *t) {
223 *(t++) = hexchar(c >> 4);
228 static char *do_escape(const char *f, char *t) {
232 /* do not create units with a leading '.', like for "/.dotdir" mount points */
234 t = do_escape_char(*f, t);
241 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
242 t = do_escape_char(*f, t);
250 char *unit_name_escape(const char *f) {
253 r = new(char, strlen(f)*4+1);
263 char *unit_name_unescape(const char *f) {
272 for (t = r; *f; f++) {
275 else if (*f == '\\') {
279 (a = unhexchar(f[2])) < 0 ||
280 (b = unhexchar(f[3])) < 0) {
281 /* Invalid escape code, let's take it literal then */
284 *(t++) = (char) ((a << 4) | b);
296 char *unit_name_path_escape(const char *f) {
297 _cleanup_free_ char *p;
305 path_kill_slashes(p);
307 if (streq(p, "/") || streq(p, ""))
310 return unit_name_escape(p[0] == '/' ? p + 1 : p);
313 char *unit_name_path_unescape(const char *f) {
318 e = unit_name_unescape(f);
325 w = strappend("/", e);
334 bool unit_name_is_template(const char *n) {
346 bool unit_name_is_instance(const char *n) {
358 char *unit_name_replace_instance(const char *f, const char *i) {
371 assert_se(e = strchr(f, 0));
376 r = new(char, a + 1 + b + strlen(e) + 1);
380 k = mempcpy(r, f, a + 1);
381 k = mempcpy(k, i, b);
387 char *unit_name_template(const char *f) {
396 assert_se(e = strrchr(f, '.'));
399 r = new(char, a + strlen(e) + 1);
403 strcpy(mempcpy(r, f, a), e);
407 char *unit_name_from_path(const char *path, const char *suffix) {
408 _cleanup_free_ char *p = NULL;
413 p = unit_name_path_escape(path);
417 return strappend(p, suffix);
420 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
421 _cleanup_free_ char *p = NULL;
427 p = unit_name_path_escape(path);
431 return strjoin(prefix, "@", p, suffix, NULL);
434 char *unit_name_to_path(const char *name) {
435 _cleanup_free_ char *w = NULL;
439 w = unit_name_to_prefix(name);
443 return unit_name_path_unescape(w);
446 char *unit_dbus_path_from_name(const char *name) {
447 _cleanup_free_ char *e = NULL;
451 e = bus_label_escape(name);
455 return strappend("/org/freedesktop/systemd1/unit/", e);
458 int unit_name_from_dbus_path(const char *path, char **name) {
462 e = startswith(path, "/org/freedesktop/systemd1/unit/");
466 n = bus_label_unescape(e);
476 * Try to turn a string that might not be a unit name into a
477 * sensible unit name.
479 char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
482 const char* valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
485 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
487 if (is_device_path(name))
488 return unit_name_from_path(name, ".device");
490 if (path_is_absolute(name))
491 return unit_name_from_path(name, ".mount");
493 /* We'll only escape the obvious characters here, to play
496 r = new(char, strlen(name) * 4 + strlen(".service") + 1);
500 for (f = name, t = r; *f; f++) {
503 else if (!strchr(valid_chars, *f))
504 t = do_escape_char(*f, t);
509 if (unit_name_to_type(name) < 0)
510 strcpy(t, ".service");
519 * Similar to unit_name_mangle(), but is called when we know
520 * that this is about a specific unit type.
522 char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
528 assert(suffix[0] == '.');
530 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
534 for (f = name, t = r; *f; f++) {
537 else if (!strchr(VALID_CHARS, *f))
538 t = do_escape_char(*f, t);
543 if (!endswith(name, suffix))
551 UnitType unit_name_to_type(const char *n) {
558 return _UNIT_TYPE_INVALID;
560 return unit_type_from_string(e + 1);
563 int build_subslice(const char *slice, const char*name, char **subslice) {
570 if (streq(slice, "-.slice"))
571 ret = strappend(name, ".slice");
575 e = endswith(slice, ".slice");
579 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
583 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");