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