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