chiark / gitweb /
build-sys: move core/build.h → shared/build.h
[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 static char *do_escape_mangle(const char *f, enum unit_name_mangle allow_globs, char *t) {
247         const char *valid_chars;
248
249         assert(f);
250         assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
251         assert(t);
252
253         /* We'll only escape the obvious characters here, to play
254          * safe. */
255
256         valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
257
258         for (; *f; f++) {
259                 if (*f == '/')
260                         *(t++) = '-';
261                 else if (!strchr(valid_chars, *f))
262                         t = do_escape_char(*f, t);
263                 else
264                         *(t++) = *f;
265         }
266
267         return t;
268 }
269
270 char *unit_name_escape(const char *f) {
271         char *r, *t;
272
273         assert(f);
274
275         r = new(char, strlen(f)*4+1);
276         if (!r)
277                 return NULL;
278
279         t = do_escape(f, r);
280         *t = 0;
281
282         return r;
283 }
284
285 char *unit_name_unescape(const char *f) {
286         char *r, *t;
287
288         assert(f);
289
290         r = strdup(f);
291         if (!r)
292                 return NULL;
293
294         for (t = r; *f; f++) {
295                 if (*f == '-')
296                         *(t++) = '/';
297                 else if (*f == '\\') {
298                         int a, b;
299
300                         if (f[1] != 'x' ||
301                             (a = unhexchar(f[2])) < 0 ||
302                             (b = unhexchar(f[3])) < 0) {
303                                 /* Invalid escape code, let's take it literal then */
304                                 *(t++) = '\\';
305                         } else {
306                                 *(t++) = (char) ((a << 4) | b);
307                                 f += 3;
308                         }
309                 } else
310                         *(t++) = *f;
311         }
312
313         *t = 0;
314
315         return r;
316 }
317
318 char *unit_name_path_escape(const char *f) {
319         _cleanup_free_ char *p = NULL;
320
321         assert(f);
322
323         p = strdup(f);
324         if (!p)
325                 return NULL;
326
327         path_kill_slashes(p);
328
329         if (STR_IN_SET(p, "/", ""))
330                 return strdup("-");
331
332         return unit_name_escape(p[0] == '/' ? p + 1 : p);
333 }
334
335 char *unit_name_path_unescape(const char *f) {
336         char *e, *w;
337
338         assert(f);
339
340         e = unit_name_unescape(f);
341         if (!e)
342                 return NULL;
343
344         if (e[0] != '/') {
345                 w = strappend("/", e);
346                 free(e);
347                 return w;
348         }
349
350         return e;
351 }
352
353 bool unit_name_is_template(const char *n) {
354         const char *p, *e;
355
356         assert(n);
357
358         p = strchr(n, '@');
359         if (!p)
360                 return false;
361
362         e = strrchr(p+1, '.');
363         if (!e)
364                 return false;
365
366         return e == p + 1;
367 }
368
369 bool unit_name_is_instance(const char *n) {
370         const char *p, *e;
371
372         assert(n);
373
374         p = strchr(n, '@');
375         if (!p)
376                 return false;
377
378         e = strrchr(p+1, '.');
379         if (!e)
380                 return false;
381
382         return e > p + 1;
383 }
384
385 char *unit_name_replace_instance(const char *f, const char *i) {
386         const char *p, *e;
387         char *r;
388         size_t a, b;
389
390         assert(f);
391         assert(i);
392
393         p = strchr(f, '@');
394         if (!p)
395                 return strdup(f);
396
397         e = strrchr(f, '.');
398         if (!e)
399                 e = strchr(f, 0);
400
401         a = p - f;
402         b = strlen(i);
403
404         r = new(char, a + 1 + b + strlen(e) + 1);
405         if (!r)
406                 return NULL;
407
408         strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e);
409         return r;
410 }
411
412 char *unit_name_template(const char *f) {
413         const char *p, *e;
414         char *r;
415         size_t a;
416
417         assert(f);
418
419         p = strchr(f, '@');
420         if (!p)
421                 return strdup(f);
422
423         e = strrchr(f, '.');
424         if (!e)
425                 e = strchr(f, 0);
426
427         a = p - f;
428
429         r = new(char, a + 1 + strlen(e) + 1);
430         if (!r)
431                 return NULL;
432
433         strcpy(mempcpy(r, f, a + 1), e);
434         return r;
435 }
436
437 char *unit_name_from_path(const char *path, const char *suffix) {
438         _cleanup_free_ char *p = NULL;
439
440         assert(path);
441         assert(suffix);
442
443         p = unit_name_path_escape(path);
444         if (!p)
445                 return NULL;
446
447         return strappend(p, suffix);
448 }
449
450 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
451         _cleanup_free_ char *p = NULL;
452
453         assert(prefix);
454         assert(path);
455         assert(suffix);
456
457         p = unit_name_path_escape(path);
458         if (!p)
459                 return NULL;
460
461         return strjoin(prefix, "@", p, suffix, NULL);
462 }
463
464 char *unit_name_to_path(const char *name) {
465         _cleanup_free_ char *w = NULL;
466
467         assert(name);
468
469         w = unit_name_to_prefix(name);
470         if (!w)
471                 return NULL;
472
473         return unit_name_path_unescape(w);
474 }
475
476 char *unit_dbus_path_from_name(const char *name) {
477         _cleanup_free_ char *e = NULL;
478
479         assert(name);
480
481         e = bus_label_escape(name);
482         if (!e)
483                 return NULL;
484
485         return strappend("/org/freedesktop/systemd1/unit/", e);
486 }
487
488 int unit_name_from_dbus_path(const char *path, char **name) {
489         const char *e;
490         char *n;
491
492         e = startswith(path, "/org/freedesktop/systemd1/unit/");
493         if (!e)
494                 return -EINVAL;
495
496         n = bus_label_unescape(e);
497         if (!n)
498                 return -ENOMEM;
499
500         *name = n;
501         return 0;
502 }
503
504 /**
505  *  Convert a string to a unit name. /dev/blah is converted to dev-blah.device,
506  *  /blah/blah is converted to blah-blah.mount, anything else is left alone,
507  *  except that @suffix is appended if a valid unit suffix is not present.
508  *
509  *  If @allow_globs, globs characters are preserved. Otherwise they are escaped.
510  */
511 char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
512         char *r, *t;
513
514         assert(name);
515         assert(suffix);
516         assert(suffix[0] == '.');
517
518         if (is_device_path(name))
519                 return unit_name_from_path(name, ".device");
520
521         if (path_is_absolute(name))
522                 return unit_name_from_path(name, ".mount");
523
524         r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
525         if (!r)
526                 return NULL;
527
528         t = do_escape_mangle(name, allow_globs, r);
529
530         if (unit_name_to_type(name) < 0)
531                 strcpy(t, suffix);
532         else
533                 *t = 0;
534
535         return r;
536 }
537
538 UnitType unit_name_to_type(const char *n) {
539         const char *e;
540
541         assert(n);
542
543         e = strrchr(n, '.');
544         if (!e)
545                 return _UNIT_TYPE_INVALID;
546
547         return unit_type_from_string(e + 1);
548 }
549
550 int build_subslice(const char *slice, const char*name, char **subslice) {
551         char *ret;
552
553         assert(slice);
554         assert(name);
555         assert(subslice);
556
557         if (streq(slice, "-.slice"))
558                 ret = strappend(name, ".slice");
559         else {
560                 char *e;
561
562                 e = endswith(slice, ".slice");
563                 if (!e)
564                         return -EINVAL;
565
566                 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
567                 if (!ret)
568                         return -ENOMEM;
569
570                 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
571         }
572
573         *subslice = ret;
574         return 0;
575 }
576
577 static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
578         [UNIT_REQUIRES] = "Requires",
579         [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
580         [UNIT_REQUISITE] = "Requisite",
581         [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
582         [UNIT_WANTS] = "Wants",
583         [UNIT_BINDS_TO] = "BindsTo",
584         [UNIT_PART_OF] = "PartOf",
585         [UNIT_REQUIRED_BY] = "RequiredBy",
586         [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
587         [UNIT_WANTED_BY] = "WantedBy",
588         [UNIT_BOUND_BY] = "BoundBy",
589         [UNIT_CONSISTS_OF] = "ConsistsOf",
590         [UNIT_CONFLICTS] = "Conflicts",
591         [UNIT_CONFLICTED_BY] = "ConflictedBy",
592         [UNIT_BEFORE] = "Before",
593         [UNIT_AFTER] = "After",
594         [UNIT_ON_FAILURE] = "OnFailure",
595         [UNIT_TRIGGERS] = "Triggers",
596         [UNIT_TRIGGERED_BY] = "TriggeredBy",
597         [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
598         [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
599         [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
600         [UNIT_REFERENCES] = "References",
601         [UNIT_REFERENCED_BY] = "ReferencedBy",
602 };
603
604 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);