chiark / gitweb /
bus-policy: append items rather than prepending them
[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 #include <assert.h>
25
26 #include "path-util.h"
27 #include "bus-label.h"
28 #include "util.h"
29 #include "unit-name.h"
30 #include "def.h"
31 #include "strv.h"
32
33 #define VALID_CHARS                             \
34         DIGITS LETTERS                          \
35         ":-_.\\"
36
37 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
38         [UNIT_SERVICE] = "service",
39         [UNIT_SOCKET] = "socket",
40         [UNIT_BUSNAME] = "busname",
41         [UNIT_TARGET] = "target",
42         [UNIT_SNAPSHOT] = "snapshot",
43         [UNIT_DEVICE] = "device",
44         [UNIT_MOUNT] = "mount",
45         [UNIT_AUTOMOUNT] = "automount",
46         [UNIT_SWAP] = "swap",
47         [UNIT_TIMER] = "timer",
48         [UNIT_PATH] = "path",
49         [UNIT_SLICE] = "slice",
50         [UNIT_SCOPE] = "scope"
51 };
52
53 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
54
55 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
56         [UNIT_STUB] = "stub",
57         [UNIT_LOADED] = "loaded",
58         [UNIT_NOT_FOUND] = "not-found",
59         [UNIT_ERROR] = "error",
60         [UNIT_MERGED] = "merged",
61         [UNIT_MASKED] = "masked"
62 };
63
64 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
65
66 bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
67         const char *e, *i, *at;
68
69         /* Valid formats:
70          *
71          *         string@instance.suffix
72          *         string.suffix
73          */
74
75         assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID));
76
77         if (isempty(n))
78                 return false;
79
80         if (strlen(n) >= UNIT_NAME_MAX)
81                 return false;
82
83         e = strrchr(n, '.');
84         if (!e || e == n)
85                 return false;
86
87         if (unit_type_from_string(e + 1) < 0)
88                 return false;
89
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
103                 if (!template_ok == TEMPLATE_VALID && at+1 == e)
104                         return false;
105         }
106
107         return true;
108 }
109
110 bool unit_instance_is_valid(const char *i) {
111
112         /* The max length depends on the length of the string, so we
113          * don't really check this here. */
114
115         if (isempty(i))
116                 return false;
117
118         /* We allow additional @ in the instance string, we do not
119          * allow them in the prefix! */
120
121         return in_charset(i, "@" VALID_CHARS);
122 }
123
124 bool unit_prefix_is_valid(const char *p) {
125
126         /* We don't allow additional @ in the instance string */
127
128         if (isempty(p))
129                 return false;
130
131         return in_charset(p, VALID_CHARS);
132 }
133
134 int 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 */
142         p = strchr(n, '@');
143         if (!p) {
144                 *instance = NULL;
145                 return 0;
146         }
147
148         d = strrchr(n, '.');
149         if (!d)
150                 return -EINVAL;
151         if (d < p)
152                 return -EINVAL;
153
154         i = strndup(p+1, d-p-1);
155         if (!i)
156                 return -ENOMEM;
157
158         *instance = i;
159         return 1;
160 }
161
162 char *unit_name_to_prefix_and_instance(const char *n) {
163         const char *d;
164
165         assert(n);
166
167         assert_se(d = strrchr(n, '.'));
168         return strndup(n, d - n);
169 }
170
171 char *unit_name_to_prefix(const char *n) {
172         const char *p;
173
174         assert(n);
175
176         p = strchr(n, '@');
177         if (p)
178                 return strndup(n, p - n);
179
180         return unit_name_to_prefix_and_instance(n);
181 }
182
183 char *unit_name_change_suffix(const char *n, const char *suffix) {
184         char *e, *r;
185         size_t a, b;
186
187         assert(n);
188         assert(suffix);
189         assert(suffix[0] == '.');
190
191         assert_se(e = strrchr(n, '.'));
192         a = e - n;
193         b = strlen(suffix);
194
195         r = new(char, a + b + 1);
196         if (!r)
197                 return NULL;
198
199         strcpy(mempcpy(r, n, a), suffix);
200         return r;
201 }
202
203 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
204         assert(prefix);
205         assert(suffix);
206
207         if (!instance)
208                 return strappend(prefix, suffix);
209
210         return strjoin(prefix, "@", instance, suffix, NULL);
211 }
212
213 static char *do_escape_char(char c, char *t) {
214         assert(t);
215
216         *(t++) = '\\';
217         *(t++) = 'x';
218         *(t++) = hexchar(c >> 4);
219         *(t++) = hexchar(c);
220
221         return t;
222 }
223
224 static char *do_escape(const char *f, char *t) {
225         assert(f);
226         assert(t);
227
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
234         for (; *f; f++) {
235                 if (*f == '/')
236                         *(t++) = '-';
237                 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
238                         t = do_escape_char(*f, t);
239                 else
240                         *(t++) = *f;
241         }
242
243         return t;
244 }
245
246 char *unit_name_escape(const char *f) {
247         char *r, *t;
248
249         assert(f);
250
251         r = new(char, strlen(f)*4+1);
252         if (!r)
253                 return NULL;
254
255         t = do_escape(f, r);
256         *t = 0;
257
258         return r;
259 }
260
261 char *unit_name_unescape(const char *f) {
262         char *r, *t;
263
264         assert(f);
265
266         r = strdup(f);
267         if (!r)
268                 return NULL;
269
270         for (t = r; *f; f++) {
271                 if (*f == '-')
272                         *(t++) = '/';
273                 else if (*f == '\\') {
274                         int a, b;
275
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 */
280                                 *(t++) = '\\';
281                         } else {
282                                 *(t++) = (char) ((a << 4) | b);
283                                 f += 3;
284                         }
285                 } else
286                         *(t++) = *f;
287         }
288
289         *t = 0;
290
291         return r;
292 }
293
294 char *unit_name_path_escape(const char *f) {
295         _cleanup_free_ char *p = NULL;
296
297         assert(f);
298
299         p = strdup(f);
300         if (!p)
301                 return NULL;
302
303         path_kill_slashes(p);
304
305         if (STR_IN_SET(p, "/", ""))
306                 return strdup("-");
307
308         return unit_name_escape(p[0] == '/' ? p + 1 : p);
309 }
310
311 char *unit_name_path_unescape(const char *f) {
312         char *e, *w;
313
314         assert(f);
315
316         e = unit_name_unescape(f);
317         if (!e)
318                 return NULL;
319
320         if (e[0] != '/') {
321                 w = strappend("/", e);
322                 free(e);
323                 return w;
324         }
325
326         return e;
327 }
328
329 bool unit_name_is_template(const char *n) {
330         const char *p, *e;
331
332         assert(n);
333
334         p = strchr(n, '@');
335         if (!p)
336                 return false;
337
338         e = strrchr(p+1, '.');
339         if (!e)
340                 return false;
341
342         return e == p + 1;
343 }
344
345 bool unit_name_is_instance(const char *n) {
346         const char *p, *e;
347
348         assert(n);
349
350         p = strchr(n, '@');
351         if (!p)
352                 return false;
353
354         e = strrchr(p+1, '.');
355         if (!e)
356                 return false;
357
358         return e > p + 1;
359 }
360
361 char *unit_name_replace_instance(const char *f, const char *i) {
362         const char *p, *e;
363         char *r;
364         size_t a, b;
365
366         assert(f);
367         assert(i);
368
369         p = strchr(f, '@');
370         if (!p)
371                 return strdup(f);
372
373         e = strrchr(f, '.');
374         if (!e)
375                 e = strchr(f, 0);
376
377         a = p - f;
378         b = strlen(i);
379
380         r = new(char, a + 1 + b + strlen(e) + 1);
381         if (!r)
382                 return NULL;
383
384         strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e);
385         return r;
386 }
387
388 char *unit_name_template(const char *f) {
389         const char *p, *e;
390         char *r;
391         size_t a;
392
393         assert(f);
394
395         p = strchr(f, '@');
396         if (!p)
397                 return strdup(f);
398
399         e = strrchr(f, '.');
400         if (!e)
401                 e = strchr(f, 0);
402
403         a = p - f;
404
405         r = new(char, a + 1 + strlen(e) + 1);
406         if (!r)
407                 return NULL;
408
409         strcpy(mempcpy(r, f, a + 1), e);
410         return r;
411 }
412
413 char *unit_name_from_path(const char *path, const char *suffix) {
414         _cleanup_free_ char *p = NULL;
415
416         assert(path);
417         assert(suffix);
418
419         p = unit_name_path_escape(path);
420         if (!p)
421                 return NULL;
422
423         return strappend(p, suffix);
424 }
425
426 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
427         _cleanup_free_ char *p = NULL;
428
429         assert(prefix);
430         assert(path);
431         assert(suffix);
432
433         p = unit_name_path_escape(path);
434         if (!p)
435                 return NULL;
436
437         return strjoin(prefix, "@", p, suffix, NULL);
438 }
439
440 char *unit_name_to_path(const char *name) {
441         _cleanup_free_ char *w = NULL;
442
443         assert(name);
444
445         w = unit_name_to_prefix(name);
446         if (!w)
447                 return NULL;
448
449         return unit_name_path_unescape(w);
450 }
451
452 char *unit_dbus_path_from_name(const char *name) {
453         _cleanup_free_ char *e = NULL;
454
455         assert(name);
456
457         e = bus_label_escape(name);
458         if (!e)
459                 return NULL;
460
461         return strappend("/org/freedesktop/systemd1/unit/", e);
462 }
463
464 int 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
472         n = bus_label_unescape(e);
473         if (!n)
474                 return -ENOMEM;
475
476         *name = n;
477         return 0;
478 }
479
480 /**
481  *  Try to turn a string that might not be a unit name into a
482  *  sensible unit name.
483  */
484 char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
485         const char *valid_chars, *f;
486         char *r, *t;
487
488         assert(name);
489         assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
490
491         if (is_device_path(name))
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
500         valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
501
502         r = new(char, strlen(name) * 4 + strlen(".service") + 1);
503         if (!r)
504                 return NULL;
505
506         for (f = name, t = r; *f; f++) {
507                 if (*f == '/')
508                         *(t++) = '-';
509                 else if (!strchr(valid_chars, *f))
510                         t = do_escape_char(*f, t);
511                 else
512                         *(t++) = *f;
513         }
514
515         if (unit_name_to_type(name) < 0)
516                 strcpy(t, ".service");
517         else
518                 *t = 0;
519
520         return r;
521 }
522
523 /**
524  *  Similar to unit_name_mangle(), but is called when we know
525  *  that this is about a specific unit type.
526  */
527 char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
528         char *r, *t;
529         const char *f;
530
531         assert(name);
532         assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
533         assert(suffix);
534         assert(suffix[0] == '.');
535
536         r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
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
549         if (!endswith(name, suffix))
550                 strcpy(t, suffix);
551         else
552                 *t = 0;
553
554         return r;
555 }
556
557 UnitType 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 }
568
569 int 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 }