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