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"
28 #include "unit-name.h"
32 "abcdefghijklmnopqrstuvwxyz" \
33 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
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",
49 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
51 bool unit_name_is_valid(const char *n, bool template_ok) {
52 const char *e, *i, *at;
56 * string@instance.suffix
62 if (strlen(n) >= UNIT_NAME_MAX)
69 if (unit_type_from_string(e + 1) < 0)
72 for (i = n, at = NULL; i < e; i++) {
77 if (!strchr("@" VALID_CHARS, *i))
85 if (!template_ok && at+1 == e)
92 bool unit_instance_is_valid(const char *i) {
95 /* The max length depends on the length of the string, so we
96 * don't really check this here. */
101 /* We allow additional @ in the instance string, we do not
102 * allow them in the prefix! */
105 if (!strchr("@" VALID_CHARS, *i))
111 bool unit_prefix_is_valid(const char *p) {
113 /* We don't allow additional @ in the instance string */
119 if (!strchr(VALID_CHARS, *p))
125 int unit_name_to_instance(const char *n, char **instance) {
132 /* Everything past the first @ and before the last . is the instance */
139 assert_se(d = strrchr(n, '.'));
142 i = strndup(p+1, d-p-1);
150 char *unit_name_to_prefix_and_instance(const char *n) {
155 assert_se(d = strrchr(n, '.'));
157 return strndup(n, d - n);
160 char *unit_name_to_prefix(const char *n) {
165 return strndup(n, p - n);
167 return unit_name_to_prefix_and_instance(n);
170 char *unit_name_change_suffix(const char *n, const char *suffix) {
175 assert(unit_name_is_valid(n, true));
178 assert_se(e = strrchr(n, '.'));
182 r = new(char, a + b + 1);
187 memcpy(r+a, suffix, b+1);
192 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
194 assert(unit_prefix_is_valid(prefix));
195 assert(!instance || unit_instance_is_valid(instance));
199 return strappend(prefix, suffix);
201 return join(prefix, "@", instance, suffix, NULL);
204 static char *do_escape_char(char c, char *t) {
207 *(t++) = hexchar(c >> 4);
212 static char *do_escape(const char *f, char *t) {
216 /* do not create units with a leading '.', like for "/.dotdir" mount points */
218 t = do_escape_char(*f, t);
225 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
226 t = do_escape_char(*f, t);
234 char *unit_name_escape(const char *f) {
237 r = new(char, strlen(f)*4+1);
247 char *unit_name_unescape(const char *f) {
256 for (t = r; *f; f++) {
259 else if (*f == '\\') {
263 (a = unhexchar(f[2])) < 0 ||
264 (b = unhexchar(f[3])) < 0) {
265 /* Invalid escape code, let's take it literal then */
268 *(t++) = (char) ((a << 4) | b);
280 char *unit_name_path_escape(const char *f) {
289 path_kill_slashes(p);
296 e = unit_name_escape(p[0] == '/' ? p + 1 : p);
302 char *unit_name_path_unescape(const char *f) {
307 e = unit_name_unescape(f);
314 w = strappend("/", e);
323 bool unit_name_is_template(const char *n) {
328 if (!(p = strchr(n, '@')))
334 char *unit_name_replace_instance(const char *f, const char *i) {
342 assert_se(e = strrchr(f, '.'));
351 r = new(char, a + 1 + b + strlen(e) + 1);
355 k = mempcpy(r, f, a + 1);
356 k = mempcpy(k, i, b);
359 r = new(char, a + strlen(e) + 1);
363 k = mempcpy(r, f, a);
370 char *unit_name_template(const char *f) {
379 assert_se(e = strrchr(f, '.'));
382 r = new(char, a + strlen(e) + 1);
386 strcpy(mempcpy(r, f, a), e);
391 char *unit_name_from_path(const char *path, const char *suffix) {
397 p = unit_name_path_escape(path);
401 r = strappend(p, suffix);
407 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
414 p = unit_name_path_escape(path);
418 r = join(prefix, "@", p, suffix, NULL);
424 char *unit_name_to_path(const char *name) {
429 w = unit_name_to_prefix(name);
433 e = unit_name_path_unescape(w);
439 char *unit_dbus_path_from_name(const char *name) {
444 e = bus_path_escape(name);
448 p = strappend("/org/freedesktop/systemd1/unit/", e);
454 char *unit_name_mangle(const char *name) {
460 /* Try to turn a string that might not be a unit name into a
461 * sensible unit name. */
463 if (path_startswith(name, "/dev/") ||
464 path_startswith(name, "/sys/"))
465 return unit_name_from_path(name, ".device");
467 if (path_is_absolute(name))
468 return unit_name_from_path(name, ".mount");
470 /* We'll only escape the obvious characters here, to play
473 r = new(char, strlen(name) * 4 + 1);
477 for (f = name, t = r; *f; f++) {
481 else if (!strchr("@" VALID_CHARS, *f))
482 t = do_escape_char(*f, t);
492 UnitType unit_name_to_type(const char *n) {
499 return _UNIT_TYPE_INVALID;
501 return unit_type_from_string(e + 1);