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