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, bool template_ok) {
66 const char *e, *i, *at;
70 * string@instance.suffix
76 if (strlen(n) >= UNIT_NAME_MAX)
83 if (unit_type_from_string(e + 1) < 0)
86 for (i = n, at = NULL; i < e; i++) {
91 if (!strchr("@" VALID_CHARS, *i))
99 if (!template_ok && at+1 == e)
106 bool unit_instance_is_valid(const char *i) {
109 /* The max length depends on the length of the string, so we
110 * don't really check this here. */
115 /* We allow additional @ in the instance string, we do not
116 * allow them in the prefix! */
119 if (!strchr("@" VALID_CHARS, *i))
125 bool unit_prefix_is_valid(const char *p) {
127 /* We don't allow additional @ in the instance string */
133 if (!strchr(VALID_CHARS, *p))
139 int unit_name_to_instance(const char *n, char **instance) {
146 /* Everything past the first @ and before the last . is the instance */
153 assert_se(d = strrchr(n, '.'));
156 i = strndup(p+1, d-p-1);
164 char *unit_name_to_prefix_and_instance(const char *n) {
169 assert_se(d = strrchr(n, '.'));
171 return strndup(n, d - n);
174 char *unit_name_to_prefix(const char *n) {
179 return strndup(n, p - n);
181 return unit_name_to_prefix_and_instance(n);
184 char *unit_name_change_suffix(const char *n, const char *suffix) {
189 assert(unit_name_is_valid(n, true));
191 assert(suffix[0] == '.');
193 assert_se(e = strrchr(n, '.'));
197 r = new(char, a + b + 1);
202 memcpy(r+a, suffix, b+1);
207 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
209 assert(unit_prefix_is_valid(prefix));
210 assert(!instance || unit_instance_is_valid(instance));
214 return strappend(prefix, suffix);
216 return strjoin(prefix, "@", instance, suffix, NULL);
219 static char *do_escape_char(char c, char *t) {
222 *(t++) = hexchar(c >> 4);
227 static char *do_escape(const char *f, char *t) {
231 /* do not create units with a leading '.', like for "/.dotdir" mount points */
233 t = do_escape_char(*f, t);
240 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
241 t = do_escape_char(*f, t);
249 char *unit_name_escape(const char *f) {
252 r = new(char, strlen(f)*4+1);
262 char *unit_name_unescape(const char *f) {
271 for (t = r; *f; f++) {
274 else if (*f == '\\') {
278 (a = unhexchar(f[2])) < 0 ||
279 (b = unhexchar(f[3])) < 0) {
280 /* Invalid escape code, let's take it literal then */
283 *(t++) = (char) ((a << 4) | b);
295 char *unit_name_path_escape(const char *f) {
304 path_kill_slashes(p);
306 if (streq(p, "/") || streq(p, "")) {
311 e = unit_name_escape(p[0] == '/' ? p + 1 : p);
317 char *unit_name_path_unescape(const char *f) {
322 e = unit_name_unescape(f);
329 w = strappend("/", e);
338 bool unit_name_is_template(const char *n) {
350 bool unit_name_is_instance(const char *n) {
362 char *unit_name_replace_instance(const char *f, const char *i) {
375 assert_se(e = strchr(f, 0));
380 r = new(char, a + 1 + b + strlen(e) + 1);
384 k = mempcpy(r, f, a + 1);
385 k = mempcpy(k, i, b);
391 char *unit_name_template(const char *f) {
400 assert_se(e = strrchr(f, '.'));
403 r = new(char, a + strlen(e) + 1);
407 strcpy(mempcpy(r, f, a), e);
411 char *unit_name_from_path(const char *path, const char *suffix) {
417 p = unit_name_path_escape(path);
421 r = strappend(p, suffix);
427 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
434 p = unit_name_path_escape(path);
438 r = strjoin(prefix, "@", p, suffix, NULL);
444 char *unit_name_to_path(const char *name) {
445 _cleanup_free_ char *w = NULL;
449 w = unit_name_to_prefix(name);
453 return unit_name_path_unescape(w);
456 char *unit_dbus_path_from_name(const char *name) {
457 _cleanup_free_ char *e = NULL;
461 e = sd_bus_label_escape(name);
465 return strappend("/org/freedesktop/systemd1/unit/", e);
468 int unit_name_from_dbus_path(const char *path, char **name) {
472 e = startswith(path, "/org/freedesktop/systemd1/unit/");
476 n = sd_bus_label_unescape(e);
484 char *unit_name_mangle(const char *name) {
490 /* Try to turn a string that might not be a unit name into a
491 * sensible unit name. */
493 if (is_device_path(name))
494 return unit_name_from_path(name, ".device");
496 if (path_is_absolute(name))
497 return unit_name_from_path(name, ".mount");
499 /* We'll only escape the obvious characters here, to play
502 r = new(char, strlen(name) * 4 + 1 + sizeof(".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");
523 char *unit_name_mangle_with_suffix(const char *name, const char *suffix) {
529 assert(suffix[0] == '.');
531 /* Similar to unit_name_mangle(), but is called when we know
532 * that this is about snapshot units. */
534 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
538 for (f = name, t = r; *f; f++) {
541 else if (!strchr(VALID_CHARS, *f))
542 t = do_escape_char(*f, t);
547 if (!endswith(name, suffix))
555 UnitType unit_name_to_type(const char *n) {
562 return _UNIT_TYPE_INVALID;
564 return unit_type_from_string(e + 1);
567 int build_subslice(const char *slice, const char*name, char **subslice) {
574 if (streq(slice, "-.slice"))
575 ret = strappend(name, ".slice");
579 e = endswith(slice, ".slice");
583 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
587 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");