]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/unit-name.c
Describe handling of an AF_UNIX socket
[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",
55 [UNIT_ERROR] = "error",
56 [UNIT_MERGED] = "merged",
57 [UNIT_MASKED] = "masked"
58};
59
60DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
61
5f739699 62bool unit_name_is_valid(const char *n, bool template_ok) {
9e2f7c11
LP
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
71fad675
LP
76 e = strrchr(n, '.');
77 if (!e || e == n)
9e2f7c11
LP
78 return false;
79
5f739699
LP
80 if (unit_type_from_string(e + 1) < 0)
81 return false;
82
9e2f7c11
LP
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
b9c0d441 96 if (!template_ok && at+1 == e)
9e2f7c11
LP
97 return false;
98 }
99
100 return true;
101}
102
103bool 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
122bool 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
136int 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 */
35eb6b12
LP
144 p = strchr(n, '@');
145 if (!p) {
9e2f7c11
LP
146 *instance = NULL;
147 return 0;
148 }
149
150 assert_se(d = strrchr(n, '.'));
151 assert(p < d);
152
35eb6b12
LP
153 i = strndup(p+1, d-p-1);
154 if (!i)
9e2f7c11
LP
155 return -ENOMEM;
156
157 *instance = i;
158 return 0;
159}
160
161char *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
171char *unit_name_to_prefix(const char *n) {
172 const char *p;
173
b0193f1c
LP
174 p = strchr(n, '@');
175 if (p)
9e2f7c11
LP
176 return strndup(n, p - n);
177
178 return unit_name_to_prefix_and_instance(n);
179}
180
181char *unit_name_change_suffix(const char *n, const char *suffix) {
182 char *e, *r;
183 size_t a, b;
184
185 assert(n);
5f739699 186 assert(unit_name_is_valid(n, true));
9e2f7c11 187 assert(suffix);
a016b922 188 assert(suffix[0] == '.');
9e2f7c11
LP
189
190 assert_se(e = strrchr(n, '.'));
191 a = e - n;
192 b = strlen(suffix);
193
b0193f1c
LP
194 r = new(char, a + b + 1);
195 if (!r)
9e2f7c11
LP
196 return NULL;
197
198 memcpy(r, n, a);
199 memcpy(r+a, suffix, b+1);
200
201 return r;
202}
203
204char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
9e2f7c11
LP
205 assert(prefix);
206 assert(unit_prefix_is_valid(prefix));
207 assert(!instance || unit_instance_is_valid(instance));
208 assert(suffix);
9e2f7c11
LP
209
210 if (!instance)
211 return strappend(prefix, suffix);
212
b7def684 213 return strjoin(prefix, "@", instance, suffix, NULL);
9e2f7c11
LP
214}
215
4b712653
KS
216static 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
224static char *do_escape(const char *f, char *t) {
9e2f7c11
LP
225 assert(f);
226 assert(t);
227
4b712653
KS
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
9e2f7c11
LP
234 for (; *f; f++) {
235 if (*f == '/')
8d567588 236 *(t++) = '-';
4b712653
KS
237 else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f))
238 t = do_escape_char(*f, t);
239 else
9e2f7c11
LP
240 *(t++) = *f;
241 }
242
243 return t;
244}
245
9e2f7c11
LP
246char *unit_name_escape(const char *f) {
247 char *r, *t;
248
b0193f1c
LP
249 r = new(char, strlen(f)*4+1);
250 if (!r)
9e2f7c11
LP
251 return NULL;
252
253 t = do_escape(f, r);
254 *t = 0;
255
256 return r;
9e2f7c11
LP
257}
258
259char *unit_name_unescape(const char *f) {
260 char *r, *t;
261
262 assert(f);
263
b0193f1c
LP
264 r = strdup(f);
265 if (!r)
9e2f7c11
LP
266 return NULL;
267
268 for (t = r; *f; f++) {
8d567588 269 if (*f == '-')
9e2f7c11
LP
270 *(t++) = '/';
271 else if (*f == '\\') {
272 int a, b;
273
15e11d81
LP
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 */
9e2f7c11
LP
278 *(t++) = '\\';
279 } else {
280 *(t++) = (char) ((a << 4) | b);
95e501f8 281 f += 3;
9e2f7c11
LP
282 }
283 } else
284 *(t++) = *f;
285 }
286
287 *t = 0;
288
289 return r;
290}
291
35eb6b12
LP
292char *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
314char *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
9e2f7c11
LP
335bool unit_name_is_template(const char *n) {
336 const char *p;
337
338 assert(n);
339
a7b9ecf9
MS
340 p = strchr(n, '@');
341 if (!p)
9e2f7c11
LP
342 return false;
343
344 return p[1] == '.';
345}
346
29283ea4
MS
347bool 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
9e2f7c11
LP
359char *unit_name_replace_instance(const char *f, const char *i) {
360 const char *p, *e;
361 char *r, *k;
8556879e 362 size_t a, b;
9e2f7c11
LP
363
364 assert(f);
365
366 p = strchr(f, '@');
8556879e
LP
367 if (!p)
368 return strdup(f);
9e2f7c11 369
8556879e
LP
370 e = strrchr(f, '.');
371 if (!e)
372 assert_se(e = strchr(f, 0));
9e2f7c11 373
8556879e
LP
374 a = p - f;
375 b = strlen(i);
9e2f7c11 376
8556879e
LP
377 r = new(char, a + 1 + b + strlen(e) + 1);
378 if (!r)
379 return NULL;
9e2f7c11 380
8556879e
LP
381 k = mempcpy(r, f, a + 1);
382 k = mempcpy(k, i, b);
9e2f7c11 383 strcpy(k, e);
8556879e 384
9e2f7c11
LP
385 return r;
386}
387
388char *unit_name_template(const char *f) {
389 const char *p, *e;
390 char *r;
391 size_t a;
392
b0193f1c
LP
393 p = strchr(f, '@');
394 if (!p)
9e2f7c11
LP
395 return strdup(f);
396
397 assert_se(e = strrchr(f, '.'));
398 a = p - f + 1;
399
b0193f1c
LP
400 r = new(char, a + strlen(e) + 1);
401 if (!r)
9e2f7c11
LP
402 return NULL;
403
404 strcpy(mempcpy(r, f, a), e);
405 return r;
406
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) {
458 char *e, *p;
459
35eb6b12
LP
460 assert(name);
461
48899192
MS
462 e = bus_path_escape(name);
463 if (!e)
464 return NULL;
465
466 p = strappend("/org/freedesktop/systemd1/unit/", e);
467 free(e);
468
469 return p;
470}
b0193f1c
LP
471
472char *unit_name_mangle(const char *name) {
473 char *r, *t;
474 const char *f;
475
476 assert(name);
477
478 /* Try to turn a string that might not be a unit name into a
479 * sensible unit name. */
480
a05f97b3 481 if (is_device_path(name))
b0193f1c
LP
482 return unit_name_from_path(name, ".device");
483
484 if (path_is_absolute(name))
485 return unit_name_from_path(name, ".mount");
486
487 /* We'll only escape the obvious characters here, to play
488 * safe. */
489
56d4fbf9 490 r = new(char, strlen(name) * 4 + 1 + sizeof(".service")-1);
b0193f1c
LP
491 if (!r)
492 return NULL;
493
494 for (f = name, t = r; *f; f++) {
b0193f1c
LP
495 if (*f == '/')
496 *(t++) = '-';
497 else if (!strchr("@" VALID_CHARS, *f))
498 t = do_escape_char(*f, t);
499 else
500 *(t++) = *f;
501 }
502
696c245a 503 if (unit_name_to_type(name) < 0)
56d4fbf9
LP
504 strcpy(t, ".service");
505 else
506 *t = 0;
b0193f1c
LP
507
508 return r;
509}
5f739699 510
a016b922 511char *unit_name_mangle_with_suffix(const char *name, const char *suffix) {
1dcf6065
LP
512 char *r, *t;
513 const char *f;
514
515 assert(name);
a016b922
LP
516 assert(suffix);
517 assert(suffix[0] == '.');
1dcf6065
LP
518
519 /* Similar to unit_name_mangle(), but is called when we know
520 * that this is about snapshot units. */
521
a016b922 522 r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
1dcf6065
LP
523 if (!r)
524 return NULL;
525
526 for (f = name, t = r; *f; f++) {
527 if (*f == '/')
528 *(t++) = '-';
529 else if (!strchr(VALID_CHARS, *f))
530 t = do_escape_char(*f, t);
531 else
532 *(t++) = *f;
533 }
534
a016b922
LP
535 if (!endswith(name, suffix))
536 strcpy(t, suffix);
1dcf6065
LP
537 else
538 *t = 0;
539
540 return r;
541}
542
5f739699
LP
543UnitType unit_name_to_type(const char *n) {
544 const char *e;
545
546 assert(n);
547
548 e = strrchr(n, '.');
549 if (!e)
550 return _UNIT_TYPE_INVALID;
551
552 return unit_type_from_string(e + 1);
553}