]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/unit-name.c
sd-dhcp: be more detailed about invalid headers
[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
28383ba1 26#include "sd-bus.h"
9eb977db 27#include "path-util.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
LP
296char *unit_name_path_escape(const char *f) {
297 char *p, *e;
298
299 assert(f);
300
301 p = strdup(f);
302 if (!p)
303 return NULL;
304
305 path_kill_slashes(p);
306
6270c1bd 307 if (streq(p, "/") || streq(p, "")) {
35eb6b12
LP
308 free(p);
309 return strdup("-");
310 }
311
312 e = unit_name_escape(p[0] == '/' ? p + 1 : p);
313 free(p);
314
315 return e;
316}
317
318char *unit_name_path_unescape(const char *f) {
319 char *e;
320
321 assert(f);
322
323 e = unit_name_unescape(f);
324 if (!e)
325 return NULL;
326
327 if (e[0] != '/') {
328 char *w;
329
330 w = strappend("/", e);
331 free(e);
332
333 return w;
334 }
335
336 return e;
337}
338
9e2f7c11
LP
339bool unit_name_is_template(const char *n) {
340 const char *p;
341
342 assert(n);
343
a7b9ecf9
MS
344 p = strchr(n, '@');
345 if (!p)
9e2f7c11
LP
346 return false;
347
348 return p[1] == '.';
349}
350
29283ea4
MS
351bool unit_name_is_instance(const char *n) {
352 const char *p;
353
354 assert(n);
355
356 p = strchr(n, '@');
357 if (!p)
358 return false;
359
360 return p[1] != '.';
361}
362
9e2f7c11
LP
363char *unit_name_replace_instance(const char *f, const char *i) {
364 const char *p, *e;
365 char *r, *k;
8556879e 366 size_t a, b;
9e2f7c11
LP
367
368 assert(f);
369
370 p = strchr(f, '@');
8556879e
LP
371 if (!p)
372 return strdup(f);
9e2f7c11 373
8556879e
LP
374 e = strrchr(f, '.');
375 if (!e)
376 assert_se(e = strchr(f, 0));
9e2f7c11 377
8556879e
LP
378 a = p - f;
379 b = strlen(i);
9e2f7c11 380
8556879e
LP
381 r = new(char, a + 1 + b + strlen(e) + 1);
382 if (!r)
383 return NULL;
9e2f7c11 384
8556879e
LP
385 k = mempcpy(r, f, a + 1);
386 k = mempcpy(k, i, b);
9e2f7c11 387 strcpy(k, e);
8556879e 388
9e2f7c11
LP
389 return r;
390}
391
392char *unit_name_template(const char *f) {
393 const char *p, *e;
394 char *r;
395 size_t a;
396
b0193f1c
LP
397 p = strchr(f, '@');
398 if (!p)
9e2f7c11
LP
399 return strdup(f);
400
401 assert_se(e = strrchr(f, '.'));
402 a = p - f + 1;
403
b0193f1c
LP
404 r = new(char, a + strlen(e) + 1);
405 if (!r)
9e2f7c11
LP
406 return NULL;
407
408 strcpy(mempcpy(r, f, a), e);
409 return r;
9e2f7c11 410}
a16e1123
LP
411
412char *unit_name_from_path(const char *path, const char *suffix) {
5ffceb2f
LP
413 char *p, *r;
414
a16e1123
LP
415 assert(path);
416 assert(suffix);
417
35eb6b12 418 p = unit_name_path_escape(path);
b0193f1c 419 if (!p)
5ffceb2f
LP
420 return NULL;
421
35eb6b12 422 r = strappend(p, suffix);
5ffceb2f 423 free(p);
a16e1123 424
5ffceb2f 425 return r;
a16e1123
LP
426}
427
9fff8a1f
LP
428char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
429 char *p, *r;
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
b7def684 439 r = strjoin(prefix, "@", p, suffix, NULL);
9fff8a1f
LP
440 free(p);
441
442 return r;
443}
444
a16e1123 445char *unit_name_to_path(const char *name) {
e821075a 446 _cleanup_free_ char *w = NULL;
a16e1123
LP
447
448 assert(name);
449
b0193f1c
LP
450 w = unit_name_to_prefix(name);
451 if (!w)
a16e1123
LP
452 return NULL;
453
e821075a 454 return unit_name_path_unescape(w);
9fc50704 455}
48899192
MS
456
457char *unit_dbus_path_from_name(const char *name) {
9444b1f2 458 _cleanup_free_ char *e = NULL;
48899192 459
35eb6b12
LP
460 assert(name);
461
28383ba1 462 e = sd_bus_label_escape(name);
48899192
MS
463 if (!e)
464 return NULL;
465
9444b1f2 466 return strappend("/org/freedesktop/systemd1/unit/", e);
48899192 467}
b0193f1c 468
ede3a796
LP
469int unit_name_from_dbus_path(const char *path, char **name) {
470 const char *e;
471 char *n;
472
473 e = startswith(path, "/org/freedesktop/systemd1/unit/");
474 if (!e)
475 return -EINVAL;
476
28383ba1 477 n = sd_bus_label_unescape(e);
ede3a796
LP
478 if (!n)
479 return -ENOMEM;
480
481 *name = n;
482 return 0;
483}
484
e3e0314b
ZJS
485
486/**
487 * Try to turn a string that might not be a unit name into a
488 * sensible unit name.
489 */
f78e6385 490char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
b0193f1c
LP
491 char *r, *t;
492 const char *f;
f78e6385 493 const char* valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
b0193f1c
LP
494
495 assert(name);
f78e6385 496 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
b0193f1c 497
a05f97b3 498 if (is_device_path(name))
b0193f1c
LP
499 return unit_name_from_path(name, ".device");
500
501 if (path_is_absolute(name))
502 return unit_name_from_path(name, ".mount");
503
504 /* We'll only escape the obvious characters here, to play
505 * safe. */
506
56d4fbf9 507 r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
b0193f1c
LP
508 if (!r)
509 return NULL;
510
511 for (f = name, t = r; *f; f++) {
b0193f1c
LP
512 if (*f == '/')
513 *(t++) = '-';
e3e0314b 514 else if (!strchr(valid_chars, *f))
b0193f1c
LP
515 t = do_escape_char(*f, t);
516 else
517 *(t++) = *f;
518 }
519
696c245a 520 if (unit_name_to_type(name) < 0)
56d4fbf9
LP
521 strcpy(t, ".service");
522 else
523 *t = 0;
b0193f1c
LP
524
525 return r;
526}
5f739699 527
e3e0314b
ZJS
528
529/**
530 * Similar to unit_name_mangle(), but is called when we know
531 * that this is about a specific unit type.
532 */
f78e6385 533char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
1dcf6065
LP
534 char *r, *t;
535 const char *f;
536
537 assert(name);
a016b922
LP
538 assert(suffix);
539 assert(suffix[0] == '.');
1dcf6065 540
a016b922 541 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
1dcf6065
LP
542 if (!r)
543 return NULL;
544
545 for (f = name, t = r; *f; f++) {
546 if (*f == '/')
547 *(t++) = '-';
548 else if (!strchr(VALID_CHARS, *f))
549 t = do_escape_char(*f, t);
550 else
551 *(t++) = *f;
552 }
553
a016b922
LP
554 if (!endswith(name, suffix))
555 strcpy(t, suffix);
1dcf6065
LP
556 else
557 *t = 0;
558
559 return r;
560}
561
5f739699
LP
562UnitType unit_name_to_type(const char *n) {
563 const char *e;
564
565 assert(n);
566
567 e = strrchr(n, '.');
568 if (!e)
569 return _UNIT_TYPE_INVALID;
570
571 return unit_type_from_string(e + 1);
572}
fb6becb4
LP
573
574int build_subslice(const char *slice, const char*name, char **subslice) {
575 char *ret;
576
577 assert(slice);
578 assert(name);
579 assert(subslice);
580
581 if (streq(slice, "-.slice"))
582 ret = strappend(name, ".slice");
583 else {
584 char *e;
585
586 e = endswith(slice, ".slice");
587 if (!e)
588 return -EINVAL;
589
590 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
591 if (!ret)
592 return -ENOMEM;
593
594 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
595 }
596
597 *subslice = ret;
598 return 0;
599}