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