]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/unit-name.c
core: add new "scope" unit type for making a unit of pre-existing processes
[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 [UNIT_SCOPE] = "scope"
49 };
50
51 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
52
53 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
54 [UNIT_STUB] = "stub",
55 [UNIT_LOADED] = "loaded",
56 [UNIT_NOT_FOUND] = "not-found",
57 [UNIT_ERROR] = "error",
58 [UNIT_MERGED] = "merged",
59 [UNIT_MASKED] = "masked"
60 };
61
62 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
63
64 bool unit_name_is_valid(const char *n, bool template_ok) {
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
78 e = strrchr(n, '.');
79 if (!e || e == n)
80 return false;
81
82 if (unit_type_from_string(e + 1) < 0)
83 return false;
84
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
98 if (!template_ok && at+1 == e)
99 return false;
100 }
101
102 return true;
103 }
104
105 bool 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
124 bool 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
138 int 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 */
146 p = strchr(n, '@');
147 if (!p) {
148 *instance = NULL;
149 return 0;
150 }
151
152 assert_se(d = strrchr(n, '.'));
153 assert(p < d);
154
155 i = strndup(p+1, d-p-1);
156 if (!i)
157 return -ENOMEM;
158
159 *instance = i;
160 return 0;
161 }
162
163 char *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
173 char *unit_name_to_prefix(const char *n) {
174 const char *p;
175
176 p = strchr(n, '@');
177 if (p)
178 return strndup(n, p - n);
179
180 return unit_name_to_prefix_and_instance(n);
181 }
182
183 char *unit_name_change_suffix(const char *n, const char *suffix) {
184 char *e, *r;
185 size_t a, b;
186
187 assert(n);
188 assert(unit_name_is_valid(n, true));
189 assert(suffix);
190 assert(suffix[0] == '.');
191
192 assert_se(e = strrchr(n, '.'));
193 a = e - n;
194 b = strlen(suffix);
195
196 r = new(char, a + b + 1);
197 if (!r)
198 return NULL;
199
200 memcpy(r, n, a);
201 memcpy(r+a, suffix, b+1);
202
203 return r;
204 }
205
206 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
207 assert(prefix);
208 assert(unit_prefix_is_valid(prefix));
209 assert(!instance || unit_instance_is_valid(instance));
210 assert(suffix);
211
212 if (!instance)
213 return strappend(prefix, suffix);
214
215 return strjoin(prefix, "@", instance, suffix, NULL);
216 }
217
218 static 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
226 static char *do_escape(const char *f, char *t) {
227 assert(f);
228 assert(t);
229
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
236 for (; *f; f++) {
237 if (*f == '/')
238 *(t++) = '-';
239 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
240 t = do_escape_char(*f, t);
241 else
242 *(t++) = *f;
243 }
244
245 return t;
246 }
247
248 char *unit_name_escape(const char *f) {
249 char *r, *t;
250
251 r = new(char, strlen(f)*4+1);
252 if (!r)
253 return NULL;
254
255 t = do_escape(f, r);
256 *t = 0;
257
258 return r;
259 }
260
261 char *unit_name_unescape(const char *f) {
262 char *r, *t;
263
264 assert(f);
265
266 r = strdup(f);
267 if (!r)
268 return NULL;
269
270 for (t = r; *f; f++) {
271 if (*f == '-')
272 *(t++) = '/';
273 else if (*f == '\\') {
274 int a, b;
275
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 */
280 *(t++) = '\\';
281 } else {
282 *(t++) = (char) ((a << 4) | b);
283 f += 3;
284 }
285 } else
286 *(t++) = *f;
287 }
288
289 *t = 0;
290
291 return r;
292 }
293
294 char *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
316 char *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
337 bool unit_name_is_template(const char *n) {
338 const char *p;
339
340 assert(n);
341
342 p = strchr(n, '@');
343 if (!p)
344 return false;
345
346 return p[1] == '.';
347 }
348
349 bool 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
361 char *unit_name_replace_instance(const char *f, const char *i) {
362 const char *p, *e;
363 char *r, *k;
364 size_t a, b;
365
366 assert(f);
367
368 p = strchr(f, '@');
369 if (!p)
370 return strdup(f);
371
372 e = strrchr(f, '.');
373 if (!e)
374 assert_se(e = strchr(f, 0));
375
376 a = p - f;
377 b = strlen(i);
378
379 r = new(char, a + 1 + b + strlen(e) + 1);
380 if (!r)
381 return NULL;
382
383 k = mempcpy(r, f, a + 1);
384 k = mempcpy(k, i, b);
385 strcpy(k, e);
386
387 return r;
388 }
389
390 char *unit_name_template(const char *f) {
391 const char *p, *e;
392 char *r;
393 size_t a;
394
395 p = strchr(f, '@');
396 if (!p)
397 return strdup(f);
398
399 assert_se(e = strrchr(f, '.'));
400 a = p - f + 1;
401
402 r = new(char, a + strlen(e) + 1);
403 if (!r)
404 return NULL;
405
406 strcpy(mempcpy(r, f, a), e);
407 return r;
408 }
409
410 char *unit_name_from_path(const char *path, const char *suffix) {
411 char *p, *r;
412
413 assert(path);
414 assert(suffix);
415
416 p = unit_name_path_escape(path);
417 if (!p)
418 return NULL;
419
420 r = strappend(p, suffix);
421 free(p);
422
423 return r;
424 }
425
426 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
427 char *p, *r;
428
429 assert(prefix);
430 assert(path);
431 assert(suffix);
432
433 p = unit_name_path_escape(path);
434 if (!p)
435 return NULL;
436
437 r = strjoin(prefix, "@", p, suffix, NULL);
438 free(p);
439
440 return r;
441 }
442
443 char *unit_name_to_path(const char *name) {
444 char *w, *e;
445
446 assert(name);
447
448 w = unit_name_to_prefix(name);
449 if (!w)
450 return NULL;
451
452 e = unit_name_path_unescape(w);
453 free(w);
454
455 return e;
456 }
457
458 char *unit_dbus_path_from_name(const char *name) {
459 _cleanup_free_ char *e = NULL;
460
461 assert(name);
462
463 e = bus_path_escape(name);
464 if (!e)
465 return NULL;
466
467 return strappend("/org/freedesktop/systemd1/unit/", e);
468 }
469
470 char *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
479 if (is_device_path(name))
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
488 r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
489 if (!r)
490 return NULL;
491
492 for (f = name, t = r; *f; f++) {
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
501 if (unit_name_to_type(name) < 0)
502 strcpy(t, ".service");
503 else
504 *t = 0;
505
506 return r;
507 }
508
509 char *unit_name_mangle_with_suffix(const char *name, const char *suffix) {
510 char *r, *t;
511 const char *f;
512
513 assert(name);
514 assert(suffix);
515 assert(suffix[0] == '.');
516
517 /* Similar to unit_name_mangle(), but is called when we know
518 * that this is about snapshot units. */
519
520 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
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
533 if (!endswith(name, suffix))
534 strcpy(t, suffix);
535 else
536 *t = 0;
537
538 return r;
539 }
540
541 UnitType 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 }