]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/unit-name.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / 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 }