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