]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/unit-name.c
unit: move UnitDependency to unit-name
[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
9e2f7c11
LP
246char *unit_name_escape(const char *f) {
247 char *r, *t;
248
b9a33026
LP
249 assert(f);
250
b0193f1c
LP
251 r = new(char, strlen(f)*4+1);
252 if (!r)
9e2f7c11
LP
253 return NULL;
254
255 t = do_escape(f, r);
256 *t = 0;
257
258 return r;
9e2f7c11
LP
259}
260
261char *unit_name_unescape(const char *f) {
262 char *r, *t;
263
264 assert(f);
265
b0193f1c
LP
266 r = strdup(f);
267 if (!r)
9e2f7c11
LP
268 return NULL;
269
270 for (t = r; *f; f++) {
8d567588 271 if (*f == '-')
9e2f7c11
LP
272 *(t++) = '/';
273 else if (*f == '\\') {
274 int a, b;
275
15e11d81
LP
276 if (f[1] != 'x' ||
277 (a = unhexchar(f[2])) < 0 ||
278 (b = unhexchar(f[3])) < 0) {
279 /* Invalid escape code, let's take it literal then */
9e2f7c11
LP
280 *(t++) = '\\';
281 } else {
282 *(t++) = (char) ((a << 4) | b);
95e501f8 283 f += 3;
9e2f7c11
LP
284 }
285 } else
286 *(t++) = *f;
287 }
288
289 *t = 0;
290
291 return r;
292}
293
35eb6b12 294char *unit_name_path_escape(const char *f) {
b9a33026 295 _cleanup_free_ char *p = NULL;
35eb6b12
LP
296
297 assert(f);
298
299 p = strdup(f);
300 if (!p)
301 return NULL;
302
303 path_kill_slashes(p);
304
b9a33026 305 if (STR_IN_SET(p, "/", ""))
35eb6b12 306 return strdup("-");
35eb6b12 307
58d08142 308 return unit_name_escape(p[0] == '/' ? p + 1 : p);
35eb6b12
LP
309}
310
311char *unit_name_path_unescape(const char *f) {
b9a33026 312 char *e, *w;
35eb6b12
LP
313
314 assert(f);
315
316 e = unit_name_unescape(f);
317 if (!e)
318 return NULL;
319
320 if (e[0] != '/') {
35eb6b12
LP
321 w = strappend("/", e);
322 free(e);
35eb6b12
LP
323 return w;
324 }
325
326 return e;
327}
328
9e2f7c11 329bool unit_name_is_template(const char *n) {
6ef9eeed 330 const char *p, *e;
9e2f7c11
LP
331
332 assert(n);
333
a7b9ecf9
MS
334 p = strchr(n, '@');
335 if (!p)
9e2f7c11
LP
336 return false;
337
6ef9eeed
LP
338 e = strrchr(p+1, '.');
339 if (!e)
340 return false;
341
342 return e == p + 1;
9e2f7c11
LP
343}
344
29283ea4 345bool unit_name_is_instance(const char *n) {
6ef9eeed 346 const char *p, *e;
29283ea4
MS
347
348 assert(n);
349
350 p = strchr(n, '@');
351 if (!p)
352 return false;
353
6ef9eeed
LP
354 e = strrchr(p+1, '.');
355 if (!e)
356 return false;
357
358 return e > p + 1;
29283ea4
MS
359}
360
9e2f7c11
LP
361char *unit_name_replace_instance(const char *f, const char *i) {
362 const char *p, *e;
b9a33026 363 char *r;
8556879e 364 size_t a, b;
9e2f7c11
LP
365
366 assert(f);
b9a33026 367 assert(i);
9e2f7c11
LP
368
369 p = strchr(f, '@');
8556879e
LP
370 if (!p)
371 return strdup(f);
9e2f7c11 372
8556879e
LP
373 e = strrchr(f, '.');
374 if (!e)
b9a33026 375 e = strchr(f, 0);
9e2f7c11 376
8556879e
LP
377 a = p - f;
378 b = strlen(i);
9e2f7c11 379
8556879e
LP
380 r = new(char, a + 1 + b + strlen(e) + 1);
381 if (!r)
382 return NULL;
9e2f7c11 383
b9a33026 384 strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e);
9e2f7c11
LP
385 return r;
386}
387
388char *unit_name_template(const char *f) {
389 const char *p, *e;
390 char *r;
391 size_t a;
392
b9a33026
LP
393 assert(f);
394
b0193f1c
LP
395 p = strchr(f, '@');
396 if (!p)
9e2f7c11
LP
397 return strdup(f);
398
b9a33026
LP
399 e = strrchr(f, '.');
400 if (!e)
401 e = strchr(f, 0);
402
403 a = p - f;
9e2f7c11 404
b9a33026 405 r = new(char, a + 1 + strlen(e) + 1);
b0193f1c 406 if (!r)
9e2f7c11
LP
407 return NULL;
408
b9a33026 409 strcpy(mempcpy(r, f, a + 1), e);
9e2f7c11 410 return r;
9e2f7c11 411}
a16e1123
LP
412
413char *unit_name_from_path(const char *path, const char *suffix) {
58d08142 414 _cleanup_free_ char *p = NULL;
5ffceb2f 415
a16e1123
LP
416 assert(path);
417 assert(suffix);
418
35eb6b12 419 p = unit_name_path_escape(path);
b0193f1c 420 if (!p)
5ffceb2f
LP
421 return NULL;
422
58d08142 423 return strappend(p, suffix);
a16e1123
LP
424}
425
9fff8a1f 426char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
58d08142 427 _cleanup_free_ char *p = NULL;
9fff8a1f 428
35eb6b12 429 assert(prefix);
9fff8a1f
LP
430 assert(path);
431 assert(suffix);
432
35eb6b12
LP
433 p = unit_name_path_escape(path);
434 if (!p)
9fff8a1f
LP
435 return NULL;
436
58d08142 437 return strjoin(prefix, "@", p, suffix, NULL);
9fff8a1f
LP
438}
439
a16e1123 440char *unit_name_to_path(const char *name) {
e821075a 441 _cleanup_free_ char *w = NULL;
a16e1123
LP
442
443 assert(name);
444
b0193f1c
LP
445 w = unit_name_to_prefix(name);
446 if (!w)
a16e1123
LP
447 return NULL;
448
e821075a 449 return unit_name_path_unescape(w);
9fc50704 450}
48899192
MS
451
452char *unit_dbus_path_from_name(const char *name) {
9444b1f2 453 _cleanup_free_ char *e = NULL;
48899192 454
35eb6b12
LP
455 assert(name);
456
f01de965 457 e = bus_label_escape(name);
48899192
MS
458 if (!e)
459 return NULL;
460
9444b1f2 461 return strappend("/org/freedesktop/systemd1/unit/", e);
48899192 462}
b0193f1c 463
ede3a796
LP
464int unit_name_from_dbus_path(const char *path, char **name) {
465 const char *e;
466 char *n;
467
468 e = startswith(path, "/org/freedesktop/systemd1/unit/");
469 if (!e)
470 return -EINVAL;
471
f01de965 472 n = bus_label_unescape(e);
ede3a796
LP
473 if (!n)
474 return -ENOMEM;
475
476 *name = n;
477 return 0;
478}
479
e3e0314b
ZJS
480/**
481 * Try to turn a string that might not be a unit name into a
482 * sensible unit name.
483 */
f78e6385 484char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
b9a33026 485 const char *valid_chars, *f;
b0193f1c 486 char *r, *t;
b0193f1c
LP
487
488 assert(name);
f78e6385 489 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
b0193f1c 490
a05f97b3 491 if (is_device_path(name))
b0193f1c
LP
492 return unit_name_from_path(name, ".device");
493
494 if (path_is_absolute(name))
495 return unit_name_from_path(name, ".mount");
496
497 /* We'll only escape the obvious characters here, to play
498 * safe. */
499
b9a33026
LP
500 valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
501
f8294e41 502 r = new(char, strlen(name) * 4 + strlen(".service") + 1);
b0193f1c
LP
503 if (!r)
504 return NULL;
505
506 for (f = name, t = r; *f; f++) {
b0193f1c
LP
507 if (*f == '/')
508 *(t++) = '-';
e3e0314b 509 else if (!strchr(valid_chars, *f))
b0193f1c
LP
510 t = do_escape_char(*f, t);
511 else
512 *(t++) = *f;
513 }
514
696c245a 515 if (unit_name_to_type(name) < 0)
56d4fbf9
LP
516 strcpy(t, ".service");
517 else
518 *t = 0;
b0193f1c
LP
519
520 return r;
521}
5f739699 522
e3e0314b
ZJS
523/**
524 * Similar to unit_name_mangle(), but is called when we know
525 * that this is about a specific unit type.
526 */
f78e6385 527char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
1dcf6065
LP
528 char *r, *t;
529 const char *f;
530
531 assert(name);
b9a33026 532 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
a016b922
LP
533 assert(suffix);
534 assert(suffix[0] == '.');
1dcf6065 535
a016b922 536 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
1dcf6065
LP
537 if (!r)
538 return NULL;
539
540 for (f = name, t = r; *f; f++) {
541 if (*f == '/')
542 *(t++) = '-';
543 else if (!strchr(VALID_CHARS, *f))
544 t = do_escape_char(*f, t);
545 else
546 *(t++) = *f;
547 }
548
a016b922
LP
549 if (!endswith(name, suffix))
550 strcpy(t, suffix);
1dcf6065
LP
551 else
552 *t = 0;
553
554 return r;
555}
556
5f739699
LP
557UnitType unit_name_to_type(const char *n) {
558 const char *e;
559
560 assert(n);
561
562 e = strrchr(n, '.');
563 if (!e)
564 return _UNIT_TYPE_INVALID;
565
566 return unit_type_from_string(e + 1);
567}
fb6becb4
LP
568
569int build_subslice(const char *slice, const char*name, char **subslice) {
570 char *ret;
571
572 assert(slice);
573 assert(name);
574 assert(subslice);
575
576 if (streq(slice, "-.slice"))
577 ret = strappend(name, ".slice");
578 else {
579 char *e;
580
581 e = endswith(slice, ".slice");
582 if (!e)
583 return -EINVAL;
584
585 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
586 if (!ret)
587 return -ENOMEM;
588
589 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
590 }
591
592 *subslice = ret;
593 return 0;
594}
cb87a73b
LN
595
596static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
597 [UNIT_REQUIRES] = "Requires",
598 [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
599 [UNIT_REQUISITE] = "Requisite",
600 [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
601 [UNIT_WANTS] = "Wants",
602 [UNIT_BINDS_TO] = "BindsTo",
603 [UNIT_PART_OF] = "PartOf",
604 [UNIT_REQUIRED_BY] = "RequiredBy",
605 [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
606 [UNIT_WANTED_BY] = "WantedBy",
607 [UNIT_BOUND_BY] = "BoundBy",
608 [UNIT_CONSISTS_OF] = "ConsistsOf",
609 [UNIT_CONFLICTS] = "Conflicts",
610 [UNIT_CONFLICTED_BY] = "ConflictedBy",
611 [UNIT_BEFORE] = "Before",
612 [UNIT_AFTER] = "After",
613 [UNIT_ON_FAILURE] = "OnFailure",
614 [UNIT_TRIGGERS] = "Triggers",
615 [UNIT_TRIGGERED_BY] = "TriggeredBy",
616 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
617 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
618 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
619 [UNIT_REFERENCES] = "References",
620 [UNIT_REFERENCED_BY] = "ReferencedBy",
621};
622
623DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);