chiark / gitweb /
1440d2f2eac59ca4271c476c13e9d56cb8f5144c
[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         if (!(p = strchr(n, '@'))) {
116                 *instance = NULL;
117                 return 0;
118         }
119
120         assert_se(d = strrchr(n, '.'));
121         assert(p < d);
122
123         if (!(i = strndup(p+1, d-p-1)))
124                 return -ENOMEM;
125
126         *instance = i;
127         return 0;
128 }
129
130 char *unit_name_to_prefix_and_instance(const char *n) {
131         const char *d;
132
133         assert(n);
134
135         assert_se(d = strrchr(n, '.'));
136
137         return strndup(n, d - n);
138 }
139
140 char *unit_name_to_prefix(const char *n) {
141         const char *p;
142
143         if ((p = strchr(n, '@')))
144                 return strndup(n, p - n);
145
146         return unit_name_to_prefix_and_instance(n);
147 }
148
149 char *unit_name_change_suffix(const char *n, const char *suffix) {
150         char *e, *r;
151         size_t a, b;
152
153         assert(n);
154         assert(unit_name_is_valid_no_type(n, true));
155         assert(suffix);
156
157         assert_se(e = strrchr(n, '.'));
158         a = e - n;
159         b = strlen(suffix);
160
161         if (!(r = new(char, a + b + 1)))
162                 return NULL;
163
164         memcpy(r, n, a);
165         memcpy(r+a, suffix, b+1);
166
167         return r;
168 }
169
170 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
171         assert(prefix);
172         assert(unit_prefix_is_valid(prefix));
173         assert(!instance || unit_instance_is_valid(instance));
174         assert(suffix);
175
176         if (!instance)
177                 return strappend(prefix, suffix);
178
179         return join(prefix, "@", instance, suffix, NULL);
180 }
181
182 static char *do_escape_char(char c, char *t) {
183         *(t++) = '\\';
184         *(t++) = 'x';
185         *(t++) = hexchar(c >> 4);
186         *(t++) = hexchar(c);
187         return t;
188 }
189
190 static char *do_escape(const char *f, char *t) {
191         assert(f);
192         assert(t);
193
194         /* do not create units with a leading '.', like for "/.dotdir" mount points */
195         if (*f == '.') {
196                 t = do_escape_char(*f, t);
197                 f++;
198         }
199
200         for (; *f; f++) {
201                 if (*f == '/')
202                         *(t++) = '-';
203                 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
204                         t = do_escape_char(*f, t);
205                 else
206                         *(t++) = *f;
207         }
208
209         return t;
210 }
211
212 char *unit_name_build_escape(const char *prefix, const char *instance, const char *suffix) {
213         char *r, *t;
214         size_t a, b, c;
215
216         assert(prefix);
217         assert(suffix);
218
219         /* Takes a arbitrary string for prefix and instance plus a
220          * suffix and makes a nice string suitable as unit name of it,
221          * escaping all weird chars on the way.
222          *
223          * / becomes -, and all chars not allowed in a unit name get
224          * escaped as \xFF, including \ and -, of course. This
225          * escaping is hence reversible.
226          *
227          * This is primarily useful to make nice unit names from
228          * strings, but is actually useful for any kind of string.
229          */
230
231         a = strlen(prefix);
232         c = strlen(suffix);
233
234         if (instance) {
235                 b = strlen(instance);
236
237                 if (!(r = new(char, a*4 + 1 + b*4 + c + 1)))
238                         return NULL;
239
240                 t = do_escape(prefix, r);
241                 *(t++) = '@';
242                 t = do_escape(instance, t);
243         } else {
244
245                 if (!(r = new(char, a*4 + c + 1)))
246                         return NULL;
247
248                 t = do_escape(prefix, r);
249         }
250
251         strcpy(t, suffix);
252         return r;
253 }
254
255 char *unit_name_escape(const char *f) {
256         char *r, *t;
257
258         if (!(r = new(char, strlen(f)*4+1)))
259                 return NULL;
260
261         t = do_escape(f, r);
262         *t = 0;
263
264         return r;
265
266 }
267
268 char *unit_name_unescape(const char *f) {
269         char *r, *t;
270
271         assert(f);
272
273         if (!(r = strdup(f)))
274                 return NULL;
275
276         for (t = r; *f; f++) {
277                 if (*f == '-')
278                         *(t++) = '/';
279                 else if (*f == '\\') {
280                         int a, b;
281
282                         if (f[1] != 'x' ||
283                             (a = unhexchar(f[2])) < 0 ||
284                             (b = unhexchar(f[3])) < 0) {
285                                 /* Invalid escape code, let's take it literal then */
286                                 *(t++) = '\\';
287                         } else {
288                                 *(t++) = (char) ((a << 4) | b);
289                                 f += 3;
290                         }
291                 } else
292                         *(t++) = *f;
293         }
294
295         *t = 0;
296
297         return r;
298 }
299
300 bool unit_name_is_template(const char *n) {
301         const char *p;
302
303         assert(n);
304
305         if (!(p = strchr(n, '@')))
306                 return false;
307
308         return p[1] == '.';
309 }
310
311 char *unit_name_replace_instance(const char *f, const char *i) {
312         const char *p, *e;
313         char *r, *k;
314         size_t a;
315
316         assert(f);
317
318         p = strchr(f, '@');
319         assert_se(e = strrchr(f, '.'));
320
321         a = p - f;
322
323         if (p) {
324                 size_t b;
325
326                 b = strlen(i);
327
328                 if (!(r = new(char, a + 1 + b + strlen(e) + 1)))
329                         return NULL;
330
331                 k = mempcpy(r, f, a + 1);
332                 k = mempcpy(k, i, b);
333         } else {
334
335                 if (!(r = new(char, a + strlen(e) + 1)))
336                         return NULL;
337
338                 k = mempcpy(r, f, a);
339         }
340
341         strcpy(k, e);
342         return r;
343 }
344
345 char *unit_name_template(const char *f) {
346         const char *p, *e;
347         char *r;
348         size_t a;
349
350         if (!(p = strchr(f, '@')))
351                 return strdup(f);
352
353         assert_se(e = strrchr(f, '.'));
354         a = p - f + 1;
355
356         if (!(r = new(char, a + strlen(e) + 1)))
357                 return NULL;
358
359         strcpy(mempcpy(r, f, a), e);
360         return r;
361
362 }
363
364 char *unit_name_from_path(const char *path, const char *suffix) {
365         char *p, *r;
366
367         assert(path);
368         assert(suffix);
369
370         if (!(p = strdup(path)))
371                 return NULL;
372
373         path_kill_slashes(p);
374
375         path = p[0] == '/' ? p + 1 : p;
376
377         if (path[0] == 0) {
378                 free(p);
379                 return strappend("-", suffix);
380         }
381
382         r = unit_name_build_escape(path, NULL, suffix);
383         free(p);
384
385         return r;
386 }
387
388 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
389         char *p, *r;
390
391         assert(path);
392         assert(suffix);
393
394         if (!(p = strdup(path)))
395                 return NULL;
396
397         path_kill_slashes(p);
398
399         path = p[0] == '/' ? p + 1 : p;
400
401         if (path[0] == 0) {
402                 free(p);
403                 return unit_name_build_escape(prefix, "-", suffix);
404         }
405
406         r = unit_name_build_escape(prefix, path, suffix);
407         free(p);
408
409         return r;
410 }
411
412 char *unit_name_to_path(const char *name) {
413         char *w, *e;
414
415         assert(name);
416
417         if (!(w = unit_name_to_prefix(name)))
418                 return NULL;
419
420         e = unit_name_unescape(w);
421         free(w);
422
423         if (!e)
424                 return NULL;
425
426         if (e[0] != '/') {
427                 w = strappend("/", e);
428                 free(e);
429
430                 if (!w)
431                         return NULL;
432
433                 e = w;
434         }
435
436         return e;
437 }
438
439 char *unit_name_path_unescape(const char *f) {
440         char *e;
441
442         assert(f);
443
444         if (!(e = unit_name_unescape(f)))
445                 return NULL;
446
447         if (e[0] != '/') {
448                 char *w;
449
450                 w = strappend("/", e);
451                 free(e);
452
453                 if (!w)
454                         return NULL;
455
456                 e = w;
457         }
458
459         return e;
460 }