]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/unit-name.c
logind: add infrastructure to keep track of machines, and move to slices
[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_ERROR] = "error",
56 [UNIT_MERGED] = "merged",
57 [UNIT_MASKED] = "masked"
58 };
59
60 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
61
62 bool unit_name_is_valid(const char *n, bool template_ok) {
63 const char *e, *i, *at;
64
65 /* Valid formats:
66 *
67 * string@instance.suffix
68 * string.suffix
69 */
70
71 assert(n);
72
73 if (strlen(n) >= UNIT_NAME_MAX)
74 return false;
75
76 e = strrchr(n, '.');
77 if (!e || e == n)
78 return false;
79
80 if (unit_type_from_string(e + 1) < 0)
81 return false;
82
83 for (i = n, at = NULL; i < e; i++) {
84
85 if (*i == '@' && !at)
86 at = i;
87
88 if (!strchr("@" VALID_CHARS, *i))
89 return false;
90 }
91
92 if (at) {
93 if (at == n)
94 return false;
95
96 if (!template_ok && at+1 == e)
97 return false;
98 }
99
100 return true;
101 }
102
103 bool unit_instance_is_valid(const char *i) {
104 assert(i);
105
106 /* The max length depends on the length of the string, so we
107 * don't really check this here. */
108
109 if (i[0] == 0)
110 return false;
111
112 /* We allow additional @ in the instance string, we do not
113 * allow them in the prefix! */
114
115 for (; *i; i++)
116 if (!strchr("@" VALID_CHARS, *i))
117 return false;
118
119 return true;
120 }
121
122 bool unit_prefix_is_valid(const char *p) {
123
124 /* We don't allow additional @ in the instance string */
125
126 if (p[0] == 0)
127 return false;
128
129 for (; *p; p++)
130 if (!strchr(VALID_CHARS, *p))
131 return false;
132
133 return true;
134 }
135
136 int unit_name_to_instance(const char *n, char **instance) {
137 const char *p, *d;
138 char *i;
139
140 assert(n);
141 assert(instance);
142
143 /* Everything past the first @ and before the last . is the instance */
144 p = strchr(n, '@');
145 if (!p) {
146 *instance = NULL;
147 return 0;
148 }
149
150 assert_se(d = strrchr(n, '.'));
151 assert(p < d);
152
153 i = strndup(p+1, d-p-1);
154 if (!i)
155 return -ENOMEM;
156
157 *instance = i;
158 return 0;
159 }
160
161 char *unit_name_to_prefix_and_instance(const char *n) {
162 const char *d;
163
164 assert(n);
165
166 assert_se(d = strrchr(n, '.'));
167
168 return strndup(n, d - n);
169 }
170
171 char *unit_name_to_prefix(const char *n) {
172 const char *p;
173
174 p = strchr(n, '@');
175 if (p)
176 return strndup(n, p - n);
177
178 return unit_name_to_prefix_and_instance(n);
179 }
180
181 char *unit_name_change_suffix(const char *n, const char *suffix) {
182 char *e, *r;
183 size_t a, b;
184
185 assert(n);
186 assert(unit_name_is_valid(n, true));
187 assert(suffix);
188 assert(suffix[0] == '.');
189
190 assert_se(e = strrchr(n, '.'));
191 a = e - n;
192 b = strlen(suffix);
193
194 r = new(char, a + b + 1);
195 if (!r)
196 return NULL;
197
198 memcpy(r, n, a);
199 memcpy(r+a, suffix, b+1);
200
201 return r;
202 }
203
204 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
205 assert(prefix);
206 assert(unit_prefix_is_valid(prefix));
207 assert(!instance || unit_instance_is_valid(instance));
208 assert(suffix);
209
210 if (!instance)
211 return strappend(prefix, suffix);
212
213 return strjoin(prefix, "@", instance, suffix, NULL);
214 }
215
216 static char *do_escape_char(char c, char *t) {
217 *(t++) = '\\';
218 *(t++) = 'x';
219 *(t++) = hexchar(c >> 4);
220 *(t++) = hexchar(c);
221 return t;
222 }
223
224 static char *do_escape(const char *f, char *t) {
225 assert(f);
226 assert(t);
227
228 /* do not create units with a leading '.', like for "/.dotdir" mount points */
229 if (*f == '.') {
230 t = do_escape_char(*f, t);
231 f++;
232 }
233
234 for (; *f; f++) {
235 if (*f == '/')
236 *(t++) = '-';
237 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
238 t = do_escape_char(*f, t);
239 else
240 *(t++) = *f;
241 }
242
243 return t;
244 }
245
246 char *unit_name_escape(const char *f) {
247 char *r, *t;
248
249 r = new(char, strlen(f)*4+1);
250 if (!r)
251 return NULL;
252
253 t = do_escape(f, r);
254 *t = 0;
255
256 return r;
257 }
258
259 char *unit_name_unescape(const char *f) {
260 char *r, *t;
261
262 assert(f);
263
264 r = strdup(f);
265 if (!r)
266 return NULL;
267
268 for (t = r; *f; f++) {
269 if (*f == '-')
270 *(t++) = '/';
271 else if (*f == '\\') {
272 int a, b;
273
274 if (f[1] != 'x' ||
275 (a = unhexchar(f[2])) < 0 ||
276 (b = unhexchar(f[3])) < 0) {
277 /* Invalid escape code, let's take it literal then */
278 *(t++) = '\\';
279 } else {
280 *(t++) = (char) ((a << 4) | b);
281 f += 3;
282 }
283 } else
284 *(t++) = *f;
285 }
286
287 *t = 0;
288
289 return r;
290 }
291
292 char *unit_name_path_escape(const char *f) {
293 char *p, *e;
294
295 assert(f);
296
297 p = strdup(f);
298 if (!p)
299 return NULL;
300
301 path_kill_slashes(p);
302
303 if (streq(p, "/")) {
304 free(p);
305 return strdup("-");
306 }
307
308 e = unit_name_escape(p[0] == '/' ? p + 1 : p);
309 free(p);
310
311 return e;
312 }
313
314 char *unit_name_path_unescape(const char *f) {
315 char *e;
316
317 assert(f);
318
319 e = unit_name_unescape(f);
320 if (!e)
321 return NULL;
322
323 if (e[0] != '/') {
324 char *w;
325
326 w = strappend("/", e);
327 free(e);
328
329 return w;
330 }
331
332 return e;
333 }
334
335 bool unit_name_is_template(const char *n) {
336 const char *p;
337
338 assert(n);
339
340 p = strchr(n, '@');
341 if (!p)
342 return false;
343
344 return p[1] == '.';
345 }
346
347 bool unit_name_is_instance(const char *n) {
348 const char *p;
349
350 assert(n);
351
352 p = strchr(n, '@');
353 if (!p)
354 return false;
355
356 return p[1] != '.';
357 }
358
359 char *unit_name_replace_instance(const char *f, const char *i) {
360 const char *p, *e;
361 char *r, *k;
362 size_t a, b;
363
364 assert(f);
365
366 p = strchr(f, '@');
367 if (!p)
368 return strdup(f);
369
370 e = strrchr(f, '.');
371 if (!e)
372 assert_se(e = strchr(f, 0));
373
374 a = p - f;
375 b = strlen(i);
376
377 r = new(char, a + 1 + b + strlen(e) + 1);
378 if (!r)
379 return NULL;
380
381 k = mempcpy(r, f, a + 1);
382 k = mempcpy(k, i, b);
383 strcpy(k, e);
384
385 return r;
386 }
387
388 char *unit_name_template(const char *f) {
389 const char *p, *e;
390 char *r;
391 size_t a;
392
393 p = strchr(f, '@');
394 if (!p)
395 return strdup(f);
396
397 assert_se(e = strrchr(f, '.'));
398 a = p - f + 1;
399
400 r = new(char, a + strlen(e) + 1);
401 if (!r)
402 return NULL;
403
404 strcpy(mempcpy(r, f, a), e);
405 return r;
406
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 }