chiark / gitweb /
bus: add .busname unit type to implement kdbus-style bus activation
[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 "sd-bus.h"
27 #include "path-util.h"
28 #include "util.h"
29 #include "unit-name.h"
30 #include "def.h"
31
32 #define VALID_CHARS                             \
33         DIGITS LETTERS                          \
34         ":-_.\\"
35
36 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
37         [UNIT_SERVICE] = "service",
38         [UNIT_SOCKET] = "socket",
39         [UNIT_BUSNAME] = "busname",
40         [UNIT_TARGET] = "target",
41         [UNIT_SNAPSHOT] = "snapshot",
42         [UNIT_DEVICE] = "device",
43         [UNIT_MOUNT] = "mount",
44         [UNIT_AUTOMOUNT] = "automount",
45         [UNIT_SWAP] = "swap",
46         [UNIT_TIMER] = "timer",
47         [UNIT_PATH] = "path",
48         [UNIT_SLICE] = "slice",
49         [UNIT_SCOPE] = "scope"
50 };
51
52 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
53
54 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
55         [UNIT_STUB] = "stub",
56         [UNIT_LOADED] = "loaded",
57         [UNIT_NOT_FOUND] = "not-found",
58         [UNIT_ERROR] = "error",
59         [UNIT_MERGED] = "merged",
60         [UNIT_MASKED] = "masked"
61 };
62
63 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
64
65 bool unit_name_is_valid(const char *n, bool template_ok) {
66         const char *e, *i, *at;
67
68         /* Valid formats:
69          *
70          *         string@instance.suffix
71          *         string.suffix
72          */
73
74         assert(n);
75
76         if (strlen(n) >= UNIT_NAME_MAX)
77                 return false;
78
79         e = strrchr(n, '.');
80         if (!e || e == n)
81                 return false;
82
83         if (unit_type_from_string(e + 1) < 0)
84                 return false;
85
86         for (i = n, at = NULL; i < e; i++) {
87
88                 if (*i == '@' && !at)
89                         at = i;
90
91                 if (!strchr("@" VALID_CHARS, *i))
92                         return false;
93         }
94
95         if (at) {
96                 if (at == n)
97                         return false;
98
99                 if (!template_ok && at+1 == e)
100                         return false;
101         }
102
103         return true;
104 }
105
106 bool unit_instance_is_valid(const char *i) {
107         assert(i);
108
109         /* The max length depends on the length of the string, so we
110          * don't really check this here. */
111
112         if (i[0] == 0)
113                 return false;
114
115         /* We allow additional @ in the instance string, we do not
116          * allow them in the prefix! */
117
118         for (; *i; i++)
119                 if (!strchr("@" VALID_CHARS, *i))
120                         return false;
121
122         return true;
123 }
124
125 bool unit_prefix_is_valid(const char *p) {
126
127         /* We don't allow additional @ in the instance string */
128
129         if (p[0] == 0)
130                 return false;
131
132         for (; *p; p++)
133                 if (!strchr(VALID_CHARS, *p))
134                         return false;
135
136         return true;
137 }
138
139 int unit_name_to_instance(const char *n, char **instance) {
140         const char *p, *d;
141         char *i;
142
143         assert(n);
144         assert(instance);
145
146         /* Everything past the first @ and before the last . is the instance */
147         p = strchr(n, '@');
148         if (!p) {
149                 *instance = NULL;
150                 return 0;
151         }
152
153         assert_se(d = strrchr(n, '.'));
154         assert(p < d);
155
156         i = strndup(p+1, d-p-1);
157         if (!i)
158                 return -ENOMEM;
159
160         *instance = i;
161         return 0;
162 }
163
164 char *unit_name_to_prefix_and_instance(const char *n) {
165         const char *d;
166
167         assert(n);
168
169         assert_se(d = strrchr(n, '.'));
170
171         return strndup(n, d - n);
172 }
173
174 char *unit_name_to_prefix(const char *n) {
175         const char *p;
176
177         p = strchr(n, '@');
178         if (p)
179                 return strndup(n, p - n);
180
181         return unit_name_to_prefix_and_instance(n);
182 }
183
184 char *unit_name_change_suffix(const char *n, const char *suffix) {
185         char *e, *r;
186         size_t a, b;
187
188         assert(n);
189         assert(unit_name_is_valid(n, true));
190         assert(suffix);
191         assert(suffix[0] == '.');
192
193         assert_se(e = strrchr(n, '.'));
194         a = e - n;
195         b = strlen(suffix);
196
197         r = new(char, a + b + 1);
198         if (!r)
199                 return NULL;
200
201         memcpy(r, n, a);
202         memcpy(r+a, suffix, b+1);
203
204         return r;
205 }
206
207 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
208         assert(prefix);
209         assert(unit_prefix_is_valid(prefix));
210         assert(!instance || unit_instance_is_valid(instance));
211         assert(suffix);
212
213         if (!instance)
214                 return strappend(prefix, suffix);
215
216         return strjoin(prefix, "@", instance, suffix, NULL);
217 }
218
219 static char *do_escape_char(char c, char *t) {
220         *(t++) = '\\';
221         *(t++) = 'x';
222         *(t++) = hexchar(c >> 4);
223         *(t++) = hexchar(c);
224         return t;
225 }
226
227 static char *do_escape(const char *f, char *t) {
228         assert(f);
229         assert(t);
230
231         /* do not create units with a leading '.', like for "/.dotdir" mount points */
232         if (*f == '.') {
233                 t = do_escape_char(*f, t);
234                 f++;
235         }
236
237         for (; *f; f++) {
238                 if (*f == '/')
239                         *(t++) = '-';
240                 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
241                         t = do_escape_char(*f, t);
242                 else
243                         *(t++) = *f;
244         }
245
246         return t;
247 }
248
249 char *unit_name_escape(const char *f) {
250         char *r, *t;
251
252         r = new(char, strlen(f)*4+1);
253         if (!r)
254                 return NULL;
255
256         t = do_escape(f, r);
257         *t = 0;
258
259         return r;
260 }
261
262 char *unit_name_unescape(const char *f) {
263         char *r, *t;
264
265         assert(f);
266
267         r = strdup(f);
268         if (!r)
269                 return NULL;
270
271         for (t = r; *f; f++) {
272                 if (*f == '-')
273                         *(t++) = '/';
274                 else if (*f == '\\') {
275                         int a, b;
276
277                         if (f[1] != 'x' ||
278                             (a = unhexchar(f[2])) < 0 ||
279                             (b = unhexchar(f[3])) < 0) {
280                                 /* Invalid escape code, let's take it literal then */
281                                 *(t++) = '\\';
282                         } else {
283                                 *(t++) = (char) ((a << 4) | b);
284                                 f += 3;
285                         }
286                 } else
287                         *(t++) = *f;
288         }
289
290         *t = 0;
291
292         return r;
293 }
294
295 char *unit_name_path_escape(const char *f) {
296         char *p, *e;
297
298         assert(f);
299
300         p = strdup(f);
301         if (!p)
302                 return NULL;
303
304         path_kill_slashes(p);
305
306         if (streq(p, "/") || streq(p, "")) {
307                 free(p);
308                 return strdup("-");
309         }
310
311         e = unit_name_escape(p[0] == '/' ? p + 1 : p);
312         free(p);
313
314         return e;
315 }
316
317 char *unit_name_path_unescape(const char *f) {
318         char *e;
319
320         assert(f);
321
322         e = unit_name_unescape(f);
323         if (!e)
324                 return NULL;
325
326         if (e[0] != '/') {
327                 char *w;
328
329                 w = strappend("/", e);
330                 free(e);
331
332                 return w;
333         }
334
335         return e;
336 }
337
338 bool unit_name_is_template(const char *n) {
339         const char *p;
340
341         assert(n);
342
343         p = strchr(n, '@');
344         if (!p)
345                 return false;
346
347         return p[1] == '.';
348 }
349
350 bool unit_name_is_instance(const char *n) {
351         const char *p;
352
353         assert(n);
354
355         p = strchr(n, '@');
356         if (!p)
357                 return false;
358
359         return p[1] != '.';
360 }
361
362 char *unit_name_replace_instance(const char *f, const char *i) {
363         const char *p, *e;
364         char *r, *k;
365         size_t a, b;
366
367         assert(f);
368
369         p = strchr(f, '@');
370         if (!p)
371                 return strdup(f);
372
373         e = strrchr(f, '.');
374         if (!e)
375                 assert_se(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         k = mempcpy(r, f, a + 1);
385         k = mempcpy(k, i, b);
386         strcpy(k, e);
387
388         return r;
389 }
390
391 char *unit_name_template(const char *f) {
392         const char *p, *e;
393         char *r;
394         size_t a;
395
396         p = strchr(f, '@');
397         if (!p)
398                 return strdup(f);
399
400         assert_se(e = strrchr(f, '.'));
401         a = p - f + 1;
402
403         r = new(char, a + strlen(e) + 1);
404         if (!r)
405                 return NULL;
406
407         strcpy(mempcpy(r, f, a), e);
408         return r;
409 }
410
411 char *unit_name_from_path(const char *path, const char *suffix) {
412         char *p, *r;
413
414         assert(path);
415         assert(suffix);
416
417         p = unit_name_path_escape(path);
418         if (!p)
419                 return NULL;
420
421         r = strappend(p, suffix);
422         free(p);
423
424         return r;
425 }
426
427 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
428         char *p, *r;
429
430         assert(prefix);
431         assert(path);
432         assert(suffix);
433
434         p = unit_name_path_escape(path);
435         if (!p)
436                 return NULL;
437
438         r = strjoin(prefix, "@", p, suffix, NULL);
439         free(p);
440
441         return r;
442 }
443
444 char *unit_name_to_path(const char *name) {
445         _cleanup_free_ char *w = NULL;
446
447         assert(name);
448
449         w = unit_name_to_prefix(name);
450         if (!w)
451                 return NULL;
452
453         return unit_name_path_unescape(w);
454 }
455
456 char *unit_dbus_path_from_name(const char *name) {
457         _cleanup_free_ char *e = NULL;
458
459         assert(name);
460
461         e = sd_bus_label_escape(name);
462         if (!e)
463                 return NULL;
464
465         return strappend("/org/freedesktop/systemd1/unit/", e);
466 }
467
468 int unit_name_from_dbus_path(const char *path, char **name) {
469         const char *e;
470         char *n;
471
472         e = startswith(path, "/org/freedesktop/systemd1/unit/");
473         if (!e)
474                 return -EINVAL;
475
476         n = sd_bus_label_unescape(e);
477         if (!n)
478                 return -ENOMEM;
479
480         *name = n;
481         return 0;
482 }
483
484 char *unit_name_mangle(const char *name) {
485         char *r, *t;
486         const char *f;
487
488         assert(name);
489
490         /* Try to turn a string that might not be a unit name into a
491          * sensible unit name. */
492
493         if (is_device_path(name))
494                 return unit_name_from_path(name, ".device");
495
496         if (path_is_absolute(name))
497                 return unit_name_from_path(name, ".mount");
498
499         /* We'll only escape the obvious characters here, to play
500          * safe. */
501
502         r = new(char, strlen(name) * 4 + 1 + sizeof(".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 char *unit_name_mangle_with_suffix(const char *name, const char *suffix) {
524         char *r, *t;
525         const char *f;
526
527         assert(name);
528         assert(suffix);
529         assert(suffix[0] == '.');
530
531         /* Similar to unit_name_mangle(), but is called when we know
532          * that this is about snapshot units. */
533
534         r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
535         if (!r)
536                 return NULL;
537
538         for (f = name, t = r; *f; f++) {
539                 if (*f == '/')
540                         *(t++) = '-';
541                 else if (!strchr(VALID_CHARS, *f))
542                         t = do_escape_char(*f, t);
543                 else
544                         *(t++) = *f;
545         }
546
547         if (!endswith(name, suffix))
548                 strcpy(t, suffix);
549         else
550                 *t = 0;
551
552         return r;
553 }
554
555 UnitType unit_name_to_type(const char *n) {
556         const char *e;
557
558         assert(n);
559
560         e = strrchr(n, '.');
561         if (!e)
562                 return _UNIT_TYPE_INVALID;
563
564         return unit_type_from_string(e + 1);
565 }
566
567 int build_subslice(const char *slice, const char*name, char **subslice) {
568         char *ret;
569
570         assert(slice);
571         assert(name);
572         assert(subslice);
573
574         if (streq(slice, "-.slice"))
575                 ret = strappend(name, ".slice");
576         else {
577                 char *e;
578
579                 e = endswith(slice, ".slice");
580                 if (!e)
581                         return -EINVAL;
582
583                 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
584                 if (!ret)
585                         return -ENOMEM;
586
587                 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
588         }
589
590         *subslice = ret;
591         return 0;
592 }