1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
10 #include "all-units.h"
11 #include "alloc-util.h"
12 #include "capability-util.h"
13 #include "conf-parser.h"
16 #include "format-util.h"
19 #include "hostname-util.h"
20 #include "install-printf.h"
22 #include "load-fragment.h"
24 #include "memory-util.h"
25 #include "open-file.h"
26 #include "pcre2-util.h"
28 #include "specifier.h"
29 #include "string-util.h"
32 #include "tmpfile-util.h"
33 #include "user-util.h"
35 /* Nontrivial value serves as a placeholder to check that parsing function (didn't) change it */
36 #define CGROUP_LIMIT_DUMMY 3
38 static char *runtime_dir
= NULL
;
40 STATIC_DESTRUCTOR_REGISTER(runtime_dir
, rm_rf_physical_and_freep
);
42 /* For testing type compatibility. */
43 _unused_ ConfigPerfItemLookup unused_lookup
= load_fragment_gperf_lookup
;
45 TEST_RET(unit_file_get_set
) {
47 _cleanup_hashmap_free_ Hashmap
*h
= NULL
;
50 h
= hashmap_new(&unit_file_list_hash_ops_free
);
53 r
= unit_file_get_list(RUNTIME_SCOPE_SYSTEM
, NULL
, h
, NULL
, NULL
);
54 if (IN_SET(r
, -EPERM
, -EACCES
))
55 return log_tests_skipped_errno(r
, "unit_file_get_list");
57 log_full_errno(r
== 0 ? LOG_INFO
: LOG_ERR
, r
,
58 "unit_file_get_list: %m");
63 printf("%s = %s\n", p
->path
, unit_file_state_to_string(p
->state
));
68 static void check_execcommand(ExecCommand
*c
,
77 log_info("expect: \"%s\" [\"%s\" \"%s\" \"%s\"]",
78 path
, argv0
?: path
, strnull(argv1
), strnull(argv2
));
79 n
= strv_length(c
->argv
);
80 log_info("actual: \"%s\" [\"%s\" \"%s\" \"%s\"]",
81 c
->path
, c
->argv
[0], n
> 0 ? c
->argv
[1] : "(null)", n
> 1 ? c
->argv
[2] : "(null)");
82 ASSERT_STREQ(c
->path
, path
);
83 ASSERT_STREQ(c
->argv
[0], argv0
?: path
);
85 ASSERT_STREQ(c
->argv
[1], argv1
);
87 ASSERT_STREQ(c
->argv
[2], argv2
);
88 assert_se(!!(c
->flags
& EXEC_COMMAND_IGNORE_FAILURE
) == ignore
);
91 TEST(config_parse_exec
) {
92 /* int config_parse_exec(
97 unsigned section_line,
105 ExecCommand
*c
= NULL
, *c1
;
107 _cleanup_(manager_freep
) Manager
*m
= NULL
;
108 _cleanup_(unit_freep
) Unit
*u
= NULL
;
110 r
= manager_new(RUNTIME_SCOPE_USER
, MANAGER_TEST_RUN_MINIMAL
, &m
);
111 if (manager_errno_skip_test(r
)) {
112 log_notice_errno(r
, "Skipping test: manager_new: %m");
117 assert_se(manager_startup(m
, NULL
, NULL
, NULL
) >= 0);
119 assert_se(u
= unit_new(m
, sizeof(Service
)));
121 log_info("/* basic test */");
122 r
= config_parse_exec(NULL
, "fake", 1, "section", 1,
123 "LValue", 0, "/RValue r1",
126 check_execcommand(c
, "/RValue", "/RValue", "r1", NULL
, false);
128 r
= config_parse_exec(NULL
, "fake", 2, "section", 1,
129 "LValue", 0, "/RValue///slashes r1///",
132 log_info("/* test slashes */");
134 c1
= c
->command_next
;
135 check_execcommand(c1
, "/RValue/slashes", "/RValue///slashes", "r1///", NULL
, false);
137 log_info("/* trailing slash */");
138 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
139 "LValue", 0, "/RValue/ argv0 r1",
141 assert_se(r
== -ENOEXEC
);
142 ASSERT_NULL(c1
->command_next
);
144 log_info("/* honour_argv0 */");
145 r
= config_parse_exec(NULL
, "fake", 3, "section", 1,
146 "LValue", 0, "@/RValue///slashes2 ///argv0 r1",
149 c1
= c1
->command_next
;
150 check_execcommand(c1
, "/RValue/slashes2", "///argv0", "r1", NULL
, false);
152 log_info("/* honour_argv0, no args */");
153 r
= config_parse_exec(NULL
, "fake", 3, "section", 1,
154 "LValue", 0, "@/RValue",
156 assert_se(r
== -ENOEXEC
);
157 ASSERT_NULL(c1
->command_next
);
159 log_info("/* no command, whitespace only, reset */");
160 r
= config_parse_exec(NULL
, "fake", 3, "section", 1,
166 log_info("/* ignore && honour_argv0 */");
167 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
168 "LValue", 0, "-@/RValue///slashes3 argv0a r1",
172 check_execcommand(c1
, "/RValue/slashes3", "argv0a", "r1", NULL
, true);
174 log_info("/* ignore && honour_argv0 */");
175 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
176 "LValue", 0, "@-/RValue///slashes4 argv0b r1",
179 c1
= c1
->command_next
;
180 check_execcommand(c1
, "/RValue/slashes4", "argv0b", "r1", NULL
, true);
182 log_info("/* ignore && ignore */");
183 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
184 "LValue", 0, "--/RValue argv0 r1",
187 ASSERT_NULL(c1
->command_next
);
189 log_info("/* ignore && ignore (2) */");
190 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
191 "LValue", 0, "-@-/RValue argv0 r1",
194 ASSERT_NULL(c1
->command_next
);
196 log_info("/* semicolon */");
197 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
199 "-@/RValue argv0 r1 ; "
203 c1
= c1
->command_next
;
204 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
206 c1
= c1
->command_next
;
207 check_execcommand(c1
, "/goo/goo", NULL
, "boo", NULL
, false);
209 log_info("/* two semicolons in a row */");
210 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
212 "-@/RValue argv0 r1 ; ; "
216 c1
= c1
->command_next
;
217 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
218 c1
= c1
->command_next
;
219 check_execcommand(c1
, "/goo/goo", "/goo/goo", "boo", NULL
, false);
221 log_info("/* trailing semicolon */");
222 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
224 "-@/RValue argv0 r1 ; ",
227 c1
= c1
->command_next
;
228 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
230 ASSERT_NULL(c1
->command_next
);
232 log_info("/* trailing semicolon, no whitespace */");
233 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
235 "-@/RValue argv0 r1 ;",
238 c1
= c1
->command_next
;
239 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
241 ASSERT_NULL(c1
->command_next
);
243 log_info("/* trailing semicolon in single quotes */");
244 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
246 "-@/RValue argv0 r1 ';'",
249 c1
= c1
->command_next
;
250 check_execcommand(c1
, "/RValue", "argv0", "r1", ";", true);
252 log_info("/* escaped semicolon */");
253 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
258 c1
= c1
->command_next
;
259 check_execcommand(c1
, "/bin/find", NULL
, ";", NULL
, false);
261 log_info("/* escaped semicolon with following arg */");
262 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
267 c1
= c1
->command_next
;
268 check_execcommand(c1
,
269 "/sbin/find", NULL
, ";", "/x", false);
271 log_info("/* escaped semicolon as part of an expression */");
272 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
277 c1
= c1
->command_next
;
278 check_execcommand(c1
,
279 "/sbin/find", NULL
, "\\;x", NULL
, false);
281 log_info("/* encoded semicolon */");
282 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
287 c1
= c1
->command_next
;
288 check_execcommand(c1
, "/bin/find", NULL
, ";", NULL
, false);
290 log_info("/* quoted semicolon */");
291 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
296 c1
= c1
->command_next
;
297 check_execcommand(c1
, "/bin/find", NULL
, ";", NULL
, false);
299 log_info("/* quoted semicolon with following arg */");
300 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
302 "/sbin/find \";\" /x",
305 c1
= c1
->command_next
;
306 check_execcommand(c1
,
307 "/sbin/find", NULL
, ";", "/x", false);
309 log_info("/* spaces in the filename */");
310 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
312 "\"/PATH WITH SPACES/daemon\" -1 -2",
315 c1
= c1
->command_next
;
316 check_execcommand(c1
,
317 "/PATH WITH SPACES/daemon", NULL
, "-1", "-2", false);
319 log_info("/* spaces in the filename, no args */");
320 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
322 "\"/PATH WITH SPACES/daemon -1 -2\"",
325 c1
= c1
->command_next
;
326 check_execcommand(c1
,
327 "/PATH WITH SPACES/daemon -1 -2", NULL
, NULL
, NULL
, false);
329 log_info("/* spaces in the filename, everything quoted */");
330 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
332 "\"/PATH WITH SPACES/daemon\" \"-1\" '-2'",
335 c1
= c1
->command_next
;
336 check_execcommand(c1
,
337 "/PATH WITH SPACES/daemon", NULL
, "-1", "-2", false);
339 log_info("/* escaped spaces in the filename */");
340 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
342 "\"/PATH\\sWITH\\sSPACES/daemon\" '-1 -2'",
345 c1
= c1
->command_next
;
346 check_execcommand(c1
,
347 "/PATH WITH SPACES/daemon", NULL
, "-1 -2", NULL
, false);
349 log_info("/* escaped spaces in the filename (2) */");
350 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
352 "\"/PATH\\x20WITH\\x20SPACES/daemon\" \"-1 -2\"",
355 c1
= c1
->command_next
;
356 check_execcommand(c1
,
357 "/PATH WITH SPACES/daemon", NULL
, "-1 -2", NULL
, false);
359 for (ccc
= "abfnrtv\\\'\"x"; *ccc
; ccc
++) {
360 /* \\x is an incomplete hexadecimal sequence, invalid because of the slash */
361 char path
[] = "/path\\X";
362 path
[sizeof(path
) - 2] = *ccc
;
364 log_info("/* invalid character: \\%c */", *ccc
);
365 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
368 assert_se(r
== -ENOEXEC
);
369 ASSERT_NULL(c1
->command_next
);
372 log_info("/* valid character: \\s */");
373 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
374 "LValue", 0, "/path\\s",
377 c1
= c1
->command_next
;
378 check_execcommand(c1
, "/path ", NULL
, NULL
, NULL
, false);
380 log_info("/* quoted backslashes */");
381 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
383 "/bin/grep '\\w+\\K'",
386 c1
= c1
->command_next
;
387 check_execcommand(c1
, "/bin/grep", NULL
, "\\w+\\K", NULL
, false);
389 log_info("/* trailing backslash: \\ */");
390 /* backslash is invalid */
391 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
392 "LValue", 0, "/path\\",
394 assert_se(r
== -ENOEXEC
);
395 ASSERT_NULL(c1
->command_next
);
397 log_info("/* missing ending ' */");
398 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
399 "LValue", 0, "/path 'foo",
401 assert_se(r
== -ENOEXEC
);
402 ASSERT_NULL(c1
->command_next
);
404 log_info("/* missing ending ' with trailing backslash */");
405 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
406 "LValue", 0, "/path 'foo\\",
408 assert_se(r
== -ENOEXEC
);
409 ASSERT_NULL(c1
->command_next
);
411 log_info("/* invalid space between modifiers */");
412 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
413 "LValue", 0, "- /path",
416 ASSERT_NULL(c1
->command_next
);
418 log_info("/* only modifiers, no path */");
419 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
423 ASSERT_NULL(c1
->command_next
);
425 log_info("/* long arg */"); /* See issue #22957. */
427 char x
[LONG_LINE_MAX
-100], *y
;
428 y
= mempcpy(x
, "/bin/echo ", STRLEN("/bin/echo "));
429 memset(y
, 'x', sizeof(x
) - STRLEN("/bin/echo ") - 1);
430 x
[sizeof(x
) - 1] = '\0';
432 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
436 c1
= c1
->command_next
;
437 check_execcommand(c1
,
438 "/bin/echo", NULL
, y
, NULL
, false);
440 log_info("/* empty argument, reset */");
441 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
447 exec_command_free_list(c
);
450 TEST(config_parse_log_extra_fields
) {
451 /* int config_parse_log_extra_fields(
453 const char *filename,
456 unsigned section_line,
465 _cleanup_(manager_freep
) Manager
*m
= NULL
;
466 _cleanup_(unit_freep
) Unit
*u
= NULL
;
469 r
= manager_new(RUNTIME_SCOPE_USER
, MANAGER_TEST_RUN_MINIMAL
, &m
);
470 if (manager_errno_skip_test(r
)) {
471 log_notice_errno(r
, "Skipping test: manager_new: %m");
476 assert_se(manager_startup(m
, NULL
, NULL
, NULL
) >= 0);
478 assert_se(u
= unit_new(m
, sizeof(Service
)));
480 log_info("/* %s – basic test */", __func__
);
481 r
= config_parse_log_extra_fields(NULL
, "fake", 1, "section", 1,
482 "LValue", 0, "FOO=BAR \"QOOF=quux ' ' \"",
485 assert_se(c
.n_log_extra_fields
== 2);
486 assert_se(strneq(c
.log_extra_fields
[0].iov_base
, "FOO=BAR", c
.log_extra_fields
[0].iov_len
));
487 assert_se(strneq(c
.log_extra_fields
[1].iov_base
, "QOOF=quux ' ' ", c
.log_extra_fields
[1].iov_len
));
489 log_info("/* %s – add some */", __func__
);
490 r
= config_parse_log_extra_fields(NULL
, "fake", 1, "section", 1,
491 "LValue", 0, "FOO2=BAR2 QOOF2=quux ' '",
494 assert_se(c
.n_log_extra_fields
== 4);
495 assert_se(strneq(c
.log_extra_fields
[0].iov_base
, "FOO=BAR", c
.log_extra_fields
[0].iov_len
));
496 assert_se(strneq(c
.log_extra_fields
[1].iov_base
, "QOOF=quux ' ' ", c
.log_extra_fields
[1].iov_len
));
497 assert_se(strneq(c
.log_extra_fields
[2].iov_base
, "FOO2=BAR2", c
.log_extra_fields
[2].iov_len
));
498 assert_se(strneq(c
.log_extra_fields
[3].iov_base
, "QOOF2=quux", c
.log_extra_fields
[3].iov_len
));
500 exec_context_dump(&c
, stdout
, " --> ");
502 log_info("/* %s – reset */", __func__
);
503 r
= config_parse_log_extra_fields(NULL
, "fake", 1, "section", 1,
507 assert_se(c
.n_log_extra_fields
== 0);
509 exec_context_free_log_extra_fields(&c
);
511 log_info("/* %s – bye */", __func__
);
514 TEST(install_printf
, .sd_booted
= true) {
515 char name
[] = "name.service",
516 path
[] = "/run/systemd/system/name.service";
517 InstallInfo i
= { .name
= name
, .path
= path
, };
518 InstallInfo i2
= { .name
= name
, .path
= path
, };
519 char name3
[] = "name@inst.service",
520 path3
[] = "/run/systemd/system/name.service";
521 InstallInfo i3
= { .name
= name3
, .path
= path3
, };
522 InstallInfo i4
= { .name
= name3
, .path
= path3
, };
524 _cleanup_free_
char *mid
= NULL
, *bid
= NULL
, *host
= NULL
, *gid
= NULL
, *group
= NULL
, *uid
= NULL
, *user
= NULL
;
526 if (sd_id128_get_machine(NULL
) >= 0)
527 assert_se(specifier_machine_id('m', NULL
, NULL
, NULL
, &mid
) >= 0 && mid
);
529 assert_se(specifier_boot_id('b', NULL
, NULL
, NULL
, &bid
) >= 0 && bid
);
530 assert_se(host
= gethostname_malloc());
531 assert_se(group
= gid_to_name(getgid()));
532 assert_se(asprintf(&gid
, UID_FMT
, getgid()) >= 0);
533 assert_se(user
= uid_to_name(getuid()));
534 assert_se(asprintf(&uid
, UID_FMT
, getuid()) >= 0);
536 #define expect(scope, src, pattern, result) \
538 _cleanup_free_ char *t = NULL, \
539 *d1 = ASSERT_PTR(strdup(i.name)), \
540 *d2 = ASSERT_PTR(strdup(i.path)); \
541 int r = install_name_printf(scope, &src, pattern, &t); \
542 assert_se(result ? r >= 0 : r < 0); \
543 memzero(i.name, strlen(i.name)); \
544 memzero(i.path, strlen(i.path)); \
547 ASSERT_STREQ(t, result); \
550 strcpy(i.name, d1); \
551 strcpy(i.path, d2); \
554 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%n", "name.service");
555 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%N", "name");
556 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%p", "name");
557 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%i", "");
558 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%j", "name");
559 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%g", "root");
560 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%G", "0");
561 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%u", "root");
562 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%U", "0");
564 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%m", mid
);
565 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%b", bid
);
566 expect(RUNTIME_SCOPE_SYSTEM
, i
, "%H", host
);
568 expect(RUNTIME_SCOPE_SYSTEM
, i2
, "%g", "root");
569 expect(RUNTIME_SCOPE_SYSTEM
, i2
, "%G", "0");
570 expect(RUNTIME_SCOPE_SYSTEM
, i2
, "%u", "root");
571 expect(RUNTIME_SCOPE_SYSTEM
, i2
, "%U", "0");
573 expect(RUNTIME_SCOPE_USER
, i2
, "%g", group
);
574 expect(RUNTIME_SCOPE_USER
, i2
, "%G", gid
);
575 expect(RUNTIME_SCOPE_USER
, i2
, "%u", user
);
576 expect(RUNTIME_SCOPE_USER
, i2
, "%U", uid
);
578 /* gcc-12.0.1-0.9.fc36.x86_64 insist that streq(…, NULL) is called,
579 * even though the call is inside of a conditional where the pointer is checked. :( */
580 #pragma GCC diagnostic push
581 #pragma GCC diagnostic ignored "-Wnonnull"
582 expect(RUNTIME_SCOPE_GLOBAL
, i2
, "%g", NULL
);
583 expect(RUNTIME_SCOPE_GLOBAL
, i2
, "%G", NULL
);
584 expect(RUNTIME_SCOPE_GLOBAL
, i2
, "%u", NULL
);
585 expect(RUNTIME_SCOPE_GLOBAL
, i2
, "%U", NULL
);
586 #pragma GCC diagnostic pop
588 expect(RUNTIME_SCOPE_SYSTEM
, i3
, "%n", "name@inst.service");
589 expect(RUNTIME_SCOPE_SYSTEM
, i3
, "%N", "name@inst");
590 expect(RUNTIME_SCOPE_SYSTEM
, i3
, "%p", "name");
591 expect(RUNTIME_SCOPE_USER
, i3
, "%g", group
);
592 expect(RUNTIME_SCOPE_USER
, i3
, "%G", gid
);
593 expect(RUNTIME_SCOPE_USER
, i3
, "%u", user
);
594 expect(RUNTIME_SCOPE_USER
, i3
, "%U", uid
);
596 expect(RUNTIME_SCOPE_SYSTEM
, i3
, "%m", mid
);
597 expect(RUNTIME_SCOPE_SYSTEM
, i3
, "%b", bid
);
598 expect(RUNTIME_SCOPE_SYSTEM
, i3
, "%H", host
);
600 expect(RUNTIME_SCOPE_USER
, i4
, "%g", group
);
601 expect(RUNTIME_SCOPE_USER
, i4
, "%G", gid
);
602 expect(RUNTIME_SCOPE_USER
, i4
, "%u", user
);
603 expect(RUNTIME_SCOPE_USER
, i4
, "%U", uid
);
606 static uint64_t make_cap(int cap
) {
607 return ((uint64_t) 1ULL << (uint64_t) cap
);
610 TEST(config_parse_capability_set
) {
611 /* int config_parse_capability_set(
613 const char *filename,
616 unsigned section_line,
623 uint64_t capability_bounding_set
= 0;
625 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
626 "CapabilityBoundingSet", 0, "CAP_NET_RAW",
627 &capability_bounding_set
, NULL
);
629 assert_se(capability_bounding_set
== make_cap(CAP_NET_RAW
));
631 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
632 "CapabilityBoundingSet", 0, "CAP_NET_ADMIN",
633 &capability_bounding_set
, NULL
);
635 assert_se(capability_bounding_set
== (make_cap(CAP_NET_RAW
) | make_cap(CAP_NET_ADMIN
)));
637 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
638 "CapabilityBoundingSet", 0, "~CAP_NET_ADMIN",
639 &capability_bounding_set
, NULL
);
641 assert_se(capability_bounding_set
== make_cap(CAP_NET_RAW
));
643 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
644 "CapabilityBoundingSet", 0, "",
645 &capability_bounding_set
, NULL
);
647 assert_se(capability_bounding_set
== UINT64_C(0));
649 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
650 "CapabilityBoundingSet", 0, "~",
651 &capability_bounding_set
, NULL
);
653 assert_se(cap_test_all(capability_bounding_set
));
655 capability_bounding_set
= 0;
656 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
657 "CapabilityBoundingSet", 0, " 'CAP_NET_RAW' WAT_CAP??? CAP_NET_ADMIN CAP'_trailing_garbage",
658 &capability_bounding_set
, NULL
);
660 assert_se(capability_bounding_set
== (make_cap(CAP_NET_RAW
) | make_cap(CAP_NET_ADMIN
)));
663 TEST(config_parse_rlimit
) {
664 struct rlimit
* rl
[_RLIMIT_MAX
] = {};
666 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "55", rl
, NULL
) >= 0);
667 assert_se(rl
[RLIMIT_NOFILE
]);
668 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 55);
669 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== rl
[RLIMIT_NOFILE
]->rlim_max
);
671 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "55:66", rl
, NULL
) >= 0);
672 assert_se(rl
[RLIMIT_NOFILE
]);
673 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 55);
674 assert_se(rl
[RLIMIT_NOFILE
]->rlim_max
== 66);
676 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "infinity", rl
, NULL
) >= 0);
677 assert_se(rl
[RLIMIT_NOFILE
]);
678 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== RLIM_INFINITY
);
679 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== rl
[RLIMIT_NOFILE
]->rlim_max
);
681 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "infinity:infinity", rl
, NULL
) >= 0);
682 assert_se(rl
[RLIMIT_NOFILE
]);
683 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== RLIM_INFINITY
);
684 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== rl
[RLIMIT_NOFILE
]->rlim_max
);
686 rl
[RLIMIT_NOFILE
]->rlim_cur
= 10;
687 rl
[RLIMIT_NOFILE
]->rlim_max
= 20;
689 /* Invalid values don't change rl */
690 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "10:20:30", rl
, NULL
) >= 0);
691 assert_se(rl
[RLIMIT_NOFILE
]);
692 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 10);
693 assert_se(rl
[RLIMIT_NOFILE
]->rlim_max
== 20);
695 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "wat:wat", rl
, NULL
) >= 0);
696 assert_se(rl
[RLIMIT_NOFILE
]);
697 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 10);
698 assert_se(rl
[RLIMIT_NOFILE
]->rlim_max
== 20);
700 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "66:wat", rl
, NULL
) >= 0);
701 assert_se(rl
[RLIMIT_NOFILE
]);
702 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 10);
703 assert_se(rl
[RLIMIT_NOFILE
]->rlim_max
== 20);
705 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "200:100", rl
, NULL
) >= 0);
706 assert_se(rl
[RLIMIT_NOFILE
]);
707 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 10);
708 assert_se(rl
[RLIMIT_NOFILE
]->rlim_max
== 20);
710 rl
[RLIMIT_NOFILE
] = mfree(rl
[RLIMIT_NOFILE
]);
712 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU
, "56", rl
, NULL
) >= 0);
713 assert_se(rl
[RLIMIT_CPU
]);
714 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== 56);
715 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== rl
[RLIMIT_CPU
]->rlim_max
);
717 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU
, "57s", rl
, NULL
) >= 0);
718 assert_se(rl
[RLIMIT_CPU
]);
719 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== 57);
720 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== rl
[RLIMIT_CPU
]->rlim_max
);
722 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU
, "40s:1m", rl
, NULL
) >= 0);
723 assert_se(rl
[RLIMIT_CPU
]);
724 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== 40);
725 assert_se(rl
[RLIMIT_CPU
]->rlim_max
== 60);
727 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU
, "infinity", rl
, NULL
) >= 0);
728 assert_se(rl
[RLIMIT_CPU
]);
729 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== RLIM_INFINITY
);
730 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== rl
[RLIMIT_CPU
]->rlim_max
);
732 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU
, "1234ms", rl
, NULL
) >= 0);
733 assert_se(rl
[RLIMIT_CPU
]);
734 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== 2);
735 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== rl
[RLIMIT_CPU
]->rlim_max
);
737 rl
[RLIMIT_CPU
] = mfree(rl
[RLIMIT_CPU
]);
739 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "58", rl
, NULL
) >= 0);
740 assert_se(rl
[RLIMIT_RTTIME
]);
741 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== 58);
742 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== rl
[RLIMIT_RTTIME
]->rlim_max
);
744 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "58:60", rl
, NULL
) >= 0);
745 assert_se(rl
[RLIMIT_RTTIME
]);
746 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== 58);
747 assert_se(rl
[RLIMIT_RTTIME
]->rlim_max
== 60);
749 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "59s", rl
, NULL
) >= 0);
750 assert_se(rl
[RLIMIT_RTTIME
]);
751 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== 59 * USEC_PER_SEC
);
752 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== rl
[RLIMIT_RTTIME
]->rlim_max
);
754 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "59s:123s", rl
, NULL
) >= 0);
755 assert_se(rl
[RLIMIT_RTTIME
]);
756 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== 59 * USEC_PER_SEC
);
757 assert_se(rl
[RLIMIT_RTTIME
]->rlim_max
== 123 * USEC_PER_SEC
);
759 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "infinity", rl
, NULL
) >= 0);
760 assert_se(rl
[RLIMIT_RTTIME
]);
761 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== RLIM_INFINITY
);
762 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== rl
[RLIMIT_RTTIME
]->rlim_max
);
764 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "infinity:infinity", rl
, NULL
) >= 0);
765 assert_se(rl
[RLIMIT_RTTIME
]);
766 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== RLIM_INFINITY
);
767 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== rl
[RLIMIT_RTTIME
]->rlim_max
);
769 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "2345ms", rl
, NULL
) >= 0);
770 assert_se(rl
[RLIMIT_RTTIME
]);
771 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== 2345 * USEC_PER_MSEC
);
772 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== rl
[RLIMIT_RTTIME
]->rlim_max
);
774 rl
[RLIMIT_RTTIME
] = mfree(rl
[RLIMIT_RTTIME
]);
777 TEST(config_parse_pass_environ
) {
778 /* int config_parse_pass_environ(
780 const char *filename,
783 unsigned section_line,
790 _cleanup_strv_free_
char **passenv
= NULL
;
792 r
= config_parse_pass_environ(NULL
, "fake", 1, "section", 1,
793 "PassEnvironment", 0, "A B",
796 assert_se(strv_length(passenv
) == 2);
797 ASSERT_STREQ(passenv
[0], "A");
798 ASSERT_STREQ(passenv
[1], "B");
800 r
= config_parse_pass_environ(NULL
, "fake", 1, "section", 1,
801 "PassEnvironment", 0, "",
804 assert_se(strv_isempty(passenv
));
806 r
= config_parse_pass_environ(NULL
, "fake", 1, "section", 1,
807 "PassEnvironment", 0, "'invalid name' 'normal_name' A=1 'special_name$$' \\",
810 assert_se(strv_length(passenv
) == 1);
811 ASSERT_STREQ(passenv
[0], "normal_name");
814 TEST(config_parse_unit_env_file
) {
815 /* int config_parse_unit_env_file(
817 const char *filename,
820 unsigned section_line,
827 _cleanup_(manager_freep
) Manager
*m
= NULL
;
829 _cleanup_strv_free_
char **files
= NULL
;
832 r
= manager_new(RUNTIME_SCOPE_USER
, MANAGER_TEST_RUN_MINIMAL
, &m
);
833 if (manager_errno_skip_test(r
)) {
834 log_notice_errno(r
, "Skipping test: manager_new: %m");
839 assert_se(manager_startup(m
, NULL
, NULL
, NULL
) >= 0);
841 assert_se(u
= unit_new(m
, sizeof(Service
)));
842 assert_se(unit_add_name(u
, "foobar.service") == 0);
844 r
= config_parse_unit_env_file(u
->id
, "fake", 1, "section", 1,
845 "EnvironmentFile", 0, "not-absolute",
848 assert_se(strv_isempty(files
));
850 r
= config_parse_unit_env_file(u
->id
, "fake", 1, "section", 1,
851 "EnvironmentFile", 0, "/absolute1",
854 assert_se(strv_length(files
) == 1);
856 r
= config_parse_unit_env_file(u
->id
, "fake", 1, "section", 1,
857 "EnvironmentFile", 0, "/absolute2",
860 assert_se(strv_length(files
) == 2);
861 ASSERT_STREQ(files
[0], "/absolute1");
862 ASSERT_STREQ(files
[1], "/absolute2");
864 r
= config_parse_unit_env_file(u
->id
, "fake", 1, "section", 1,
865 "EnvironmentFile", 0, "",
868 assert_se(strv_isempty(files
));
870 r
= config_parse_unit_env_file(u
->id
, "fake", 1, "section", 1,
871 "EnvironmentFile", 0, "/path/%n.conf",
874 assert_se(strv_length(files
) == 1);
875 ASSERT_STREQ(files
[0], "/path/foobar.service.conf");
878 TEST(unit_dump_config_items
) {
879 unit_dump_config_items(stdout
);
882 TEST(config_parse_memory_limit
) {
883 /* int config_parse_memory_limit(
885 const char *filename,
888 unsigned section_line,
901 { "MemoryMin", "", &c
.memory_min
, CGROUP_LIMIT_MIN
},
902 { "MemoryMin", "0", &c
.memory_min
, CGROUP_LIMIT_MIN
},
903 { "MemoryMin", "10", &c
.memory_min
, 10 },
904 { "MemoryMin", "infinity", &c
.memory_min
, CGROUP_LIMIT_MAX
},
905 { "MemoryLow", "", &c
.memory_low
, CGROUP_LIMIT_MIN
},
906 { "MemoryLow", "0", &c
.memory_low
, CGROUP_LIMIT_MIN
},
907 { "MemoryLow", "10", &c
.memory_low
, 10 },
908 { "MemoryLow", "infinity", &c
.memory_low
, CGROUP_LIMIT_MAX
},
909 { "MemoryHigh", "", &c
.memory_high
, CGROUP_LIMIT_MAX
},
910 { "MemoryHigh", "0", &c
.memory_high
, CGROUP_LIMIT_DUMMY
},
911 { "MemoryHigh", "10", &c
.memory_high
, 10 },
912 { "MemoryHigh", "infinity", &c
.memory_high
, CGROUP_LIMIT_MAX
},
913 { "MemoryMax", "", &c
.memory_max
, CGROUP_LIMIT_MAX
},
914 { "MemoryMax", "0", &c
.memory_max
, CGROUP_LIMIT_DUMMY
},
915 { "MemoryMax", "10", &c
.memory_max
, 10 },
916 { "MemoryMax", "infinity", &c
.memory_max
, CGROUP_LIMIT_MAX
},
921 for (i
= 0; i
< ELEMENTSOF(limit_tests
); i
++) {
922 c
.memory_min
= CGROUP_LIMIT_DUMMY
;
923 c
.memory_low
= CGROUP_LIMIT_DUMMY
;
924 c
.memory_high
= CGROUP_LIMIT_DUMMY
;
925 c
.memory_max
= CGROUP_LIMIT_DUMMY
;
926 r
= config_parse_memory_limit(NULL
, "fake", 1, "section", 1,
927 limit_tests
[i
].limit
, 1,
928 limit_tests
[i
].value
, &c
, NULL
);
929 log_info("%s=%s\t%"PRIu64
"==%"PRIu64
,
930 limit_tests
[i
].limit
, limit_tests
[i
].value
,
931 *limit_tests
[i
].result
, limit_tests
[i
].expected
);
933 assert_se(*limit_tests
[i
].result
== limit_tests
[i
].expected
);
938 TEST(contains_instance_specifier_superset
) {
939 assert_se(contains_instance_specifier_superset("foobar@a%i"));
940 assert_se(contains_instance_specifier_superset("foobar@%ia"));
941 assert_se(contains_instance_specifier_superset("foobar@%n"));
942 assert_se(contains_instance_specifier_superset("foobar@%n.service"));
943 assert_se(contains_instance_specifier_superset("foobar@%N"));
944 assert_se(contains_instance_specifier_superset("foobar@%N.service"));
945 assert_se(contains_instance_specifier_superset("foobar@baz.%N.service"));
946 assert_se(contains_instance_specifier_superset("@%N.service"));
947 assert_se(contains_instance_specifier_superset("@%N"));
948 assert_se(contains_instance_specifier_superset("@%a%N"));
950 assert_se(!contains_instance_specifier_superset("foobar@%i.service"));
951 assert_se(!contains_instance_specifier_superset("foobar%ia.service"));
952 assert_se(!contains_instance_specifier_superset("foobar@%%n.service"));
953 assert_se(!contains_instance_specifier_superset("foobar@baz.service"));
954 assert_se(!contains_instance_specifier_superset("%N.service"));
955 assert_se(!contains_instance_specifier_superset("%N"));
956 assert_se(!contains_instance_specifier_superset("@%aN"));
957 assert_se(!contains_instance_specifier_superset("@%a%b"));
960 TEST(unit_is_recursive_template_dependency
) {
961 _cleanup_(manager_freep
) Manager
*m
= NULL
;
965 r
= manager_new(RUNTIME_SCOPE_USER
, MANAGER_TEST_RUN_MINIMAL
, &m
);
966 if (manager_errno_skip_test(r
)) {
967 log_notice_errno(r
, "Skipping test: manager_new: %m");
972 assert_se(manager_startup(m
, NULL
, NULL
, NULL
) >= 0);
974 assert_se(u
= unit_new(m
, sizeof(Service
)));
975 assert_se(unit_add_name(u
, "foobar@1.service") == 0);
976 u
->fragment_path
= strdup("/foobar@.service");
978 assert_se(hashmap_put_strdup(&m
->unit_id_map
, "foobar@foobar@123.service", "/foobar@.service"));
979 assert_se(hashmap_put_strdup(&m
->unit_id_map
, "foobar@foobar@456.service", "/custom.service"));
981 /* Test that %n, %N and any extension of %i specifiers in the instance are detected as recursive. */
982 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@%N.service") == 1);
983 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@%n.service") == 1);
984 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@a%i.service") == 1);
985 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@%ia.service") == 1);
986 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@%x%n.service") == 1);
987 /* Test that %i on its own is not detected as recursive. */
988 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@%i.service") == 0);
989 /* Test that a specifier other than %i, %n and %N is not detected as recursive. */
990 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@%xn.service") == 0);
991 /* Test that an expanded specifier is not detected as recursive. */
992 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@foobar@123.service") == 0);
993 /* Test that a dependency with a custom fragment path is not detected as recursive. */
994 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@456.service", "foobar@%n.service") == 0);
995 /* Test that a dependency without a fragment path is not detected as recursive. */
996 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@789.service", "foobar@%n.service") == 0);
997 /* Test that a dependency with a different prefix is not detected as recursive. */
998 assert_se(unit_is_likely_recursive_template_dependency(u
, "quux@foobar@123.service", "quux@%n.service") == 0);
999 /* Test that a dependency of a different type is not detected as recursive. */
1000 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.mount", "foobar@%n.mount") == 0);
1003 #define TEST_PATTERN(_regex, _allowed_patterns_count, _denied_patterns_count) \
1006 .allowed_patterns_count = _allowed_patterns_count, \
1007 .denied_patterns_count = _denied_patterns_count \
1010 TEST(config_parse_log_filter_patterns
) {
1013 static const struct {
1015 size_t allowed_patterns_count
;
1016 size_t denied_patterns_count
;
1018 TEST_PATTERN("", 0, 0),
1019 TEST_PATTERN(".*", 1, 0),
1020 TEST_PATTERN("~.*", 1, 1),
1021 TEST_PATTERN("", 0, 0),
1022 TEST_PATTERN("~.*", 0, 1),
1023 TEST_PATTERN("[.*", 0, 1), /* Invalid pattern. */
1024 TEST_PATTERN(".*gg.*", 1, 1),
1025 TEST_PATTERN("~.*", 1, 1), /* Already in the patterns list. */
1026 TEST_PATTERN("[.*", 1, 1), /* Invalid pattern. */
1027 TEST_PATTERN("\\x7ehello", 2, 1),
1028 TEST_PATTERN("", 0, 0),
1029 TEST_PATTERN("~foobar", 0, 1),
1032 if (ERRNO_IS_NOT_SUPPORTED(dlopen_pcre2()))
1033 return (void) log_tests_skipped("PCRE2 support is not available");
1035 for (size_t i
= 0; i
< ELEMENTSOF(regex_tests
); i
++) {
1036 assert_se(config_parse_log_filter_patterns(NULL
, "fake", 1, "section", 1, "LogFilterPatterns", 1,
1037 regex_tests
[i
].regex
, &c
, NULL
) >= 0);
1039 assert_se(set_size(c
.log_filter_allowed_patterns
) == regex_tests
[i
].allowed_patterns_count
);
1040 assert_se(set_size(c
.log_filter_denied_patterns
) == regex_tests
[i
].denied_patterns_count
);
1042 /* Ensure `~` is properly removed */
1044 SET_FOREACH(p
, c
.log_filter_allowed_patterns
)
1045 assert_se(p
&& p
[0] != '~');
1046 SET_FOREACH(p
, c
.log_filter_denied_patterns
)
1047 assert_se(p
&& p
[0] != '~');
1050 exec_context_done(&c
);
1053 TEST(config_parse_open_file
) {
1054 _cleanup_(manager_freep
) Manager
*m
= NULL
;
1055 _cleanup_(unit_freep
) Unit
*u
= NULL
;
1056 _cleanup_(open_file_freep
) OpenFile
*of
= NULL
;
1059 r
= manager_new(RUNTIME_SCOPE_USER
, MANAGER_TEST_RUN_MINIMAL
, &m
);
1060 if (manager_errno_skip_test(r
)) {
1061 log_notice_errno(r
, "Skipping test: manager_new: %m");
1066 assert_se(manager_startup(m
, NULL
, NULL
, NULL
) >= 0);
1068 assert_se(u
= unit_new(m
, sizeof(Service
)));
1069 assert_se(unit_add_name(u
, "foobar.service") == 0);
1071 r
= config_parse_open_file(NULL
, "fake", 1, "section", 1,
1072 "OpenFile", 0, "/proc/1/ns/mnt:host-mount-namespace:read-only",
1076 ASSERT_STREQ(of
->path
, "/proc/1/ns/mnt");
1077 ASSERT_STREQ(of
->fdname
, "host-mount-namespace");
1078 assert_se(of
->flags
== OPENFILE_READ_ONLY
);
1080 of
= open_file_free(of
);
1081 r
= config_parse_open_file(NULL
, "fake", 1, "section", 1,
1082 "OpenFile", 0, "/proc/1/ns/mnt::read-only",
1086 ASSERT_STREQ(of
->path
, "/proc/1/ns/mnt");
1087 ASSERT_STREQ(of
->fdname
, "mnt");
1088 assert_se(of
->flags
== OPENFILE_READ_ONLY
);
1090 r
= config_parse_open_file(NULL
, "fake", 1, "section", 1,
1097 static int intro(void) {
1098 if (enter_cgroup_subroot(NULL
) == -ENOMEDIUM
)
1099 return log_tests_skipped("cgroupfs not available");
1101 assert_se(runtime_dir
= setup_fake_runtime_dir());
1102 return EXIT_SUCCESS
;
1105 DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO
, intro
);