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