]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/unit-name.c
Merge pull request #153 from crawford/cciss
[thirdparty/systemd.git] / src / basic / unit-name.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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
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.
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
16 Lesser General Public License for more details.
17
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/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24
25 #include "path-util.h"
26 #include "bus-label.h"
27 #include "util.h"
28 #include "unit-name.h"
29 #include "def.h"
30 #include "strv.h"
31
32 #define VALID_CHARS \
33 DIGITS LETTERS \
34 ":-_.\\"
35
36 bool unit_name_is_valid(const char *n, UnitNameFlags flags) {
37 const char *e, *i, *at;
38
39 assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0);
40
41 if (_unlikely_(flags == 0))
42 return false;
43
44 if (isempty(n))
45 return false;
46
47 if (strlen(n) >= UNIT_NAME_MAX)
48 return false;
49
50 e = strrchr(n, '.');
51 if (!e || e == n)
52 return false;
53
54 if (unit_type_from_string(e + 1) < 0)
55 return false;
56
57 for (i = n, at = NULL; i < e; i++) {
58
59 if (*i == '@' && !at)
60 at = i;
61
62 if (!strchr("@" VALID_CHARS, *i))
63 return false;
64 }
65
66 if (at == n)
67 return false;
68
69 if (flags & UNIT_NAME_PLAIN)
70 if (!at)
71 return true;
72
73 if (flags & UNIT_NAME_INSTANCE)
74 if (at && e > at + 1)
75 return true;
76
77 if (flags & UNIT_NAME_TEMPLATE)
78 if (at && e == at + 1)
79 return true;
80
81 return false;
82 }
83
84 bool unit_prefix_is_valid(const char *p) {
85
86 /* We don't allow additional @ in the prefix string */
87
88 if (isempty(p))
89 return false;
90
91 return in_charset(p, VALID_CHARS);
92 }
93
94 bool unit_instance_is_valid(const char *i) {
95
96 /* The max length depends on the length of the string, so we
97 * don't really check this here. */
98
99 if (isempty(i))
100 return false;
101
102 /* We allow additional @ in the instance string, we do not
103 * allow them in the prefix! */
104
105 return in_charset(i, "@" VALID_CHARS);
106 }
107
108 bool unit_suffix_is_valid(const char *s) {
109 if (isempty(s))
110 return false;
111
112 if (s[0] != '.')
113 return false;
114
115 if (unit_type_from_string(s + 1) < 0)
116 return false;
117
118 return true;
119 }
120
121 int unit_name_to_prefix(const char *n, char **ret) {
122 const char *p;
123 char *s;
124
125 assert(n);
126 assert(ret);
127
128 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
129 return -EINVAL;
130
131 p = strchr(n, '@');
132 if (!p)
133 p = strrchr(n, '.');
134
135 assert_se(p);
136
137 s = strndup(n, p - n);
138 if (!s)
139 return -ENOMEM;
140
141 *ret = s;
142 return 0;
143 }
144
145 int unit_name_to_instance(const char *n, char **instance) {
146 const char *p, *d;
147 char *i;
148
149 assert(n);
150 assert(instance);
151
152 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
153 return -EINVAL;
154
155 /* Everything past the first @ and before the last . is the instance */
156 p = strchr(n, '@');
157 if (!p) {
158 *instance = NULL;
159 return 0;
160 }
161
162 p++;
163
164 d = strrchr(p, '.');
165 if (!d)
166 return -EINVAL;
167
168 i = strndup(p, d-p);
169 if (!i)
170 return -ENOMEM;
171
172 *instance = i;
173 return 1;
174 }
175
176 int unit_name_to_prefix_and_instance(const char *n, char **ret) {
177 const char *d;
178 char *s;
179
180 assert(n);
181 assert(ret);
182
183 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
184 return -EINVAL;
185
186 d = strrchr(n, '.');
187 if (!d)
188 return -EINVAL;
189
190 s = strndup(n, d - n);
191 if (!s)
192 return -ENOMEM;
193
194 *ret = s;
195 return 0;
196 }
197
198 UnitType unit_name_to_type(const char *n) {
199 const char *e;
200
201 assert(n);
202
203 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
204 return _UNIT_TYPE_INVALID;
205
206 assert_se(e = strrchr(n, '.'));
207
208 return unit_type_from_string(e + 1);
209 }
210
211 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
212 char *e, *s;
213 size_t a, b;
214
215 assert(n);
216 assert(suffix);
217 assert(ret);
218
219 if (!unit_name_is_valid(n, UNIT_NAME_ANY))
220 return -EINVAL;
221
222 if (!unit_suffix_is_valid(suffix))
223 return -EINVAL;
224
225 assert_se(e = strrchr(n, '.'));
226
227 a = e - n;
228 b = strlen(suffix);
229
230 s = new(char, a + b + 1);
231 if (!s)
232 return -ENOMEM;
233
234 strcpy(mempcpy(s, n, a), suffix);
235 *ret = s;
236
237 return 0;
238 }
239
240 int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) {
241 char *s;
242
243 assert(prefix);
244 assert(suffix);
245 assert(ret);
246
247 if (!unit_prefix_is_valid(prefix))
248 return -EINVAL;
249
250 if (instance && !unit_instance_is_valid(instance))
251 return -EINVAL;
252
253 if (!unit_suffix_is_valid(suffix))
254 return -EINVAL;
255
256 if (!instance)
257 s = strappend(prefix, suffix);
258 else
259 s = strjoin(prefix, "@", instance, suffix, NULL);
260 if (!s)
261 return -ENOMEM;
262
263 *ret = s;
264 return 0;
265 }
266
267 static char *do_escape_char(char c, char *t) {
268 assert(t);
269
270 *(t++) = '\\';
271 *(t++) = 'x';
272 *(t++) = hexchar(c >> 4);
273 *(t++) = hexchar(c);
274
275 return t;
276 }
277
278 static char *do_escape(const char *f, char *t) {
279 assert(f);
280 assert(t);
281
282 /* do not create units with a leading '.', like for "/.dotdir" mount points */
283 if (*f == '.') {
284 t = do_escape_char(*f, t);
285 f++;
286 }
287
288 for (; *f; f++) {
289 if (*f == '/')
290 *(t++) = '-';
291 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
292 t = do_escape_char(*f, t);
293 else
294 *(t++) = *f;
295 }
296
297 return t;
298 }
299
300 char *unit_name_escape(const char *f) {
301 char *r, *t;
302
303 assert(f);
304
305 r = new(char, strlen(f)*4+1);
306 if (!r)
307 return NULL;
308
309 t = do_escape(f, r);
310 *t = 0;
311
312 return r;
313 }
314
315 int unit_name_unescape(const char *f, char **ret) {
316 _cleanup_free_ char *r = NULL;
317 char *t;
318
319 assert(f);
320
321 r = strdup(f);
322 if (!r)
323 return -ENOMEM;
324
325 for (t = r; *f; f++) {
326 if (*f == '-')
327 *(t++) = '/';
328 else if (*f == '\\') {
329 int a, b;
330
331 if (f[1] != 'x')
332 return -EINVAL;
333
334 a = unhexchar(f[2]);
335 if (a < 0)
336 return -EINVAL;
337
338 b = unhexchar(f[3]);
339 if (b < 0)
340 return -EINVAL;
341
342 *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b);
343 f += 3;
344 } else
345 *(t++) = *f;
346 }
347
348 *t = 0;
349
350 *ret = r;
351 r = NULL;
352
353 return 0;
354 }
355
356 int unit_name_path_escape(const char *f, char **ret) {
357 char *p, *s;
358
359 assert(f);
360 assert(ret);
361
362 p = strdupa(f);
363 if (!p)
364 return -ENOMEM;
365
366 path_kill_slashes(p);
367
368 if (STR_IN_SET(p, "/", ""))
369 s = strdup("-");
370 else {
371 char *e;
372
373 if (!path_is_safe(p))
374 return -EINVAL;
375
376 /* Truncate trailing slashes */
377 e = endswith(p, "/");
378 if (e)
379 *e = 0;
380
381 /* Truncate leading slashes */
382 if (p[0] == '/')
383 p++;
384
385 s = unit_name_escape(p);
386 }
387 if (!s)
388 return -ENOMEM;
389
390 *ret = s;
391 return 0;
392 }
393
394 int unit_name_path_unescape(const char *f, char **ret) {
395 char *s;
396 int r;
397
398 assert(f);
399
400 if (isempty(f))
401 return -EINVAL;
402
403 if (streq(f, "-")) {
404 s = strdup("/");
405 if (!s)
406 return -ENOMEM;
407 } else {
408 char *w;
409
410 r = unit_name_unescape(f, &w);
411 if (r < 0)
412 return r;
413
414 /* Don't accept trailing or leading slashes */
415 if (startswith(w, "/") || endswith(w, "/")) {
416 free(w);
417 return -EINVAL;
418 }
419
420 /* Prefix a slash again */
421 s = strappend("/", w);
422 free(w);
423 if (!s)
424 return -ENOMEM;
425
426 if (!path_is_safe(s)) {
427 free(s);
428 return -EINVAL;
429 }
430 }
431
432 if (ret)
433 *ret = s;
434 else
435 free(s);
436
437 return 0;
438 }
439
440 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
441 const char *p, *e;
442 char *s;
443 size_t a, b;
444
445 assert(f);
446 assert(i);
447 assert(ret);
448
449 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
450 return -EINVAL;
451 if (!unit_instance_is_valid(i))
452 return -EINVAL;
453
454 assert_se(p = strchr(f, '@'));
455 assert_se(e = strrchr(f, '.'));
456
457 a = p - f;
458 b = strlen(i);
459
460 s = new(char, a + 1 + b + strlen(e) + 1);
461 if (!s)
462 return -ENOMEM;
463
464 strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
465
466 *ret = s;
467 return 0;
468 }
469
470 int unit_name_template(const char *f, char **ret) {
471 const char *p, *e;
472 char *s;
473 size_t a;
474
475 assert(f);
476 assert(ret);
477
478 if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
479 return -EINVAL;
480
481 assert_se(p = strchr(f, '@'));
482 assert_se(e = strrchr(f, '.'));
483
484 a = p - f;
485
486 s = new(char, a + 1 + strlen(e) + 1);
487 if (!s)
488 return -ENOMEM;
489
490 strcpy(mempcpy(s, f, a + 1), e);
491
492 *ret = s;
493 return 0;
494 }
495
496 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
497 _cleanup_free_ char *p = NULL;
498 char *s = NULL;
499 int r;
500
501 assert(path);
502 assert(suffix);
503 assert(ret);
504
505 if (!unit_suffix_is_valid(suffix))
506 return -EINVAL;
507
508 r = unit_name_path_escape(path, &p);
509 if (r < 0)
510 return r;
511
512 s = strappend(p, suffix);
513 if (!s)
514 return -ENOMEM;
515
516 *ret = s;
517 return 0;
518 }
519
520 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
521 _cleanup_free_ char *p = NULL;
522 char *s;
523 int r;
524
525 assert(prefix);
526 assert(path);
527 assert(suffix);
528 assert(ret);
529
530 if (!unit_prefix_is_valid(prefix))
531 return -EINVAL;
532
533 if (!unit_suffix_is_valid(suffix))
534 return -EINVAL;
535
536 r = unit_name_path_escape(path, &p);
537 if (r < 0)
538 return r;
539
540 s = strjoin(prefix, "@", p, suffix, NULL);
541 if (!s)
542 return -ENOMEM;
543
544 *ret = s;
545 return 0;
546 }
547
548 int unit_name_to_path(const char *name, char **ret) {
549 _cleanup_free_ char *prefix = NULL;
550 int r;
551
552 assert(name);
553
554 r = unit_name_to_prefix(name, &prefix);
555 if (r < 0)
556 return r;
557
558 return unit_name_path_unescape(prefix, ret);
559 }
560
561 char *unit_dbus_path_from_name(const char *name) {
562 _cleanup_free_ char *e = NULL;
563
564 assert(name);
565
566 e = bus_label_escape(name);
567 if (!e)
568 return NULL;
569
570 return strappend("/org/freedesktop/systemd1/unit/", e);
571 }
572
573 int unit_name_from_dbus_path(const char *path, char **name) {
574 const char *e;
575 char *n;
576
577 e = startswith(path, "/org/freedesktop/systemd1/unit/");
578 if (!e)
579 return -EINVAL;
580
581 n = bus_label_unescape(e);
582 if (!n)
583 return -ENOMEM;
584
585 *name = n;
586 return 0;
587 }
588
589 static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) {
590 const char *valid_chars;
591
592 assert(f);
593 assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB));
594 assert(t);
595
596 /* We'll only escape the obvious characters here, to play
597 * safe. */
598
599 valid_chars = allow_globs == UNIT_NAME_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
600
601 for (; *f; f++) {
602 if (*f == '/')
603 *(t++) = '-';
604 else if (!strchr(valid_chars, *f))
605 t = do_escape_char(*f, t);
606 else
607 *(t++) = *f;
608 }
609
610 return t;
611 }
612
613 /**
614 * Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
615 * /blah/blah is converted to blah-blah.mount, anything else is left alone,
616 * except that @suffix is appended if a valid unit suffix is not present.
617 *
618 * If @allow_globs, globs characters are preserved. Otherwise they are escaped.
619 */
620 int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) {
621 char *s, *t;
622 int r;
623
624 assert(name);
625 assert(suffix);
626 assert(ret);
627
628 if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */
629 return -EINVAL;
630
631 if (!unit_suffix_is_valid(suffix))
632 return -EINVAL;
633
634 if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
635 /* No mangling necessary... */
636 s = strdup(name);
637 if (!s)
638 return -ENOMEM;
639
640 *ret = s;
641 return 0;
642 }
643
644 if (is_device_path(name)) {
645 r = unit_name_from_path(name, ".device", ret);
646 if (r >= 0)
647 return 1;
648 if (r != -EINVAL)
649 return r;
650 }
651
652 if (path_is_absolute(name)) {
653 r = unit_name_from_path(name, ".mount", ret);
654 if (r >= 0)
655 return 1;
656 if (r != -EINVAL)
657 return r;
658 }
659
660 s = new(char, strlen(name) * 4 + strlen(suffix) + 1);
661 if (!s)
662 return -ENOMEM;
663
664 t = do_escape_mangle(name, allow_globs, s);
665 *t = 0;
666
667 if (unit_name_to_type(s) < 0)
668 strcpy(t, suffix);
669
670 *ret = s;
671 return 1;
672 }
673
674 int slice_build_parent_slice(const char *slice, char **ret) {
675 char *s, *dash;
676
677 assert(slice);
678 assert(ret);
679
680 if (!slice_name_is_valid(slice))
681 return -EINVAL;
682
683 if (streq(slice, "-.slice")) {
684 *ret = NULL;
685 return 0;
686 }
687
688 s = strdup(slice);
689 if (!s)
690 return -ENOMEM;
691
692 dash = strrchr(s, '-');
693 if (dash)
694 strcpy(dash, ".slice");
695 else {
696 free(s);
697
698 s = strdup("-.slice");
699 if (!s)
700 return -ENOMEM;
701 }
702
703 *ret = s;
704 return 1;
705 }
706
707 int slice_build_subslice(const char *slice, const char*name, char **ret) {
708 char *subslice;
709
710 assert(slice);
711 assert(name);
712 assert(ret);
713
714 if (!slice_name_is_valid(slice))
715 return -EINVAL;
716
717 if (!unit_prefix_is_valid(name))
718 return -EINVAL;
719
720 if (streq(slice, "-.slice"))
721 subslice = strappend(name, ".slice");
722 else {
723 char *e;
724
725 assert_se(e = endswith(slice, ".slice"));
726
727 subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
728 if (!subslice)
729 return -ENOMEM;
730
731 stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice");
732 }
733
734 *ret = subslice;
735 return 0;
736 }
737
738 bool slice_name_is_valid(const char *name) {
739 const char *p, *e;
740 bool dash = false;
741
742 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
743 return false;
744
745 if (streq(name, "-.slice"))
746 return true;
747
748 e = endswith(name, ".slice");
749 if (!e)
750 return false;
751
752 for (p = name; p < e; p++) {
753
754 if (*p == '-') {
755
756 /* Don't allow initial dash */
757 if (p == name)
758 return false;
759
760 /* Don't allow multiple dashes */
761 if (dash)
762 return false;
763
764 dash = true;
765 } else
766 dash = false;
767 }
768
769 /* Don't allow trailing hash */
770 if (dash)
771 return false;
772
773 return true;
774 }
775
776 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
777 [UNIT_SERVICE] = "service",
778 [UNIT_SOCKET] = "socket",
779 [UNIT_BUSNAME] = "busname",
780 [UNIT_TARGET] = "target",
781 [UNIT_SNAPSHOT] = "snapshot",
782 [UNIT_DEVICE] = "device",
783 [UNIT_MOUNT] = "mount",
784 [UNIT_AUTOMOUNT] = "automount",
785 [UNIT_SWAP] = "swap",
786 [UNIT_TIMER] = "timer",
787 [UNIT_PATH] = "path",
788 [UNIT_SLICE] = "slice",
789 [UNIT_SCOPE] = "scope"
790 };
791
792 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
793
794 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
795 [UNIT_STUB] = "stub",
796 [UNIT_LOADED] = "loaded",
797 [UNIT_NOT_FOUND] = "not-found",
798 [UNIT_ERROR] = "error",
799 [UNIT_MERGED] = "merged",
800 [UNIT_MASKED] = "masked"
801 };
802
803 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
804
805 static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
806 [UNIT_REQUIRES] = "Requires",
807 [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
808 [UNIT_REQUISITE] = "Requisite",
809 [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
810 [UNIT_WANTS] = "Wants",
811 [UNIT_BINDS_TO] = "BindsTo",
812 [UNIT_PART_OF] = "PartOf",
813 [UNIT_REQUIRED_BY] = "RequiredBy",
814 [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
815 [UNIT_REQUISITE_OF] = "RequisiteOf",
816 [UNIT_REQUISITE_OF_OVERRIDABLE] = "RequisiteOfOverridable",
817 [UNIT_WANTED_BY] = "WantedBy",
818 [UNIT_BOUND_BY] = "BoundBy",
819 [UNIT_CONSISTS_OF] = "ConsistsOf",
820 [UNIT_CONFLICTS] = "Conflicts",
821 [UNIT_CONFLICTED_BY] = "ConflictedBy",
822 [UNIT_BEFORE] = "Before",
823 [UNIT_AFTER] = "After",
824 [UNIT_ON_FAILURE] = "OnFailure",
825 [UNIT_TRIGGERS] = "Triggers",
826 [UNIT_TRIGGERED_BY] = "TriggeredBy",
827 [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
828 [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
829 [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
830 [UNIT_REFERENCES] = "References",
831 [UNIT_REFERENCED_BY] = "ReferencedBy",
832 };
833
834 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);