chiark / gitweb /
update TODO
[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, 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         char *p, *e;
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                 free(p);
309                 return strdup("-");
310         }
311
312         e = unit_name_escape(p[0] == '/' ? p + 1 : p);
313         free(p);
314
315         return e;
316 }
317
318 char *unit_name_path_unescape(const char *f) {
319         char *e;
320
321         assert(f);
322
323         e = unit_name_unescape(f);
324         if (!e)
325                 return NULL;
326
327         if (e[0] != '/') {
328                 char *w;
329
330                 w = strappend("/", e);
331                 free(e);
332
333                 return w;
334         }
335
336         return e;
337 }
338
339 bool unit_name_is_template(const char *n) {
340         const char *p;
341
342         assert(n);
343
344         p = strchr(n, '@');
345         if (!p)
346                 return false;
347
348         return p[1] == '.';
349 }
350
351 bool unit_name_is_instance(const char *n) {
352         const char *p;
353
354         assert(n);
355
356         p = strchr(n, '@');
357         if (!p)
358                 return false;
359
360         return p[1] != '.';
361 }
362
363 char *unit_name_replace_instance(const char *f, const char *i) {
364         const char *p, *e;
365         char *r, *k;
366         size_t a, b;
367
368         assert(f);
369
370         p = strchr(f, '@');
371         if (!p)
372                 return strdup(f);
373
374         e = strrchr(f, '.');
375         if (!e)
376                 assert_se(e = strchr(f, 0));
377
378         a = p - f;
379         b = strlen(i);
380
381         r = new(char, a + 1 + b + strlen(e) + 1);
382         if (!r)
383                 return NULL;
384
385         k = mempcpy(r, f, a + 1);
386         k = mempcpy(k, i, b);
387         strcpy(k, e);
388
389         return r;
390 }
391
392 char *unit_name_template(const char *f) {
393         const char *p, *e;
394         char *r;
395         size_t a;
396
397         p = strchr(f, '@');
398         if (!p)
399                 return strdup(f);
400
401         assert_se(e = strrchr(f, '.'));
402         a = p - f + 1;
403
404         r = new(char, a + strlen(e) + 1);
405         if (!r)
406                 return NULL;
407
408         strcpy(mempcpy(r, f, a), e);
409         return r;
410 }
411
412 char *unit_name_from_path(const char *path, const char *suffix) {
413         char *p, *r;
414
415         assert(path);
416         assert(suffix);
417
418         p = unit_name_path_escape(path);
419         if (!p)
420                 return NULL;
421
422         r = strappend(p, suffix);
423         free(p);
424
425         return r;
426 }
427
428 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
429         char *p, *r;
430
431         assert(prefix);
432         assert(path);
433         assert(suffix);
434
435         p = unit_name_path_escape(path);
436         if (!p)
437                 return NULL;
438
439         r = strjoin(prefix, "@", p, suffix, NULL);
440         free(p);
441
442         return r;
443 }
444
445 char *unit_name_to_path(const char *name) {
446         _cleanup_free_ char *w = NULL;
447
448         assert(name);
449
450         w = unit_name_to_prefix(name);
451         if (!w)
452                 return NULL;
453
454         return unit_name_path_unescape(w);
455 }
456
457 char *unit_dbus_path_from_name(const char *name) {
458         _cleanup_free_ char *e = NULL;
459
460         assert(name);
461
462         e = sd_bus_label_escape(name);
463         if (!e)
464                 return NULL;
465
466         return strappend("/org/freedesktop/systemd1/unit/", e);
467 }
468
469 int unit_name_from_dbus_path(const char *path, char **name) {
470         const char *e;
471         char *n;
472
473         e = startswith(path, "/org/freedesktop/systemd1/unit/");
474         if (!e)
475                 return -EINVAL;
476
477         n = sd_bus_label_unescape(e);
478         if (!n)
479                 return -ENOMEM;
480
481         *name = n;
482         return 0;
483 }
484
485
486 /**
487  *  Try to turn a string that might not be a unit name into a
488  *  sensible unit name.
489  */
490 char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
491         char *r, *t;
492         const char *f;
493         const char* valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
494
495         assert(name);
496         assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
497
498         if (is_device_path(name))
499                 return unit_name_from_path(name, ".device");
500
501         if (path_is_absolute(name))
502                 return unit_name_from_path(name, ".mount");
503
504         /* We'll only escape the obvious characters here, to play
505          * safe. */
506
507         r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
508         if (!r)
509                 return NULL;
510
511         for (f = name, t = r; *f; f++) {
512                 if (*f == '/')
513                         *(t++) = '-';
514                 else if (!strchr(valid_chars, *f))
515                         t = do_escape_char(*f, t);
516                 else
517                         *(t++) = *f;
518         }
519
520         if (unit_name_to_type(name) < 0)
521                 strcpy(t, ".service");
522         else
523                 *t = 0;
524
525         return r;
526 }
527
528
529 /**
530  *  Similar to unit_name_mangle(), but is called when we know
531  *  that this is about a specific unit type.
532  */
533 char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
534         char *r, *t;
535         const char *f;
536
537         assert(name);
538         assert(suffix);
539         assert(suffix[0] == '.');
540
541         r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
542         if (!r)
543                 return NULL;
544
545         for (f = name, t = r; *f; f++) {
546                 if (*f == '/')
547                         *(t++) = '-';
548                 else if (!strchr(VALID_CHARS, *f))
549                         t = do_escape_char(*f, t);
550                 else
551                         *(t++) = *f;
552         }
553
554         if (!endswith(name, suffix))
555                 strcpy(t, suffix);
556         else
557                 *t = 0;
558
559         return r;
560 }
561
562 UnitType unit_name_to_type(const char *n) {
563         const char *e;
564
565         assert(n);
566
567         e = strrchr(n, '.');
568         if (!e)
569                 return _UNIT_TYPE_INVALID;
570
571         return unit_type_from_string(e + 1);
572 }
573
574 int build_subslice(const char *slice, const char*name, char **subslice) {
575         char *ret;
576
577         assert(slice);
578         assert(name);
579         assert(subslice);
580
581         if (streq(slice, "-.slice"))
582                 ret = strappend(name, ".slice");
583         else {
584                 char *e;
585
586                 e = endswith(slice, ".slice");
587                 if (!e)
588                         return -EINVAL;
589
590                 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
591                 if (!ret)
592                         return -ENOMEM;
593
594                 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
595         }
596
597         *subslice = ret;
598         return 0;
599 }