]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/unit-name.c
core: add transient units
[thirdparty/systemd.git] / src / shared / unit-name.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
9e2f7c11
LP
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
5430f7f2
LP
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
9e2f7c11
LP
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
5430f7f2 16 Lesser General Public License for more details.
9e2f7c11 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
9e2f7c11
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <string.h>
71fad675 24#include <assert.h>
9e2f7c11 25
9eb977db 26#include "path-util.h"
71fad675 27#include "util.h"
9e2f7c11
LP
28#include "unit-name.h"
29
30#define VALID_CHARS \
31 "0123456789" \
32 "abcdefghijklmnopqrstuvwxyz" \
33 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
4f2d528d 34 ":-_.\\"
9e2f7c11 35
830f01f0 36static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
0a9f8ed0
ZJS
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",
a016b922 47 [UNIT_SLICE] = "slice"
0a9f8ed0
ZJS
48};
49
50DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
51
830f01f0 52static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
f69614f8
ZJS
53 [UNIT_STUB] = "stub",
54 [UNIT_LOADED] = "loaded",
c2756a68 55 [UNIT_NOT_FOUND] = "not-found",
f69614f8
ZJS
56 [UNIT_ERROR] = "error",
57 [UNIT_MERGED] = "merged",
58 [UNIT_MASKED] = "masked"
59};
60
61DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
62
5f739699 63bool unit_name_is_valid(const char *n, bool template_ok) {
9e2f7c11
LP
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
71fad675
LP
77 e = strrchr(n, '.');
78 if (!e || e == n)
9e2f7c11
LP
79 return false;
80
5f739699
LP
81 if (unit_type_from_string(e + 1) < 0)
82 return false;
83
9e2f7c11
LP
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
b9c0d441 97 if (!template_ok && at+1 == e)
9e2f7c11
LP
98 return false;
99 }
100
101 return true;
102}
103
104bool 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
123bool 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
137int 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 */
35eb6b12
LP
145 p = strchr(n, '@');
146 if (!p) {
9e2f7c11
LP
147 *instance = NULL;
148 return 0;
149 }
150
151 assert_se(d = strrchr(n, '.'));
152 assert(p < d);
153
35eb6b12
LP
154 i = strndup(p+1, d-p-1);
155 if (!i)
9e2f7c11
LP
156 return -ENOMEM;
157
158 *instance = i;
159 return 0;
160}
161
162char *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
172char *unit_name_to_prefix(const char *n) {
173 const char *p;
174
b0193f1c
LP
175 p = strchr(n, '@');
176 if (p)
9e2f7c11
LP
177 return strndup(n, p - n);
178
179 return unit_name_to_prefix_and_instance(n);
180}
181
182char *unit_name_change_suffix(const char *n, const char *suffix) {
183 char *e, *r;
184 size_t a, b;
185
186 assert(n);
5f739699 187 assert(unit_name_is_valid(n, true));
9e2f7c11 188 assert(suffix);
a016b922 189 assert(suffix[0] == '.');
9e2f7c11
LP
190
191 assert_se(e = strrchr(n, '.'));
192 a = e - n;
193 b = strlen(suffix);
194
b0193f1c
LP
195 r = new(char, a + b + 1);
196 if (!r)
9e2f7c11
LP
197 return NULL;
198
199 memcpy(r, n, a);
200 memcpy(r+a, suffix, b+1);
201
202 return r;
203}
204
205char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
9e2f7c11
LP
206 assert(prefix);
207 assert(unit_prefix_is_valid(prefix));
208 assert(!instance || unit_instance_is_valid(instance));
209 assert(suffix);
9e2f7c11
LP
210
211 if (!instance)
212 return strappend(prefix, suffix);
213
b7def684 214 return strjoin(prefix, "@", instance, suffix, NULL);
9e2f7c11
LP
215}
216
4b712653
KS
217static 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
225static char *do_escape(const char *f, char *t) {
9e2f7c11
LP
226 assert(f);
227 assert(t);
228
4b712653
KS
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
9e2f7c11
LP
235 for (; *f; f++) {
236 if (*f == '/')
8d567588 237 *(t++) = '-';
4b712653
KS
238 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
239 t = do_escape_char(*f, t);
240 else
9e2f7c11
LP
241 *(t++) = *f;
242 }
243
244 return t;
245}
246
9e2f7c11
LP
247char *unit_name_escape(const char *f) {
248 char *r, *t;
249
b0193f1c
LP
250 r = new(char, strlen(f)*4+1);
251 if (!r)
9e2f7c11
LP
252 return NULL;
253
254 t = do_escape(f, r);
255 *t = 0;
256
257 return r;
9e2f7c11
LP
258}
259
260char *unit_name_unescape(const char *f) {
261 char *r, *t;
262
263 assert(f);
264
b0193f1c
LP
265 r = strdup(f);
266 if (!r)
9e2f7c11
LP
267 return NULL;
268
269 for (t = r; *f; f++) {
8d567588 270 if (*f == '-')
9e2f7c11
LP
271 *(t++) = '/';
272 else if (*f == '\\') {
273 int a, b;
274
15e11d81
LP
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 */
9e2f7c11
LP
279 *(t++) = '\\';
280 } else {
281 *(t++) = (char) ((a << 4) | b);
95e501f8 282 f += 3;
9e2f7c11
LP
283 }
284 } else
285 *(t++) = *f;
286 }
287
288 *t = 0;
289
290 return r;
291}
292
35eb6b12
LP
293char *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
315char *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
9e2f7c11
LP
336bool unit_name_is_template(const char *n) {
337 const char *p;
338
339 assert(n);
340
a7b9ecf9
MS
341 p = strchr(n, '@');
342 if (!p)
9e2f7c11
LP
343 return false;
344
345 return p[1] == '.';
346}
347
29283ea4
MS
348bool 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
9e2f7c11
LP
360char *unit_name_replace_instance(const char *f, const char *i) {
361 const char *p, *e;
362 char *r, *k;
8556879e 363 size_t a, b;
9e2f7c11
LP
364
365 assert(f);
366
367 p = strchr(f, '@');
8556879e
LP
368 if (!p)
369 return strdup(f);
9e2f7c11 370
8556879e
LP
371 e = strrchr(f, '.');
372 if (!e)
373 assert_se(e = strchr(f, 0));
9e2f7c11 374
8556879e
LP
375 a = p - f;
376 b = strlen(i);
9e2f7c11 377
8556879e
LP
378 r = new(char, a + 1 + b + strlen(e) + 1);
379 if (!r)
380 return NULL;
9e2f7c11 381
8556879e
LP
382 k = mempcpy(r, f, a + 1);
383 k = mempcpy(k, i, b);
9e2f7c11 384 strcpy(k, e);
8556879e 385
9e2f7c11
LP
386 return r;
387}
388
389char *unit_name_template(const char *f) {
390 const char *p, *e;
391 char *r;
392 size_t a;
393
b0193f1c
LP
394 p = strchr(f, '@');
395 if (!p)
9e2f7c11
LP
396 return strdup(f);
397
398 assert_se(e = strrchr(f, '.'));
399 a = p - f + 1;
400
b0193f1c
LP
401 r = new(char, a + strlen(e) + 1);
402 if (!r)
9e2f7c11
LP
403 return NULL;
404
405 strcpy(mempcpy(r, f, a), e);
406 return r;
9e2f7c11 407}
a16e1123
LP
408
409char *unit_name_from_path(const char *path, const char *suffix) {
5ffceb2f
LP
410 char *p, *r;
411
a16e1123
LP
412 assert(path);
413 assert(suffix);
414
35eb6b12 415 p = unit_name_path_escape(path);
b0193f1c 416 if (!p)
5ffceb2f
LP
417 return NULL;
418
35eb6b12 419 r = strappend(p, suffix);
5ffceb2f 420 free(p);
a16e1123 421
5ffceb2f 422 return r;
a16e1123
LP
423}
424
9fff8a1f
LP
425char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
426 char *p, *r;
427
35eb6b12 428 assert(prefix);
9fff8a1f
LP
429 assert(path);
430 assert(suffix);
431
35eb6b12
LP
432 p = unit_name_path_escape(path);
433 if (!p)
9fff8a1f
LP
434 return NULL;
435
b7def684 436 r = strjoin(prefix, "@", p, suffix, NULL);
9fff8a1f
LP
437 free(p);
438
439 return r;
440}
441
a16e1123
LP
442char *unit_name_to_path(const char *name) {
443 char *w, *e;
444
445 assert(name);
446
b0193f1c
LP
447 w = unit_name_to_prefix(name);
448 if (!w)
a16e1123
LP
449 return NULL;
450
35eb6b12 451 e = unit_name_path_unescape(w);
a16e1123
LP
452 free(w);
453
9fc50704
LP
454 return e;
455}
48899192
MS
456
457char *unit_dbus_path_from_name(const char *name) {
9444b1f2 458 _cleanup_free_ char *e = NULL;
48899192 459
35eb6b12
LP
460 assert(name);
461
48899192
MS
462 e = bus_path_escape(name);
463 if (!e)
464 return NULL;
465
9444b1f2 466 return strappend("/org/freedesktop/systemd1/unit/", e);
48899192 467}
b0193f1c
LP
468
469char *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
a05f97b3 478 if (is_device_path(name))
b0193f1c
LP
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
56d4fbf9 487 r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
b0193f1c
LP
488 if (!r)
489 return NULL;
490
491 for (f = name, t = r; *f; f++) {
b0193f1c
LP
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
696c245a 500 if (unit_name_to_type(name) < 0)
56d4fbf9
LP
501 strcpy(t, ".service");
502 else
503 *t = 0;
b0193f1c
LP
504
505 return r;
506}
5f739699 507
a016b922 508char *unit_name_mangle_with_suffix(const char *name, const char *suffix) {
1dcf6065
LP
509 char *r, *t;
510 const char *f;
511
512 assert(name);
a016b922
LP
513 assert(suffix);
514 assert(suffix[0] == '.');
1dcf6065
LP
515
516 /* Similar to unit_name_mangle(), but is called when we know
517 * that this is about snapshot units. */
518
a016b922 519 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
1dcf6065
LP
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
a016b922
LP
532 if (!endswith(name, suffix))
533 strcpy(t, suffix);
1dcf6065
LP
534 else
535 *t = 0;
536
537 return r;
538}
539
5f739699
LP
540UnitType 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}