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_TARGET] = "target",
40 [UNIT_DEVICE] = "device",
41 [UNIT_MOUNT] = "mount",
42 [UNIT_AUTOMOUNT] = "automount",
43 [UNIT_SNAPSHOT] = "snapshot",
44 [UNIT_TIMER] = "timer",
47 [UNIT_SLICE] = "slice",
48 [UNIT_SCOPE] = "scope"
51 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
53 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
55 [UNIT_LOADED] = "loaded",
56 [UNIT_NOT_FOUND] = "not-found",
57 [UNIT_ERROR] = "error",
58 [UNIT_MERGED] = "merged",
59 [UNIT_MASKED] = "masked"
62 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
64 bool unit_name_is_valid(const char *n, bool template_ok) {
65 const char *e, *i, *at;
69 * string@instance.suffix
75 if (strlen(n) >= UNIT_NAME_MAX)
82 if (unit_type_from_string(e + 1) < 0)
85 for (i = n, at = NULL; i < e; i++) {
90 if (!strchr("@" VALID_CHARS, *i))
98 if (!template_ok && at+1 == e)
105 bool unit_instance_is_valid(const char *i) {
108 /* The max length depends on the length of the string, so we
109 * don't really check this here. */
114 /* We allow additional @ in the instance string, we do not
115 * allow them in the prefix! */
118 if (!strchr("@" VALID_CHARS, *i))
124 bool unit_prefix_is_valid(const char *p) {
126 /* We don't allow additional @ in the instance string */
132 if (!strchr(VALID_CHARS, *p))
138 int unit_name_to_instance(const char *n, char **instance) {
145 /* Everything past the first @ and before the last . is the instance */
152 assert_se(d = strrchr(n, '.'));
155 i = strndup(p+1, d-p-1);
163 char *unit_name_to_prefix_and_instance(const char *n) {
168 assert_se(d = strrchr(n, '.'));
170 return strndup(n, d - n);
173 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) {
188 assert(unit_name_is_valid(n, true));
190 assert(suffix[0] == '.');
192 assert_se(e = strrchr(n, '.'));
196 r = new(char, a + b + 1);
201 memcpy(r+a, suffix, b+1);
206 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
208 assert(unit_prefix_is_valid(prefix));
209 assert(!instance || unit_instance_is_valid(instance));
213 return strappend(prefix, suffix);
215 return strjoin(prefix, "@", instance, suffix, NULL);
218 static char *do_escape_char(char c, char *t) {
221 *(t++) = hexchar(c >> 4);
226 static char *do_escape(const char *f, char *t) {
230 /* do not create units with a leading '.', like for "/.dotdir" mount points */
232 t = do_escape_char(*f, t);
239 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
240 t = do_escape_char(*f, t);
248 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) {
303 path_kill_slashes(p);
305 if (streq(p, "/") || streq(p, "")) {
310 e = unit_name_escape(p[0] == '/' ? p + 1 : p);
316 char *unit_name_path_unescape(const char *f) {
321 e = unit_name_unescape(f);
328 w = strappend("/", e);
337 bool unit_name_is_template(const char *n) {
349 bool unit_name_is_instance(const char *n) {
361 char *unit_name_replace_instance(const char *f, const char *i) {
374 assert_se(e = strchr(f, 0));
379 r = new(char, a + 1 + b + strlen(e) + 1);
383 k = mempcpy(r, f, a + 1);
384 k = mempcpy(k, i, b);
390 char *unit_name_template(const char *f) {
399 assert_se(e = strrchr(f, '.'));
402 r = new(char, a + strlen(e) + 1);
406 strcpy(mempcpy(r, f, a), e);
410 char *unit_name_from_path(const char *path, const char *suffix) {
416 p = unit_name_path_escape(path);
420 r = strappend(p, suffix);
426 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
433 p = unit_name_path_escape(path);
437 r = strjoin(prefix, "@", p, suffix, NULL);
443 char *unit_name_to_path(const char *name) {
448 w = unit_name_to_prefix(name);
452 e = unit_name_path_unescape(w);
458 char *unit_dbus_path_from_name(const char *name) {
459 _cleanup_free_ char *e = NULL;
463 e = sd_bus_label_escape(name);
467 return strappend("/org/freedesktop/systemd1/unit/", e);
470 int unit_name_from_dbus_path(const char *path, char **name) {
474 e = startswith(path, "/org/freedesktop/systemd1/unit/");
478 n = sd_bus_label_unescape(e);
486 char *unit_name_mangle(const char *name) {
492 /* Try to turn a string that might not be a unit name into a
493 * sensible unit name. */
495 if (is_device_path(name))
496 return unit_name_from_path(name, ".device");
498 if (path_is_absolute(name))
499 return unit_name_from_path(name, ".mount");
501 /* We'll only escape the obvious characters here, to play
504 r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
508 for (f = name, t = r; *f; f++) {
511 else if (!strchr("@" VALID_CHARS, *f))
512 t = do_escape_char(*f, t);
517 if (unit_name_to_type(name) < 0)
518 strcpy(t, ".service");
525 char *unit_name_mangle_with_suffix(const char *name, const char *suffix) {
531 assert(suffix[0] == '.');
533 /* Similar to unit_name_mangle(), but is called when we know
534 * that this is about snapshot units. */
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");