]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-unit-name.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / test / test-unit-name.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2012 Lennart Poettering
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7 Copyright 2014 Ronny Chevalier
8
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <pwd.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "alloc-util.h"
29 #include "glob-util.h"
30 #include "hostname-util.h"
31 #include "macro.h"
32 #include "manager.h"
33 #include "path-util.h"
34 #include "rm-rf.h"
35 #include "specifier.h"
36 #include "string-util.h"
37 #include "test-helper.h"
38 #include "tests.h"
39 #include "unit-name.h"
40 #include "unit-printf.h"
41 #include "unit.h"
42 #include "user-util.h"
43 #include "util.h"
44
45 static void test_unit_name_is_valid(void) {
46 assert_se(unit_name_is_valid("foo.service", UNIT_NAME_ANY));
47 assert_se(unit_name_is_valid("foo.service", UNIT_NAME_PLAIN));
48 assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_INSTANCE));
49 assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_TEMPLATE));
50 assert_se(!unit_name_is_valid("foo.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
51
52 assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_ANY));
53 assert_se(!unit_name_is_valid("foo@bar.service", UNIT_NAME_PLAIN));
54 assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_INSTANCE));
55 assert_se(!unit_name_is_valid("foo@bar.service", UNIT_NAME_TEMPLATE));
56 assert_se(unit_name_is_valid("foo@bar.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
57
58 assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_ANY));
59 assert_se(!unit_name_is_valid("foo@.service", UNIT_NAME_PLAIN));
60 assert_se(!unit_name_is_valid("foo@.service", UNIT_NAME_INSTANCE));
61 assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_TEMPLATE));
62 assert_se(unit_name_is_valid("foo@.service", UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE));
63
64 assert_se(!unit_name_is_valid(".service", UNIT_NAME_ANY));
65 assert_se(!unit_name_is_valid("", UNIT_NAME_ANY));
66 assert_se(!unit_name_is_valid("foo.waldo", UNIT_NAME_ANY));
67 assert_se(!unit_name_is_valid("@.service", UNIT_NAME_ANY));
68 assert_se(!unit_name_is_valid("@piep.service", UNIT_NAME_ANY));
69 }
70
71 static void test_unit_name_replace_instance_one(const char *pattern, const char *repl, const char *expected, int ret) {
72 _cleanup_free_ char *t = NULL;
73 assert_se(unit_name_replace_instance(pattern, repl, &t) == ret);
74 puts(strna(t));
75 assert_se(streq_ptr(t, expected));
76 }
77
78 static void test_unit_name_replace_instance(void) {
79 puts("-------------------------------------------------");
80 test_unit_name_replace_instance_one("foo@.service", "waldo", "foo@waldo.service", 0);
81 test_unit_name_replace_instance_one("foo@xyz.service", "waldo", "foo@waldo.service", 0);
82 test_unit_name_replace_instance_one("xyz", "waldo", NULL, -EINVAL);
83 test_unit_name_replace_instance_one("", "waldo", NULL, -EINVAL);
84 test_unit_name_replace_instance_one("foo.service", "waldo", NULL, -EINVAL);
85 test_unit_name_replace_instance_one(".service", "waldo", NULL, -EINVAL);
86 test_unit_name_replace_instance_one("foo@", "waldo", NULL, -EINVAL);
87 test_unit_name_replace_instance_one("@bar", "waldo", NULL, -EINVAL);
88 }
89
90 static void test_unit_name_from_path_one(const char *path, const char *suffix, const char *expected, int ret) {
91 _cleanup_free_ char *t = NULL;
92
93 assert_se(unit_name_from_path(path, suffix, &t) == ret);
94 puts(strna(t));
95 assert_se(streq_ptr(t, expected));
96
97 if (t) {
98 _cleanup_free_ char *k = NULL;
99 assert_se(unit_name_to_path(t, &k) == 0);
100 puts(strna(k));
101 assert_se(path_equal(k, isempty(path) ? "/" : path));
102 }
103 }
104
105 static void test_unit_name_from_path(void) {
106 puts("-------------------------------------------------");
107 test_unit_name_from_path_one("/waldo", ".mount", "waldo.mount", 0);
108 test_unit_name_from_path_one("/waldo/quuix", ".mount", "waldo-quuix.mount", 0);
109 test_unit_name_from_path_one("/waldo/quuix/", ".mount", "waldo-quuix.mount", 0);
110 test_unit_name_from_path_one("", ".mount", "-.mount", 0);
111 test_unit_name_from_path_one("/", ".mount", "-.mount", 0);
112 test_unit_name_from_path_one("///", ".mount", "-.mount", 0);
113 test_unit_name_from_path_one("/foo/../bar", ".mount", NULL, -EINVAL);
114 test_unit_name_from_path_one("/foo/./bar", ".mount", NULL, -EINVAL);
115 }
116
117 static void test_unit_name_from_path_instance_one(const char *pattern, const char *path, const char *suffix, const char *expected, int ret) {
118 _cleanup_free_ char *t = NULL;
119
120 assert_se(unit_name_from_path_instance(pattern, path, suffix, &t) == ret);
121 puts(strna(t));
122 assert_se(streq_ptr(t, expected));
123
124 if (t) {
125 _cleanup_free_ char *k = NULL, *v = NULL;
126
127 assert_se(unit_name_to_instance(t, &k) > 0);
128 assert_se(unit_name_path_unescape(k, &v) == 0);
129 assert_se(path_equal(v, isempty(path) ? "/" : path));
130 }
131 }
132
133 static void test_unit_name_from_path_instance(void) {
134 puts("-------------------------------------------------");
135
136 test_unit_name_from_path_instance_one("waldo", "/waldo", ".mount", "waldo@waldo.mount", 0);
137 test_unit_name_from_path_instance_one("waldo", "/waldo////quuix////", ".mount", "waldo@waldo-quuix.mount", 0);
138 test_unit_name_from_path_instance_one("waldo", "/", ".mount", "waldo@-.mount", 0);
139 test_unit_name_from_path_instance_one("waldo", "", ".mount", "waldo@-.mount", 0);
140 test_unit_name_from_path_instance_one("waldo", "///", ".mount", "waldo@-.mount", 0);
141 test_unit_name_from_path_instance_one("waldo", "..", ".mount", NULL, -EINVAL);
142 test_unit_name_from_path_instance_one("waldo", "/foo", ".waldi", NULL, -EINVAL);
143 test_unit_name_from_path_instance_one("wa--ldo", "/--", ".mount", "wa--ldo@\\x2d\\x2d.mount", 0);
144 }
145
146 static void test_unit_name_to_path_one(const char *unit, const char *path, int ret) {
147 _cleanup_free_ char *p = NULL;
148
149 assert_se(unit_name_to_path(unit, &p) == ret);
150 assert_se(streq_ptr(path, p));
151 }
152
153 static void test_unit_name_to_path(void) {
154 test_unit_name_to_path_one("home.mount", "/home", 0);
155 test_unit_name_to_path_one("home-lennart.mount", "/home/lennart", 0);
156 test_unit_name_to_path_one("home-lennart-.mount", NULL, -EINVAL);
157 test_unit_name_to_path_one("-home-lennart.mount", NULL, -EINVAL);
158 test_unit_name_to_path_one("-home--lennart.mount", NULL, -EINVAL);
159 test_unit_name_to_path_one("home-..-lennart.mount", NULL, -EINVAL);
160 test_unit_name_to_path_one("", NULL, -EINVAL);
161 test_unit_name_to_path_one("home/foo", NULL, -EINVAL);
162 }
163
164 static void test_unit_name_mangle_one(UnitNameMangle allow_globs, const char *pattern, const char *expect, int ret) {
165 _cleanup_free_ char *t = NULL;
166
167 assert_se(unit_name_mangle(pattern, allow_globs, &t) == ret);
168 puts(strna(t));
169 assert_se(streq_ptr(t, expect));
170
171 if (t) {
172 _cleanup_free_ char *k = NULL;
173
174 assert_se(unit_name_is_valid(t, UNIT_NAME_ANY) ||
175 (allow_globs == UNIT_NAME_GLOB && string_is_glob(t)));
176
177 assert_se(unit_name_mangle(t, allow_globs, &k) == 0);
178 assert_se(streq_ptr(t, k));
179 }
180 }
181
182 static void test_unit_name_mangle(void) {
183 puts("-------------------------------------------------");
184 test_unit_name_mangle_one(UNIT_NAME_NOGLOB, "foo.service", "foo.service", 0);
185 test_unit_name_mangle_one(UNIT_NAME_NOGLOB, "/home", "home.mount", 1);
186 test_unit_name_mangle_one(UNIT_NAME_NOGLOB, "/dev/sda", "dev-sda.device", 1);
187 test_unit_name_mangle_one(UNIT_NAME_NOGLOB, "üxknürz.service", "\\xc3\\xbcxkn\\xc3\\xbcrz.service", 1);
188 test_unit_name_mangle_one(UNIT_NAME_NOGLOB, "foobar-meh...waldi.service", "foobar-meh...waldi.service", 0);
189 test_unit_name_mangle_one(UNIT_NAME_NOGLOB, "_____####----.....service", "_____\\x23\\x23\\x23\\x23----.....service", 1);
190 test_unit_name_mangle_one(UNIT_NAME_NOGLOB, "_____##@;;;,,,##----.....service", "_____\\x23\\x23@\\x3b\\x3b\\x3b\\x2c\\x2c\\x2c\\x23\\x23----.....service", 1);
191 test_unit_name_mangle_one(UNIT_NAME_NOGLOB, "xxx@@@@/////\\\\\\\\\\yyy.service", "xxx@@@@-----\\\\\\\\\\yyy.service", 1);
192 test_unit_name_mangle_one(UNIT_NAME_NOGLOB, "", NULL, -EINVAL);
193
194 test_unit_name_mangle_one(UNIT_NAME_GLOB, "foo.service", "foo.service", 0);
195 test_unit_name_mangle_one(UNIT_NAME_GLOB, "foo", "foo.service", 1);
196 test_unit_name_mangle_one(UNIT_NAME_GLOB, "foo*", "foo*", 0);
197 test_unit_name_mangle_one(UNIT_NAME_GLOB, "ü*", "\\xc3\\xbc*", 1);
198 }
199
200 static int test_unit_printf(void) {
201 Manager *m = NULL;
202 Unit *u, *u2;
203 int r;
204
205 _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *uid = NULL, *user = NULL, *shell = NULL, *home = NULL;
206
207 assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
208 assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
209 assert_se(host = gethostname_malloc());
210 assert_se(user = getusername_malloc());
211 assert_se(asprintf(&uid, UID_FMT, getuid()));
212 assert_se(get_home_dir(&home) >= 0);
213 assert_se(get_shell(&shell) >= 0);
214
215 r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
216 if (MANAGER_SKIP_TEST(r)) {
217 log_notice_errno(r, "Skipping test: manager_new: %m");
218 return EXIT_TEST_SKIP;
219 }
220 assert_se(r == 0);
221
222 #define expect(unit, pattern, expected) \
223 { \
224 char *e; \
225 _cleanup_free_ char *t = NULL; \
226 assert_se(unit_full_printf(unit, pattern, &t) >= 0); \
227 printf("result: %s\nexpect: %s\n", t, expected); \
228 if ((e = endswith(expected, "*"))) \
229 assert_se(strncmp(t, e, e-expected)); \
230 else \
231 assert_se(streq(t, expected)); \
232 }
233
234 assert_se(u = unit_new(m, sizeof(Service)));
235 assert_se(unit_add_name(u, "blah.service") == 0);
236 assert_se(unit_add_name(u, "blah.service") == 0);
237
238 /* general tests */
239 expect(u, "%%", "%");
240 expect(u, "%%s", "%s");
241 expect(u, "%,", "%,");
242 expect(u, "%", "%");
243
244 /* normal unit */
245 expect(u, "%n", "blah.service");
246 expect(u, "%f", "/blah");
247 expect(u, "%N", "blah");
248 expect(u, "%p", "blah");
249 expect(u, "%P", "blah");
250 expect(u, "%i", "");
251 expect(u, "%u", user);
252 expect(u, "%U", uid);
253 expect(u, "%h", home);
254 expect(u, "%m", mid);
255 expect(u, "%b", bid);
256 expect(u, "%H", host);
257 expect(u, "%t", "/run/user/*");
258
259 /* templated */
260 assert_se(u2 = unit_new(m, sizeof(Service)));
261 assert_se(unit_add_name(u2, "blah@foo-foo.service") == 0);
262 assert_se(unit_add_name(u2, "blah@foo-foo.service") == 0);
263
264 expect(u2, "%n", "blah@foo-foo.service");
265 expect(u2, "%N", "blah@foo-foo");
266 expect(u2, "%f", "/foo/foo");
267 expect(u2, "%p", "blah");
268 expect(u2, "%P", "blah");
269 expect(u2, "%i", "foo-foo");
270 expect(u2, "%I", "foo/foo");
271 expect(u2, "%u", user);
272 expect(u2, "%U", uid);
273 expect(u2, "%h", home);
274 expect(u2, "%m", mid);
275 expect(u2, "%b", bid);
276 expect(u2, "%H", host);
277 expect(u2, "%t", "/run/user/*");
278
279 manager_free(m);
280 #undef expect
281
282 return 0;
283 }
284
285 static void test_unit_instance_is_valid(void) {
286 assert_se(unit_instance_is_valid("fooBar"));
287 assert_se(unit_instance_is_valid("foo-bar"));
288 assert_se(unit_instance_is_valid("foo.stUff"));
289 assert_se(unit_instance_is_valid("fOo123.stuff"));
290 assert_se(unit_instance_is_valid("@f_oo123.Stuff"));
291
292 assert_se(!unit_instance_is_valid("$¢£"));
293 assert_se(!unit_instance_is_valid(""));
294 assert_se(!unit_instance_is_valid("foo bar"));
295 assert_se(!unit_instance_is_valid("foo/bar"));
296 }
297
298 static void test_unit_prefix_is_valid(void) {
299 assert_se(unit_prefix_is_valid("fooBar"));
300 assert_se(unit_prefix_is_valid("foo-bar"));
301 assert_se(unit_prefix_is_valid("foo.stUff"));
302 assert_se(unit_prefix_is_valid("fOo123.stuff"));
303 assert_se(unit_prefix_is_valid("foo123.Stuff"));
304
305 assert_se(!unit_prefix_is_valid("$¢£"));
306 assert_se(!unit_prefix_is_valid(""));
307 assert_se(!unit_prefix_is_valid("foo bar"));
308 assert_se(!unit_prefix_is_valid("foo/bar"));
309 assert_se(!unit_prefix_is_valid("@foo-bar"));
310 }
311
312 static void test_unit_name_change_suffix(void) {
313 char *t;
314
315 assert_se(unit_name_change_suffix("foo.mount", ".service", &t) == 0);
316 assert_se(streq(t, "foo.service"));
317 free(t);
318
319 assert_se(unit_name_change_suffix("foo@stuff.service", ".socket", &t) == 0);
320 assert_se(streq(t, "foo@stuff.socket"));
321 free(t);
322 }
323
324 static void test_unit_name_build(void) {
325 char *t;
326
327 assert_se(unit_name_build("foo", "bar", ".service", &t) == 0);
328 assert_se(streq(t, "foo@bar.service"));
329 free(t);
330
331 assert_se(unit_name_build("fo0-stUff_b", "bar", ".mount", &t) == 0);
332 assert_se(streq(t, "fo0-stUff_b@bar.mount"));
333 free(t);
334
335 assert_se(unit_name_build("foo", NULL, ".service", &t) == 0);
336 assert_se(streq(t, "foo.service"));
337 free(t);
338 }
339
340 static void test_slice_name_is_valid(void) {
341 assert_se(slice_name_is_valid("-.slice"));
342 assert_se(slice_name_is_valid("foo.slice"));
343 assert_se(slice_name_is_valid("foo-bar.slice"));
344 assert_se(slice_name_is_valid("foo-bar-baz.slice"));
345 assert_se(!slice_name_is_valid("-foo-bar-baz.slice"));
346 assert_se(!slice_name_is_valid("foo-bar-baz-.slice"));
347 assert_se(!slice_name_is_valid("-foo-bar-baz-.slice"));
348 assert_se(!slice_name_is_valid("foo-bar--baz.slice"));
349 assert_se(!slice_name_is_valid("foo--bar--baz.slice"));
350 assert_se(!slice_name_is_valid(".slice"));
351 assert_se(!slice_name_is_valid(""));
352 assert_se(!slice_name_is_valid("foo.service"));
353 }
354
355 static void test_build_subslice(void) {
356 char *a;
357 char *b;
358
359 assert_se(slice_build_subslice("-.slice", "foo", &a) >= 0);
360 assert_se(slice_build_subslice(a, "bar", &b) >= 0);
361 free(a);
362 assert_se(slice_build_subslice(b, "barfoo", &a) >= 0);
363 free(b);
364 assert_se(slice_build_subslice(a, "foobar", &b) >= 0);
365 free(a);
366 assert_se(streq(b, "foo-bar-barfoo-foobar.slice"));
367 free(b);
368
369 assert_se(slice_build_subslice("foo.service", "bar", &a) < 0);
370 assert_se(slice_build_subslice("foo", "bar", &a) < 0);
371 }
372
373 static void test_build_parent_slice_one(const char *name, const char *expect, int ret) {
374 _cleanup_free_ char *s = NULL;
375
376 assert_se(slice_build_parent_slice(name, &s) == ret);
377 assert_se(streq_ptr(s, expect));
378 }
379
380 static void test_build_parent_slice(void) {
381 test_build_parent_slice_one("-.slice", NULL, 0);
382 test_build_parent_slice_one("foo.slice", "-.slice", 1);
383 test_build_parent_slice_one("foo-bar.slice", "foo.slice", 1);
384 test_build_parent_slice_one("foo-bar-baz.slice", "foo-bar.slice", 1);
385 test_build_parent_slice_one("foo-bar--baz.slice", NULL, -EINVAL);
386 test_build_parent_slice_one("-foo-bar.slice", NULL, -EINVAL);
387 test_build_parent_slice_one("foo-bar-.slice", NULL, -EINVAL);
388 test_build_parent_slice_one("foo-bar.service", NULL, -EINVAL);
389 test_build_parent_slice_one(".slice", NULL, -EINVAL);
390 }
391
392 static void test_unit_name_to_instance(void) {
393 char *instance;
394 int r;
395
396 r = unit_name_to_instance("foo@bar.service", &instance);
397 assert_se(r >= 0);
398 assert_se(streq(instance, "bar"));
399 free(instance);
400
401 r = unit_name_to_instance("foo@.service", &instance);
402 assert_se(r >= 0);
403 assert_se(streq(instance, ""));
404 free(instance);
405
406 r = unit_name_to_instance("fo0-stUff_b@b.service", &instance);
407 assert_se(r >= 0);
408 assert_se(streq(instance, "b"));
409 free(instance);
410
411 r = unit_name_to_instance("foo.service", &instance);
412 assert_se(r == 0);
413 assert_se(!instance);
414
415 r = unit_name_to_instance("fooj@unk", &instance);
416 assert_se(r < 0);
417
418 r = unit_name_to_instance("foo@", &instance);
419 assert_se(r < 0);
420 }
421
422 static void test_unit_name_escape(void) {
423 _cleanup_free_ char *r;
424
425 r = unit_name_escape("ab+-c.a/bc@foo.service");
426 assert_se(r);
427 assert_se(streq(r, "ab\\x2b\\x2dc.a-bc\\x40foo.service"));
428 }
429
430
431 static void test_u_n_t_one(const char *name, const char *expected, int ret) {
432 _cleanup_free_ char *f = NULL;
433
434 assert_se(unit_name_template(name, &f) == ret);
435 printf("got: %s, expected: %s\n", strna(f), strna(expected));
436 assert_se(streq_ptr(f, expected));
437 }
438
439 static void test_unit_name_template(void) {
440 test_u_n_t_one("foo@bar.service", "foo@.service", 0);
441 test_u_n_t_one("foo.mount", NULL, -EINVAL);
442 }
443
444 static void test_unit_name_path_unescape_one(const char *name, const char *path, int ret) {
445 _cleanup_free_ char *p = NULL;
446
447 assert_se(unit_name_path_unescape(name, &p) == ret);
448 assert_se(streq_ptr(path, p));
449 }
450
451 static void test_unit_name_path_unescape(void) {
452
453 test_unit_name_path_unescape_one("foo", "/foo", 0);
454 test_unit_name_path_unescape_one("foo-bar", "/foo/bar", 0);
455 test_unit_name_path_unescape_one("foo-.bar", "/foo/.bar", 0);
456 test_unit_name_path_unescape_one("foo-bar-baz", "/foo/bar/baz", 0);
457 test_unit_name_path_unescape_one("-", "/", 0);
458 test_unit_name_path_unescape_one("--", NULL, -EINVAL);
459 test_unit_name_path_unescape_one("-foo-bar", NULL, -EINVAL);
460 test_unit_name_path_unescape_one("foo--bar", NULL, -EINVAL);
461 test_unit_name_path_unescape_one("foo-bar-", NULL, -EINVAL);
462 test_unit_name_path_unescape_one(".-bar", NULL, -EINVAL);
463 test_unit_name_path_unescape_one("foo-..", NULL, -EINVAL);
464 test_unit_name_path_unescape_one("", NULL, -EINVAL);
465 }
466
467 int main(int argc, char* argv[]) {
468 _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
469 int r, rc = 0;
470
471 log_parse_environment();
472 log_open();
473
474 r = enter_cgroup_subroot();
475 if (r == -ENOMEDIUM) {
476 log_notice_errno(r, "Skipping test: cgroupfs not available");
477 return EXIT_TEST_SKIP;
478 }
479
480 assert_se(runtime_dir = setup_fake_runtime_dir());
481
482 test_unit_name_is_valid();
483 test_unit_name_replace_instance();
484 test_unit_name_from_path();
485 test_unit_name_from_path_instance();
486 test_unit_name_mangle();
487 test_unit_name_to_path();
488 TEST_REQ_RUNNING_SYSTEMD(rc = test_unit_printf());
489 test_unit_instance_is_valid();
490 test_unit_prefix_is_valid();
491 test_unit_name_change_suffix();
492 test_unit_name_build();
493 test_slice_name_is_valid();
494 test_build_subslice();
495 test_build_parent_slice();
496 test_unit_name_to_instance();
497 test_unit_name_escape();
498 test_unit_name_template();
499 test_unit_name_path_unescape();
500
501 return rc;
502 }