1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2012 Lennart Poettering
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7 Copyright 2014 Ronny Chevalier
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.
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.
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/>.
28 #include "alloc-util.h"
29 #include "glob-util.h"
30 #include "hostname-util.h"
33 #include "path-util.h"
35 #include "specifier.h"
36 #include "string-util.h"
37 #include "test-helper.h"
39 #include "unit-name.h"
40 #include "unit-printf.h"
42 #include "user-util.h"
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
));
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
));
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
));
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
));
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
);
75 assert_se(streq_ptr(t
, expected
));
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
);
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
;
93 assert_se(unit_name_from_path(path
, suffix
, &t
) == ret
);
95 assert_se(streq_ptr(t
, expected
));
98 _cleanup_free_
char *k
= NULL
;
99 assert_se(unit_name_to_path(t
, &k
) == 0);
101 assert_se(path_equal(k
, isempty(path
) ? "/" : path
));
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
);
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
;
120 assert_se(unit_name_from_path_instance(pattern
, path
, suffix
, &t
) == ret
);
122 assert_se(streq_ptr(t
, expected
));
125 _cleanup_free_
char *k
= NULL
, *v
= NULL
;
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
));
133 static void test_unit_name_from_path_instance(void) {
134 puts("-------------------------------------------------");
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);
146 static void test_unit_name_to_path_one(const char *unit
, const char *path
, int ret
) {
147 _cleanup_free_
char *p
= NULL
;
149 assert_se(unit_name_to_path(unit
, &p
) == ret
);
150 assert_se(streq_ptr(path
, p
));
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
);
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
;
167 assert_se(unit_name_mangle(pattern
, allow_globs
, &t
) == ret
);
169 assert_se(streq_ptr(t
, expect
));
172 _cleanup_free_
char *k
= NULL
;
174 assert_se(unit_name_is_valid(t
, UNIT_NAME_ANY
) ||
175 (allow_globs
== UNIT_NAME_GLOB
&& string_is_glob(t
)));
177 assert_se(unit_name_mangle(t
, allow_globs
, &k
) == 0);
178 assert_se(streq_ptr(t
, k
));
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
);
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);
200 static int test_unit_printf(void) {
205 _cleanup_free_
char *mid
= NULL
, *bid
= NULL
, *host
= NULL
, *uid
= NULL
, *user
= NULL
, *shell
= NULL
, *home
= NULL
;
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
= uid_to_name(getuid()));
211 assert_se(asprintf(&uid
, UID_FMT
, getuid()));
212 assert_se(get_home_dir(&home
) >= 0);
213 assert_se(get_shell(&shell
) >= 0);
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
;
222 #define expect(unit, pattern, expected) \
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)); \
231 assert_se(streq(t, expected)); \
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);
239 expect(u
, "%%", "%");
240 expect(u
, "%%s", "%s");
241 expect(u
, "%,", "%,");
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");
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/*");
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);
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/*");
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"));
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"));
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"));
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"));
312 static void test_unit_name_change_suffix(void) {
315 assert_se(unit_name_change_suffix("foo.mount", ".service", &t
) == 0);
316 assert_se(streq(t
, "foo.service"));
319 assert_se(unit_name_change_suffix("foo@stuff.service", ".socket", &t
) == 0);
320 assert_se(streq(t
, "foo@stuff.socket"));
324 static void test_unit_name_build(void) {
327 assert_se(unit_name_build("foo", "bar", ".service", &t
) == 0);
328 assert_se(streq(t
, "foo@bar.service"));
331 assert_se(unit_name_build("fo0-stUff_b", "bar", ".mount", &t
) == 0);
332 assert_se(streq(t
, "fo0-stUff_b@bar.mount"));
335 assert_se(unit_name_build("foo", NULL
, ".service", &t
) == 0);
336 assert_se(streq(t
, "foo.service"));
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"));
355 static void test_build_subslice(void) {
359 assert_se(slice_build_subslice("-.slice", "foo", &a
) >= 0);
360 assert_se(slice_build_subslice(a
, "bar", &b
) >= 0);
362 assert_se(slice_build_subslice(b
, "barfoo", &a
) >= 0);
364 assert_se(slice_build_subslice(a
, "foobar", &b
) >= 0);
366 assert_se(streq(b
, "foo-bar-barfoo-foobar.slice"));
369 assert_se(slice_build_subslice("foo.service", "bar", &a
) < 0);
370 assert_se(slice_build_subslice("foo", "bar", &a
) < 0);
373 static void test_build_parent_slice_one(const char *name
, const char *expect
, int ret
) {
374 _cleanup_free_
char *s
= NULL
;
376 assert_se(slice_build_parent_slice(name
, &s
) == ret
);
377 assert_se(streq_ptr(s
, expect
));
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
);
392 static void test_unit_name_to_instance(void) {
396 r
= unit_name_to_instance("foo@bar.service", &instance
);
398 assert_se(streq(instance
, "bar"));
401 r
= unit_name_to_instance("foo@.service", &instance
);
403 assert_se(streq(instance
, ""));
406 r
= unit_name_to_instance("fo0-stUff_b@b.service", &instance
);
408 assert_se(streq(instance
, "b"));
411 r
= unit_name_to_instance("foo.service", &instance
);
413 assert_se(!instance
);
415 r
= unit_name_to_instance("fooj@unk", &instance
);
418 r
= unit_name_to_instance("foo@", &instance
);
422 static void test_unit_name_escape(void) {
423 _cleanup_free_
char *r
;
425 r
= unit_name_escape("ab+-c.a/bc@foo.service");
427 assert_se(streq(r
, "ab\\x2b\\x2dc.a-bc\\x40foo.service"));
431 static void test_u_n_t_one(const char *name
, const char *expected
, int ret
) {
432 _cleanup_free_
char *f
= NULL
;
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
));
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
);
444 static void test_unit_name_path_unescape_one(const char *name
, const char *path
, int ret
) {
445 _cleanup_free_
char *p
= NULL
;
447 assert_se(unit_name_path_unescape(name
, &p
) == ret
);
448 assert_se(streq_ptr(path
, p
));
451 static void test_unit_name_path_unescape(void) {
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
);
467 int main(int argc
, char* argv
[]) {
468 _cleanup_(rm_rf_physical_and_freep
) char *runtime_dir
= NULL
;
471 log_parse_environment();
474 r
= enter_cgroup_subroot();
475 if (r
== -ENOMEDIUM
) {
476 log_notice_errno(r
, "Skipping test: cgroupfs not available");
477 return EXIT_TEST_SKIP
;
480 assert_se(runtime_dir
= setup_fake_runtime_dir());
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();