]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/unit-name.c
unit-name: fix detection of unit templates/instances
[thirdparty/systemd.git] / src / shared / unit-name.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
9e2f7c11
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
9e2f7c11
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
9e2f7c11 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
9e2f7c11
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <string.h>
71fad675 24#include <assert.h>
9e2f7c11 25
9eb977db 26#include "path-util.h"
f01de965 27#include "bus-label.h"
71fad675 28#include "util.h"
9e2f7c11 29#include "unit-name.h"
4b549144 30#include "def.h"
9e2f7c11
LP
31
32#define VALID_CHARS \
4b549144 33 DIGITS LETTERS \
4f2d528d 34 ":-_.\\"
9e2f7c11 35
830f01f0 36static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
0a9f8ed0
ZJS
37 [UNIT_SERVICE] = "service",
38 [UNIT_SOCKET] = "socket",
e821075a 39 [UNIT_BUSNAME] = "busname",
0a9f8ed0 40 [UNIT_TARGET] = "target",
e821075a 41 [UNIT_SNAPSHOT] = "snapshot",
0a9f8ed0
ZJS
42 [UNIT_DEVICE] = "device",
43 [UNIT_MOUNT] = "mount",
44 [UNIT_AUTOMOUNT] = "automount",
0a9f8ed0 45 [UNIT_SWAP] = "swap",
e821075a 46 [UNIT_TIMER] = "timer",
0a9f8ed0 47 [UNIT_PATH] = "path",
6c12b52e
LP
48 [UNIT_SLICE] = "slice",
49 [UNIT_SCOPE] = "scope"
0a9f8ed0
ZJS
50};
51
52DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
53
830f01f0 54static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
f69614f8
ZJS
55 [UNIT_STUB] = "stub",
56 [UNIT_LOADED] = "loaded",
c2756a68 57 [UNIT_NOT_FOUND] = "not-found",
f69614f8
ZJS
58 [UNIT_ERROR] = "error",
59 [UNIT_MERGED] = "merged",
60 [UNIT_MASKED] = "masked"
61};
62
63DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
64
f78e6385 65bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
9e2f7c11
LP
66 const char *e, *i, *at;
67
68 /* Valid formats:
69 *
70 * string@instance.suffix
71 * string.suffix
72 */
73
74 assert(n);
f78e6385 75 assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID));
9e2f7c11
LP
76
77 if (strlen(n) >= UNIT_NAME_MAX)
78 return false;
79
71fad675
LP
80 e = strrchr(n, '.');
81 if (!e || e == n)
9e2f7c11
LP
82 return false;
83
5f739699
LP
84 if (unit_type_from_string(e + 1) < 0)
85 return false;
86
9e2f7c11
LP
87 for (i = n, at = NULL; i < e; i++) {
88
89 if (*i == '@' && !at)
90 at = i;
91
92 if (!strchr("@" VALID_CHARS, *i))
93 return false;
94 }
95
96 if (at) {
97 if (at == n)
98 return false;
99
f78e6385 100 if (!template_ok == TEMPLATE_VALID && at+1 == e)
9e2f7c11
LP
101 return false;
102 }
103
104 return true;
105}
106
107bool unit_instance_is_valid(const char *i) {
108 assert(i);
109
110 /* The max length depends on the length of the string, so we
111 * don't really check this here. */
112
113 if (i[0] == 0)
114 return false;
115
116 /* We allow additional @ in the instance string, we do not
117 * allow them in the prefix! */
118
119 for (; *i; i++)
120 if (!strchr("@" VALID_CHARS, *i))
121 return false;
122
123 return true;
124}
125
126bool unit_prefix_is_valid(const char *p) {
127
128 /* We don't allow additional @ in the instance string */
129
130 if (p[0] == 0)
131 return false;
132
133 for (; *p; p++)
134 if (!strchr(VALID_CHARS, *p))
135 return false;
136
137 return true;
138}
139
140int unit_name_to_instance(const char *n, char **instance) {
141 const char *p, *d;
142 char *i;
143
144 assert(n);
145 assert(instance);
146
147 /* Everything past the first @ and before the last . is the instance */
35eb6b12
LP
148 p = strchr(n, '@');
149 if (!p) {
9e2f7c11
LP
150 *instance = NULL;
151 return 0;
152 }
153
154 assert_se(d = strrchr(n, '.'));
155 assert(p < d);
156
35eb6b12
LP
157 i = strndup(p+1, d-p-1);
158 if (!i)
9e2f7c11
LP
159 return -ENOMEM;
160
161 *instance = i;
162 return 0;
163}
164
165char *unit_name_to_prefix_and_instance(const char *n) {
166 const char *d;
167
168 assert(n);
169
170 assert_se(d = strrchr(n, '.'));
171
172 return strndup(n, d - n);
173}
174
175char *unit_name_to_prefix(const char *n) {
176 const char *p;
177
b0193f1c
LP
178 p = strchr(n, '@');
179 if (p)
9e2f7c11
LP
180 return strndup(n, p - n);
181
182 return unit_name_to_prefix_and_instance(n);
183}
184
185char *unit_name_change_suffix(const char *n, const char *suffix) {
186 char *e, *r;
187 size_t a, b;
188
189 assert(n);
f78e6385 190 assert(unit_name_is_valid(n, TEMPLATE_VALID));
9e2f7c11 191 assert(suffix);
a016b922 192 assert(suffix[0] == '.');
9e2f7c11
LP
193
194 assert_se(e = strrchr(n, '.'));
195 a = e - n;
196 b = strlen(suffix);
197
b0193f1c
LP
198 r = new(char, a + b + 1);
199 if (!r)
9e2f7c11
LP
200 return NULL;
201
202 memcpy(r, n, a);
203 memcpy(r+a, suffix, b+1);
204
205 return r;
206}
207
208char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
9e2f7c11
LP
209 assert(prefix);
210 assert(unit_prefix_is_valid(prefix));
211 assert(!instance || unit_instance_is_valid(instance));
212 assert(suffix);
9e2f7c11
LP
213
214 if (!instance)
215 return strappend(prefix, suffix);
216
b7def684 217 return strjoin(prefix, "@", instance, suffix, NULL);
9e2f7c11
LP
218}
219
4b712653
KS
220static char *do_escape_char(char c, char *t) {
221 *(t++) = '\\';
222 *(t++) = 'x';
223 *(t++) = hexchar(c >> 4);
224 *(t++) = hexchar(c);
225 return t;
226}
227
228static char *do_escape(const char *f, char *t) {
9e2f7c11
LP
229 assert(f);
230 assert(t);
231
4b712653
KS
232 /* do not create units with a leading '.', like for "/.dotdir" mount points */
233 if (*f == '.') {
234 t = do_escape_char(*f, t);
235 f++;
236 }
237
9e2f7c11
LP
238 for (; *f; f++) {
239 if (*f == '/')
8d567588 240 *(t++) = '-';
4b712653
KS
241 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
242 t = do_escape_char(*f, t);
243 else
9e2f7c11
LP
244 *(t++) = *f;
245 }
246
247 return t;
248}
249
9e2f7c11
LP
250char *unit_name_escape(const char *f) {
251 char *r, *t;
252
b0193f1c
LP
253 r = new(char, strlen(f)*4+1);
254 if (!r)
9e2f7c11
LP
255 return NULL;
256
257 t = do_escape(f, r);
258 *t = 0;
259
260 return r;
9e2f7c11
LP
261}
262
263char *unit_name_unescape(const char *f) {
264 char *r, *t;
265
266 assert(f);
267
b0193f1c
LP
268 r = strdup(f);
269 if (!r)
9e2f7c11
LP
270 return NULL;
271
272 for (t = r; *f; f++) {
8d567588 273 if (*f == '-')
9e2f7c11
LP
274 *(t++) = '/';
275 else if (*f == '\\') {
276 int a, b;
277
15e11d81
LP
278 if (f[1] != 'x' ||
279 (a = unhexchar(f[2])) < 0 ||
280 (b = unhexchar(f[3])) < 0) {
281 /* Invalid escape code, let's take it literal then */
9e2f7c11
LP
282 *(t++) = '\\';
283 } else {
284 *(t++) = (char) ((a << 4) | b);
95e501f8 285 f += 3;
9e2f7c11
LP
286 }
287 } else
288 *(t++) = *f;
289 }
290
291 *t = 0;
292
293 return r;
294}
295
35eb6b12 296char *unit_name_path_escape(const char *f) {
58d08142 297 _cleanup_free_ char *p;
35eb6b12
LP
298
299 assert(f);
300
301 p = strdup(f);
302 if (!p)
303 return NULL;
304
305 path_kill_slashes(p);
306
58d08142 307 if (streq(p, "/") || streq(p, ""))
35eb6b12 308 return strdup("-");
35eb6b12 309
58d08142 310 return unit_name_escape(p[0] == '/' ? p + 1 : p);
35eb6b12
LP
311}
312
313char *unit_name_path_unescape(const char *f) {
314 char *e;
315
316 assert(f);
317
318 e = unit_name_unescape(f);
319 if (!e)
320 return NULL;
321
322 if (e[0] != '/') {
323 char *w;
324
325 w = strappend("/", e);
326 free(e);
327
328 return w;
329 }
330
331 return e;
332}
333
9e2f7c11 334bool unit_name_is_template(const char *n) {
6ef9eeed 335 const char *p, *e;
9e2f7c11
LP
336
337 assert(n);
338
a7b9ecf9
MS
339 p = strchr(n, '@');
340 if (!p)
9e2f7c11
LP
341 return false;
342
6ef9eeed
LP
343 e = strrchr(p+1, '.');
344 if (!e)
345 return false;
346
347 return e == p + 1;
9e2f7c11
LP
348}
349
29283ea4 350bool unit_name_is_instance(const char *n) {
6ef9eeed 351 const char *p, *e;
29283ea4
MS
352
353 assert(n);
354
355 p = strchr(n, '@');
356 if (!p)
357 return false;
358
6ef9eeed
LP
359 e = strrchr(p+1, '.');
360 if (!e)
361 return false;
362
363 return e > p + 1;
29283ea4
MS
364}
365
9e2f7c11
LP
366char *unit_name_replace_instance(const char *f, const char *i) {
367 const char *p, *e;
368 char *r, *k;
8556879e 369 size_t a, b;
9e2f7c11
LP
370
371 assert(f);
372
373 p = strchr(f, '@');
8556879e
LP
374 if (!p)
375 return strdup(f);
9e2f7c11 376
8556879e
LP
377 e = strrchr(f, '.');
378 if (!e)
379 assert_se(e = strchr(f, 0));
9e2f7c11 380
8556879e
LP
381 a = p - f;
382 b = strlen(i);
9e2f7c11 383
8556879e
LP
384 r = new(char, a + 1 + b + strlen(e) + 1);
385 if (!r)
386 return NULL;
9e2f7c11 387
8556879e
LP
388 k = mempcpy(r, f, a + 1);
389 k = mempcpy(k, i, b);
9e2f7c11 390 strcpy(k, e);
8556879e 391
9e2f7c11
LP
392 return r;
393}
394
395char *unit_name_template(const char *f) {
396 const char *p, *e;
397 char *r;
398 size_t a;
399
b0193f1c
LP
400 p = strchr(f, '@');
401 if (!p)
9e2f7c11
LP
402 return strdup(f);
403
404 assert_se(e = strrchr(f, '.'));
405 a = p - f + 1;
406
b0193f1c
LP
407 r = new(char, a + strlen(e) + 1);
408 if (!r)
9e2f7c11
LP
409 return NULL;
410
411 strcpy(mempcpy(r, f, a), e);
412 return r;
9e2f7c11 413}
a16e1123
LP
414
415char *unit_name_from_path(const char *path, const char *suffix) {
58d08142 416 _cleanup_free_ char *p = NULL;
5ffceb2f 417
a16e1123
LP
418 assert(path);
419 assert(suffix);
420
35eb6b12 421 p = unit_name_path_escape(path);
b0193f1c 422 if (!p)
5ffceb2f
LP
423 return NULL;
424
58d08142 425 return strappend(p, suffix);
a16e1123
LP
426}
427
9fff8a1f 428char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
58d08142 429 _cleanup_free_ char *p = NULL;
9fff8a1f 430
35eb6b12 431 assert(prefix);
9fff8a1f
LP
432 assert(path);
433 assert(suffix);
434
35eb6b12
LP
435 p = unit_name_path_escape(path);
436 if (!p)
9fff8a1f
LP
437 return NULL;
438
58d08142 439 return strjoin(prefix, "@", p, suffix, NULL);
9fff8a1f
LP
440}
441
a16e1123 442char *unit_name_to_path(const char *name) {
e821075a 443 _cleanup_free_ char *w = NULL;
a16e1123
LP
444
445 assert(name);
446
b0193f1c
LP
447 w = unit_name_to_prefix(name);
448 if (!w)
a16e1123
LP
449 return NULL;
450
e821075a 451 return unit_name_path_unescape(w);
9fc50704 452}
48899192
MS
453
454char *unit_dbus_path_from_name(const char *name) {
9444b1f2 455 _cleanup_free_ char *e = NULL;
48899192 456
35eb6b12
LP
457 assert(name);
458
f01de965 459 e = bus_label_escape(name);
48899192
MS
460 if (!e)
461 return NULL;
462
9444b1f2 463 return strappend("/org/freedesktop/systemd1/unit/", e);
48899192 464}
b0193f1c 465
ede3a796
LP
466int unit_name_from_dbus_path(const char *path, char **name) {
467 const char *e;
468 char *n;
469
470 e = startswith(path, "/org/freedesktop/systemd1/unit/");
471 if (!e)
472 return -EINVAL;
473
f01de965 474 n = bus_label_unescape(e);
ede3a796
LP
475 if (!n)
476 return -ENOMEM;
477
478 *name = n;
479 return 0;
480}
481
e3e0314b
ZJS
482
483/**
484 * Try to turn a string that might not be a unit name into a
485 * sensible unit name.
486 */
f78e6385 487char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
b0193f1c
LP
488 char *r, *t;
489 const char *f;
f78e6385 490 const char* valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
b0193f1c
LP
491
492 assert(name);
f78e6385 493 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
b0193f1c 494
a05f97b3 495 if (is_device_path(name))
b0193f1c
LP
496 return unit_name_from_path(name, ".device");
497
498 if (path_is_absolute(name))
499 return unit_name_from_path(name, ".mount");
500
501 /* We'll only escape the obvious characters here, to play
502 * safe. */
503
f8294e41 504 r = new(char, strlen(name) * 4 + strlen(".service") + 1);
b0193f1c
LP
505 if (!r)
506 return NULL;
507
508 for (f = name, t = r; *f; f++) {
b0193f1c
LP
509 if (*f == '/')
510 *(t++) = '-';
e3e0314b 511 else if (!strchr(valid_chars, *f))
b0193f1c
LP
512 t = do_escape_char(*f, t);
513 else
514 *(t++) = *f;
515 }
516
696c245a 517 if (unit_name_to_type(name) < 0)
56d4fbf9
LP
518 strcpy(t, ".service");
519 else
520 *t = 0;
b0193f1c
LP
521
522 return r;
523}
5f739699 524
e3e0314b
ZJS
525
526/**
527 * Similar to unit_name_mangle(), but is called when we know
528 * that this is about a specific unit type.
529 */
f78e6385 530char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
1dcf6065
LP
531 char *r, *t;
532 const char *f;
533
534 assert(name);
a016b922
LP
535 assert(suffix);
536 assert(suffix[0] == '.');
1dcf6065 537
a016b922 538 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
1dcf6065
LP
539 if (!r)
540 return NULL;
541
542 for (f = name, t = r; *f; f++) {
543 if (*f == '/')
544 *(t++) = '-';
545 else if (!strchr(VALID_CHARS, *f))
546 t = do_escape_char(*f, t);
547 else
548 *(t++) = *f;
549 }
550
a016b922
LP
551 if (!endswith(name, suffix))
552 strcpy(t, suffix);
1dcf6065
LP
553 else
554 *t = 0;
555
556 return r;
557}
558
5f739699
LP
559UnitType unit_name_to_type(const char *n) {
560 const char *e;
561
562 assert(n);
563
564 e = strrchr(n, '.');
565 if (!e)
566 return _UNIT_TYPE_INVALID;
567
568 return unit_type_from_string(e + 1);
569}
fb6becb4
LP
570
571int build_subslice(const char *slice, const char*name, char **subslice) {
572 char *ret;
573
574 assert(slice);
575 assert(name);
576 assert(subslice);
577
578 if (streq(slice, "-.slice"))
579 ret = strappend(name, ".slice");
580 else {
581 char *e;
582
583 e = endswith(slice, ".slice");
584 if (!e)
585 return -EINVAL;
586
587 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
588 if (!ret)
589 return -ENOMEM;
590
591 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
592 }
593
594 *subslice = ret;
595 return 0;
596}