chiark / gitweb /
Prep v220: Apply "Fixes to user and session saving"
[elogind.git] / src / shared / 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);