chiark / gitweb /
systemd: enable/disable instances of template
[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 };
48
49 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
50
51 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
52         [UNIT_STUB] = "stub",
53         [UNIT_LOADED] = "loaded",
54         [UNIT_ERROR] = "error",
55         [UNIT_MERGED] = "merged",
56         [UNIT_MASKED] = "masked"
57 };
58
59 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
60
61 bool unit_name_is_valid(const char *n, bool template_ok) {
62         const char *e, *i, *at;
63
64         /* Valid formats:
65          *
66          *         string@instance.suffix
67          *         string.suffix
68          */
69
70         assert(n);
71
72         if (strlen(n) >= UNIT_NAME_MAX)
73                 return false;
74
75         e = strrchr(n, '.');
76         if (!e || e == n)
77                 return false;
78
79         if (unit_type_from_string(e + 1) < 0)
80                 return false;
81
82         for (i = n, at = NULL; i < e; i++) {
83
84                 if (*i == '@' && !at)
85                         at = i;
86
87                 if (!strchr("@" VALID_CHARS, *i))
88                         return false;
89         }
90
91         if (at) {
92                 if (at == n)
93                         return false;
94
95                 if (!template_ok && at+1 == e)
96                         return false;
97         }
98
99         return true;
100 }
101
102 bool unit_instance_is_valid(const char *i) {
103         assert(i);
104
105         /* The max length depends on the length of the string, so we
106          * don't really check this here. */
107
108         if (i[0] == 0)
109                 return false;
110
111         /* We allow additional @ in the instance string, we do not
112          * allow them in the prefix! */
113
114         for (; *i; i++)
115                 if (!strchr("@" VALID_CHARS, *i))
116                         return false;
117
118         return true;
119 }
120
121 bool unit_prefix_is_valid(const char *p) {
122
123         /* We don't allow additional @ in the instance string */
124
125         if (p[0] == 0)
126                 return false;
127
128         for (; *p; p++)
129                 if (!strchr(VALID_CHARS, *p))
130                         return false;
131
132         return true;
133 }
134
135 int unit_name_to_instance(const char *n, char **instance) {
136         const char *p, *d;
137         char *i;
138
139         assert(n);
140         assert(instance);
141
142         /* Everything past the first @ and before the last . is the instance */
143         p = strchr(n, '@');
144         if (!p) {
145                 *instance = NULL;
146                 return 0;
147         }
148
149         assert_se(d = strrchr(n, '.'));
150         assert(p < d);
151
152         i = strndup(p+1, d-p-1);
153         if (!i)
154                 return -ENOMEM;
155
156         *instance = i;
157         return 0;
158 }
159
160 char *unit_name_to_prefix_and_instance(const char *n) {
161         const char *d;
162
163         assert(n);
164
165         assert_se(d = strrchr(n, '.'));
166
167         return strndup(n, d - n);
168 }
169
170 char *unit_name_to_prefix(const char *n) {
171         const char *p;
172
173         p = strchr(n, '@');
174         if (p)
175                 return strndup(n, p - n);
176
177         return unit_name_to_prefix_and_instance(n);
178 }
179
180 char *unit_name_change_suffix(const char *n, const char *suffix) {
181         char *e, *r;
182         size_t a, b;
183
184         assert(n);
185         assert(unit_name_is_valid(n, true));
186         assert(suffix);
187
188         assert_se(e = strrchr(n, '.'));
189         a = e - n;
190         b = strlen(suffix);
191
192         r = new(char, a + b + 1);
193         if (!r)
194                 return NULL;
195
196         memcpy(r, n, a);
197         memcpy(r+a, suffix, b+1);
198
199         return r;
200 }
201
202 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
203         assert(prefix);
204         assert(unit_prefix_is_valid(prefix));
205         assert(!instance || unit_instance_is_valid(instance));
206         assert(suffix);
207
208         if (!instance)
209                 return strappend(prefix, suffix);
210
211         return strjoin(prefix, "@", instance, suffix, NULL);
212 }
213
214 static char *do_escape_char(char c, char *t) {
215         *(t++) = '\\';
216         *(t++) = 'x';
217         *(t++) = hexchar(c >> 4);
218         *(t++) = hexchar(c);
219         return t;
220 }
221
222 static char *do_escape(const char *f, char *t) {
223         assert(f);
224         assert(t);
225
226         /* do not create units with a leading '.', like for "/.dotdir" mount points */
227         if (*f == '.') {
228                 t = do_escape_char(*f, t);
229                 f++;
230         }
231
232         for (; *f; f++) {
233                 if (*f == '/')
234                         *(t++) = '-';
235                 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
236                         t = do_escape_char(*f, t);
237                 else
238                         *(t++) = *f;
239         }
240
241         return t;
242 }
243
244 char *unit_name_escape(const char *f) {
245         char *r, *t;
246
247         r = new(char, strlen(f)*4+1);
248         if (!r)
249                 return NULL;
250
251         t = do_escape(f, r);
252         *t = 0;
253
254         return r;
255 }
256
257 char *unit_name_unescape(const char *f) {
258         char *r, *t;
259
260         assert(f);
261
262         r = strdup(f);
263         if (!r)
264                 return NULL;
265
266         for (t = r; *f; f++) {
267                 if (*f == '-')
268                         *(t++) = '/';
269                 else if (*f == '\\') {
270                         int a, b;
271
272                         if (f[1] != 'x' ||
273                             (a = unhexchar(f[2])) < 0 ||
274                             (b = unhexchar(f[3])) < 0) {
275                                 /* Invalid escape code, let's take it literal then */
276                                 *(t++) = '\\';
277                         } else {
278                                 *(t++) = (char) ((a << 4) | b);
279                                 f += 3;
280                         }
281                 } else
282                         *(t++) = *f;
283         }
284
285         *t = 0;
286
287         return r;
288 }
289
290 char *unit_name_path_escape(const char *f) {
291         char *p, *e;
292
293         assert(f);
294
295         p = strdup(f);
296         if (!p)
297                 return NULL;
298
299         path_kill_slashes(p);
300
301         if (streq(p, "/")) {
302                 free(p);
303                 return strdup("-");
304         }
305
306         e = unit_name_escape(p[0] == '/' ? p + 1 : p);
307         free(p);
308
309         return e;
310 }
311
312 char *unit_name_path_unescape(const char *f) {
313         char *e;
314
315         assert(f);
316
317         e = unit_name_unescape(f);
318         if (!e)
319                 return NULL;
320
321         if (e[0] != '/') {
322                 char *w;
323
324                 w = strappend("/", e);
325                 free(e);
326
327                 return w;
328         }
329
330         return e;
331 }
332
333 bool unit_name_is_template(const char *n) {
334         const char *p;
335
336         assert(n);
337
338         if (!(p = strchr(n, '@')))
339                 return false;
340
341         return p[1] == '.';
342 }
343
344 bool unit_name_is_instance(const char *n) {
345         const char *p;
346
347         assert(n);
348
349         p = strchr(n, '@');
350         if (!p)
351                 return false;
352
353         return p[1] != '.';
354 }
355
356 char *unit_name_replace_instance(const char *f, const char *i) {
357         const char *p, *e;
358         char *r, *k;
359         size_t a;
360
361         assert(f);
362
363         p = strchr(f, '@');
364         assert_se(e = strrchr(f, '.'));
365
366         a = p - f;
367
368         if (p) {
369                 size_t b;
370
371                 b = strlen(i);
372
373                 r = new(char, a + 1 + b + strlen(e) + 1);
374                 if (!r)
375                         return NULL;
376
377                 k = mempcpy(r, f, a + 1);
378                 k = mempcpy(k, i, b);
379         } else {
380
381                 r = new(char, a + strlen(e) + 1);
382                 if (!r)
383                         return NULL;
384
385                 k = mempcpy(r, f, a);
386         }
387
388         strcpy(k, e);
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
413 char *unit_name_from_path(const char *path, const char *suffix) {
414         char *p, *r;
415
416         assert(path);
417         assert(suffix);
418
419         p = unit_name_path_escape(path);
420         if (!p)
421                 return NULL;
422
423         r = strappend(p, suffix);
424         free(p);
425
426         return r;
427 }
428
429 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
430         char *p, *r;
431
432         assert(prefix);
433         assert(path);
434         assert(suffix);
435
436         p = unit_name_path_escape(path);
437         if (!p)
438                 return NULL;
439
440         r = strjoin(prefix, "@", p, suffix, NULL);
441         free(p);
442
443         return r;
444 }
445
446 char *unit_name_to_path(const char *name) {
447         char *w, *e;
448
449         assert(name);
450
451         w = unit_name_to_prefix(name);
452         if (!w)
453                 return NULL;
454
455         e = unit_name_path_unescape(w);
456         free(w);
457
458         return e;
459 }
460
461 char *unit_dbus_path_from_name(const char *name) {
462         char *e, *p;
463
464         assert(name);
465
466         e = bus_path_escape(name);
467         if (!e)
468                 return NULL;
469
470         p = strappend("/org/freedesktop/systemd1/unit/", e);
471         free(e);
472
473         return p;
474 }
475
476 char *unit_name_mangle(const char *name) {
477         char *r, *t;
478         const char *f;
479
480         assert(name);
481
482         /* Try to turn a string that might not be a unit name into a
483          * sensible unit name. */
484
485         if (path_startswith(name, "/dev/") ||
486             path_startswith(name, "/sys/"))
487                 return unit_name_from_path(name, ".device");
488
489         if (path_is_absolute(name))
490                 return unit_name_from_path(name, ".mount");
491
492         /* We'll only escape the obvious characters here, to play
493          * safe. */
494
495         r = new(char, strlen(name) * 4 + 1);
496         if (!r)
497                 return NULL;
498
499         for (f = name, t = r; *f; f++) {
500
501                 if (*f == '/')
502                         *(t++) = '-';
503                 else if (!strchr("@" VALID_CHARS, *f))
504                         t = do_escape_char(*f, t);
505                 else
506                         *(t++) = *f;
507         }
508
509         *t = 0;
510
511         return r;
512 }
513
514 UnitType unit_name_to_type(const char *n) {
515         const char *e;
516
517         assert(n);
518
519         e = strrchr(n, '.');
520         if (!e)
521                 return _UNIT_TYPE_INVALID;
522
523         return unit_type_from_string(e + 1);
524 }