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 TEST_RET(unit_file_get_set
) {
47 h
= hashmap_new(&string_hash_ops
);
50 r
= unit_file_get_list(LOOKUP_SCOPE_SYSTEM
, NULL
, h
, NULL
, NULL
);
51 if (IN_SET(r
, -EPERM
, -EACCES
))
52 return log_tests_skipped_errno(r
, "unit_file_get_list");
54 log_full_errno(r
== 0 ? LOG_INFO
: LOG_ERR
, r
,
55 "unit_file_get_list: %m");
60 printf("%s = %s\n", p
->path
, unit_file_state_to_string(p
->state
));
62 unit_file_list_free(h
);
67 static void check_execcommand(ExecCommand
*c
,
76 log_info("expect: \"%s\" [\"%s\" \"%s\" \"%s\"]",
77 path
, argv0
?: path
, strnull(argv1
), strnull(argv2
));
78 n
= strv_length(c
->argv
);
79 log_info("actual: \"%s\" [\"%s\" \"%s\" \"%s\"]",
80 c
->path
, c
->argv
[0], n
> 0 ? c
->argv
[1] : "(null)", n
> 1 ? c
->argv
[2] : "(null)");
81 assert_se(streq(c
->path
, path
));
82 assert_se(streq(c
->argv
[0], argv0
?: path
));
84 assert_se(streq_ptr(c
->argv
[1], argv1
));
86 assert_se(streq_ptr(c
->argv
[2], argv2
));
87 assert_se(!!(c
->flags
& EXEC_COMMAND_IGNORE_FAILURE
) == ignore
);
90 TEST(config_parse_exec
) {
91 /* int config_parse_exec(
96 unsigned section_line,
104 ExecCommand
*c
= NULL
, *c1
;
106 _cleanup_(manager_freep
) Manager
*m
= NULL
;
107 _cleanup_(unit_freep
) Unit
*u
= NULL
;
109 r
= manager_new(LOOKUP_SCOPE_USER
, MANAGER_TEST_RUN_MINIMAL
, &m
);
110 if (manager_errno_skip_test(r
)) {
111 log_notice_errno(r
, "Skipping test: manager_new: %m");
116 assert_se(manager_startup(m
, NULL
, NULL
, NULL
) >= 0);
118 assert_se(u
= unit_new(m
, sizeof(Service
)));
120 log_info("/* basic test */");
121 r
= config_parse_exec(NULL
, "fake", 1, "section", 1,
122 "LValue", 0, "/RValue r1",
125 check_execcommand(c
, "/RValue", "/RValue", "r1", NULL
, false);
127 r
= config_parse_exec(NULL
, "fake", 2, "section", 1,
128 "LValue", 0, "/RValue///slashes r1///",
131 log_info("/* test slashes */");
133 c1
= c
->command_next
;
134 check_execcommand(c1
, "/RValue/slashes", "/RValue///slashes", "r1///", NULL
, false);
136 log_info("/* trailing slash */");
137 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
138 "LValue", 0, "/RValue/ argv0 r1",
140 assert_se(r
== -ENOEXEC
);
141 assert_se(c1
->command_next
== NULL
);
143 log_info("/* honour_argv0 */");
144 r
= config_parse_exec(NULL
, "fake", 3, "section", 1,
145 "LValue", 0, "@/RValue///slashes2 ///argv0 r1",
148 c1
= c1
->command_next
;
149 check_execcommand(c1
, "/RValue/slashes2", "///argv0", "r1", NULL
, false);
151 log_info("/* honour_argv0, no args */");
152 r
= config_parse_exec(NULL
, "fake", 3, "section", 1,
153 "LValue", 0, "@/RValue",
155 assert_se(r
== -ENOEXEC
);
156 assert_se(c1
->command_next
== NULL
);
158 log_info("/* no command, whitespace only, reset */");
159 r
= config_parse_exec(NULL
, "fake", 3, "section", 1,
163 assert_se(c
== NULL
);
165 log_info("/* ignore && honour_argv0 */");
166 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
167 "LValue", 0, "-@/RValue///slashes3 argv0a r1",
171 check_execcommand(c1
, "/RValue/slashes3", "argv0a", "r1", NULL
, true);
173 log_info("/* ignore && honour_argv0 */");
174 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
175 "LValue", 0, "@-/RValue///slashes4 argv0b r1",
178 c1
= c1
->command_next
;
179 check_execcommand(c1
, "/RValue/slashes4", "argv0b", "r1", NULL
, true);
181 log_info("/* ignore && ignore */");
182 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
183 "LValue", 0, "--/RValue argv0 r1",
186 assert_se(c1
->command_next
== NULL
);
188 log_info("/* ignore && ignore (2) */");
189 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
190 "LValue", 0, "-@-/RValue argv0 r1",
193 assert_se(c1
->command_next
== NULL
);
195 log_info("/* semicolon */");
196 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
198 "-@/RValue argv0 r1 ; "
202 c1
= c1
->command_next
;
203 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
205 c1
= c1
->command_next
;
206 check_execcommand(c1
, "/goo/goo", NULL
, "boo", NULL
, false);
208 log_info("/* two semicolons in a row */");
209 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
211 "-@/RValue argv0 r1 ; ; "
215 c1
= c1
->command_next
;
216 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
217 c1
= c1
->command_next
;
218 check_execcommand(c1
, "/goo/goo", "/goo/goo", "boo", NULL
, false);
220 log_info("/* trailing semicolon */");
221 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
223 "-@/RValue argv0 r1 ; ",
226 c1
= c1
->command_next
;
227 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
229 assert_se(c1
->command_next
== NULL
);
231 log_info("/* trailing semicolon, no whitespace */");
232 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
234 "-@/RValue argv0 r1 ;",
237 c1
= c1
->command_next
;
238 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
240 assert_se(c1
->command_next
== NULL
);
242 log_info("/* trailing semicolon in single quotes */");
243 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
245 "-@/RValue argv0 r1 ';'",
248 c1
= c1
->command_next
;
249 check_execcommand(c1
, "/RValue", "argv0", "r1", ";", true);
251 log_info("/* escaped semicolon */");
252 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
257 c1
= c1
->command_next
;
258 check_execcommand(c1
, "/bin/find", NULL
, ";", NULL
, false);
260 log_info("/* escaped semicolon with following arg */");
261 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
266 c1
= c1
->command_next
;
267 check_execcommand(c1
,
268 "/sbin/find", NULL
, ";", "/x", false);
270 log_info("/* escaped semicolon as part of an expression */");
271 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
276 c1
= c1
->command_next
;
277 check_execcommand(c1
,
278 "/sbin/find", NULL
, "\\;x", NULL
, false);
280 log_info("/* encoded semicolon */");
281 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
286 c1
= c1
->command_next
;
287 check_execcommand(c1
, "/bin/find", NULL
, ";", NULL
, false);
289 log_info("/* quoted semicolon */");
290 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
295 c1
= c1
->command_next
;
296 check_execcommand(c1
, "/bin/find", NULL
, ";", NULL
, false);
298 log_info("/* quoted semicolon with following arg */");
299 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
301 "/sbin/find \";\" /x",
304 c1
= c1
->command_next
;
305 check_execcommand(c1
,
306 "/sbin/find", NULL
, ";", "/x", false);
308 log_info("/* spaces in the filename */");
309 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
311 "\"/PATH WITH SPACES/daemon\" -1 -2",
314 c1
= c1
->command_next
;
315 check_execcommand(c1
,
316 "/PATH WITH SPACES/daemon", NULL
, "-1", "-2", false);
318 log_info("/* spaces in the filename, no args */");
319 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
321 "\"/PATH WITH SPACES/daemon -1 -2\"",
324 c1
= c1
->command_next
;
325 check_execcommand(c1
,
326 "/PATH WITH SPACES/daemon -1 -2", NULL
, NULL
, NULL
, false);
328 log_info("/* spaces in the filename, everything quoted */");
329 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
331 "\"/PATH WITH SPACES/daemon\" \"-1\" '-2'",
334 c1
= c1
->command_next
;
335 check_execcommand(c1
,
336 "/PATH WITH SPACES/daemon", NULL
, "-1", "-2", false);
338 log_info("/* escaped spaces in the filename */");
339 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
341 "\"/PATH\\sWITH\\sSPACES/daemon\" '-1 -2'",
344 c1
= c1
->command_next
;
345 check_execcommand(c1
,
346 "/PATH WITH SPACES/daemon", NULL
, "-1 -2", NULL
, false);
348 log_info("/* escaped spaces in the filename (2) */");
349 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
351 "\"/PATH\\x20WITH\\x20SPACES/daemon\" \"-1 -2\"",
354 c1
= c1
->command_next
;
355 check_execcommand(c1
,
356 "/PATH WITH SPACES/daemon", NULL
, "-1 -2", NULL
, false);
358 for (ccc
= "abfnrtv\\\'\"x"; *ccc
; ccc
++) {
359 /* \\x is an incomplete hexadecimal sequence, invalid because of the slash */
360 char path
[] = "/path\\X";
361 path
[sizeof(path
) - 2] = *ccc
;
363 log_info("/* invalid character: \\%c */", *ccc
);
364 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
367 assert_se(r
== -ENOEXEC
);
368 assert_se(c1
->command_next
== NULL
);
371 log_info("/* valid character: \\s */");
372 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
373 "LValue", 0, "/path\\s",
376 c1
= c1
->command_next
;
377 check_execcommand(c1
, "/path ", NULL
, NULL
, NULL
, false);
379 log_info("/* quoted backslashes */");
380 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
382 "/bin/grep '\\w+\\K'",
385 c1
= c1
->command_next
;
386 check_execcommand(c1
, "/bin/grep", NULL
, "\\w+\\K", NULL
, false);
388 log_info("/* trailing backslash: \\ */");
389 /* backslash is invalid */
390 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
391 "LValue", 0, "/path\\",
393 assert_se(r
== -ENOEXEC
);
394 assert_se(c1
->command_next
== NULL
);
396 log_info("/* missing ending ' */");
397 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
398 "LValue", 0, "/path 'foo",
400 assert_se(r
== -ENOEXEC
);
401 assert_se(c1
->command_next
== NULL
);
403 log_info("/* missing ending ' with trailing backslash */");
404 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
405 "LValue", 0, "/path 'foo\\",
407 assert_se(r
== -ENOEXEC
);
408 assert_se(c1
->command_next
== NULL
);
410 log_info("/* invalid space between modifiers */");
411 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
412 "LValue", 0, "- /path",
415 assert_se(c1
->command_next
== NULL
);
417 log_info("/* only modifiers, no path */");
418 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
422 assert_se(c1
->command_next
== NULL
);
424 log_info("/* long arg */"); /* See issue #22957. */
426 char x
[LONG_LINE_MAX
-100], *y
;
427 y
= mempcpy(x
, "/bin/echo ", STRLEN("/bin/echo "));
428 memset(y
, 'x', sizeof(x
) - STRLEN("/bin/echo ") - 1);
429 x
[sizeof(x
) - 1] = '\0';
431 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
435 c1
= c1
->command_next
;
436 check_execcommand(c1
,
437 "/bin/echo", NULL
, y
, NULL
, false);
439 log_info("/* empty argument, reset */");
440 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
444 assert_se(c
== NULL
);
446 exec_command_free_list(c
);
449 TEST(config_parse_log_extra_fields
) {
450 /* int config_parse_log_extra_fields(
452 const char *filename,
455 unsigned section_line,
464 _cleanup_(manager_freep
) Manager
*m
= NULL
;
465 _cleanup_(unit_freep
) Unit
*u
= NULL
;
468 r
= manager_new(LOOKUP_SCOPE_USER
, MANAGER_TEST_RUN_MINIMAL
, &m
);
469 if (manager_errno_skip_test(r
)) {
470 log_notice_errno(r
, "Skipping test: manager_new: %m");
475 assert_se(manager_startup(m
, NULL
, NULL
, NULL
) >= 0);
477 assert_se(u
= unit_new(m
, sizeof(Service
)));
479 log_info("/* %s – basic test */", __func__
);
480 r
= config_parse_log_extra_fields(NULL
, "fake", 1, "section", 1,
481 "LValue", 0, "FOO=BAR \"QOOF=quux ' ' \"",
484 assert_se(c
.n_log_extra_fields
== 2);
485 assert_se(strneq(c
.log_extra_fields
[0].iov_base
, "FOO=BAR", c
.log_extra_fields
[0].iov_len
));
486 assert_se(strneq(c
.log_extra_fields
[1].iov_base
, "QOOF=quux ' ' ", c
.log_extra_fields
[1].iov_len
));
488 log_info("/* %s – add some */", __func__
);
489 r
= config_parse_log_extra_fields(NULL
, "fake", 1, "section", 1,
490 "LValue", 0, "FOO2=BAR2 QOOF2=quux ' '",
493 assert_se(c
.n_log_extra_fields
== 4);
494 assert_se(strneq(c
.log_extra_fields
[0].iov_base
, "FOO=BAR", c
.log_extra_fields
[0].iov_len
));
495 assert_se(strneq(c
.log_extra_fields
[1].iov_base
, "QOOF=quux ' ' ", c
.log_extra_fields
[1].iov_len
));
496 assert_se(strneq(c
.log_extra_fields
[2].iov_base
, "FOO2=BAR2", c
.log_extra_fields
[2].iov_len
));
497 assert_se(strneq(c
.log_extra_fields
[3].iov_base
, "QOOF2=quux", c
.log_extra_fields
[3].iov_len
));
499 exec_context_dump(&c
, stdout
, " --> ");
501 log_info("/* %s – reset */", __func__
);
502 r
= config_parse_log_extra_fields(NULL
, "fake", 1, "section", 1,
506 assert_se(c
.n_log_extra_fields
== 0);
508 exec_context_free_log_extra_fields(&c
);
510 log_info("/* %s – bye */", __func__
);
513 TEST(install_printf
, .sd_booted
= true) {
514 char name
[] = "name.service",
515 path
[] = "/run/systemd/system/name.service";
516 InstallInfo i
= { .name
= name
, .path
= path
, };
517 InstallInfo i2
= { .name
= name
, .path
= path
, };
518 char name3
[] = "name@inst.service",
519 path3
[] = "/run/systemd/system/name.service";
520 InstallInfo i3
= { .name
= name3
, .path
= path3
, };
521 InstallInfo i4
= { .name
= name3
, .path
= path3
, };
523 _cleanup_free_
char *mid
= NULL
, *bid
= NULL
, *host
= NULL
, *gid
= NULL
, *group
= NULL
, *uid
= NULL
, *user
= NULL
;
525 if (sd_id128_get_machine(NULL
) >= 0)
526 assert_se(specifier_machine_id('m', NULL
, NULL
, NULL
, &mid
) >= 0 && mid
);
528 assert_se(specifier_boot_id('b', NULL
, NULL
, NULL
, &bid
) >= 0 && bid
);
529 assert_se(host
= gethostname_malloc());
530 assert_se(group
= gid_to_name(getgid()));
531 assert_se(asprintf(&gid
, UID_FMT
, getgid()) >= 0);
532 assert_se(user
= uid_to_name(getuid()));
533 assert_se(asprintf(&uid
, UID_FMT
, getuid()) >= 0);
535 #define expect(scope, src, pattern, result) \
537 _cleanup_free_ char *t = NULL, \
538 *d1 = ASSERT_PTR(strdup(i.name)), \
539 *d2 = ASSERT_PTR(strdup(i.path)); \
540 int r = install_name_printf(scope, &src, pattern, &t); \
541 assert_se(result ? r >= 0 : r < 0); \
542 memzero(i.name, strlen(i.name)); \
543 memzero(i.path, strlen(i.path)); \
546 assert_se(streq(t, result)); \
549 strcpy(i.name, d1); \
550 strcpy(i.path, d2); \
553 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%n", "name.service");
554 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%N", "name");
555 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%p", "name");
556 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%i", "");
557 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%j", "name");
558 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%g", "root");
559 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%G", "0");
560 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%u", "root");
561 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%U", "0");
563 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%m", mid
);
564 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%b", bid
);
565 expect(LOOKUP_SCOPE_SYSTEM
, i
, "%H", host
);
567 expect(LOOKUP_SCOPE_SYSTEM
, i2
, "%g", "root");
568 expect(LOOKUP_SCOPE_SYSTEM
, i2
, "%G", "0");
569 expect(LOOKUP_SCOPE_SYSTEM
, i2
, "%u", "root");
570 expect(LOOKUP_SCOPE_SYSTEM
, i2
, "%U", "0");
572 expect(LOOKUP_SCOPE_USER
, i2
, "%g", group
);
573 expect(LOOKUP_SCOPE_USER
, i2
, "%G", gid
);
574 expect(LOOKUP_SCOPE_USER
, i2
, "%u", user
);
575 expect(LOOKUP_SCOPE_USER
, i2
, "%U", uid
);
577 /* gcc-12.0.1-0.9.fc36.x86_64 insist that streq(…, NULL) is called,
578 * even though the call is inside of a conditional where the pointer is checked. :( */
579 #pragma GCC diagnostic push
580 #pragma GCC diagnostic ignored "-Wnonnull"
581 expect(LOOKUP_SCOPE_GLOBAL
, i2
, "%g", NULL
);
582 expect(LOOKUP_SCOPE_GLOBAL
, i2
, "%G", NULL
);
583 expect(LOOKUP_SCOPE_GLOBAL
, i2
, "%u", NULL
);
584 expect(LOOKUP_SCOPE_GLOBAL
, i2
, "%U", NULL
);
585 #pragma GCC diagnostic pop
587 expect(LOOKUP_SCOPE_SYSTEM
, i3
, "%n", "name@inst.service");
588 expect(LOOKUP_SCOPE_SYSTEM
, i3
, "%N", "name@inst");
589 expect(LOOKUP_SCOPE_SYSTEM
, i3
, "%p", "name");
590 expect(LOOKUP_SCOPE_USER
, i3
, "%g", group
);
591 expect(LOOKUP_SCOPE_USER
, i3
, "%G", gid
);
592 expect(LOOKUP_SCOPE_USER
, i3
, "%u", user
);
593 expect(LOOKUP_SCOPE_USER
, i3
, "%U", uid
);
595 expect(LOOKUP_SCOPE_SYSTEM
, i3
, "%m", mid
);
596 expect(LOOKUP_SCOPE_SYSTEM
, i3
, "%b", bid
);
597 expect(LOOKUP_SCOPE_SYSTEM
, i3
, "%H", host
);
599 expect(LOOKUP_SCOPE_USER
, i4
, "%g", group
);
600 expect(LOOKUP_SCOPE_USER
, i4
, "%G", gid
);
601 expect(LOOKUP_SCOPE_USER
, i4
, "%u", user
);
602 expect(LOOKUP_SCOPE_USER
, i4
, "%U", uid
);
605 static uint64_t make_cap(int cap
) {
606 return ((uint64_t) 1ULL << (uint64_t) cap
);
609 TEST(config_parse_capability_set
) {
610 /* int config_parse_capability_set(
612 const char *filename,
615 unsigned section_line,
622 uint64_t capability_bounding_set
= 0;
624 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
625 "CapabilityBoundingSet", 0, "CAP_NET_RAW",
626 &capability_bounding_set
, NULL
);
628 assert_se(capability_bounding_set
== make_cap(CAP_NET_RAW
));
630 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
631 "CapabilityBoundingSet", 0, "CAP_NET_ADMIN",
632 &capability_bounding_set
, NULL
);
634 assert_se(capability_bounding_set
== (make_cap(CAP_NET_RAW
) | make_cap(CAP_NET_ADMIN
)));
636 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
637 "CapabilityBoundingSet", 0, "~CAP_NET_ADMIN",
638 &capability_bounding_set
, NULL
);
640 assert_se(capability_bounding_set
== make_cap(CAP_NET_RAW
));
642 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
643 "CapabilityBoundingSet", 0, "",
644 &capability_bounding_set
, NULL
);
646 assert_se(capability_bounding_set
== UINT64_C(0));
648 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
649 "CapabilityBoundingSet", 0, "~",
650 &capability_bounding_set
, NULL
);
652 assert_se(cap_test_all(capability_bounding_set
));
654 capability_bounding_set
= 0;
655 r
= config_parse_capability_set(NULL
, "fake", 1, "section", 1,
656 "CapabilityBoundingSet", 0, " 'CAP_NET_RAW' WAT_CAP??? CAP_NET_ADMIN CAP'_trailing_garbage",
657 &capability_bounding_set
, NULL
);
659 assert_se(capability_bounding_set
== (make_cap(CAP_NET_RAW
) | make_cap(CAP_NET_ADMIN
)));
662 TEST(config_parse_rlimit
) {
663 struct rlimit
* rl
[_RLIMIT_MAX
] = {};
665 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "55", rl
, NULL
) >= 0);
666 assert_se(rl
[RLIMIT_NOFILE
]);
667 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 55);
668 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== rl
[RLIMIT_NOFILE
]->rlim_max
);
670 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "55:66", rl
, NULL
) >= 0);
671 assert_se(rl
[RLIMIT_NOFILE
]);
672 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 55);
673 assert_se(rl
[RLIMIT_NOFILE
]->rlim_max
== 66);
675 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "infinity", rl
, NULL
) >= 0);
676 assert_se(rl
[RLIMIT_NOFILE
]);
677 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== RLIM_INFINITY
);
678 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== rl
[RLIMIT_NOFILE
]->rlim_max
);
680 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "infinity:infinity", rl
, NULL
) >= 0);
681 assert_se(rl
[RLIMIT_NOFILE
]);
682 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== RLIM_INFINITY
);
683 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== rl
[RLIMIT_NOFILE
]->rlim_max
);
685 rl
[RLIMIT_NOFILE
]->rlim_cur
= 10;
686 rl
[RLIMIT_NOFILE
]->rlim_max
= 20;
688 /* Invalid values don't change rl */
689 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "10:20:30", rl
, NULL
) >= 0);
690 assert_se(rl
[RLIMIT_NOFILE
]);
691 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 10);
692 assert_se(rl
[RLIMIT_NOFILE
]->rlim_max
== 20);
694 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "wat:wat", rl
, NULL
) >= 0);
695 assert_se(rl
[RLIMIT_NOFILE
]);
696 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 10);
697 assert_se(rl
[RLIMIT_NOFILE
]->rlim_max
== 20);
699 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "66:wat", rl
, NULL
) >= 0);
700 assert_se(rl
[RLIMIT_NOFILE
]);
701 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 10);
702 assert_se(rl
[RLIMIT_NOFILE
]->rlim_max
== 20);
704 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE
, "200:100", rl
, NULL
) >= 0);
705 assert_se(rl
[RLIMIT_NOFILE
]);
706 assert_se(rl
[RLIMIT_NOFILE
]->rlim_cur
== 10);
707 assert_se(rl
[RLIMIT_NOFILE
]->rlim_max
== 20);
709 rl
[RLIMIT_NOFILE
] = mfree(rl
[RLIMIT_NOFILE
]);
711 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU
, "56", rl
, NULL
) >= 0);
712 assert_se(rl
[RLIMIT_CPU
]);
713 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== 56);
714 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== rl
[RLIMIT_CPU
]->rlim_max
);
716 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU
, "57s", rl
, NULL
) >= 0);
717 assert_se(rl
[RLIMIT_CPU
]);
718 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== 57);
719 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== rl
[RLIMIT_CPU
]->rlim_max
);
721 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU
, "40s:1m", rl
, NULL
) >= 0);
722 assert_se(rl
[RLIMIT_CPU
]);
723 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== 40);
724 assert_se(rl
[RLIMIT_CPU
]->rlim_max
== 60);
726 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU
, "infinity", rl
, NULL
) >= 0);
727 assert_se(rl
[RLIMIT_CPU
]);
728 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== RLIM_INFINITY
);
729 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== rl
[RLIMIT_CPU
]->rlim_max
);
731 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU
, "1234ms", rl
, NULL
) >= 0);
732 assert_se(rl
[RLIMIT_CPU
]);
733 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== 2);
734 assert_se(rl
[RLIMIT_CPU
]->rlim_cur
== rl
[RLIMIT_CPU
]->rlim_max
);
736 rl
[RLIMIT_CPU
] = mfree(rl
[RLIMIT_CPU
]);
738 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "58", rl
, NULL
) >= 0);
739 assert_se(rl
[RLIMIT_RTTIME
]);
740 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== 58);
741 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== rl
[RLIMIT_RTTIME
]->rlim_max
);
743 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "58:60", rl
, NULL
) >= 0);
744 assert_se(rl
[RLIMIT_RTTIME
]);
745 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== 58);
746 assert_se(rl
[RLIMIT_RTTIME
]->rlim_max
== 60);
748 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "59s", rl
, NULL
) >= 0);
749 assert_se(rl
[RLIMIT_RTTIME
]);
750 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== 59 * USEC_PER_SEC
);
751 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== rl
[RLIMIT_RTTIME
]->rlim_max
);
753 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "59s:123s", rl
, NULL
) >= 0);
754 assert_se(rl
[RLIMIT_RTTIME
]);
755 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== 59 * USEC_PER_SEC
);
756 assert_se(rl
[RLIMIT_RTTIME
]->rlim_max
== 123 * USEC_PER_SEC
);
758 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "infinity", rl
, NULL
) >= 0);
759 assert_se(rl
[RLIMIT_RTTIME
]);
760 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== RLIM_INFINITY
);
761 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== rl
[RLIMIT_RTTIME
]->rlim_max
);
763 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "infinity:infinity", rl
, NULL
) >= 0);
764 assert_se(rl
[RLIMIT_RTTIME
]);
765 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== RLIM_INFINITY
);
766 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== rl
[RLIMIT_RTTIME
]->rlim_max
);
768 assert_se(config_parse_rlimit(NULL
, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME
, "2345ms", rl
, NULL
) >= 0);
769 assert_se(rl
[RLIMIT_RTTIME
]);
770 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== 2345 * USEC_PER_MSEC
);
771 assert_se(rl
[RLIMIT_RTTIME
]->rlim_cur
== rl
[RLIMIT_RTTIME
]->rlim_max
);
773 rl
[RLIMIT_RTTIME
] = mfree(rl
[RLIMIT_RTTIME
]);
776 TEST(config_parse_pass_environ
) {
777 /* int config_parse_pass_environ(
779 const char *filename,
782 unsigned section_line,
789 _cleanup_strv_free_
char **passenv
= NULL
;
791 r
= config_parse_pass_environ(NULL
, "fake", 1, "section", 1,
792 "PassEnvironment", 0, "A B",
795 assert_se(strv_length(passenv
) == 2);
796 assert_se(streq(passenv
[0], "A"));
797 assert_se(streq(passenv
[1], "B"));
799 r
= config_parse_pass_environ(NULL
, "fake", 1, "section", 1,
800 "PassEnvironment", 0, "",
803 assert_se(strv_isempty(passenv
));
805 r
= config_parse_pass_environ(NULL
, "fake", 1, "section", 1,
806 "PassEnvironment", 0, "'invalid name' 'normal_name' A=1 'special_name$$' \\",
809 assert_se(strv_length(passenv
) == 1);
810 assert_se(streq(passenv
[0], "normal_name"));
813 TEST(config_parse_unit_env_file
) {
814 /* int config_parse_unit_env_file(
816 const char *filename,
819 unsigned section_line,
826 _cleanup_(manager_freep
) Manager
*m
= NULL
;
828 _cleanup_strv_free_
char **files
= NULL
;
831 r
= manager_new(LOOKUP_SCOPE_USER
, MANAGER_TEST_RUN_MINIMAL
, &m
);
832 if (manager_errno_skip_test(r
)) {
833 log_notice_errno(r
, "Skipping test: manager_new: %m");
838 assert_se(manager_startup(m
, NULL
, NULL
, NULL
) >= 0);
840 assert_se(u
= unit_new(m
, sizeof(Service
)));
841 assert_se(unit_add_name(u
, "foobar.service") == 0);
843 r
= config_parse_unit_env_file(u
->id
, "fake", 1, "section", 1,
844 "EnvironmentFile", 0, "not-absolute",
847 assert_se(strv_isempty(files
));
849 r
= config_parse_unit_env_file(u
->id
, "fake", 1, "section", 1,
850 "EnvironmentFile", 0, "/absolute1",
853 assert_se(strv_length(files
) == 1);
855 r
= config_parse_unit_env_file(u
->id
, "fake", 1, "section", 1,
856 "EnvironmentFile", 0, "/absolute2",
859 assert_se(strv_length(files
) == 2);
860 assert_se(streq(files
[0], "/absolute1"));
861 assert_se(streq(files
[1], "/absolute2"));
863 r
= config_parse_unit_env_file(u
->id
, "fake", 1, "section", 1,
864 "EnvironmentFile", 0, "",
867 assert_se(strv_isempty(files
));
869 r
= config_parse_unit_env_file(u
->id
, "fake", 1, "section", 1,
870 "EnvironmentFile", 0, "/path/%n.conf",
873 assert_se(strv_length(files
) == 1);
874 assert_se(streq(files
[0], "/path/foobar.service.conf"));
877 TEST(unit_dump_config_items
) {
878 unit_dump_config_items(stdout
);
881 TEST(config_parse_memory_limit
) {
882 /* int config_parse_memory_limit(
884 const char *filename,
887 unsigned section_line,
900 { "MemoryMin", "", &c
.memory_min
, CGROUP_LIMIT_MIN
},
901 { "MemoryMin", "0", &c
.memory_min
, CGROUP_LIMIT_MIN
},
902 { "MemoryMin", "10", &c
.memory_min
, 10 },
903 { "MemoryMin", "infinity", &c
.memory_min
, CGROUP_LIMIT_MAX
},
904 { "MemoryLow", "", &c
.memory_low
, CGROUP_LIMIT_MIN
},
905 { "MemoryLow", "0", &c
.memory_low
, CGROUP_LIMIT_MIN
},
906 { "MemoryLow", "10", &c
.memory_low
, 10 },
907 { "MemoryLow", "infinity", &c
.memory_low
, CGROUP_LIMIT_MAX
},
908 { "MemoryHigh", "", &c
.memory_high
, CGROUP_LIMIT_MAX
},
909 { "MemoryHigh", "0", &c
.memory_high
, CGROUP_LIMIT_DUMMY
},
910 { "MemoryHigh", "10", &c
.memory_high
, 10 },
911 { "MemoryHigh", "infinity", &c
.memory_high
, CGROUP_LIMIT_MAX
},
912 { "MemoryMax", "", &c
.memory_max
, CGROUP_LIMIT_MAX
},
913 { "MemoryMax", "0", &c
.memory_max
, CGROUP_LIMIT_DUMMY
},
914 { "MemoryMax", "10", &c
.memory_max
, 10 },
915 { "MemoryMax", "infinity", &c
.memory_max
, CGROUP_LIMIT_MAX
},
920 for (i
= 0; i
< ELEMENTSOF(limit_tests
); i
++) {
921 c
.memory_min
= CGROUP_LIMIT_DUMMY
;
922 c
.memory_low
= CGROUP_LIMIT_DUMMY
;
923 c
.memory_high
= CGROUP_LIMIT_DUMMY
;
924 c
.memory_max
= CGROUP_LIMIT_DUMMY
;
925 r
= config_parse_memory_limit(NULL
, "fake", 1, "section", 1,
926 limit_tests
[i
].limit
, 1,
927 limit_tests
[i
].value
, &c
, NULL
);
928 log_info("%s=%s\t%"PRIu64
"==%"PRIu64
"\n",
929 limit_tests
[i
].limit
, limit_tests
[i
].value
,
930 *limit_tests
[i
].result
, limit_tests
[i
].expected
);
932 assert_se(*limit_tests
[i
].result
== limit_tests
[i
].expected
);
937 TEST(contains_instance_specifier_superset
) {
938 assert_se(contains_instance_specifier_superset("foobar@a%i"));
939 assert_se(contains_instance_specifier_superset("foobar@%ia"));
940 assert_se(contains_instance_specifier_superset("foobar@%n"));
941 assert_se(contains_instance_specifier_superset("foobar@%n.service"));
942 assert_se(contains_instance_specifier_superset("foobar@%N"));
943 assert_se(contains_instance_specifier_superset("foobar@%N.service"));
944 assert_se(contains_instance_specifier_superset("foobar@baz.%N.service"));
945 assert_se(contains_instance_specifier_superset("@%N.service"));
946 assert_se(contains_instance_specifier_superset("@%N"));
947 assert_se(contains_instance_specifier_superset("@%a%N"));
949 assert_se(!contains_instance_specifier_superset("foobar@%i.service"));
950 assert_se(!contains_instance_specifier_superset("foobar%ia.service"));
951 assert_se(!contains_instance_specifier_superset("foobar@%%n.service"));
952 assert_se(!contains_instance_specifier_superset("foobar@baz.service"));
953 assert_se(!contains_instance_specifier_superset("%N.service"));
954 assert_se(!contains_instance_specifier_superset("%N"));
955 assert_se(!contains_instance_specifier_superset("@%aN"));
956 assert_se(!contains_instance_specifier_superset("@%a%b"));
959 TEST(unit_is_recursive_template_dependency
) {
960 _cleanup_(manager_freep
) Manager
*m
= NULL
;
964 r
= manager_new(LOOKUP_SCOPE_USER
, MANAGER_TEST_RUN_MINIMAL
, &m
);
965 if (manager_errno_skip_test(r
)) {
966 log_notice_errno(r
, "Skipping test: manager_new: %m");
971 assert_se(manager_startup(m
, NULL
, NULL
, NULL
) >= 0);
973 assert_se(u
= unit_new(m
, sizeof(Service
)));
974 assert_se(unit_add_name(u
, "foobar@1.service") == 0);
975 u
->fragment_path
= strdup("/foobar@.service");
977 assert_se(hashmap_put_strdup(&m
->unit_id_map
, "foobar@foobar@123.service", "/foobar@.service"));
978 assert_se(hashmap_put_strdup(&m
->unit_id_map
, "foobar@foobar@456.service", "/custom.service"));
980 /* Test that %n, %N and any extension of %i specifiers in the instance are detected as recursive. */
981 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@%N.service") == 1);
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@a%i.service") == 1);
984 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@%ia.service") == 1);
985 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@%x%n.service") == 1);
986 /* Test that %i on its own is not detected as recursive. */
987 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@%i.service") == 0);
988 /* Test that a specifier other than %i, %n and %N is not detected as recursive. */
989 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@%xn.service") == 0);
990 /* Test that an expanded specifier is not detected as recursive. */
991 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.service", "foobar@foobar@123.service") == 0);
992 /* Test that a dependency with a custom fragment path is not detected as recursive. */
993 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@456.service", "foobar@%n.service") == 0);
994 /* Test that a dependency without a fragment path is not detected as recursive. */
995 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@789.service", "foobar@%n.service") == 0);
996 /* Test that a dependency with a different prefix is not detected as recursive. */
997 assert_se(unit_is_likely_recursive_template_dependency(u
, "quux@foobar@123.service", "quux@%n.service") == 0);
998 /* Test that a dependency of a different type is not detected as recursive. */
999 assert_se(unit_is_likely_recursive_template_dependency(u
, "foobar@foobar@123.mount", "foobar@%n.mount") == 0);
1002 #define TEST_PATTERN(_regex, _allowed_patterns_count, _denied_patterns_count) \
1005 .allowed_patterns_count = _allowed_patterns_count, \
1006 .denied_patterns_count = _denied_patterns_count \
1009 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 r
= config_parse_log_filter_patterns(NULL
, "fake", 1, "section", 1, "LogFilterPatterns", 1,
1037 regex_tests
[i
].regex
, &c
, NULL
);
1040 assert_se(set_size(c
.log_filter_allowed_patterns
) == regex_tests
[i
].allowed_patterns_count
);
1041 assert_se(set_size(c
.log_filter_denied_patterns
) == regex_tests
[i
].denied_patterns_count
);
1043 /* Ensure `~` is properly removed */
1045 SET_FOREACH(p
, c
.log_filter_allowed_patterns
)
1046 assert_se(p
&& p
[0] != '~');
1047 SET_FOREACH(p
, c
.log_filter_denied_patterns
)
1048 assert_se(p
&& p
[0] != '~');
1052 TEST(config_parse_open_file
) {
1053 _cleanup_(manager_freep
) Manager
*m
= NULL
;
1054 _cleanup_(unit_freep
) Unit
*u
= NULL
;
1055 _cleanup_(open_file_freep
) OpenFile
*of
= NULL
;
1058 r
= manager_new(LOOKUP_SCOPE_USER
, MANAGER_TEST_RUN_MINIMAL
, &m
);
1059 if (manager_errno_skip_test(r
)) {
1060 log_notice_errno(r
, "Skipping test: manager_new: %m");
1065 assert_se(manager_startup(m
, NULL
, NULL
, NULL
) >= 0);
1067 assert_se(u
= unit_new(m
, sizeof(Service
)));
1068 assert_se(unit_add_name(u
, "foobar.service") == 0);
1070 r
= config_parse_open_file(NULL
, "fake", 1, "section", 1,
1071 "OpenFile", 0, "/proc/1/ns/mnt:host-mount-namespace:read-only",
1075 assert_se(streq(of
->path
, "/proc/1/ns/mnt"));
1076 assert_se(streq(of
->fdname
, "host-mount-namespace"));
1077 assert_se(of
->flags
== OPENFILE_READ_ONLY
);
1079 of
= open_file_free(of
);
1080 r
= config_parse_open_file(NULL
, "fake", 1, "section", 1,
1081 "OpenFile", 0, "/proc/1/ns/mnt::read-only",
1085 assert_se(streq(of
->path
, "/proc/1/ns/mnt"));
1086 assert_se(streq(of
->fdname
, "mnt"));
1087 assert_se(of
->flags
== OPENFILE_READ_ONLY
);
1089 r
= config_parse_open_file(NULL
, "fake", 1, "section", 1,
1096 static int intro(void) {
1097 if (enter_cgroup_subroot(NULL
) == -ENOMEDIUM
)
1098 return log_tests_skipped("cgroupfs not available");
1100 assert_se(runtime_dir
= setup_fake_runtime_dir());
1101 return EXIT_SUCCESS
;
1104 DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO
, intro
);