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