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/>.
27 #include "path-util.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) {
305 path_kill_slashes(p);
307 if (streq(p, "/") || streq(p, "")) {
312 e = unit_name_escape(p[0] == '/' ? p + 1 : p);
318 char *unit_name_path_unescape(const char *f) {
323 e = unit_name_unescape(f);
330 w = strappend("/", e);
339 bool unit_name_is_template(const char *n) {
351 bool unit_name_is_instance(const char *n) {
363 char *unit_name_replace_instance(const char *f, const char *i) {
376 assert_se(e = strchr(f, 0));
381 r = new(char, a + 1 + b + strlen(e) + 1);
385 k = mempcpy(r, f, a + 1);
386 k = mempcpy(k, i, b);
392 char *unit_name_template(const char *f) {
401 assert_se(e = strrchr(f, '.'));
404 r = new(char, a + strlen(e) + 1);
408 strcpy(mempcpy(r, f, a), e);
412 char *unit_name_from_path(const char *path, const char *suffix) {
418 p = unit_name_path_escape(path);
422 r = strappend(p, suffix);
428 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
435 p = unit_name_path_escape(path);
439 r = strjoin(prefix, "@", p, suffix, NULL);
445 char *unit_name_to_path(const char *name) {
446 _cleanup_free_ char *w = NULL;
450 w = unit_name_to_prefix(name);
454 return unit_name_path_unescape(w);
457 char *unit_dbus_path_from_name(const char *name) {
458 _cleanup_free_ char *e = NULL;
462 e = sd_bus_label_escape(name);
466 return strappend("/org/freedesktop/systemd1/unit/", e);
469 int unit_name_from_dbus_path(const char *path, char **name) {
473 e = startswith(path, "/org/freedesktop/systemd1/unit/");
477 n = sd_bus_label_unescape(e);
487 * Try to turn a string that might not be a unit name into a
488 * sensible unit name.
490 char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
493 const char* valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
496 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
498 if (is_device_path(name))
499 return unit_name_from_path(name, ".device");
501 if (path_is_absolute(name))
502 return unit_name_from_path(name, ".mount");
504 /* We'll only escape the obvious characters here, to play
507 r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
511 for (f = name, t = r; *f; f++) {
514 else if (!strchr(valid_chars, *f))
515 t = do_escape_char(*f, t);
520 if (unit_name_to_type(name) < 0)
521 strcpy(t, ".service");
530 * Similar to unit_name_mangle(), but is called when we know
531 * that this is about a specific unit type.
533 char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
539 assert(suffix[0] == '.');
541 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
545 for (f = name, t = r; *f; f++) {
548 else if (!strchr(VALID_CHARS, *f))
549 t = do_escape_char(*f, t);
554 if (!endswith(name, suffix))
562 UnitType unit_name_to_type(const char *n) {
569 return _UNIT_TYPE_INVALID;
571 return unit_type_from_string(e + 1);
574 int build_subslice(const char *slice, const char*name, char **subslice) {
581 if (streq(slice, "-.slice"))
582 ret = strappend(name, ".slice");
586 e = endswith(slice, ".slice");
590 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
594 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");