chiark / gitweb /
0910e86f107139affb5b793f7d0002243609484f
[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         _cleanup_free_ char *e = NULL;
459
460         assert(name);
461
462         e = bus_path_escape(name);
463         if (!e)
464                 return NULL;
465
466         return strappend("/org/freedesktop/systemd1/unit/", e);
467 }
468
469 char *unit_name_mangle(const char *name) {
470         char *r, *t;
471         const char *f;
472
473         assert(name);
474
475         /* Try to turn a string that might not be a unit name into a
476          * sensible unit name. */
477
478         if (is_device_path(name))
479                 return unit_name_from_path(name, ".device");
480
481         if (path_is_absolute(name))
482                 return unit_name_from_path(name, ".mount");
483
484         /* We'll only escape the obvious characters here, to play
485          * safe. */
486
487         r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
488         if (!r)
489                 return NULL;
490
491         for (f = name, t = r; *f; f++) {
492                 if (*f == '/')
493                         *(t++) = '-';
494                 else if (!strchr("@" VALID_CHARS, *f))
495                         t = do_escape_char(*f, t);
496                 else
497                         *(t++) = *f;
498         }
499
500         if (unit_name_to_type(name) < 0)
501                 strcpy(t, ".service");
502         else
503                 *t = 0;
504
505         return r;
506 }
507
508 char *unit_name_mangle_with_suffix(const char *name, const char *suffix) {
509         char *r, *t;
510         const char *f;
511
512         assert(name);
513         assert(suffix);
514         assert(suffix[0] == '.');
515
516         /* Similar to unit_name_mangle(), but is called when we know
517          * that this is about snapshot units. */
518
519         r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
520         if (!r)
521                 return NULL;
522
523         for (f = name, t = r; *f; f++) {
524                 if (*f == '/')
525                         *(t++) = '-';
526                 else if (!strchr(VALID_CHARS, *f))
527                         t = do_escape_char(*f, t);
528                 else
529                         *(t++) = *f;
530         }
531
532         if (!endswith(name, suffix))
533                 strcpy(t, suffix);
534         else
535                 *t = 0;
536
537         return r;
538 }
539
540 UnitType unit_name_to_type(const char *n) {
541         const char *e;
542
543         assert(n);
544
545         e = strrchr(n, '.');
546         if (!e)
547                 return _UNIT_TYPE_INVALID;
548
549         return unit_type_from_string(e + 1);
550 }