chiark / gitweb /
71937187922835fb3fcf3d6c3c8037356f0bfa9d
[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         p = strchr(n, '@');
339         if (!p)
340                 return false;
341
342         return p[1] == '.';
343 }
344
345 bool unit_name_is_instance(const char *n) {
346         const char *p;
347
348         assert(n);
349
350         p = strchr(n, '@');
351         if (!p)
352                 return false;
353
354         return p[1] != '.';
355 }
356
357 char *unit_name_replace_instance(const char *f, const char *i) {
358         const char *p, *e;
359         char *r, *k;
360         size_t a;
361
362         assert(f);
363
364         p = strchr(f, '@');
365         assert_se(e = strrchr(f, '.'));
366
367         a = p - f;
368
369         if (p) {
370                 size_t b;
371
372                 b = strlen(i);
373
374                 r = new(char, a + 1 + b + strlen(e) + 1);
375                 if (!r)
376                         return NULL;
377
378                 k = mempcpy(r, f, a + 1);
379                 k = mempcpy(k, i, b);
380         } else {
381
382                 r = new(char, a + strlen(e) + 1);
383                 if (!r)
384                         return NULL;
385
386                 k = mempcpy(r, f, a);
387         }
388
389         strcpy(k, e);
390         return r;
391 }
392
393 char *unit_name_template(const char *f) {
394         const char *p, *e;
395         char *r;
396         size_t a;
397
398         p = strchr(f, '@');
399         if (!p)
400                 return strdup(f);
401
402         assert_se(e = strrchr(f, '.'));
403         a = p - f + 1;
404
405         r = new(char, a + strlen(e) + 1);
406         if (!r)
407                 return NULL;
408
409         strcpy(mempcpy(r, f, a), e);
410         return r;
411
412 }
413
414 char *unit_name_from_path(const char *path, const char *suffix) {
415         char *p, *r;
416
417         assert(path);
418         assert(suffix);
419
420         p = unit_name_path_escape(path);
421         if (!p)
422                 return NULL;
423
424         r = strappend(p, suffix);
425         free(p);
426
427         return r;
428 }
429
430 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
431         char *p, *r;
432
433         assert(prefix);
434         assert(path);
435         assert(suffix);
436
437         p = unit_name_path_escape(path);
438         if (!p)
439                 return NULL;
440
441         r = strjoin(prefix, "@", p, suffix, NULL);
442         free(p);
443
444         return r;
445 }
446
447 char *unit_name_to_path(const char *name) {
448         char *w, *e;
449
450         assert(name);
451
452         w = unit_name_to_prefix(name);
453         if (!w)
454                 return NULL;
455
456         e = unit_name_path_unescape(w);
457         free(w);
458
459         return e;
460 }
461
462 char *unit_dbus_path_from_name(const char *name) {
463         char *e, *p;
464
465         assert(name);
466
467         e = bus_path_escape(name);
468         if (!e)
469                 return NULL;
470
471         p = strappend("/org/freedesktop/systemd1/unit/", e);
472         free(e);
473
474         return p;
475 }
476
477 char *unit_name_mangle(const char *name) {
478         char *r, *t;
479         const char *f;
480         bool dot = false;
481
482         assert(name);
483
484         /* Try to turn a string that might not be a unit name into a
485          * sensible unit name. */
486
487         if (path_startswith(name, "/dev/") ||
488             path_startswith(name, "/sys/"))
489                 return unit_name_from_path(name, ".device");
490
491         if (path_is_absolute(name))
492                 return unit_name_from_path(name, ".mount");
493
494         /* We'll only escape the obvious characters here, to play
495          * safe. */
496
497         r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
498         if (!r)
499                 return NULL;
500
501         for (f = name, t = r; *f; f++) {
502
503                 if (*f == '.')
504                         dot = true;
505
506                 if (*f == '/')
507                         *(t++) = '-';
508                 else if (!strchr("@" VALID_CHARS, *f))
509                         t = do_escape_char(*f, t);
510                 else
511                         *(t++) = *f;
512         }
513
514         if (!dot)
515                 strcpy(t, ".service");
516         else
517                 *t = 0;
518
519         return r;
520 }
521
522 UnitType unit_name_to_type(const char *n) {
523         const char *e;
524
525         assert(n);
526
527         e = strrchr(n, '.');
528         if (!e)
529                 return _UNIT_TYPE_INVALID;
530
531         return unit_type_from_string(e + 1);
532 }