]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-execute.c
tree-wide: introduce PIPE_EBADF macro
[thirdparty/systemd.git] / src / test / test-execute.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
281e05b6 2
ff4ca461 3#include <stdio.h>
70d7aea5 4#include <sys/prctl.h>
ff4ca461 5#include <sys/types.h>
281e05b6 6
42867dfe
YW
7#include "sd-event.h"
8
b7856f92 9#include "capability-util.h"
4e79aeaa 10#include "cpu-set-util.h"
8c35c10d 11#include "copy.h"
42867dfe 12#include "dropin.h"
b4891260 13#include "errno-list.h"
42867dfe 14#include "fd-util.h"
03bd70dd 15#include "fileio.h"
f4f15635 16#include "fs-util.h"
281e05b6 17#include "macro.h"
f4f15635 18#include "manager.h"
a22692d7 19#include "missing_prctl.h"
281e05b6 20#include "mkdir.h"
ff4ca461 21#include "path-util.h"
42867dfe 22#include "process-util.h"
c6878637 23#include "rm-rf.h"
349cc4a5 24#if HAVE_SECCOMP
83f12b27
FS
25#include "seccomp-util.h"
26#endif
57b7a260 27#include "service.h"
42867dfe
YW
28#include "signal-util.h"
29#include "static-destruct.h"
34b86909 30#include "stat-util.h"
cc100a5a 31#include "tests.h"
8c35c10d 32#include "tmpfile-util.h"
f4f15635 33#include "unit.h"
57c2efa0 34#include "user-util.h"
4dd4cb8f 35#include "virt.h"
281e05b6 36
42867dfe 37static char *user_runtime_unit_dir = NULL;
5f00dc4d 38static bool can_unshare;
b7172f34 39
42867dfe
YW
40STATIC_DESTRUCTOR_REGISTER(user_runtime_unit_dir, freep);
41
281e05b6
RC
42typedef void (*test_function_t)(Manager *m);
43
c3ab2c38
LP
44static 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
31cd5f63 50static void wait_for_service_finish(Manager *m, Unit *unit) {
281e05b6
RC
51 Service *service = NULL;
52 usec_t ts;
8adb3d63 53 usec_t timeout = 2 * USEC_PER_MINUTE;
281e05b6
RC
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);
ec2ce0c5 62 while (!IN_SET(service->state, SERVICE_DEAD, SERVICE_FAILED)) {
281e05b6
RC
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);
1acacd73 72 r = unit_kill(unit, KILL_ALL, SIGKILL, NULL);
f7f8e8cb
EV
73 if (r < 0)
74 log_error_errno(r, "Failed to kill %s: %m", unit->id);
281e05b6
RC
75 exit(EXIT_FAILURE);
76 }
77 }
31cd5f63
AZ
78}
79
fe65d692
ZJS
80static void check_main_result(const char *file, unsigned line, const char *func,
81 Manager *m, Unit *unit, int status_expected, int code_expected) {
31cd5f63
AZ
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);
281e05b6 90 exec_status_dump(&service->main_exec_status, stdout, "\t");
18f8c5d4 91
c3ab2c38 92 if (cld_dumped_to_killed(service->main_exec_status.code) != cld_dumped_to_killed(code_expected)) {
aa272751
YW
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),
6aed6a11
ZJS
95 service->main_exec_status.code, code_expected);
96 abort();
18f8c5d4
LP
97 }
98
99 if (service->main_exec_status.status != status_expected) {
aa272751
YW
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),
18f8c5d4
LP
102 service->main_exec_status.status, status_expected);
103 abort();
6aed6a11 104 }
281e05b6
RC
105}
106
fe65d692
ZJS
107static void check_service_result(const char *file, unsigned line, const char *func,
108 Manager *m, Unit *unit, ServiceResult result_expected) {
31cd5f63
AZ
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) {
aa272751
YW
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),
31cd5f63
AZ
121 service_result_to_string(service->result),
122 service_result_to_string(result_expected));
123 abort();
124 }
125}
126
57c2efa0
YW
127static 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
167invalid:
168 cache = 0;
169 return false;
170}
171
9f82d685
YW
172static bool check_user_has_group_with_same_name(const char *name) {
173 struct passwd *p;
174 struct group *g;
175
f21b863e 176 assert_se(name);
9f82d685
YW
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
6086d2da 191static bool is_inaccessible_available(void) {
6086d2da 192 FOREACH_STRING(p,
5980d463
ZJS
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")
6086d2da
DP
199 if (access(p, F_OK) < 0)
200 return false;
6086d2da
DP
201
202 return true;
203}
204
fe65d692
ZJS
205static void _test(const char *file, unsigned line, const char *func,
206 Manager *m, const char *unit_name, int status_expected, int code_expected) {
281e05b6
RC
207 Unit *unit;
208
209 assert_se(unit_name);
210
ba412430 211 assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
48b92b37 212 assert_se(unit_start(unit, NULL) >= 0);
fe65d692 213 check_main_result(file, line, func, m, unit, status_expected, code_expected);
31cd5f63 214}
fe65d692
ZJS
215#define test(m, unit_name, status_expected, code_expected) \
216 _test(PROJECT_FILE, __LINE__, __func__, m, unit_name, status_expected, code_expected)
31cd5f63 217
fe65d692
ZJS
218static void _test_service(const char *file, unsigned line, const char *func,
219 Manager *m, const char *unit_name, ServiceResult result_expected) {
31cd5f63
AZ
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);
48b92b37 225 assert_se(unit_start(unit, NULL) >= 0);
fe65d692 226 check_service_result(file, line, func, m, unit, result_expected);
281e05b6 227}
fe65d692
ZJS
228#define test_service(m, unit_name, result_expected) \
229 _test_service(PROJECT_FILE, __LINE__, __func__, m, unit_name, result_expected)
281e05b6 230
f0e018e7
YW
231static 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);
d053b72b 234
fe65d692 235 test(m, "exec-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
d053b72b 236
f0e018e7
YW
237 (void) rm_rf("/tmp/test-exec-bindpaths", REMOVE_ROOT|REMOVE_PHYSICAL);
238 (void) rm_rf("/tmp/test-exec-bindreadonlypaths", REMOVE_ROOT|REMOVE_PHYSICAL);
d053b72b
YW
239}
240
4e79aeaa 241static void test_exec_cpuaffinity(Manager *m) {
167a776d 242 _cleanup_(cpu_set_reset) CPUSet c = {};
4e79aeaa 243
167a776d
ZJS
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);
4e79aeaa 246
167a776d 247 if (!CPU_ISSET_S(0, c.allocated, c.set)) {
4e79aeaa
YW
248 log_notice("Cannot use CPU 0, skipping %s", __func__);
249 return;
250 }
251
fe65d692
ZJS
252 test(m, "exec-cpuaffinity1.service", 0, CLD_EXITED);
253 test(m, "exec-cpuaffinity2.service", 0, CLD_EXITED);
4e79aeaa 254
167a776d
ZJS
255 if (!CPU_ISSET_S(1, c.allocated, c.set) ||
256 !CPU_ISSET_S(2, c.allocated, c.set)) {
4e79aeaa
YW
257 log_notice("Cannot use CPU 1 or 2, skipping remaining tests in %s", __func__);
258 return;
259 }
260
fe65d692 261 test(m, "exec-cpuaffinity3.service", 0, CLD_EXITED);
4e79aeaa
YW
262}
263
281e05b6
RC
264static void test_exec_workingdirectory(Manager *m) {
265 assert_se(mkdir_p("/tmp/test-exec_workingdirectory", 0755) >= 0);
266
fe65d692
ZJS
267 test(m, "exec-workingdirectory.service", 0, CLD_EXITED);
268 test(m, "exec-workingdirectory-trailing-dot.service", 0, CLD_EXITED);
281e05b6 269
c6878637 270 (void) rm_rf("/tmp/test-exec_workingdirectory", REMOVE_ROOT|REMOVE_PHYSICAL);
281e05b6
RC
271}
272
8c35c10d 273static 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
285static void test_exec_execsearchpath_specifier(Manager *m) {
286 test(m, "exec-execsearchpath-unit-specifier.service", 0, CLD_EXITED);
287}
288
289static 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
294static 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
340static 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
281e05b6 360static void test_exec_personality(Manager *m) {
281e05b6 361#if defined(__x86_64__)
fe65d692 362 test(m, "exec-personality-x86-64.service", 0, CLD_EXITED);
7517f51e
HB
363
364#elif defined(__s390__)
fe65d692 365 test(m, "exec-personality-s390.service", 0, CLD_EXITED);
7517f51e 366
12591863
JS
367#elif defined(__powerpc64__)
368# if __BYTE_ORDER == __BIG_ENDIAN
fe65d692 369 test(m, "exec-personality-ppc64.service", 0, CLD_EXITED);
12591863 370# else
fe65d692 371 test(m, "exec-personality-ppc64le.service", 0, CLD_EXITED);
12591863
JS
372# endif
373
374#elif defined(__aarch64__)
fe65d692 375 test(m, "exec-personality-aarch64.service", 0, CLD_EXITED);
12591863 376
5798eb4c 377#elif defined(__i386__)
fe65d692 378 test(m, "exec-personality-x86.service", 0, CLD_EXITED);
646b0112
XW
379#elif defined(__loongarch64)
380 test(m, "exec-personality-loongarch64.service", 0, CLD_EXITED);
f0e018e7
YW
381#else
382 log_notice("Unknown personality, skipping %s", __func__);
281e05b6
RC
383#endif
384}
385
386static void test_exec_ignoresigpipe(Manager *m) {
fe65d692
ZJS
387 test(m, "exec-ignoresigpipe-yes.service", 0, CLD_EXITED);
388 test(m, "exec-ignoresigpipe-no.service", SIGPIPE, CLD_KILLED);
281e05b6
RC
389}
390
391static void test_exec_privatetmp(Manager *m) {
392 assert_se(touch("/tmp/test-exec_privatetmp") >= 0);
393
fe65d692
ZJS
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);
281e05b6
RC
397
398 unlink("/tmp/test-exec_privatetmp");
399}
400
401static void test_exec_privatedevices(Manager *m) {
f0e018e7
YW
402 int r;
403
4dd4cb8f 404 if (detect_container() > 0) {
f0e018e7 405 log_notice("Testing in container, skipping %s", __func__);
4dd4cb8f
SM
406 return;
407 }
6086d2da 408 if (!is_inaccessible_available()) {
f0e018e7 409 log_notice("Testing without inaccessible, skipping %s", __func__);
6086d2da
DP
410 return;
411 }
412
fe65d692
ZJS
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);
6086d2da 417
0608ba98
ZJS
418 /* We use capsh to test if the capabilities are
419 * properly set, so be sure that it exists */
f7bc0c32 420 r = find_executable("capsh", NULL);
0608ba98 421 if (r < 0) {
5cd33ccc 422 log_notice_errno(r, "Could not find capsh binary, skipping remaining tests in %s: %m", __func__);
6086d2da
DP
423 return;
424 }
425
fe65d692
ZJS
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);
615a1f4b
DH
430}
431
7e46b29b 432static void test_exec_protecthome(Manager *m) {
9ca58284
ZJS
433 if (!can_unshare) {
434 log_notice("Cannot reliably unshare, skipping %s", __func__);
435 return;
436 }
437
fe65d692 438 test(m, "exec-protecthome-tmpfs-vs-protectsystem-strict.service", 0, CLD_EXITED);
7e46b29b
YW
439}
440
4982dbcc 441static void test_exec_protectkernelmodules(Manager *m) {
0608ba98
ZJS
442 int r;
443
3ae33295 444 if (detect_container() > 0) {
f0e018e7 445 log_notice("Testing in container, skipping %s", __func__);
3ae33295
DH
446 return;
447 }
6086d2da 448 if (!is_inaccessible_available()) {
f0e018e7 449 log_notice("Testing without inaccessible, skipping %s", __func__);
6086d2da
DP
450 return;
451 }
3ae33295 452
f7bc0c32 453 r = find_executable("capsh", NULL);
0608ba98 454 if (r < 0) {
5cd33ccc 455 log_notice_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
0608ba98
ZJS
456 return;
457 }
458
fe65d692
ZJS
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);
3ae33295
DH
462}
463
f78b36f0 464static void test_exec_readonlypaths(Manager *m) {
34b86909 465
fe65d692 466 test(m, "exec-readonlypaths-simple.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
f0e018e7
YW
467
468 if (path_is_read_only_fs("/var") > 0) {
469 log_notice("Directory /var is readonly, skipping remaining tests in %s", __func__);
34b86909 470 return;
f0e018e7 471 }
34b86909 472
fe65d692
ZJS
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);
cdfbd1fb
DH
476}
477
478static void test_exec_readwritepaths(Manager *m) {
34b86909 479
f0e018e7
YW
480 if (path_is_read_only_fs("/") > 0) {
481 log_notice("Root directory is readonly, skipping %s", __func__);
34b86909 482 return;
f0e018e7 483 }
34b86909 484
fe65d692 485 test(m, "exec-readwritepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
cdfbd1fb
DH
486}
487
488static void test_exec_inaccessiblepaths(Manager *m) {
34b86909 489
f0e018e7
YW
490 if (!is_inaccessible_available()) {
491 log_notice("Testing without inaccessible, skipping %s", __func__);
34b86909 492 return;
f0e018e7 493 }
34b86909 494
fe65d692 495 test(m, "exec-inaccessiblepaths-sys.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
f78b36f0 496
f0e018e7
YW
497 if (path_is_read_only_fs("/") > 0) {
498 log_notice("Root directory is readonly, skipping remaining tests in %s", __func__);
af4af186
EV
499 return;
500 }
501
fe65d692 502 test(m, "exec-inaccessiblepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
c090d74d
TR
503}
504
42867dfe
YW
505static 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
f21b863e
YW
510 assert_se(s);
511 assert_se(fd >= 0);
42867dfe
YW
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
529reenable:
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
535static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
536 pid_t *pid = userdata;
537
f21b863e 538 assert_se(pid);
42867dfe
YW
539
540 (void) kill(*pid, SIGKILL);
541
542 return 1;
543}
544
545static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
546 int ret = -EIO;
547
f21b863e 548 assert_se(si);
42867dfe
YW
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
557static 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;
19ee48a6 562 _cleanup_close_pair_ int outpipe[2] = PIPE_EBADF, errpipe[2] = PIPE_EBADF;
42867dfe
YW
563 _cleanup_strv_free_ char **libraries = NULL;
564 _cleanup_free_ char *result = NULL;
565 pid_t pid;
566 int r;
567
f21b863e
YW
568 assert_se(exec);
569 assert_se(ret);
42867dfe
YW
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) {
aedec452 579 if (rearrange_stdio(-1, TAKE_FD(outpipe[1]), TAKE_FD(errpipe[1])) < 0)
42867dfe
YW
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
42867dfe
YW
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
647static 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
f21b863e 652 assert_se(user_runtime_unit_dir);
42867dfe
YW
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
42867dfe
YW
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
ddc155b2
TM
687static void test_exec_noexecpaths(Manager *m) {
688
689 test(m, "exec-noexecpaths-simple.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
690}
691
4cac89bd
YW
692static void test_exec_temporaryfilesystem(Manager *m) {
693
fe65d692
ZJS
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);
4cac89bd
YW
698}
699
281e05b6 700static void test_exec_systemcallfilter(Manager *m) {
349cc4a5 701#if HAVE_SECCOMP
738c74d7
YW
702 int r;
703
f0e018e7
YW
704 if (!is_seccomp_available()) {
705 log_notice("Seccomp not available, skipping %s", __func__);
83f12b27 706 return;
f0e018e7
YW
707 }
708
fe65d692
ZJS
709 test(m, "exec-systemcallfilter-not-failing.service", 0, CLD_EXITED);
710 test(m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED);
e975a945 711 test(m, "exec-systemcallfilter-not-failing3.service", 0, CLD_EXITED);
fe65d692
ZJS
712 test(m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED);
713 test(m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED);
e975a945 714 test(m, "exec-systemcallfilter-failing3.service", SIGSYS, CLD_KILLED);
738c74d7 715
f7bc0c32 716 r = find_executable("python3", NULL);
738c74d7
YW
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
fe65d692
ZJS
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);
a62f651b 725 test(m, "exec-systemcallfilter-with-errno-in-allow-list.service", errno_from_name("EILSEQ"), CLD_EXITED);
fe65d692
ZJS
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);
281e05b6
RC
728#endif
729}
730
731static void test_exec_systemcallerrornumber(Manager *m) {
349cc4a5 732#if HAVE_SECCOMP
738c74d7
YW
733 int r;
734
f0e018e7
YW
735 if (!is_seccomp_available()) {
736 log_notice("Seccomp not available, skipping %s", __func__);
7a18854f 737 return;
f0e018e7
YW
738 }
739
f7bc0c32 740 r = find_executable("python3", NULL);
738c74d7
YW
741 if (r < 0) {
742 log_notice_errno(r, "Skipping %s, could not find python3 binary: %m", __func__);
743 return;
744 }
745
fe65d692
ZJS
746 test(m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED);
747 test(m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED);
281e05b6
RC
748#endif
749}
750
f0e018e7 751static void test_exec_restrictnamespaces(Manager *m) {
349cc4a5 752#if HAVE_SECCOMP
f0e018e7
YW
753 if (!is_seccomp_available()) {
754 log_notice("Seccomp not available, skipping %s", __func__);
97e60383 755 return;
f0e018e7 756 }
97e60383 757
fe65d692
ZJS
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);
97e60383
DH
765#endif
766}
767
f0e018e7 768static void test_exec_systemcallfilter_system(Manager *m) {
4967da2d
FS
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
f0e018e7
YW
775 if (!is_seccomp_available()) {
776 log_notice("Seccomp not available, skipping %s", __func__);
83f12b27 777 return;
f0e018e7 778 }
57c2efa0 779
fe65d692 780 test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED);
69b07407 781
57c2efa0 782 if (!check_nobody_user_and_group()) {
5cd33ccc 783 log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
57c2efa0
YW
784 return;
785 }
786
69b07407 787 if (!STR_IN_SET(NOBODY_USER_NAME, "nobody", "nfsnobody")) {
5cd33ccc 788 log_notice("Unsupported nobody user name '%s', skipping remaining tests in %s", NOBODY_USER_NAME, __func__);
69b07407
YW
789 return;
790 }
791
fe65d692 792 test(m, "exec-systemcallfilter-system-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
19c0b0b9
RC
793#endif
794}
795
281e05b6 796static void test_exec_user(Manager *m) {
fe65d692 797 test(m, "exec-user.service", 0, CLD_EXITED);
69b07407 798
57c2efa0 799 if (!check_nobody_user_and_group()) {
5cd33ccc 800 log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
57c2efa0
YW
801 return;
802 }
803
69b07407 804 if (!STR_IN_SET(NOBODY_USER_NAME, "nobody", "nfsnobody")) {
5cd33ccc 805 log_notice("Unsupported nobody user name '%s', skipping remaining tests in %s", NOBODY_USER_NAME, __func__);
69b07407
YW
806 return;
807 }
808
fe65d692 809 test(m, "exec-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
281e05b6
RC
810}
811
812static void test_exec_group(Manager *m) {
fe65d692 813 test(m, "exec-group.service", 0, CLD_EXITED);
69b07407 814
57c2efa0 815 if (!check_nobody_user_and_group()) {
5cd33ccc 816 log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
57c2efa0
YW
817 return;
818 }
819
69b07407 820 if (!STR_IN_SET(NOBODY_GROUP_NAME, "nobody", "nfsnobody", "nogroup")) {
5cd33ccc 821 log_notice("Unsupported nobody group name '%s', skipping remaining tests in %s", NOBODY_GROUP_NAME, __func__);
69b07407
YW
822 return;
823 }
824
fe65d692 825 test(m, "exec-group-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED);
281e05b6
RC
826}
827
f0e018e7 828static void test_exec_supplementarygroups(Manager *m) {
fe65d692
ZJS
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);
86b838ea
DH
835}
836
cced2b98
ZJS
837static 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
f0e018e7 854static void test_exec_dynamicuser(Manager *m) {
cced2b98
ZJS
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 }
b7172f34 860
d1b74295
FS
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
fe65d692 866 test(m, "exec-dynamicuser-fixeduser.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
9f82d685 867 if (check_user_has_group_with_same_name("adm"))
fe65d692 868 test(m, "exec-dynamicuser-fixeduser-adm.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
9f82d685 869 if (check_user_has_group_with_same_name("games"))
fe65d692
ZJS
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);
028f3a7f 874
f5939651 875 (void) rm_rf("/var/lib/quux", REMOVE_ROOT|REMOVE_PHYSICAL);
1c587309
YW
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);
f5939651
TM
878 (void) rm_rf("/var/lib/waldo", REMOVE_ROOT|REMOVE_PHYSICAL);
879 (void) rm_rf("/var/lib/private/quux", REMOVE_ROOT|REMOVE_PHYSICAL);
1c587309
YW
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);
f5939651 882 (void) rm_rf("/var/lib/private/waldo", REMOVE_ROOT|REMOVE_PHYSICAL);
1c587309 883
fe65d692
ZJS
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);
1c587309 887
028f3a7f
YW
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);
e4c01fe6 892
fe65d692
ZJS
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);
2b9ac11e
DH
896}
897
281e05b6 898static void test_exec_environment(Manager *m) {
fe65d692
ZJS
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);
281e05b6
RC
903}
904
03bd70dd
RC
905static 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"
9b796f35 914 "VAR3='$word 5 6'\n"
b6887d7a
JH
915 "VAR4='new\nline'\n"
916 "VAR5=password\\with\\backslashes";
03bd70dd
RC
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
fe65d692 922 test(m, "exec-environmentfile.service", 0, CLD_EXITED);
03bd70dd 923
f0e018e7 924 (void) unlink("/tmp/test-exec_environmentfile.conf");
03bd70dd
RC
925}
926
4c80d201 927static void test_exec_passenvironment(Manager *m) {
e1abca2e
FB
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 */
4c80d201
FB
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);
9b796f35 942 assert_se(setenv("VAR4", "new\nline", 1) == 0);
b6887d7a 943 assert_se(setenv("VAR5", "passwordwithbackslashes", 1) == 0);
fe65d692
ZJS
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);
4c80d201
FB
947 assert_se(unsetenv("VAR1") == 0);
948 assert_se(unsetenv("VAR2") == 0);
949 assert_se(unsetenv("VAR3") == 0);
9b796f35 950 assert_se(unsetenv("VAR4") == 0);
b6887d7a 951 assert_se(unsetenv("VAR5") == 0);
fe65d692 952 test(m, "exec-passenvironment-absent.service", 0, CLD_EXITED);
4c80d201
FB
953}
954
27c5347c 955static void test_exec_umask(Manager *m) {
fe65d692
ZJS
956 test(m, "exec-umask-default.service", 0, CLD_EXITED);
957 test(m, "exec-umask-0177.service", 0, CLD_EXITED);
27c5347c
RC
958}
959
cc3ddc85 960static void test_exec_runtimedirectory(Manager *m) {
fe65d692
ZJS
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);
57c2efa0
YW
964
965 if (!check_nobody_user_and_group()) {
5cd33ccc 966 log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
69b07407
YW
967 return;
968 }
969
970 if (!STR_IN_SET(NOBODY_GROUP_NAME, "nobody", "nfsnobody", "nogroup")) {
5cd33ccc 971 log_notice("Unsupported nobody group name '%s', skipping remaining tests in %s", NOBODY_GROUP_NAME, __func__);
57c2efa0
YW
972 return;
973 }
974
fe65d692 975 test(m, "exec-runtimedirectory-owner-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED);
ff4ca461
RC
976}
977
978static void test_exec_capabilityboundingset(Manager *m) {
979 int r;
980
f7bc0c32 981 r = find_executable("capsh", NULL);
ff4ca461 982 if (r < 0) {
5cd33ccc 983 log_notice_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
ff4ca461
RC
984 return;
985 }
986
b7856f92
YW
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
fe65d692
ZJS
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);
cc3ddc85
RC
998}
999
5008da1e 1000static void test_exec_basic(Manager *m) {
fe65d692 1001 test(m, "exec-basic.service", 0, CLD_EXITED);
5008da1e
ZJS
1002}
1003
b6dc25ee 1004static void test_exec_ambientcapabilities(Manager *m) {
70d7aea5
IP
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);
f0e018e7 1012 if (r < 0 && IN_SET(errno, EINVAL, EOPNOTSUPP, ENOSYS)) {
5cd33ccc 1013 log_notice("Skipping %s, the kernel does not support ambient capabilities", __func__);
f0e018e7
YW
1014 return;
1015 }
1016
e5ba1d32 1017 if (have_effective_cap(CAP_CHOWN) <= 0 ||
b7856f92
YW
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
fe65d692
ZJS
1023 test(m, "exec-ambientcapabilities.service", 0, CLD_EXITED);
1024 test(m, "exec-ambientcapabilities-merge.service", 0, CLD_EXITED);
69b07407 1025
57c2efa0 1026 if (!check_nobody_user_and_group()) {
5cd33ccc 1027 log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
69b07407
YW
1028 return;
1029 }
1030
1031 if (!STR_IN_SET(NOBODY_USER_NAME, "nobody", "nfsnobody")) {
5cd33ccc 1032 log_notice("Unsupported nobody user name '%s', skipping remaining tests in %s", NOBODY_USER_NAME, __func__);
57c2efa0
YW
1033 return;
1034 }
1035
fe65d692
ZJS
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);
70d7aea5
IP
1038}
1039
63447f11
RC
1040static void test_exec_privatenetwork(Manager *m) {
1041 int r;
1042
f7bc0c32 1043 r = find_executable("ip", NULL);
63447f11 1044 if (r < 0) {
5cd33ccc 1045 log_notice_errno(r, "Skipping %s, could not find ip binary: %m", __func__);
63447f11
RC
1046 return;
1047 }
1048
fe65d692 1049 test(m, "exec-privatenetwork-yes.service", can_unshare ? 0 : EXIT_NETWORK, CLD_EXITED);
63447f11
RC
1050}
1051
c388dfea 1052static void test_exec_oomscoreadjust(Manager *m) {
fe65d692 1053 test(m, "exec-oomscoreadjust-positive.service", 0, CLD_EXITED);
642d1a6d
YW
1054
1055 if (detect_container() > 0) {
1056 log_notice("Testing in container, skipping remaining tests in %s", __func__);
1057 return;
1058 }
fe65d692 1059 test(m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED);
c388dfea
RC
1060}
1061
a6226758 1062static void test_exec_ioschedulingclass(Manager *m) {
fe65d692
ZJS
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);
642d1a6d
YW
1066
1067 if (detect_container() > 0) {
1068 log_notice("Testing in container, skipping remaining tests in %s", __func__);
1069 return;
1070 }
fe65d692 1071 test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED);
a6226758
RC
1072}
1073
f0e018e7 1074static void test_exec_unsetenvironment(Manager *m) {
fe65d692 1075 test(m, "exec-unsetenvironment.service", 0, CLD_EXITED);
42cc99d5
LP
1076}
1077
9672b583 1078static void test_exec_specifier(Manager *m) {
fe65d692
ZJS
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);
43b9b205 1082 test(m, "exec-specifier-credentials-dir.service", 0, CLD_EXITED);
9672b583
LP
1083}
1084
f0e018e7 1085static void test_exec_standardinput(Manager *m) {
fe65d692
ZJS
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);
666d7877
LP
1089}
1090
566b7d23 1091static void test_exec_standardoutput(Manager *m) {
fe65d692 1092 test(m, "exec-standardoutput-file.service", 0, CLD_EXITED);
566b7d23
ZD
1093}
1094
1095static void test_exec_standardoutput_append(Manager *m) {
fe65d692 1096 test(m, "exec-standardoutput-append.service", 0, CLD_EXITED);
566b7d23
ZD
1097}
1098
8d7dab1f
LW
1099static void test_exec_standardoutput_truncate(Manager *m) {
1100 test(m, "exec-standardoutput-truncate.service", 0, CLD_EXITED);
1101}
1102
31cd5f63 1103static void test_exec_condition(Manager *m) {
fe65d692
ZJS
1104 test_service(m, "exec-condition-failed.service", SERVICE_FAILURE_EXIT_CODE);
1105 test_service(m, "exec-condition-skip.service", SERVICE_SKIP_CONDITION);
31cd5f63
AZ
1106}
1107
875afa02 1108static void test_exec_umask_namespace(Manager *m) {
bfd67106
YW
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 }
875afa02
LP
1115 test(m, "exec-umask-namespace.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
1116}
1117
9efb9631
ZJS
1118typedef struct test_entry {
1119 test_function_t f;
1120 const char *name;
1121} test_entry;
1122
1123#define entry(x) {x, #x}
1124
b380b643 1125static int run_tests(LookupScope scope, const test_entry tests[], char **patterns) {
c70cac54 1126 _cleanup_(manager_freep) Manager *m = NULL;
19c0b0b9
RC
1127 int r;
1128
1129 assert_se(tests);
1130
e8112e67 1131 r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m);
e56a8790 1132 m->default_std_output = EXEC_OUTPUT_NULL; /* don't rely on host journald */
5eecb103 1133 if (manager_errno_skip_test(r))
730d989a 1134 return log_tests_skipped_errno(r, "manager_new");
19c0b0b9 1135 assert_se(r >= 0);
2a7cf953 1136 assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
19c0b0b9 1137
f90d2d7b 1138 for (const test_entry *test = tests; test->f; test++)
9efb9631
ZJS
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);
19c0b0b9 1143
19c0b0b9
RC
1144 return 0;
1145}
1146
281e05b6 1147int main(int argc, char *argv[]) {
ac1f08b9 1148 _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
9efb9631
ZJS
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),
31cd5f63 1155 entry(test_exec_condition),
9efb9631
ZJS
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),
42867dfe 1163 entry(test_exec_mount_apivfs),
ddc155b2 1164 entry(test_exec_noexecpaths),
9efb9631
ZJS
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),
8d7dab1f 1180 entry(test_exec_standardoutput_truncate),
9efb9631
ZJS
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),
8c35c10d 1189 entry(test_exec_execsearchpath),
1190 entry(test_exec_execsearchpath_environment),
1191 entry(test_exec_execsearchpath_environment_files),
1192 entry(test_exec_execsearchpath_passenvironment),
9efb9631 1193 {},
281e05b6 1194 };
9efb9631
ZJS
1195 static const test_entry system_tests[] = {
1196 entry(test_exec_dynamicuser),
1197 entry(test_exec_specifier),
8c35c10d 1198 entry(test_exec_execsearchpath_specifier),
9efb9631 1199 entry(test_exec_systemcallfilter_system),
875afa02 1200 entry(test_exec_umask_namespace),
9efb9631 1201 {},
19c0b0b9 1202 };
281e05b6
RC
1203 int r;
1204
6d7c4033 1205 test_setup_logging(LOG_DEBUG);
281e05b6 1206
0df54921 1207#if HAS_FEATURE_ADDRESS_SANITIZER
f1a8fed2
FS
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");
176ceb2c
EV
1210 return EXIT_TEST_SKIP;
1211 }
1212#endif
1213
44ee03d1
ZJS
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);
2482f88d 1219
5f00dc4d
LP
1220 can_unshare = have_namespaces();
1221
607ff5f9 1222 /* It is needed otherwise cgroup creation fails */
c75370cc
LP
1223 if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0)
1224 return log_tests_skipped("not privileged");
607ff5f9 1225
64ad9e08 1226 r = enter_cgroup_subroot(NULL);
317bb217
ZJS
1227 if (r == -ENOMEDIUM)
1228 return log_tests_skipped("cgroupfs not available");
8c759b33 1229
42867dfe 1230 _cleanup_free_ char *unit_dir = NULL, *unit_paths = NULL;
7b432953 1231 assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
ac1f08b9 1232 assert_se(runtime_dir = setup_fake_runtime_dir());
42867dfe
YW
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);
281e05b6 1236
e1abca2e
FB
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
b380b643 1247 r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1);
b7172f34
YW
1248 if (r != 0)
1249 return r;
1250
b380b643 1251 r = run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1);
b7172f34
YW
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
2bd061a4 1262 _cleanup_hashmap_free_ Hashmap *s = NULL;
b7172f34
YW
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
b380b643 1273 r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1);
19c0b0b9
RC
1274 if (r != 0)
1275 return r;
281e05b6 1276
b380b643 1277 return run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1);
b7172f34
YW
1278#else
1279 return 0;
1280#endif
281e05b6 1281}