]> git.proxmox.com Git - systemd.git/blob - src/shared/unit-name.c
Imported Upstream version 208
[systemd.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 #include "def.h"
30
31 #define VALID_CHARS \
32 DIGITS LETTERS \
33 ":-_.\\"
34
35 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
36 [UNIT_SERVICE] = "service",
37 [UNIT_SOCKET] = "socket",
38 [UNIT_TARGET] = "target",
39 [UNIT_DEVICE] = "device",
40 [UNIT_MOUNT] = "mount",
41 [UNIT_AUTOMOUNT] = "automount",
42 [UNIT_SNAPSHOT] = "snapshot",
43 [UNIT_TIMER] = "timer",
44 [UNIT_SWAP] = "swap",
45 [UNIT_PATH] = "path",
46 [UNIT_SLICE] = "slice",
47 [UNIT_SCOPE] = "scope"
48 };
49
50 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
51
52 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
53 [UNIT_STUB] = "stub",
54 [UNIT_LOADED] = "loaded",
55 [UNIT_NOT_FOUND] = "not-found",
56 [UNIT_ERROR] = "error",
57 [UNIT_MERGED] = "merged",
58 [UNIT_MASKED] = "masked"
59 };
60
61 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
62
63 bool unit_name_is_valid(const char *n, bool template_ok) {
64 const char *e, *i, *at;
65
66 /* Valid formats:
67 *
68 * string@instance.suffix
69 * string.suffix
70 */
71
72 assert(n);
73
74 if (strlen(n) >= UNIT_NAME_MAX)
75 return false;
76
77 e = strrchr(n, '.');
78 if (!e || e == n)
79 return false;
80
81 if (unit_type_from_string(e + 1) < 0)
82 return false;
83
84 for (i = n, at = NULL; i < e; i++) {
85
86 if (*i == '@' && !at)
87 at = i;
88
89 if (!strchr("@" VALID_CHARS, *i))
90 return false;
91 }
92
93 if (at) {
94 if (at == n)
95 return false;
96
97 if (!template_ok && at+1 == e)
98 return false;
99 }
100
101 return true;
102 }
103
104 bool unit_instance_is_valid(const char *i) {
105 assert(i);
106
107 /* The max length depends on the length of the string, so we
108 * don't really check this here. */
109
110 if (i[0] == 0)
111 return false;
112
113 /* We allow additional @ in the instance string, we do not
114 * allow them in the prefix! */
115
116 for (; *i; i++)
117 if (!strchr("@" VALID_CHARS, *i))
118 return false;
119
120 return true;
121 }
122
123 bool unit_prefix_is_valid(const char *p) {
124
125 /* We don't allow additional @ in the instance string */
126
127 if (p[0] == 0)
128 return false;
129
130 for (; *p; p++)
131 if (!strchr(VALID_CHARS, *p))
132 return false;
133
134 return true;
135 }
136
137 int unit_name_to_instance(const char *n, char **instance) {
138 const char *p, *d;
139 char *i;
140
141 assert(n);
142 assert(instance);
143
144 /* Everything past the first @ and before the last . is the instance */
145 p = strchr(n, '@');
146 if (!p) {
147 *instance = NULL;
148 return 0;
149 }
150
151 assert_se(d = strrchr(n, '.'));
152 assert(p < d);
153
154 i = strndup(p+1, d-p-1);
155 if (!i)
156 return -ENOMEM;
157
158 *instance = i;
159 return 0;
160 }
161
162 char *unit_name_to_prefix_and_instance(const char *n) {
163 const char *d;
164
165 assert(n);
166
167 assert_se(d = strrchr(n, '.'));
168
169 return strndup(n, d - n);
170 }
171
172 char *unit_name_to_prefix(const char *n) {
173 const char *p;
174
175 p = strchr(n, '@');
176 if (p)
177 return strndup(n, p - n);
178
179 return unit_name_to_prefix_and_instance(n);
180 }
181
182 char *unit_name_change_suffix(const char *n, const char *suffix) {
183 char *e, *r;
184 size_t a, b;
185
186 assert(n);
187 assert(unit_name_is_valid(n, true));
188 assert(suffix);
189 assert(suffix[0] == '.');
190
191 assert_se(e = strrchr(n, '.'));
192 a = e - n;
193 b = strlen(suffix);
194
195 r = new(char, a + b + 1);
196 if (!r)
197 return NULL;
198
199 memcpy(r, n, a);
200 memcpy(r+a, suffix, b+1);
201
202 return r;
203 }
204
205 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
206 assert(prefix);
207 assert(unit_prefix_is_valid(prefix));
208 assert(!instance || unit_instance_is_valid(instance));
209 assert(suffix);
210
211 if (!instance)
212 return strappend(prefix, suffix);
213
214 return strjoin(prefix, "@", instance, suffix, NULL);
215 }
216
217 static char *do_escape_char(char c, char *t) {
218 *(t++) = '\\';
219 *(t++) = 'x';
220 *(t++) = hexchar(c >> 4);
221 *(t++) = hexchar(c);
222 return t;
223 }
224
225 static char *do_escape(const char *f, char *t) {
226 assert(f);
227 assert(t);
228
229 /* do not create units with a leading '.', like for "/.dotdir" mount points */
230 if (*f == '.') {
231 t = do_escape_char(*f, t);
232 f++;
233 }
234
235 for (; *f; f++) {
236 if (*f == '/')
237 *(t++) = '-';
238 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
239 t = do_escape_char(*f, t);
240 else
241 *(t++) = *f;
242 }
243
244 return t;
245 }
246
247 char *unit_name_escape(const char *f) {
248 char *r, *t;
249
250 r = new(char, strlen(f)*4+1);
251 if (!r)
252 return NULL;
253
254 t = do_escape(f, r);
255 *t = 0;
256
257 return r;
258 }
259
260 char *unit_name_unescape(const char *f) {
261 char *r, *t;
262
263 assert(f);
264
265 r = strdup(f);
266 if (!r)
267 return NULL;
268
269 for (t = r; *f; f++) {
270 if (*f == '-')
271 *(t++) = '/';
272 else if (*f == '\\') {
273 int a, b;
274
275 if (f[1] != 'x' ||
276 (a = unhexchar(f[2])) < 0 ||
277 (b = unhexchar(f[3])) < 0) {
278 /* Invalid escape code, let's take it literal then */
279 *(t++) = '\\';
280 } else {
281 *(t++) = (char) ((a << 4) | b);
282 f += 3;
283 }
284 } else
285 *(t++) = *f;
286 }
287
288 *t = 0;
289
290 return r;
291 }
292
293 char *unit_name_path_escape(const char *f) {
294 char *p, *e;
295
296 assert(f);
297
298 p = strdup(f);
299 if (!p)
300 return NULL;
301
302 path_kill_slashes(p);
303
304 if (streq(p, "/") || streq(p, "")) {
305 free(p);
306 return strdup("-");
307 }
308
309 e = unit_name_escape(p[0] == '/' ? p + 1 : p);
310 free(p);
311
312 return e;
313 }
314
315 char *unit_name_path_unescape(const char *f) {
316 char *e;
317
318 assert(f);
319
320 e = unit_name_unescape(f);
321 if (!e)
322 return NULL;
323
324 if (e[0] != '/') {
325 char *w;
326
327 w = strappend("/", e);
328 free(e);
329
330 return w;
331 }
332
333 return e;
334 }
335
336 bool unit_name_is_template(const char *n) {
337 const char *p;
338
339 assert(n);
340
341 p = strchr(n, '@');
342 if (!p)
343 return false;
344
345 return p[1] == '.';
346 }
347
348 bool unit_name_is_instance(const char *n) {
349 const char *p;
350
351 assert(n);
352
353 p = strchr(n, '@');
354 if (!p)
355 return false;
356
357 return p[1] != '.';
358 }
359
360 char *unit_name_replace_instance(const char *f, const char *i) {
361 const char *p, *e;
362 char *r, *k;
363 size_t a, b;
364
365 assert(f);
366
367 p = strchr(f, '@');
368 if (!p)
369 return strdup(f);
370
371 e = strrchr(f, '.');
372 if (!e)
373 assert_se(e = strchr(f, 0));
374
375 a = p - f;
376 b = strlen(i);
377
378 r = new(char, a + 1 + b + strlen(e) + 1);
379 if (!r)
380 return NULL;
381
382 k = mempcpy(r, f, a + 1);
383 k = mempcpy(k, i, b);
384 strcpy(k, e);
385
386 return r;
387 }
388
389 char *unit_name_template(const char *f) {
390 const char *p, *e;
391 char *r;
392 size_t a;
393
394 p = strchr(f, '@');
395 if (!p)
396 return strdup(f);
397
398 assert_se(e = strrchr(f, '.'));
399 a = p - f + 1;
400
401 r = new(char, a + strlen(e) + 1);
402 if (!r)
403 return NULL;
404
405 strcpy(mempcpy(r, f, a), e);
406 return r;
407 }
408
409 char *unit_name_from_path(const char *path, const char *suffix) {
410 char *p, *r;
411
412 assert(path);
413 assert(suffix);
414
415 p = unit_name_path_escape(path);
416 if (!p)
417 return NULL;
418
419 r = strappend(p, suffix);
420 free(p);
421
422 return r;
423 }
424
425 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
426 char *p, *r;
427
428 assert(prefix);
429 assert(path);
430 assert(suffix);
431
432 p = unit_name_path_escape(path);
433 if (!p)
434 return NULL;
435
436 r = strjoin(prefix, "@", p, suffix, NULL);
437 free(p);
438
439 return r;
440 }
441
442 char *unit_name_to_path(const char *name) {
443 char *w, *e;
444
445 assert(name);
446
447 w = unit_name_to_prefix(name);
448 if (!w)
449 return NULL;
450
451 e = unit_name_path_unescape(w);
452 free(w);
453
454 return e;
455 }
456
457 char *unit_dbus_path_from_name(const char *name) {
458 _cleanup_free_ char *e = NULL;
459
460 assert(name);
461
462 e = bus_path_escape(name);
463 if (!e)
464 return NULL;
465
466 return strappend("/org/freedesktop/systemd1/unit/", e);
467 }
468
469 int unit_name_from_dbus_path(const char *path, char **name) {
470 const char *e;
471 char *n;
472
473 e = startswith(path, "/org/freedesktop/systemd1/unit/");
474 if (!e)
475 return -EINVAL;
476
477 n = bus_path_unescape(e);
478 if (!n)
479 return -ENOMEM;
480
481 *name = n;
482 return 0;
483 }
484
485 char *unit_name_mangle(const char *name) {
486 char *r, *t;
487 const char *f;
488
489 assert(name);
490
491 /* Try to turn a string that might not be a unit name into a
492 * sensible unit name. */
493
494 if (is_device_path(name))
495 return unit_name_from_path(name, ".device");
496
497 if (path_is_absolute(name))
498 return unit_name_from_path(name, ".mount");
499
500 /* We'll only escape the obvious characters here, to play
501 * safe. */
502
503 r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
504 if (!r)
505 return NULL;
506
507 for (f = name, t = r; *f; f++) {
508 if (*f == '/')
509 *(t++) = '-';
510 else if (!strchr("@" VALID_CHARS, *f))
511 t = do_escape_char(*f, t);
512 else
513 *(t++) = *f;
514 }
515
516 if (unit_name_to_type(name) < 0)
517 strcpy(t, ".service");
518 else
519 *t = 0;
520
521 return r;
522 }
523
524 char *unit_name_mangle_with_suffix(const char *name, const char *suffix) {
525 char *r, *t;
526 const char *f;
527
528 assert(name);
529 assert(suffix);
530 assert(suffix[0] == '.');
531
532 /* Similar to unit_name_mangle(), but is called when we know
533 * that this is about snapshot units. */
534
535 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
536 if (!r)
537 return NULL;
538
539 for (f = name, t = r; *f; f++) {
540 if (*f == '/')
541 *(t++) = '-';
542 else if (!strchr(VALID_CHARS, *f))
543 t = do_escape_char(*f, t);
544 else
545 *(t++) = *f;
546 }
547
548 if (!endswith(name, suffix))
549 strcpy(t, suffix);
550 else
551 *t = 0;
552
553 return r;
554 }
555
556 UnitType unit_name_to_type(const char *n) {
557 const char *e;
558
559 assert(n);
560
561 e = strrchr(n, '.');
562 if (!e)
563 return _UNIT_TYPE_INVALID;
564
565 return unit_type_from_string(e + 1);
566 }
567
568 int build_subslice(const char *slice, const char*name, char **subslice) {
569 char *ret;
570
571 assert(slice);
572 assert(name);
573 assert(subslice);
574
575 if (streq(slice, "-.slice"))
576 ret = strappend(name, ".slice");
577 else {
578 char *e;
579
580 e = endswith(slice, ".slice");
581 if (!e)
582 return -EINVAL;
583
584 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
585 if (!ret)
586 return -ENOMEM;
587
588 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
589 }
590
591 *subslice = ret;
592 return 0;
593 }