]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/unit-name.c
hashmap, set: remove unused functions
[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 "bus-label.h"
28 #include "util.h"
29 #include "unit-name.h"
30 #include "def.h"
31 #include "strv.h"
32
33 #define VALID_CHARS \
34 DIGITS LETTERS \
35 ":-_.\\"
36
37 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
38 [UNIT_SERVICE] = "service",
39 [UNIT_SOCKET] = "socket",
40 [UNIT_BUSNAME] = "busname",
41 [UNIT_TARGET] = "target",
42 [UNIT_SNAPSHOT] = "snapshot",
43 [UNIT_DEVICE] = "device",
44 [UNIT_MOUNT] = "mount",
45 [UNIT_AUTOMOUNT] = "automount",
46 [UNIT_SWAP] = "swap",
47 [UNIT_TIMER] = "timer",
48 [UNIT_PATH] = "path",
49 [UNIT_SLICE] = "slice",
50 [UNIT_SCOPE] = "scope"
51 };
52
53 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
54
55 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
56 [UNIT_STUB] = "stub",
57 [UNIT_LOADED] = "loaded",
58 [UNIT_NOT_FOUND] = "not-found",
59 [UNIT_ERROR] = "error",
60 [UNIT_MERGED] = "merged",
61 [UNIT_MASKED] = "masked"
62 };
63
64 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
65
66 bool unit_name_is_valid(const char *n, enum template_valid template_ok) {
67 const char *e, *i, *at;
68
69 /* Valid formats:
70 *
71 * string@instance.suffix
72 * string.suffix
73 */
74
75 assert(IN_SET(template_ok, TEMPLATE_VALID, TEMPLATE_INVALID));
76
77 if (isempty(n))
78 return false;
79
80 if (strlen(n) >= UNIT_NAME_MAX)
81 return false;
82
83 e = strrchr(n, '.');
84 if (!e || e == n)
85 return false;
86
87 if (unit_type_from_string(e + 1) < 0)
88 return false;
89
90 for (i = n, at = NULL; i < e; i++) {
91
92 if (*i == '@' && !at)
93 at = i;
94
95 if (!strchr("@" VALID_CHARS, *i))
96 return false;
97 }
98
99 if (at) {
100 if (at == n)
101 return false;
102
103 if (!template_ok == TEMPLATE_VALID && at+1 == e)
104 return false;
105 }
106
107 return true;
108 }
109
110 bool unit_instance_is_valid(const char *i) {
111
112 /* The max length depends on the length of the string, so we
113 * don't really check this here. */
114
115 if (isempty(i))
116 return false;
117
118 /* We allow additional @ in the instance string, we do not
119 * allow them in the prefix! */
120
121 return in_charset(i, "@" VALID_CHARS);
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 (isempty(p))
129 return false;
130
131 return in_charset(p, VALID_CHARS);
132 }
133
134 int unit_name_to_instance(const char *n, char **instance) {
135 const char *p, *d;
136 char *i;
137
138 assert(n);
139 assert(instance);
140
141 /* Everything past the first @ and before the last . is the instance */
142 p = strchr(n, '@');
143 if (!p) {
144 *instance = NULL;
145 return 0;
146 }
147
148 d = strrchr(n, '.');
149 if (!d)
150 return -EINVAL;
151 if (d < p)
152 return -EINVAL;
153
154 i = strndup(p+1, d-p-1);
155 if (!i)
156 return -ENOMEM;
157
158 *instance = i;
159 return 1;
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 return strndup(n, d - n);
169 }
170
171 char *unit_name_to_prefix(const char *n) {
172 const char *p;
173
174 assert(n);
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(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 strcpy(mempcpy(r, n, a), suffix);
200 return r;
201 }
202
203 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
204 assert(prefix);
205 assert(suffix);
206
207 if (!instance)
208 return strappend(prefix, suffix);
209
210 return strjoin(prefix, "@", instance, suffix, NULL);
211 }
212
213 static char *do_escape_char(char c, char *t) {
214 assert(t);
215
216 *(t++) = '\\';
217 *(t++) = 'x';
218 *(t++) = hexchar(c >> 4);
219 *(t++) = hexchar(c);
220
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 assert(f);
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 _cleanup_free_ char *p = NULL;
296
297 assert(f);
298
299 p = strdup(f);
300 if (!p)
301 return NULL;
302
303 path_kill_slashes(p);
304
305 if (STR_IN_SET(p, "/", ""))
306 return strdup("-");
307
308 return unit_name_escape(p[0] == '/' ? p + 1 : p);
309 }
310
311 char *unit_name_path_unescape(const char *f) {
312 char *e, *w;
313
314 assert(f);
315
316 e = unit_name_unescape(f);
317 if (!e)
318 return NULL;
319
320 if (e[0] != '/') {
321 w = strappend("/", e);
322 free(e);
323 return w;
324 }
325
326 return e;
327 }
328
329 bool unit_name_is_template(const char *n) {
330 const char *p, *e;
331
332 assert(n);
333
334 p = strchr(n, '@');
335 if (!p)
336 return false;
337
338 e = strrchr(p+1, '.');
339 if (!e)
340 return false;
341
342 return e == p + 1;
343 }
344
345 bool unit_name_is_instance(const char *n) {
346 const char *p, *e;
347
348 assert(n);
349
350 p = strchr(n, '@');
351 if (!p)
352 return false;
353
354 e = strrchr(p+1, '.');
355 if (!e)
356 return false;
357
358 return e > p + 1;
359 }
360
361 char *unit_name_replace_instance(const char *f, const char *i) {
362 const char *p, *e;
363 char *r;
364 size_t a, b;
365
366 assert(f);
367 assert(i);
368
369 p = strchr(f, '@');
370 if (!p)
371 return strdup(f);
372
373 e = strrchr(f, '.');
374 if (!e)
375 e = strchr(f, 0);
376
377 a = p - f;
378 b = strlen(i);
379
380 r = new(char, a + 1 + b + strlen(e) + 1);
381 if (!r)
382 return NULL;
383
384 strcpy(mempcpy(mempcpy(r, f, a + 1), i, b), e);
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 assert(f);
394
395 p = strchr(f, '@');
396 if (!p)
397 return strdup(f);
398
399 e = strrchr(f, '.');
400 if (!e)
401 e = strchr(f, 0);
402
403 a = p - f;
404
405 r = new(char, a + 1 + strlen(e) + 1);
406 if (!r)
407 return NULL;
408
409 strcpy(mempcpy(r, f, a + 1), e);
410 return r;
411 }
412
413 char *unit_name_from_path(const char *path, const char *suffix) {
414 _cleanup_free_ char *p = NULL;
415
416 assert(path);
417 assert(suffix);
418
419 p = unit_name_path_escape(path);
420 if (!p)
421 return NULL;
422
423 return strappend(p, suffix);
424 }
425
426 char *unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix) {
427 _cleanup_free_ char *p = NULL;
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 return strjoin(prefix, "@", p, suffix, NULL);
438 }
439
440 char *unit_name_to_path(const char *name) {
441 _cleanup_free_ char *w = NULL;
442
443 assert(name);
444
445 w = unit_name_to_prefix(name);
446 if (!w)
447 return NULL;
448
449 return unit_name_path_unescape(w);
450 }
451
452 char *unit_dbus_path_from_name(const char *name) {
453 _cleanup_free_ char *e = NULL;
454
455 assert(name);
456
457 e = bus_label_escape(name);
458 if (!e)
459 return NULL;
460
461 return strappend("/org/freedesktop/systemd1/unit/", e);
462 }
463
464 int unit_name_from_dbus_path(const char *path, char **name) {
465 const char *e;
466 char *n;
467
468 e = startswith(path, "/org/freedesktop/systemd1/unit/");
469 if (!e)
470 return -EINVAL;
471
472 n = bus_label_unescape(e);
473 if (!n)
474 return -ENOMEM;
475
476 *name = n;
477 return 0;
478 }
479
480 /**
481 * Try to turn a string that might not be a unit name into a
482 * sensible unit name.
483 */
484 char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) {
485 const char *valid_chars, *f;
486 char *r, *t;
487
488 assert(name);
489 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
490
491 if (is_device_path(name))
492 return unit_name_from_path(name, ".device");
493
494 if (path_is_absolute(name))
495 return unit_name_from_path(name, ".mount");
496
497 /* We'll only escape the obvious characters here, to play
498 * safe. */
499
500 valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS;
501
502 r = new(char, strlen(name) * 4 + strlen(".service") + 1);
503 if (!r)
504 return NULL;
505
506 for (f = name, t = r; *f; f++) {
507 if (*f == '/')
508 *(t++) = '-';
509 else if (!strchr(valid_chars, *f))
510 t = do_escape_char(*f, t);
511 else
512 *(t++) = *f;
513 }
514
515 if (unit_name_to_type(name) < 0)
516 strcpy(t, ".service");
517 else
518 *t = 0;
519
520 return r;
521 }
522
523 /**
524 * Similar to unit_name_mangle(), but is called when we know
525 * that this is about a specific unit type.
526 */
527 char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) {
528 char *r, *t;
529 const char *f;
530
531 assert(name);
532 assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB));
533 assert(suffix);
534 assert(suffix[0] == '.');
535
536 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
537 if (!r)
538 return NULL;
539
540 for (f = name, t = r; *f; f++) {
541 if (*f == '/')
542 *(t++) = '-';
543 else if (!strchr(VALID_CHARS, *f))
544 t = do_escape_char(*f, t);
545 else
546 *(t++) = *f;
547 }
548
549 if (!endswith(name, suffix))
550 strcpy(t, suffix);
551 else
552 *t = 0;
553
554 return r;
555 }
556
557 UnitType unit_name_to_type(const char *n) {
558 const char *e;
559
560 assert(n);
561
562 e = strrchr(n, '.');
563 if (!e)
564 return _UNIT_TYPE_INVALID;
565
566 return unit_type_from_string(e + 1);
567 }
568
569 int build_subslice(const char *slice, const char*name, char **subslice) {
570 char *ret;
571
572 assert(slice);
573 assert(name);
574 assert(subslice);
575
576 if (streq(slice, "-.slice"))
577 ret = strappend(name, ".slice");
578 else {
579 char *e;
580
581 e = endswith(slice, ".slice");
582 if (!e)
583 return -EINVAL;
584
585 ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
586 if (!ret)
587 return -ENOMEM;
588
589 stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
590 }
591
592 *subslice = ret;
593 return 0;
594 }