chiark / gitweb /
unit-name: fix escaping logic in unit_name_mangle_with_suffix()
[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  *  Try to turn a string that might not be a unit name into a
506  *  sensible unit name.
507  */
508 char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
509         char *r, *t;
510
511         assert(name);
512
513         if (is_device_path(name))
514                 return unit_name_from_path(name, ".device");
515
516         if (path_is_absolute(name))
517                 return unit_name_from_path(name, ".mount");
518
519         r = new(char, strlen(name) * 4 + strlen(".service") + 1);
520         if (!r)
521                 return NULL;
522
523         t = do_escape_mangle(name, allow_globs, r);
524
525         if (unit_name_to_type(name) < 0)
526                 strcpy(t, ".service");
527         else
528                 *t = 0;
529
530         return r;
531 }
532
533 /**
534  *  Similar to unit_name_mangle(), but is called when we know
535  *  that this is about a specific unit type.
536  */
537 char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
538         char *r, *t;
539
540         assert(name);
541         assert(suffix);
542         assert(suffix[0] == '.');
543
544         r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
545         if (!r)
546                 return NULL;
547
548         t = do_escape_mangle(name, allow_globs, r);
549
550         if (!endswith(name, suffix))
551                 strcpy(t, suffix);
552         else
553                 *t = 0;
554
555         return r;
556 }
557
558 UnitType unit_name_to_type(const char *n) {
559         const char *e;
560
561         assert(n);
562
563         e = strrchr(n, '.');
564         if (!e)
565                 return _UNIT_TYPE_INVALID;
566
567         return unit_type_from_string(e + 1);
568 }
569
570 int build_subslice(const char *slice, const char*name, char **subslice) {
571         char *ret;
572
573         assert(slice);
574         assert(name);
575         assert(subslice);
576
577         if (streq(slice, "-.slice"))
578                 ret = strappend(name, ".slice");
579         else {
580                 char *e;
581
582                 e = endswith(slice, ".slice");
583                 if (!e)
584                         return -EINVAL;
585
586                 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
587                 if (!ret)
588                         return -ENOMEM;
589
590                 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
591         }
592
593         *subslice = ret;
594         return 0;
595 }
596
597 static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
598         [UNIT_REQUIRES] = "Requires",
599         [UNIT_REQUIRES_OVERRIDABLE] = "RequiresOverridable",
600         [UNIT_REQUISITE] = "Requisite",
601         [UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
602         [UNIT_WANTS] = "Wants",
603         [UNIT_BINDS_TO] = "BindsTo",
604         [UNIT_PART_OF] = "PartOf",
605         [UNIT_REQUIRED_BY] = "RequiredBy",
606         [UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
607         [UNIT_WANTED_BY] = "WantedBy",
608         [UNIT_BOUND_BY] = "BoundBy",
609         [UNIT_CONSISTS_OF] = "ConsistsOf",
610         [UNIT_CONFLICTS] = "Conflicts",
611         [UNIT_CONFLICTED_BY] = "ConflictedBy",
612         [UNIT_BEFORE] = "Before",
613         [UNIT_AFTER] = "After",
614         [UNIT_ON_FAILURE] = "OnFailure",
615         [UNIT_TRIGGERS] = "Triggers",
616         [UNIT_TRIGGERED_BY] = "TriggeredBy",
617         [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
618         [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
619         [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
620         [UNIT_REFERENCES] = "References",
621         [UNIT_REFERENCED_BY] = "ReferencedBy",
622 };
623
624 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);