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 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
53 [UNIT_LOADED] = "loaded",
54 [UNIT_ERROR] = "error",
55 [UNIT_MERGED] = "merged",
56 [UNIT_MASKED] = "masked"
59 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
61 bool unit_name_is_valid(const char *n, bool template_ok) {
62 const char *e, *i, *at;
66 * string@instance.suffix
72 if (strlen(n) >= UNIT_NAME_MAX)
79 if (unit_type_from_string(e + 1) < 0)
82 for (i = n, at = NULL; i < e; i++) {
87 if (!strchr("@" VALID_CHARS, *i))
95 if (!template_ok && at+1 == e)
102 bool unit_instance_is_valid(const char *i) {
105 /* The max length depends on the length of the string, so we
106 * don't really check this here. */
111 /* We allow additional @ in the instance string, we do not
112 * allow them in the prefix! */
115 if (!strchr("@" VALID_CHARS, *i))
121 bool unit_prefix_is_valid(const char *p) {
123 /* We don't allow additional @ in the instance string */
129 if (!strchr(VALID_CHARS, *p))
135 int unit_name_to_instance(const char *n, char **instance) {
142 /* Everything past the first @ and before the last . is the instance */
149 assert_se(d = strrchr(n, '.'));
152 i = strndup(p+1, d-p-1);
160 char *unit_name_to_prefix_and_instance(const char *n) {
165 assert_se(d = strrchr(n, '.'));
167 return strndup(n, d - n);
170 char *unit_name_to_prefix(const char *n) {
175 return strndup(n, p - n);
177 return unit_name_to_prefix_and_instance(n);
180 char *unit_name_change_suffix(const char *n, const char *suffix) {
185 assert(unit_name_is_valid(n, true));
188 assert_se(e = strrchr(n, '.'));
192 r = new(char, a + b + 1);
197 memcpy(r+a, suffix, b+1);
202 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
204 assert(unit_prefix_is_valid(prefix));
205 assert(!instance || unit_instance_is_valid(instance));
209 return strappend(prefix, suffix);
211 return strjoin(prefix, "@", instance, suffix, NULL);
214 static char *do_escape_char(char c, char *t) {
217 *(t++) = hexchar(c >> 4);
222 static char *do_escape(const char *f, char *t) {
226 /* do not create units with a leading '.', like for "/.dotdir" mount points */
228 t = do_escape_char(*f, t);
235 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
236 t = do_escape_char(*f, t);
244 char *unit_name_escape(const char *f) {
247 r = new(char, strlen(f)*4+1);
257 char *unit_name_unescape(const char *f) {
266 for (t = r; *f; f++) {
269 else if (*f == '\\') {
273 (a = unhexchar(f[2])) < 0 ||
274 (b = unhexchar(f[3])) < 0) {
275 /* Invalid escape code, let's take it literal then */
278 *(t++) = (char) ((a << 4) | b);
290 char *unit_name_path_escape(const char *f) {
299 path_kill_slashes(p);
306 e = unit_name_escape(p[0] == '/' ? p + 1 : p);
312 char *unit_name_path_unescape(const char *f) {
317 e = unit_name_unescape(f);
324 w = strappend("/", e);
333 bool unit_name_is_template(const char *n) {
345 bool unit_name_is_instance(const char *n) {
357 char *unit_name_replace_instance(const char *f, const char *i) {
370 assert_se(e = strchr(f, 0));
375 r = new(char, a + 1 + b + strlen(e) + 1);
379 k = mempcpy(r, f, a + 1);
380 k = mempcpy(k, i, b);
386 char *unit_name_template(const char *f) {
395 assert_se(e = strrchr(f, '.'));
398 r = new(char, a + strlen(e) + 1);
402 strcpy(mempcpy(r, f, a), e);
407 char *unit_name_from_path(const char *path, const char *suffix) {
413 p = unit_name_path_escape(path);
417 r = strappend(p, suffix);
423 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
430 p = unit_name_path_escape(path);
434 r = strjoin(prefix, "@", p, suffix, NULL);
440 char *unit_name_to_path(const char *name) {
445 w = unit_name_to_prefix(name);
449 e = unit_name_path_unescape(w);
455 char *unit_dbus_path_from_name(const char *name) {
460 e = bus_path_escape(name);
464 p = strappend("/org/freedesktop/systemd1/unit/", e);
470 char *unit_name_mangle(const char *name) {
477 /* Try to turn a string that might not be a unit name into a
478 * sensible unit name. */
480 if (is_device_path(name))
481 return unit_name_from_path(name, ".device");
483 if (path_is_absolute(name))
484 return unit_name_from_path(name, ".mount");
486 /* We'll only escape the obvious characters here, to play
489 r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
493 for (f = name, t = r; *f; f++) {
500 else if (!strchr("@" VALID_CHARS, *f))
501 t = do_escape_char(*f, t);
507 strcpy(t, ".service");
514 UnitType unit_name_to_type(const char *n) {
521 return _UNIT_TYPE_INVALID;
523 return unit_type_from_string(e + 1);