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