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_no_type(const char *n, bool template_ok) {
52 const char *e, *i, *at;
56 * string@instance.suffix
62 if (strlen(n) >= UNIT_NAME_MAX)
69 for (i = n, at = NULL; i < e; i++) {
74 if (!strchr("@" VALID_CHARS, *i))
82 if (!template_ok && at+1 == e)
89 bool unit_instance_is_valid(const char *i) {
92 /* The max length depends on the length of the string, so we
93 * don't really check this here. */
98 /* We allow additional @ in the instance string, we do not
99 * allow them in the prefix! */
102 if (!strchr("@" VALID_CHARS, *i))
108 bool unit_prefix_is_valid(const char *p) {
110 /* We don't allow additional @ in the instance string */
116 if (!strchr(VALID_CHARS, *p))
122 int unit_name_to_instance(const char *n, char **instance) {
129 /* Everything past the first @ and before the last . is the instance */
136 assert_se(d = strrchr(n, '.'));
139 i = strndup(p+1, d-p-1);
147 char *unit_name_to_prefix_and_instance(const char *n) {
152 assert_se(d = strrchr(n, '.'));
154 return strndup(n, d - n);
157 char *unit_name_to_prefix(const char *n) {
162 return strndup(n, p - n);
164 return unit_name_to_prefix_and_instance(n);
167 char *unit_name_change_suffix(const char *n, const char *suffix) {
172 assert(unit_name_is_valid_no_type(n, true));
175 assert_se(e = strrchr(n, '.'));
179 r = new(char, a + b + 1);
184 memcpy(r+a, suffix, b+1);
189 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
191 assert(unit_prefix_is_valid(prefix));
192 assert(!instance || unit_instance_is_valid(instance));
196 return strappend(prefix, suffix);
198 return join(prefix, "@", instance, suffix, NULL);
201 static char *do_escape_char(char c, char *t) {
204 *(t++) = hexchar(c >> 4);
209 static char *do_escape(const char *f, char *t) {
213 /* do not create units with a leading '.', like for "/.dotdir" mount points */
215 t = do_escape_char(*f, t);
222 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
223 t = do_escape_char(*f, t);
231 char *unit_name_escape(const char *f) {
234 r = new(char, strlen(f)*4+1);
244 char *unit_name_unescape(const char *f) {
253 for (t = r; *f; f++) {
256 else if (*f == '\\') {
260 (a = unhexchar(f[2])) < 0 ||
261 (b = unhexchar(f[3])) < 0) {
262 /* Invalid escape code, let's take it literal then */
265 *(t++) = (char) ((a << 4) | b);
277 char *unit_name_path_escape(const char *f) {
286 path_kill_slashes(p);
293 e = unit_name_escape(p[0] == '/' ? p + 1 : p);
299 char *unit_name_path_unescape(const char *f) {
304 e = unit_name_unescape(f);
311 w = strappend("/", e);
320 bool unit_name_is_template(const char *n) {
325 if (!(p = strchr(n, '@')))
331 char *unit_name_replace_instance(const char *f, const char *i) {
339 assert_se(e = strrchr(f, '.'));
348 r = new(char, a + 1 + b + strlen(e) + 1);
352 k = mempcpy(r, f, a + 1);
353 k = mempcpy(k, i, b);
356 r = new(char, a + strlen(e) + 1);
360 k = mempcpy(r, f, a);
367 char *unit_name_template(const char *f) {
376 assert_se(e = strrchr(f, '.'));
379 r = new(char, a + strlen(e) + 1);
383 strcpy(mempcpy(r, f, a), e);
388 char *unit_name_from_path(const char *path, const char *suffix) {
394 p = unit_name_path_escape(path);
398 r = strappend(p, suffix);
404 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
411 p = unit_name_path_escape(path);
415 r = join(prefix, "@", p, suffix, NULL);
421 char *unit_name_to_path(const char *name) {
426 w = unit_name_to_prefix(name);
430 e = unit_name_path_unescape(w);
436 char *unit_dbus_path_from_name(const char *name) {
441 e = bus_path_escape(name);
445 p = strappend("/org/freedesktop/systemd1/unit/", e);
451 char *unit_name_mangle(const char *name) {
457 /* Try to turn a string that might not be a unit name into a
458 * sensible unit name. */
460 if (path_startswith(name, "/dev/") ||
461 path_startswith(name, "/sys/"))
462 return unit_name_from_path(name, ".device");
464 if (path_is_absolute(name))
465 return unit_name_from_path(name, ".mount");
467 /* We'll only escape the obvious characters here, to play
470 r = new(char, strlen(name) * 4 + 1);
474 for (f = name, t = r; *f; f++) {
478 else if (!strchr("@" VALID_CHARS, *f))
479 t = do_escape_char(*f, t);