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