]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/unit-name.c
core: add transient units
[thirdparty/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
30 #define VALID_CHARS \
31 "0123456789" \
32 "abcdefghijklmnopqrstuvwxyz" \
33 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
34 ":-_.\\"
35
36 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
37 [UNIT_SERVICE] = "service",
38 [UNIT_SOCKET] = "socket",
39 [UNIT_TARGET] = "target",
40 [UNIT_DEVICE] = "device",
41 [UNIT_MOUNT] = "mount",
42 [UNIT_AUTOMOUNT] = "automount",
43 [UNIT_SNAPSHOT] = "snapshot",
44 [UNIT_TIMER] = "timer",
45 [UNIT_SWAP] = "swap",
46 [UNIT_PATH] = "path",
47 [UNIT_SLICE] = "slice"
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, "/")) {
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 char *unit_name_mangle(const char *name) {
470 char *r, *t;
471 const char *f;
472
473 assert(name);
474
475 /* Try to turn a string that might not be a unit name into a
476 * sensible unit name. */
477
478 if (is_device_path(name))
479 return unit_name_from_path(name, ".device");
480
481 if (path_is_absolute(name))
482 return unit_name_from_path(name, ".mount");
483
484 /* We'll only escape the obvious characters here, to play
485 * safe. */
486
487 r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
488 if (!r)
489 return NULL;
490
491 for (f = name, t = r; *f; f++) {
492 if (*f == '/')
493 *(t++) = '-';
494 else if (!strchr("@" VALID_CHARS, *f))
495 t = do_escape_char(*f, t);
496 else
497 *(t++) = *f;
498 }
499
500 if (unit_name_to_type(name) < 0)
501 strcpy(t, ".service");
502 else
503 *t = 0;
504
505 return r;
506 }
507
508 char *unit_name_mangle_with_suffix(const char *name, const char *suffix) {
509 char *r, *t;
510 const char *f;
511
512 assert(name);
513 assert(suffix);
514 assert(suffix[0] == '.');
515
516 /* Similar to unit_name_mangle(), but is called when we know
517 * that this is about snapshot units. */
518
519 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
520 if (!r)
521 return NULL;
522
523 for (f = name, t = r; *f; f++) {
524 if (*f == '/')
525 *(t++) = '-';
526 else if (!strchr(VALID_CHARS, *f))
527 t = do_escape_char(*f, t);
528 else
529 *(t++) = *f;
530 }
531
532 if (!endswith(name, suffix))
533 strcpy(t, suffix);
534 else
535 *t = 0;
536
537 return r;
538 }
539
540 UnitType unit_name_to_type(const char *n) {
541 const char *e;
542
543 assert(n);
544
545 e = strrchr(n, '.');
546 if (!e)
547 return _UNIT_TYPE_INVALID;
548
549 return unit_type_from_string(e + 1);
550 }