]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-execute.c
tree-wide: introduce PIPE_EBADF macro
[thirdparty/systemd.git] / src / test / test-execute.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <stdio.h>
4 #include <sys/prctl.h>
5 #include <sys/types.h>
6
7 #include "sd-event.h"
8
9 #include "capability-util.h"
10 #include "cpu-set-util.h"
11 #include "copy.h"
12 #include "dropin.h"
13 #include "errno-list.h"
14 #include "fd-util.h"
15 #include "fileio.h"
16 #include "fs-util.h"
17 #include "macro.h"
18 #include "manager.h"
19 #include "missing_prctl.h"
20 #include "mkdir.h"
21 #include "path-util.h"
22 #include "process-util.h"
23 #include "rm-rf.h"
24 #if HAVE_SECCOMP
25 #include "seccomp-util.h"
26 #endif
27 #include "service.h"
28 #include "signal-util.h"
29 #include "static-destruct.h"
30 #include "stat-util.h"
31 #include "tests.h"
32 #include "tmpfile-util.h"
33 #include "unit.h"
34 #include "user-util.h"
35 #include "virt.h"
36
37 static char *user_runtime_unit_dir = NULL;
38 static bool can_unshare;
39
40 STATIC_DESTRUCTOR_REGISTER(user_runtime_unit_dir, freep);
41
42 typedef void (*test_function_t)(Manager *m);
43
44 static int cld_dumped_to_killed(int code) {
45 /* Depending on the system, seccomp version, … some signals might result in dumping, others in plain
46 * killing. Let's ignore the difference here, and map both cases to CLD_KILLED */
47 return code == CLD_DUMPED ? CLD_KILLED : code;
48 }
49
50 static void wait_for_service_finish(Manager *m, Unit *unit) {
51 Service *service = NULL;
52 usec_t ts;
53 usec_t timeout = 2 * USEC_PER_MINUTE;
54
55 assert_se(m);
56 assert_se(unit);
57
58 service = SERVICE(unit);
59 printf("%s\n", unit->id);
60 exec_context_dump(&service->exec_context, stdout, "\t");
61 ts = now(CLOCK_MONOTONIC);
62 while (!IN_SET(service->state, SERVICE_DEAD, SERVICE_FAILED)) {
63 int r;
64 usec_t n;
65
66 r = sd_event_run(m->event, 100 * USEC_PER_MSEC);
67 assert_se(r >= 0);
68
69 n = now(CLOCK_MONOTONIC);
70 if (ts + timeout < n) {
71 log_error("Test timeout when testing %s", unit->id);
72 r = unit_kill(unit, KILL_ALL, SIGKILL, NULL);
73 if (r < 0)
74 log_error_errno(r, "Failed to kill %s: %m", unit->id);
75 exit(EXIT_FAILURE);
76 }
77 }
78 }
79
80 static void check_main_result(const char *file, unsigned line, const char *func,
81 Manager *m, Unit *unit, int status_expected, int code_expected) {
82 Service *service = NULL;
83
84 assert_se(m);
85 assert_se(unit);
86
87 wait_for_service_finish(m, unit);
88
89 service = SERVICE(unit);
90 exec_status_dump(&service->main_exec_status, stdout, "\t");
91
92 if (cld_dumped_to_killed(service->main_exec_status.code) != cld_dumped_to_killed(code_expected)) {
93 log_error("%s:%u:%s %s: can_unshare=%s: exit code %d, expected %d",
94 file, line, func, unit->id, yes_no(can_unshare),
95 service->main_exec_status.code, code_expected);
96 abort();
97 }
98
99 if (service->main_exec_status.status != status_expected) {
100 log_error("%s:%u:%s: %s: can_unshare=%s: exit status %d, expected %d",
101 file, line, func, unit->id, yes_no(can_unshare),
102 service->main_exec_status.status, status_expected);
103 abort();
104 }
105 }
106
107 static void check_service_result(const char *file, unsigned line, const char *func,
108 Manager *m, Unit *unit, ServiceResult result_expected) {
109 Service *service = NULL;
110
111 assert_se(m);
112 assert_se(unit);
113
114 wait_for_service_finish(m, unit);
115
116 service = SERVICE(unit);
117
118 if (service->result != result_expected) {
119 log_error("%s:%u:%s: %s: can_unshare=%s: service end result %s, expected %s",
120 file, line, func, unit->id, yes_no(can_unshare),
121 service_result_to_string(service->result),
122 service_result_to_string(result_expected));
123 abort();
124 }
125 }
126
127 static bool check_nobody_user_and_group(void) {
128 static int cache = -1;
129 struct passwd *p;
130 struct group *g;
131
132 if (cache >= 0)
133 return !!cache;
134
135 if (!synthesize_nobody())
136 goto invalid;
137
138 p = getpwnam(NOBODY_USER_NAME);
139 if (!p ||
140 !streq(p->pw_name, NOBODY_USER_NAME) ||
141 p->pw_uid != UID_NOBODY ||
142 p->pw_gid != GID_NOBODY)
143 goto invalid;
144
145 p = getpwuid(UID_NOBODY);
146 if (!p ||
147 !streq(p->pw_name, NOBODY_USER_NAME) ||
148 p->pw_uid != UID_NOBODY ||
149 p->pw_gid != GID_NOBODY)
150 goto invalid;
151
152 g = getgrnam(NOBODY_GROUP_NAME);
153 if (!g ||
154 !streq(g->gr_name, NOBODY_GROUP_NAME) ||
155 g->gr_gid != GID_NOBODY)
156 goto invalid;
157
158 g = getgrgid(GID_NOBODY);
159 if (!g ||
160 !streq(g->gr_name, NOBODY_GROUP_NAME) ||
161 g->gr_gid != GID_NOBODY)
162 goto invalid;
163
164 cache = 1;
165 return true;
166
167 invalid:
168 cache = 0;
169 return false;
170 }
171
172 static bool check_user_has_group_with_same_name(const char *name) {
173 struct passwd *p;
174 struct group *g;
175
176 assert_se(name);
177
178 p = getpwnam(name);
179 if (!p ||
180 !streq(p->pw_name, name))
181 return false;
182
183 g = getgrgid(p->pw_gid);
184 if (!g ||
185 !streq(g->gr_name, name))
186 return false;
187
188 return true;
189 }
190
191 static bool is_inaccessible_available(void) {
192 FOREACH_STRING(p,
193 "/run/systemd/inaccessible/reg",
194 "/run/systemd/inaccessible/dir",
195 "/run/systemd/inaccessible/chr",
196 "/run/systemd/inaccessible/blk",
197 "/run/systemd/inaccessible/fifo",
198 "/run/systemd/inaccessible/sock")
199 if (access(p, F_OK) < 0)
200 return false;
201
202 return true;
203 }
204
205 static void _test(const char *file, unsigned line, const char *func,
206 Manager *m, const char *unit_name, int status_expected, int code_expected) {
207 Unit *unit;
208
209 assert_se(unit_name);
210
211 assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
212 assert_se(unit_start(unit, NULL) >= 0);
213 check_main_result(file, line, func, m, unit, status_expected, code_expected);
214 }
215 #define test(m, unit_name, status_expected, code_expected) \
216 _test(PROJECT_FILE, __LINE__, __func__, m, unit_name, status_expected, code_expected)
217
218 static void _test_service(const char *file, unsigned line, const char *func,
219 Manager *m, const char *unit_name, ServiceResult result_expected) {
220 Unit *unit;
221
222 assert_se(unit_name);
223
224 assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
225 assert_se(unit_start(unit, NULL) >= 0);
226 check_service_result(file, line, func, m, unit, result_expected);
227 }
228 #define test_service(m, unit_name, result_expected) \
229 _test_service(PROJECT_FILE, __LINE__, __func__, m, unit_name, result_expected)
230
231 static void test_exec_bindpaths(Manager *m) {
232 assert_se(mkdir_p("/tmp/test-exec-bindpaths", 0755) >= 0);
233 assert_se(mkdir_p("/tmp/test-exec-bindreadonlypaths", 0755) >= 0);
234
235 test(m, "exec-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
236
237 (void) rm_rf("/tmp/test-exec-bindpaths", REMOVE_ROOT|REMOVE_PHYSICAL);
238 (void) rm_rf("/tmp/test-exec-bindreadonlypaths", REMOVE_ROOT|REMOVE_PHYSICAL);
239 }
240
241 static void test_exec_cpuaffinity(Manager *m) {
242 _cleanup_(cpu_set_reset) CPUSet c = {};
243
244 assert_se(cpu_set_realloc(&c, 8192) >= 0); /* just allocate the maximum possible size */
245 assert_se(sched_getaffinity(0, c.allocated, c.set) >= 0);
246
247 if (!CPU_ISSET_S(0, c.allocated, c.set)) {
248 log_notice("Cannot use CPU 0, skipping %s", __func__);
249 return;
250 }
251
252 test(m, "exec-cpuaffinity1.service", 0, CLD_EXITED);
253 test(m, "exec-cpuaffinity2.service", 0, CLD_EXITED);
254
255 if (!CPU_ISSET_S(1, c.allocated, c.set) ||
256 !CPU_ISSET_S(2, c.allocated, c.set)) {
257 log_notice("Cannot use CPU 1 or 2, skipping remaining tests in %s", __func__);
258 return;
259 }
260
261 test(m, "exec-cpuaffinity3.service", 0, CLD_EXITED);
262 }
263
264 static void test_exec_workingdirectory(Manager *m) {
265 assert_se(mkdir_p("/tmp/test-exec_workingdirectory", 0755) >= 0);
266
267 test(m, "exec-workingdirectory.service", 0, CLD_EXITED);
268 test(m, "exec-workingdirectory-trailing-dot.service", 0, CLD_EXITED);
269
270 (void) rm_rf("/tmp/test-exec_workingdirectory", REMOVE_ROOT|REMOVE_PHYSICAL);
271 }
272
273 static void test_exec_execsearchpath(Manager *m) {
274 assert_se(mkdir_p("/tmp/test-exec_execsearchpath", 0755) >= 0);
275
276 assert_se(copy_file("/bin/ls", "/tmp/test-exec_execsearchpath/ls_temp", 0, 0777, 0, 0, COPY_REPLACE) >= 0);
277
278 test(m, "exec-execsearchpath.service", 0, CLD_EXITED);
279
280 assert_se(rm_rf("/tmp/test-exec_execsearchpath", REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
281
282 test(m, "exec-execsearchpath.service", EXIT_EXEC, CLD_EXITED);
283 }
284
285 static void test_exec_execsearchpath_specifier(Manager *m) {
286 test(m, "exec-execsearchpath-unit-specifier.service", 0, CLD_EXITED);
287 }
288
289 static void test_exec_execsearchpath_environment(Manager *m) {
290 test(m, "exec-execsearchpath-environment.service", 0, CLD_EXITED);
291 test(m, "exec-execsearchpath-environment-path-set.service", 0, CLD_EXITED);
292 }
293
294 static void test_exec_execsearchpath_environment_files(Manager *m) {
295 static const char path_not_set[] =
296 "VAR1='word1 word2'\n"
297 "VAR2=word3 \n"
298 "# comment1\n"
299 "\n"
300 "; comment2\n"
301 " ; # comment3\n"
302 "line without an equal\n"
303 "VAR3='$word 5 6'\n"
304 "VAR4='new\nline'\n"
305 "VAR5=password\\with\\backslashes";
306
307 static const char path_set[] =
308 "VAR1='word1 word2'\n"
309 "VAR2=word3 \n"
310 "# comment1\n"
311 "\n"
312 "; comment2\n"
313 " ; # comment3\n"
314 "line without an equal\n"
315 "VAR3='$word 5 6'\n"
316 "VAR4='new\nline'\n"
317 "VAR5=password\\with\\backslashes\n"
318 "PATH=/usr";
319
320 int r;
321
322 r = write_string_file("/tmp/test-exec_execsearchpath_environmentfile.conf", path_not_set, WRITE_STRING_FILE_CREATE);
323
324 assert_se(r == 0);
325
326 test(m, "exec-execsearchpath-environmentfile.service", 0, CLD_EXITED);
327
328 (void) unlink("/tmp/test-exec_environmentfile.conf");
329
330
331 r = write_string_file("/tmp/test-exec_execsearchpath_environmentfile-set.conf", path_set, WRITE_STRING_FILE_CREATE);
332
333 assert_se(r == 0);
334
335 test(m, "exec-execsearchpath-environmentfile-set.service", 0, CLD_EXITED);
336
337 (void) unlink("/tmp/test-exec_environmentfile-set.conf");
338 }
339
340 static void test_exec_execsearchpath_passenvironment(Manager *m) {
341 assert_se(setenv("VAR1", "word1 word2", 1) == 0);
342 assert_se(setenv("VAR2", "word3", 1) == 0);
343 assert_se(setenv("VAR3", "$word 5 6", 1) == 0);
344 assert_se(setenv("VAR4", "new\nline", 1) == 0);
345 assert_se(setenv("VAR5", "passwordwithbackslashes", 1) == 0);
346
347 test(m, "exec-execsearchpath-passenvironment.service", 0, CLD_EXITED);
348
349 assert_se(setenv("PATH", "/usr", 1) == 0);
350 test(m, "exec-execsearchpath-passenvironment-set.service", 0, CLD_EXITED);
351
352 assert_se(unsetenv("VAR1") == 0);
353 assert_se(unsetenv("VAR2") == 0);
354 assert_se(unsetenv("VAR3") == 0);
355 assert_se(unsetenv("VAR4") == 0);
356 assert_se(unsetenv("VAR5") == 0);
357 assert_se(unsetenv("PATH") == 0);
358 }
359
360 static void test_exec_personality(Manager *m) {
361 #if defined(__x86_64__)
362 test(m, "exec-personality-x86-64.service", 0, CLD_EXITED);
363
364 #elif defined(__s390__)
365 test(m, "exec-personality-s390.service", 0, CLD_EXITED);
366
367 #elif defined(__powerpc64__)
368 # if __BYTE_ORDER == __BIG_ENDIAN
369 test(m, "exec-personality-ppc64.service", 0, CLD_EXITED);
370 # else
371 test(m, "exec-personality-ppc64le.service", 0, CLD_EXITED);
372 # endif
373
374 #elif defined(__aarch64__)
375 test(m, "exec-personality-aarch64.service", 0, CLD_EXITED);
376
377 #elif defined(__i386__)
378 test(m, "exec-personality-x86.service", 0, CLD_EXITED);
379 #elif defined(__loongarch64)
380 test(m, "exec-personality-loongarch64.service", 0, CLD_EXITED);
381 #else
382 log_notice("Unknown personality, skipping %s", __func__);
383 #endif
384 }
385
386 static void test_exec_ignoresigpipe(Manager *m) {
387 test(m, "exec-ignoresigpipe-yes.service", 0, CLD_EXITED);
388 test(m, "exec-ignoresigpipe-no.service", SIGPIPE, CLD_KILLED);
389 }
390
391 static void test_exec_privatetmp(Manager *m) {
392 assert_se(touch("/tmp/test-exec_privatetmp") >= 0);
393
394 test(m, "exec-privatetmp-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
395 test(m, "exec-privatetmp-no.service", 0, CLD_EXITED);
396 test(m, "exec-privatetmp-disabled-by-prefix.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
397
398 unlink("/tmp/test-exec_privatetmp");
399 }
400
401 static void test_exec_privatedevices(Manager *m) {
402 int r;
403
404 if (detect_container() > 0) {
405 log_notice("Testing in container, skipping %s", __func__);
406 return;
407 }
408 if (!is_inaccessible_available()) {
409 log_notice("Testing without inaccessible, skipping %s", __func__);
410 return;
411 }
412
413 test(m, "exec-privatedevices-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
414 test(m, "exec-privatedevices-no.service", 0, CLD_EXITED);
415 test(m, "exec-privatedevices-disabled-by-prefix.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
416 test(m, "exec-privatedevices-yes-with-group.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
417
418 /* We use capsh to test if the capabilities are
419 * properly set, so be sure that it exists */
420 r = find_executable("capsh", NULL);
421 if (r < 0) {
422 log_notice_errno(r, "Could not find capsh binary, skipping remaining tests in %s: %m", __func__);
423 return;
424 }
425
426 test(m, "exec-privatedevices-yes-capability-mknod.service", 0, CLD_EXITED);
427 test(m, "exec-privatedevices-no-capability-mknod.service", 0, CLD_EXITED);
428 test(m, "exec-privatedevices-yes-capability-sys-rawio.service", 0, CLD_EXITED);
429 test(m, "exec-privatedevices-no-capability-sys-rawio.service", 0, CLD_EXITED);
430 }
431
432 static void test_exec_protecthome(Manager *m) {
433 if (!can_unshare) {
434 log_notice("Cannot reliably unshare, skipping %s", __func__);
435 return;
436 }
437
438 test(m, "exec-protecthome-tmpfs-vs-protectsystem-strict.service", 0, CLD_EXITED);
439 }
440
441 static void test_exec_protectkernelmodules(Manager *m) {
442 int r;
443
444 if (detect_container() > 0) {
445 log_notice("Testing in container, skipping %s", __func__);
446 return;
447 }
448 if (!is_inaccessible_available()) {
449 log_notice("Testing without inaccessible, skipping %s", __func__);
450 return;
451 }
452
453 r = find_executable("capsh", NULL);
454 if (r < 0) {
455 log_notice_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
456 return;
457 }
458
459 test(m, "exec-protectkernelmodules-no-capabilities.service", 0, CLD_EXITED);
460 test(m, "exec-protectkernelmodules-yes-capabilities.service", 0, CLD_EXITED);
461 test(m, "exec-protectkernelmodules-yes-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
462 }
463
464 static void test_exec_readonlypaths(Manager *m) {
465
466 test(m, "exec-readonlypaths-simple.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
467
468 if (path_is_read_only_fs("/var") > 0) {
469 log_notice("Directory /var is readonly, skipping remaining tests in %s", __func__);
470 return;
471 }
472
473 test(m, "exec-readonlypaths.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
474 test(m, "exec-readonlypaths-with-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
475 test(m, "exec-readonlypaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
476 }
477
478 static void test_exec_readwritepaths(Manager *m) {
479
480 if (path_is_read_only_fs("/") > 0) {
481 log_notice("Root directory is readonly, skipping %s", __func__);
482 return;
483 }
484
485 test(m, "exec-readwritepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
486 }
487
488 static void test_exec_inaccessiblepaths(Manager *m) {
489
490 if (!is_inaccessible_available()) {
491 log_notice("Testing without inaccessible, skipping %s", __func__);
492 return;
493 }
494
495 test(m, "exec-inaccessiblepaths-sys.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
496
497 if (path_is_read_only_fs("/") > 0) {
498 log_notice("Root directory is readonly, skipping remaining tests in %s", __func__);
499 return;
500 }
501
502 test(m, "exec-inaccessiblepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
503 }
504
505 static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
506 char **result = userdata;
507 char buf[4096];
508 ssize_t l;
509
510 assert_se(s);
511 assert_se(fd >= 0);
512
513 l = read(fd, buf, sizeof(buf) - 1);
514 if (l < 0) {
515 if (errno == EAGAIN)
516 goto reenable;
517
518 return 0;
519 }
520 if (l == 0)
521 return 0;
522
523 buf[l] = '\0';
524 if (result)
525 assert_se(strextend(result, buf));
526 else
527 log_error("ldd: %s", buf);
528
529 reenable:
530 /* Re-enable the event source if we did not encounter EOF */
531 assert_se(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT) >= 0);
532 return 0;
533 }
534
535 static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
536 pid_t *pid = userdata;
537
538 assert_se(pid);
539
540 (void) kill(*pid, SIGKILL);
541
542 return 1;
543 }
544
545 static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
546 int ret = -EIO;
547
548 assert_se(si);
549
550 if (si->si_code == CLD_EXITED)
551 ret = si->si_status;
552
553 sd_event_exit(sd_event_source_get_event(s), ret);
554 return 1;
555 }
556
557 static int find_libraries(const char *exec, char ***ret) {
558 _cleanup_(sd_event_unrefp) sd_event *e = NULL;
559 _cleanup_(sd_event_source_unrefp) sd_event_source *sigchld_source = NULL;
560 _cleanup_(sd_event_source_unrefp) sd_event_source *stdout_source = NULL;
561 _cleanup_(sd_event_source_unrefp) sd_event_source *stderr_source = NULL;
562 _cleanup_close_pair_ int outpipe[2] = PIPE_EBADF, errpipe[2] = PIPE_EBADF;
563 _cleanup_strv_free_ char **libraries = NULL;
564 _cleanup_free_ char *result = NULL;
565 pid_t pid;
566 int r;
567
568 assert_se(exec);
569 assert_se(ret);
570
571 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
572
573 assert_se(pipe2(outpipe, O_NONBLOCK|O_CLOEXEC) == 0);
574 assert_se(pipe2(errpipe, O_NONBLOCK|O_CLOEXEC) == 0);
575
576 r = safe_fork("(spawn-ldd)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
577 assert_se(r >= 0);
578 if (r == 0) {
579 if (rearrange_stdio(-1, TAKE_FD(outpipe[1]), TAKE_FD(errpipe[1])) < 0)
580 _exit(EXIT_FAILURE);
581
582 (void) close_all_fds(NULL, 0);
583
584 execlp("ldd", "ldd", exec, NULL);
585 _exit(EXIT_FAILURE);
586 }
587
588 outpipe[1] = safe_close(outpipe[1]);
589 errpipe[1] = safe_close(errpipe[1]);
590
591 assert_se(sd_event_new(&e) >= 0);
592
593 assert_se(sd_event_add_time_relative(e, NULL, CLOCK_MONOTONIC,
594 10 * USEC_PER_SEC, USEC_PER_SEC, on_spawn_timeout, &pid) >= 0);
595 assert_se(sd_event_add_io(e, &stdout_source, outpipe[0], EPOLLIN, on_spawn_io, &result) >= 0);
596 assert_se(sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT) >= 0);
597 assert_se(sd_event_add_io(e, &stderr_source, errpipe[0], EPOLLIN, on_spawn_io, NULL) >= 0);
598 assert_se(sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT) >= 0);
599 assert_se(sd_event_add_child(e, &sigchld_source, pid, WEXITED, on_spawn_sigchld, NULL) >= 0);
600 /* SIGCHLD should be processed after IO is complete */
601 assert_se(sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1) >= 0);
602
603 assert_se(sd_event_loop(e) >= 0);
604
605 _cleanup_strv_free_ char **v = NULL;
606 assert_se(strv_split_newlines_full(&v, result, 0) >= 0);
607
608 STRV_FOREACH(q, v) {
609 _cleanup_free_ char *word = NULL;
610 const char *p = *q;
611
612 r = extract_first_word(&p, &word, NULL, 0);
613 assert_se(r >= 0);
614 if (r == 0)
615 continue;
616
617 if (path_is_absolute(word)) {
618 assert_se(strv_consume(&libraries, TAKE_PTR(word)) >= 0);
619 continue;
620 }
621
622 word = mfree(word);
623 r = extract_first_word(&p, &word, NULL, 0);
624 assert_se(r >= 0);
625 if (r == 0)
626 continue;
627
628 if (!streq_ptr(word, "=>"))
629 continue;
630
631 word = mfree(word);
632 r = extract_first_word(&p, &word, NULL, 0);
633 assert_se(r >= 0);
634 if (r == 0)
635 continue;
636
637 if (path_is_absolute(word)) {
638 assert_se(strv_consume(&libraries, TAKE_PTR(word)) >= 0);
639 continue;
640 }
641 }
642
643 *ret = TAKE_PTR(libraries);
644 return 0;
645 }
646
647 static void test_exec_mount_apivfs(Manager *m) {
648 _cleanup_free_ char *fullpath_touch = NULL, *fullpath_test = NULL, *data = NULL;
649 _cleanup_strv_free_ char **libraries = NULL, **libraries_test = NULL;
650 int r;
651
652 assert_se(user_runtime_unit_dir);
653
654 r = find_executable("touch", &fullpath_touch);
655 if (r < 0) {
656 log_notice_errno(r, "Skipping %s, could not find 'touch' command: %m", __func__);
657 return;
658 }
659 r = find_executable("test", &fullpath_test);
660 if (r < 0) {
661 log_notice_errno(r, "Skipping %s, could not find 'test' command: %m", __func__);
662 return;
663 }
664
665 assert_se(find_libraries(fullpath_touch, &libraries) >= 0);
666 assert_se(find_libraries(fullpath_test, &libraries_test) >= 0);
667 assert_se(strv_extend_strv(&libraries, libraries_test, true) >= 0);
668
669 assert_se(strextend(&data, "[Service]\n"));
670 assert_se(strextend(&data, "ExecStart=", fullpath_touch, " /aaa\n"));
671 assert_se(strextend(&data, "ExecStart=", fullpath_test, " -f /aaa\n"));
672 assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_touch, "\n"));
673 assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_test, "\n"));
674
675 STRV_FOREACH(p, libraries)
676 assert_se(strextend(&data, "BindReadOnlyPaths=", *p, "\n"));
677
678 assert_se(write_drop_in(user_runtime_unit_dir, "exec-mount-apivfs-no.service", 10, "bind-mount", data) >= 0);
679
680 assert_se(mkdir_p("/tmp/test-exec-mount-apivfs-no/root", 0755) >= 0);
681
682 test(m, "exec-mount-apivfs-no.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
683
684 (void) rm_rf("/tmp/test-exec-mount-apivfs-no/root", REMOVE_ROOT|REMOVE_PHYSICAL);
685 }
686
687 static void test_exec_noexecpaths(Manager *m) {
688
689 test(m, "exec-noexecpaths-simple.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
690 }
691
692 static void test_exec_temporaryfilesystem(Manager *m) {
693
694 test(m, "exec-temporaryfilesystem-options.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
695 test(m, "exec-temporaryfilesystem-ro.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
696 test(m, "exec-temporaryfilesystem-rw.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
697 test(m, "exec-temporaryfilesystem-usr.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
698 }
699
700 static void test_exec_systemcallfilter(Manager *m) {
701 #if HAVE_SECCOMP
702 int r;
703
704 if (!is_seccomp_available()) {
705 log_notice("Seccomp not available, skipping %s", __func__);
706 return;
707 }
708
709 test(m, "exec-systemcallfilter-not-failing.service", 0, CLD_EXITED);
710 test(m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED);
711 test(m, "exec-systemcallfilter-not-failing3.service", 0, CLD_EXITED);
712 test(m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED);
713 test(m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED);
714 test(m, "exec-systemcallfilter-failing3.service", SIGSYS, CLD_KILLED);
715
716 r = find_executable("python3", NULL);
717 if (r < 0) {
718 log_notice_errno(r, "Skipping remaining tests in %s, could not find python3 binary: %m", __func__);
719 return;
720 }
721
722 test(m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED);
723 test(m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED);
724 test(m, "exec-systemcallfilter-with-errno-multi.service", errno_from_name("EILSEQ"), CLD_EXITED);
725 test(m, "exec-systemcallfilter-with-errno-in-allow-list.service", errno_from_name("EILSEQ"), CLD_EXITED);
726 test(m, "exec-systemcallfilter-override-error-action.service", SIGSYS, CLD_KILLED);
727 test(m, "exec-systemcallfilter-override-error-action2.service", errno_from_name("EILSEQ"), CLD_EXITED);
728 #endif
729 }
730
731 static void test_exec_systemcallerrornumber(Manager *m) {
732 #if HAVE_SECCOMP
733 int r;
734
735 if (!is_seccomp_available()) {
736 log_notice("Seccomp not available, skipping %s", __func__);
737 return;
738 }
739
740 r = find_executable("python3", NULL);
741 if (r < 0) {
742 log_notice_errno(r, "Skipping %s, could not find python3 binary: %m", __func__);
743 return;
744 }
745
746 test(m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED);
747 test(m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED);
748 #endif
749 }
750
751 static void test_exec_restrictnamespaces(Manager *m) {
752 #if HAVE_SECCOMP
753 if (!is_seccomp_available()) {
754 log_notice("Seccomp not available, skipping %s", __func__);
755 return;
756 }
757
758 test(m, "exec-restrictnamespaces-no.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
759 test(m, "exec-restrictnamespaces-yes.service", 1, CLD_EXITED);
760 test(m, "exec-restrictnamespaces-mnt.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
761 test(m, "exec-restrictnamespaces-mnt-deny-list.service", 1, CLD_EXITED);
762 test(m, "exec-restrictnamespaces-merge-and.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
763 test(m, "exec-restrictnamespaces-merge-or.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
764 test(m, "exec-restrictnamespaces-merge-all.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
765 #endif
766 }
767
768 static void test_exec_systemcallfilter_system(Manager *m) {
769 /* Skip this particular test case when running under ASan, as
770 * LSan intermittently segfaults when accessing memory right
771 * after the test finishes. Generally, ASan & LSan don't like
772 * the seccomp stuff.
773 */
774 #if HAVE_SECCOMP && !HAS_FEATURE_ADDRESS_SANITIZER
775 if (!is_seccomp_available()) {
776 log_notice("Seccomp not available, skipping %s", __func__);
777 return;
778 }
779
780 test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED);
781
782 if (!check_nobody_user_and_group()) {
783 log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
784 return;
785 }
786
787 if (!STR_IN_SET(NOBODY_USER_NAME, "nobody", "nfsnobody")) {
788 log_notice("Unsupported nobody user name '%s', skipping remaining tests in %s", NOBODY_USER_NAME, __func__);
789 return;
790 }
791
792 test(m, "exec-systemcallfilter-system-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
793 #endif
794 }
795
796 static void test_exec_user(Manager *m) {
797 test(m, "exec-user.service", 0, CLD_EXITED);
798
799 if (!check_nobody_user_and_group()) {
800 log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
801 return;
802 }
803
804 if (!STR_IN_SET(NOBODY_USER_NAME, "nobody", "nfsnobody")) {
805 log_notice("Unsupported nobody user name '%s', skipping remaining tests in %s", NOBODY_USER_NAME, __func__);
806 return;
807 }
808
809 test(m, "exec-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
810 }
811
812 static void test_exec_group(Manager *m) {
813 test(m, "exec-group.service", 0, CLD_EXITED);
814
815 if (!check_nobody_user_and_group()) {
816 log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
817 return;
818 }
819
820 if (!STR_IN_SET(NOBODY_GROUP_NAME, "nobody", "nfsnobody", "nogroup")) {
821 log_notice("Unsupported nobody group name '%s', skipping remaining tests in %s", NOBODY_GROUP_NAME, __func__);
822 return;
823 }
824
825 test(m, "exec-group-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED);
826 }
827
828 static void test_exec_supplementarygroups(Manager *m) {
829 test(m, "exec-supplementarygroups.service", 0, CLD_EXITED);
830 test(m, "exec-supplementarygroups-single-group.service", 0, CLD_EXITED);
831 test(m, "exec-supplementarygroups-single-group-user.service", 0, CLD_EXITED);
832 test(m, "exec-supplementarygroups-multiple-groups-default-group-user.service", 0, CLD_EXITED);
833 test(m, "exec-supplementarygroups-multiple-groups-withgid.service", 0, CLD_EXITED);
834 test(m, "exec-supplementarygroups-multiple-groups-withuid.service", 0, CLD_EXITED);
835 }
836
837 static char* private_directory_bad(Manager *m) {
838 /* This mirrors setup_exec_directory(). */
839
840 for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
841 _cleanup_free_ char *p = NULL;
842 struct stat st;
843
844 assert_se(p = path_join(m->prefix[dt], "private"));
845
846 if (stat(p, &st) >= 0 &&
847 (st.st_mode & (S_IRWXG|S_IRWXO)))
848 return TAKE_PTR(p);
849 }
850
851 return NULL;
852 }
853
854 static void test_exec_dynamicuser(Manager *m) {
855 _cleanup_free_ char *bad = private_directory_bad(m);
856 if (bad) {
857 log_warning("%s: %s has bad permissions, skipping test.", __func__, bad);
858 return;
859 }
860
861 if (strstr_ptr(ci_environment(), "github-actions")) {
862 log_notice("%s: skipping test on GH Actions because of systemd/systemd#10337", __func__);
863 return;
864 }
865
866 test(m, "exec-dynamicuser-fixeduser.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
867 if (check_user_has_group_with_same_name("adm"))
868 test(m, "exec-dynamicuser-fixeduser-adm.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
869 if (check_user_has_group_with_same_name("games"))
870 test(m, "exec-dynamicuser-fixeduser-games.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
871 test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
872 test(m, "exec-dynamicuser-supplementarygroups.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
873 test(m, "exec-dynamicuser-statedir.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
874
875 (void) rm_rf("/var/lib/quux", REMOVE_ROOT|REMOVE_PHYSICAL);
876 (void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
877 (void) rm_rf("/var/lib/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
878 (void) rm_rf("/var/lib/waldo", REMOVE_ROOT|REMOVE_PHYSICAL);
879 (void) rm_rf("/var/lib/private/quux", REMOVE_ROOT|REMOVE_PHYSICAL);
880 (void) rm_rf("/var/lib/private/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
881 (void) rm_rf("/var/lib/private/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
882 (void) rm_rf("/var/lib/private/waldo", REMOVE_ROOT|REMOVE_PHYSICAL);
883
884 test(m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
885 test(m, "exec-dynamicuser-statedir-migrate-step2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
886 test(m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
887
888 (void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
889 (void) rm_rf("/var/lib/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
890 (void) rm_rf("/var/lib/private/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
891 (void) rm_rf("/var/lib/private/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
892
893 test(m, "exec-dynamicuser-runtimedirectory1.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
894 test(m, "exec-dynamicuser-runtimedirectory2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
895 test(m, "exec-dynamicuser-runtimedirectory3.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
896 }
897
898 static void test_exec_environment(Manager *m) {
899 test(m, "exec-environment-no-substitute.service", 0, CLD_EXITED);
900 test(m, "exec-environment.service", 0, CLD_EXITED);
901 test(m, "exec-environment-multiple.service", 0, CLD_EXITED);
902 test(m, "exec-environment-empty.service", 0, CLD_EXITED);
903 }
904
905 static void test_exec_environmentfile(Manager *m) {
906 static const char e[] =
907 "VAR1='word1 word2'\n"
908 "VAR2=word3 \n"
909 "# comment1\n"
910 "\n"
911 "; comment2\n"
912 " ; # comment3\n"
913 "line without an equal\n"
914 "VAR3='$word 5 6'\n"
915 "VAR4='new\nline'\n"
916 "VAR5=password\\with\\backslashes";
917 int r;
918
919 r = write_string_file("/tmp/test-exec_environmentfile.conf", e, WRITE_STRING_FILE_CREATE);
920 assert_se(r == 0);
921
922 test(m, "exec-environmentfile.service", 0, CLD_EXITED);
923
924 (void) unlink("/tmp/test-exec_environmentfile.conf");
925 }
926
927 static void test_exec_passenvironment(Manager *m) {
928 /* test-execute runs under MANAGER_USER which, by default, forwards all
929 * variables present in the environment, but only those that are
930 * present _at the time it is created_!
931 *
932 * So these PassEnvironment checks are still expected to work, since we
933 * are ensuring the variables are not present at manager creation (they
934 * are unset explicitly in main) and are only set here.
935 *
936 * This is still a good approximation of how a test for MANAGER_SYSTEM
937 * would work.
938 */
939 assert_se(setenv("VAR1", "word1 word2", 1) == 0);
940 assert_se(setenv("VAR2", "word3", 1) == 0);
941 assert_se(setenv("VAR3", "$word 5 6", 1) == 0);
942 assert_se(setenv("VAR4", "new\nline", 1) == 0);
943 assert_se(setenv("VAR5", "passwordwithbackslashes", 1) == 0);
944 test(m, "exec-passenvironment.service", 0, CLD_EXITED);
945 test(m, "exec-passenvironment-repeated.service", 0, CLD_EXITED);
946 test(m, "exec-passenvironment-empty.service", 0, CLD_EXITED);
947 assert_se(unsetenv("VAR1") == 0);
948 assert_se(unsetenv("VAR2") == 0);
949 assert_se(unsetenv("VAR3") == 0);
950 assert_se(unsetenv("VAR4") == 0);
951 assert_se(unsetenv("VAR5") == 0);
952 test(m, "exec-passenvironment-absent.service", 0, CLD_EXITED);
953 }
954
955 static void test_exec_umask(Manager *m) {
956 test(m, "exec-umask-default.service", 0, CLD_EXITED);
957 test(m, "exec-umask-0177.service", 0, CLD_EXITED);
958 }
959
960 static void test_exec_runtimedirectory(Manager *m) {
961 test(m, "exec-runtimedirectory.service", 0, CLD_EXITED);
962 test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED);
963 test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED);
964
965 if (!check_nobody_user_and_group()) {
966 log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
967 return;
968 }
969
970 if (!STR_IN_SET(NOBODY_GROUP_NAME, "nobody", "nfsnobody", "nogroup")) {
971 log_notice("Unsupported nobody group name '%s', skipping remaining tests in %s", NOBODY_GROUP_NAME, __func__);
972 return;
973 }
974
975 test(m, "exec-runtimedirectory-owner-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED);
976 }
977
978 static void test_exec_capabilityboundingset(Manager *m) {
979 int r;
980
981 r = find_executable("capsh", NULL);
982 if (r < 0) {
983 log_notice_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
984 return;
985 }
986
987 if (have_effective_cap(CAP_CHOWN) <= 0 ||
988 have_effective_cap(CAP_FOWNER) <= 0 ||
989 have_effective_cap(CAP_KILL) <= 0) {
990 log_notice("Skipping %s, this process does not have enough capabilities", __func__);
991 return;
992 }
993
994 test(m, "exec-capabilityboundingset-simple.service", 0, CLD_EXITED);
995 test(m, "exec-capabilityboundingset-reset.service", 0, CLD_EXITED);
996 test(m, "exec-capabilityboundingset-merge.service", 0, CLD_EXITED);
997 test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED);
998 }
999
1000 static void test_exec_basic(Manager *m) {
1001 test(m, "exec-basic.service", 0, CLD_EXITED);
1002 }
1003
1004 static void test_exec_ambientcapabilities(Manager *m) {
1005 int r;
1006
1007 /* Check if the kernel has support for ambient capabilities. Run
1008 * the tests only if that's the case. Clearing all ambient
1009 * capabilities is fine, since we are expecting them to be unset
1010 * in the first place for the tests. */
1011 r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
1012 if (r < 0 && IN_SET(errno, EINVAL, EOPNOTSUPP, ENOSYS)) {
1013 log_notice("Skipping %s, the kernel does not support ambient capabilities", __func__);
1014 return;
1015 }
1016
1017 if (have_effective_cap(CAP_CHOWN) <= 0 ||
1018 have_effective_cap(CAP_NET_RAW) <= 0) {
1019 log_notice("Skipping %s, this process does not have enough capabilities", __func__);
1020 return;
1021 }
1022
1023 test(m, "exec-ambientcapabilities.service", 0, CLD_EXITED);
1024 test(m, "exec-ambientcapabilities-merge.service", 0, CLD_EXITED);
1025
1026 if (!check_nobody_user_and_group()) {
1027 log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
1028 return;
1029 }
1030
1031 if (!STR_IN_SET(NOBODY_USER_NAME, "nobody", "nfsnobody")) {
1032 log_notice("Unsupported nobody user name '%s', skipping remaining tests in %s", NOBODY_USER_NAME, __func__);
1033 return;
1034 }
1035
1036 test(m, "exec-ambientcapabilities-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
1037 test(m, "exec-ambientcapabilities-merge-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
1038 }
1039
1040 static void test_exec_privatenetwork(Manager *m) {
1041 int r;
1042
1043 r = find_executable("ip", NULL);
1044 if (r < 0) {
1045 log_notice_errno(r, "Skipping %s, could not find ip binary: %m", __func__);
1046 return;
1047 }
1048
1049 test(m, "exec-privatenetwork-yes.service", can_unshare ? 0 : EXIT_NETWORK, CLD_EXITED);
1050 }
1051
1052 static void test_exec_oomscoreadjust(Manager *m) {
1053 test(m, "exec-oomscoreadjust-positive.service", 0, CLD_EXITED);
1054
1055 if (detect_container() > 0) {
1056 log_notice("Testing in container, skipping remaining tests in %s", __func__);
1057 return;
1058 }
1059 test(m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED);
1060 }
1061
1062 static void test_exec_ioschedulingclass(Manager *m) {
1063 test(m, "exec-ioschedulingclass-none.service", 0, CLD_EXITED);
1064 test(m, "exec-ioschedulingclass-idle.service", 0, CLD_EXITED);
1065 test(m, "exec-ioschedulingclass-best-effort.service", 0, CLD_EXITED);
1066
1067 if (detect_container() > 0) {
1068 log_notice("Testing in container, skipping remaining tests in %s", __func__);
1069 return;
1070 }
1071 test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED);
1072 }
1073
1074 static void test_exec_unsetenvironment(Manager *m) {
1075 test(m, "exec-unsetenvironment.service", 0, CLD_EXITED);
1076 }
1077
1078 static void test_exec_specifier(Manager *m) {
1079 test(m, "exec-specifier.service", 0, CLD_EXITED);
1080 test(m, "exec-specifier@foo-bar.service", 0, CLD_EXITED);
1081 test(m, "exec-specifier-interpolation.service", 0, CLD_EXITED);
1082 test(m, "exec-specifier-credentials-dir.service", 0, CLD_EXITED);
1083 }
1084
1085 static void test_exec_standardinput(Manager *m) {
1086 test(m, "exec-standardinput-data.service", 0, CLD_EXITED);
1087 test(m, "exec-standardinput-file.service", 0, CLD_EXITED);
1088 test(m, "exec-standardinput-file-cat.service", 0, CLD_EXITED);
1089 }
1090
1091 static void test_exec_standardoutput(Manager *m) {
1092 test(m, "exec-standardoutput-file.service", 0, CLD_EXITED);
1093 }
1094
1095 static void test_exec_standardoutput_append(Manager *m) {
1096 test(m, "exec-standardoutput-append.service", 0, CLD_EXITED);
1097 }
1098
1099 static void test_exec_standardoutput_truncate(Manager *m) {
1100 test(m, "exec-standardoutput-truncate.service", 0, CLD_EXITED);
1101 }
1102
1103 static void test_exec_condition(Manager *m) {
1104 test_service(m, "exec-condition-failed.service", SERVICE_FAILURE_EXIT_CODE);
1105 test_service(m, "exec-condition-skip.service", SERVICE_SKIP_CONDITION);
1106 }
1107
1108 static void test_exec_umask_namespace(Manager *m) {
1109 /* exec-specifier-credentials-dir.service creates /run/credentials and enables implicit
1110 * InaccessiblePath= for the directory for all later services with mount namespace. */
1111 if (!is_inaccessible_available()) {
1112 log_notice("Testing without inaccessible, skipping %s", __func__);
1113 return;
1114 }
1115 test(m, "exec-umask-namespace.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
1116 }
1117
1118 typedef struct test_entry {
1119 test_function_t f;
1120 const char *name;
1121 } test_entry;
1122
1123 #define entry(x) {x, #x}
1124
1125 static int run_tests(LookupScope scope, const test_entry tests[], char **patterns) {
1126 _cleanup_(manager_freep) Manager *m = NULL;
1127 int r;
1128
1129 assert_se(tests);
1130
1131 r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m);
1132 m->default_std_output = EXEC_OUTPUT_NULL; /* don't rely on host journald */
1133 if (manager_errno_skip_test(r))
1134 return log_tests_skipped_errno(r, "manager_new");
1135 assert_se(r >= 0);
1136 assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
1137
1138 for (const test_entry *test = tests; test->f; test++)
1139 if (strv_fnmatch_or_empty(patterns, test->name, FNM_NOESCAPE))
1140 test->f(m);
1141 else
1142 log_info("Skipping %s because it does not match any pattern.", test->name);
1143
1144 return 0;
1145 }
1146
1147 int main(int argc, char *argv[]) {
1148 _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
1149
1150 static const test_entry user_tests[] = {
1151 entry(test_exec_basic),
1152 entry(test_exec_ambientcapabilities),
1153 entry(test_exec_bindpaths),
1154 entry(test_exec_capabilityboundingset),
1155 entry(test_exec_condition),
1156 entry(test_exec_cpuaffinity),
1157 entry(test_exec_environment),
1158 entry(test_exec_environmentfile),
1159 entry(test_exec_group),
1160 entry(test_exec_ignoresigpipe),
1161 entry(test_exec_inaccessiblepaths),
1162 entry(test_exec_ioschedulingclass),
1163 entry(test_exec_mount_apivfs),
1164 entry(test_exec_noexecpaths),
1165 entry(test_exec_oomscoreadjust),
1166 entry(test_exec_passenvironment),
1167 entry(test_exec_personality),
1168 entry(test_exec_privatedevices),
1169 entry(test_exec_privatenetwork),
1170 entry(test_exec_privatetmp),
1171 entry(test_exec_protecthome),
1172 entry(test_exec_protectkernelmodules),
1173 entry(test_exec_readonlypaths),
1174 entry(test_exec_readwritepaths),
1175 entry(test_exec_restrictnamespaces),
1176 entry(test_exec_runtimedirectory),
1177 entry(test_exec_standardinput),
1178 entry(test_exec_standardoutput),
1179 entry(test_exec_standardoutput_append),
1180 entry(test_exec_standardoutput_truncate),
1181 entry(test_exec_supplementarygroups),
1182 entry(test_exec_systemcallerrornumber),
1183 entry(test_exec_systemcallfilter),
1184 entry(test_exec_temporaryfilesystem),
1185 entry(test_exec_umask),
1186 entry(test_exec_unsetenvironment),
1187 entry(test_exec_user),
1188 entry(test_exec_workingdirectory),
1189 entry(test_exec_execsearchpath),
1190 entry(test_exec_execsearchpath_environment),
1191 entry(test_exec_execsearchpath_environment_files),
1192 entry(test_exec_execsearchpath_passenvironment),
1193 {},
1194 };
1195 static const test_entry system_tests[] = {
1196 entry(test_exec_dynamicuser),
1197 entry(test_exec_specifier),
1198 entry(test_exec_execsearchpath_specifier),
1199 entry(test_exec_systemcallfilter_system),
1200 entry(test_exec_umask_namespace),
1201 {},
1202 };
1203 int r;
1204
1205 test_setup_logging(LOG_DEBUG);
1206
1207 #if HAS_FEATURE_ADDRESS_SANITIZER
1208 if (strstr_ptr(ci_environment(), "travis") || strstr_ptr(ci_environment(), "github-actions")) {
1209 log_notice("Running on Travis CI/GH Actions under ASan, skipping, see https://github.com/systemd/systemd/issues/10696");
1210 return EXIT_TEST_SKIP;
1211 }
1212 #endif
1213
1214 assert_se(unsetenv("USER") == 0);
1215 assert_se(unsetenv("LOGNAME") == 0);
1216 assert_se(unsetenv("SHELL") == 0);
1217 assert_se(unsetenv("HOME") == 0);
1218 assert_se(unsetenv("TMPDIR") == 0);
1219
1220 can_unshare = have_namespaces();
1221
1222 /* It is needed otherwise cgroup creation fails */
1223 if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0)
1224 return log_tests_skipped("not privileged");
1225
1226 r = enter_cgroup_subroot(NULL);
1227 if (r == -ENOMEDIUM)
1228 return log_tests_skipped("cgroupfs not available");
1229
1230 _cleanup_free_ char *unit_dir = NULL, *unit_paths = NULL;
1231 assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
1232 assert_se(runtime_dir = setup_fake_runtime_dir());
1233 assert_se(user_runtime_unit_dir = path_join(runtime_dir, "systemd/user"));
1234 assert_se(unit_paths = strjoin(unit_dir, ":", user_runtime_unit_dir));
1235 assert_se(set_unit_path(unit_paths) >= 0);
1236
1237 /* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test
1238 * cases, otherwise (and if they are present in the environment),
1239 * `manager_default_environment` will copy them into the default
1240 * environment which is passed to each created job, which will make the
1241 * tests that expect those not to be present to fail.
1242 */
1243 assert_se(unsetenv("VAR1") == 0);
1244 assert_se(unsetenv("VAR2") == 0);
1245 assert_se(unsetenv("VAR3") == 0);
1246
1247 r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1);
1248 if (r != 0)
1249 return r;
1250
1251 r = run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1);
1252 if (r != 0)
1253 return r;
1254
1255 #if HAVE_SECCOMP
1256 /* The following tests are for 1beab8b0d0ff2d7d1436b52d4a0c3d56dc908962. */
1257 if (!is_seccomp_available()) {
1258 log_notice("Seccomp not available, skipping unshare() filtered tests.");
1259 return 0;
1260 }
1261
1262 _cleanup_hashmap_free_ Hashmap *s = NULL;
1263 assert_se(s = hashmap_new(NULL));
1264 r = seccomp_syscall_resolve_name("unshare");
1265 assert_se(r != __NR_SCMP_ERROR);
1266 assert_se(hashmap_put(s, UINT32_TO_PTR(r + 1), INT_TO_PTR(-1)) >= 0);
1267 assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EOPNOTSUPP), true) >= 0);
1268 assert_se(unshare(CLONE_NEWNS) < 0);
1269 assert_se(errno == EOPNOTSUPP);
1270
1271 can_unshare = false;
1272
1273 r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1);
1274 if (r != 0)
1275 return r;
1276
1277 return run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1);
1278 #else
1279 return 0;
1280 #endif
1281 }