chiark / gitweb /
bf1ab794a8dc22fa413f1e9d19470c4ffb27d73f
[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_NOT_FOUND] = "not-found",
56         [UNIT_ERROR] = "error",
57         [UNIT_MERGED] = "merged",
58         [UNIT_MASKED] = "masked"
59 };
60
61 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
62
63 bool unit_name_is_valid(const char *n, bool template_ok) {
64         const char *e, *i, *at;
65
66         /* Valid formats:
67          *
68          *         string@instance.suffix
69          *         string.suffix
70          */
71
72         assert(n);
73
74         if (strlen(n) >= UNIT_NAME_MAX)
75                 return false;
76
77         e = strrchr(n, '.');
78         if (!e || e == n)
79                 return false;
80
81         if (unit_type_from_string(e + 1) < 0)
82                 return false;
83
84         for (i = n, at = NULL; i < e; i++) {
85
86                 if (*i == '@' && !at)
87                         at = i;
88
89                 if (!strchr("@" VALID_CHARS, *i))
90                         return false;
91         }
92
93         if (at) {
94                 if (at == n)
95                         return false;
96
97                 if (!template_ok && at+1 == e)
98                         return false;
99         }
100
101         return true;
102 }
103
104 bool unit_instance_is_valid(const char *i) {
105         assert(i);
106
107         /* The max length depends on the length of the string, so we
108          * don't really check this here. */
109
110         if (i[0] == 0)
111                 return false;
112
113         /* We allow additional @ in the instance string, we do not
114          * allow them in the prefix! */
115
116         for (; *i; i++)
117                 if (!strchr("@" VALID_CHARS, *i))
118                         return false;
119
120         return true;
121 }
122
123 bool unit_prefix_is_valid(const char *p) {
124
125         /* We don't allow additional @ in the instance string */
126
127         if (p[0] == 0)
128                 return false;
129
130         for (; *p; p++)
131                 if (!strchr(VALID_CHARS, *p))
132                         return false;
133
134         return true;
135 }
136
137 int unit_name_to_instance(const char *n, char **instance) {
138         const char *p, *d;
139         char *i;
140
141         assert(n);
142         assert(instance);
143
144         /* Everything past the first @ and before the last . is the instance */
145         p = strchr(n, '@');
146         if (!p) {
147                 *instance = NULL;
148                 return 0;
149         }
150
151         assert_se(d = strrchr(n, '.'));
152         assert(p < d);
153
154         i = strndup(p+1, d-p-1);
155         if (!i)
156                 return -ENOMEM;
157
158         *instance = i;
159         return 0;
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
169         return strndup(n, d - n);
170 }
171
172 char *unit_name_to_prefix(const char *n) {
173         const char *p;
174
175         p = strchr(n, '@');
176         if (p)
177                 return strndup(n, p - n);
178
179         return unit_name_to_prefix_and_instance(n);
180 }
181
182 char *unit_name_change_suffix(const char *n, const char *suffix) {
183         char *e, *r;
184         size_t a, b;
185
186         assert(n);
187         assert(unit_name_is_valid(n, true));
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         memcpy(r, n, a);
200         memcpy(r+a, suffix, b+1);
201
202         return r;
203 }
204
205 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
206         assert(prefix);
207         assert(unit_prefix_is_valid(prefix));
208         assert(!instance || unit_instance_is_valid(instance));
209         assert(suffix);
210
211         if (!instance)
212                 return strappend(prefix, suffix);
213
214         return strjoin(prefix, "@", instance, suffix, NULL);
215 }
216
217 static char *do_escape_char(char c, char *t) {
218         *(t++) = '\\';
219         *(t++) = 'x';
220         *(t++) = hexchar(c >> 4);
221         *(t++) = hexchar(c);
222         return t;
223 }
224
225 static char *do_escape(const char *f, char *t) {
226         assert(f);
227         assert(t);
228
229         /* do not create units with a leading '.', like for "/.dotdir" mount points */
230         if (*f == '.') {
231                 t = do_escape_char(*f, t);
232                 f++;
233         }
234
235         for (; *f; f++) {
236                 if (*f == '/')
237                         *(t++) = '-';
238                 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
239                         t = do_escape_char(*f, t);
240                 else
241                         *(t++) = *f;
242         }
243
244         return t;
245 }
246
247 char *unit_name_escape(const char *f) {
248         char *r, *t;
249
250         r = new(char, strlen(f)*4+1);
251         if (!r)
252                 return NULL;
253
254         t = do_escape(f, r);
255         *t = 0;
256
257         return r;
258 }
259
260 char *unit_name_unescape(const char *f) {
261         char *r, *t;
262
263         assert(f);
264
265         r = strdup(f);
266         if (!r)
267                 return NULL;
268
269         for (t = r; *f; f++) {
270                 if (*f == '-')
271                         *(t++) = '/';
272                 else if (*f == '\\') {
273                         int a, b;
274
275                         if (f[1] != 'x' ||
276                             (a = unhexchar(f[2])) < 0 ||
277                             (b = unhexchar(f[3])) < 0) {
278                                 /* Invalid escape code, let's take it literal then */
279                                 *(t++) = '\\';
280                         } else {
281                                 *(t++) = (char) ((a << 4) | b);
282                                 f += 3;
283                         }
284                 } else
285                         *(t++) = *f;
286         }
287
288         *t = 0;
289
290         return r;
291 }
292
293 char *unit_name_path_escape(const char *f) {
294         char *p, *e;
295
296         assert(f);
297
298         p = strdup(f);
299         if (!p)
300                 return NULL;
301
302         path_kill_slashes(p);
303
304         if (streq(p, "/")) {
305                 free(p);
306                 return strdup("-");
307         }
308
309         e = unit_name_escape(p[0] == '/' ? p + 1 : p);
310         free(p);
311
312         return e;
313 }
314
315 char *unit_name_path_unescape(const char *f) {
316         char *e;
317
318         assert(f);
319
320         e = unit_name_unescape(f);
321         if (!e)
322                 return NULL;
323
324         if (e[0] != '/') {
325                 char *w;
326
327                 w = strappend("/", e);
328                 free(e);
329
330                 return w;
331         }
332
333         return e;
334 }
335
336 bool unit_name_is_template(const char *n) {
337         const char *p;
338
339         assert(n);
340
341         p = strchr(n, '@');
342         if (!p)
343                 return false;
344
345         return p[1] == '.';
346 }
347
348 bool unit_name_is_instance(const char *n) {
349         const char *p;
350
351         assert(n);
352
353         p = strchr(n, '@');
354         if (!p)
355                 return false;
356
357         return p[1] != '.';
358 }
359
360 char *unit_name_replace_instance(const char *f, const char *i) {
361         const char *p, *e;
362         char *r, *k;
363         size_t a, b;
364
365         assert(f);
366
367         p = strchr(f, '@');
368         if (!p)
369                 return strdup(f);
370
371         e = strrchr(f, '.');
372         if (!e)
373                 assert_se(e = strchr(f, 0));
374
375         a = p - f;
376         b = strlen(i);
377
378         r = new(char, a + 1 + b + strlen(e) + 1);
379         if (!r)
380                 return NULL;
381
382         k = mempcpy(r, f, a + 1);
383         k = mempcpy(k, i, b);
384         strcpy(k, e);
385
386         return r;
387 }
388
389 char *unit_name_template(const char *f) {
390         const char *p, *e;
391         char *r;
392         size_t a;
393
394         p = strchr(f, '@');
395         if (!p)
396                 return strdup(f);
397
398         assert_se(e = strrchr(f, '.'));
399         a = p - f + 1;
400
401         r = new(char, a + strlen(e) + 1);
402         if (!r)
403                 return NULL;
404
405         strcpy(mempcpy(r, f, a), e);
406         return r;
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 }