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