]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-execute.c
test-execute: cleanup
[thirdparty/systemd.git] / src / test / test-execute.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2014 Ronny Chevalier
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <grp.h>
22 #include <pwd.h>
23 #include <stdio.h>
24 #include <sys/prctl.h>
25 #include <sys/types.h>
26
27 #include "errno-list.h"
28 #include "fileio.h"
29 #include "fs-util.h"
30 #include "macro.h"
31 #include "manager.h"
32 #include "mkdir.h"
33 #include "path-util.h"
34 #include "rm-rf.h"
35 #if HAVE_SECCOMP
36 #include "seccomp-util.h"
37 #endif
38 #include "stat-util.h"
39 #include "test-helper.h"
40 #include "tests.h"
41 #include "unit.h"
42 #include "util.h"
43 #include "virt.h"
44
45 typedef void (*test_function_t)(Manager *m);
46
47 static void check(Manager *m, Unit *unit, int status_expected, int code_expected) {
48 Service *service = NULL;
49 usec_t ts;
50 usec_t timeout = 2 * USEC_PER_MINUTE;
51
52 assert_se(m);
53 assert_se(unit);
54
55 service = SERVICE(unit);
56 printf("%s\n", unit->id);
57 exec_context_dump(&service->exec_context, stdout, "\t");
58 ts = now(CLOCK_MONOTONIC);
59 while (!IN_SET(service->state, SERVICE_DEAD, SERVICE_FAILED)) {
60 int r;
61 usec_t n;
62
63 r = sd_event_run(m->event, 100 * USEC_PER_MSEC);
64 assert_se(r >= 0);
65
66 n = now(CLOCK_MONOTONIC);
67 if (ts + timeout < n) {
68 log_error("Test timeout when testing %s", unit->id);
69 exit(EXIT_FAILURE);
70 }
71 }
72 exec_status_dump(&service->main_exec_status, stdout, "\t");
73 assert_se(service->main_exec_status.status == status_expected);
74 assert_se(service->main_exec_status.code == code_expected);
75 }
76
77 static bool is_inaccessible_available(void) {
78 char *p;
79
80 FOREACH_STRING(p,
81 "/run/systemd/inaccessible/reg",
82 "/run/systemd/inaccessible/dir",
83 "/run/systemd/inaccessible/chr",
84 "/run/systemd/inaccessible/blk",
85 "/run/systemd/inaccessible/fifo",
86 "/run/systemd/inaccessible/sock"
87 ) {
88 if (access(p, F_OK) < 0)
89 return false;
90 }
91
92 return true;
93 }
94
95 static void test(Manager *m, const char *unit_name, int status_expected, int code_expected) {
96 Unit *unit;
97
98 assert_se(unit_name);
99
100 assert_se(manager_load_unit(m, unit_name, NULL, NULL, &unit) >= 0);
101 assert_se(UNIT_VTABLE(unit)->start(unit) >= 0);
102 check(m, unit, status_expected, code_expected);
103 }
104
105 static void test_exec_bindpaths(Manager *m) {
106 assert_se(mkdir_p("/tmp/test-exec-bindpaths", 0755) >= 0);
107 assert_se(mkdir_p("/tmp/test-exec-bindreadonlypaths", 0755) >= 0);
108
109 test(m, "exec-bindpaths.service", 0, CLD_EXITED);
110
111 (void) rm_rf("/tmp/test-exec-bindpaths", REMOVE_ROOT|REMOVE_PHYSICAL);
112 (void) rm_rf("/tmp/test-exec-bindreadonlypaths", REMOVE_ROOT|REMOVE_PHYSICAL);
113 }
114
115 static void test_exec_workingdirectory(Manager *m) {
116 assert_se(mkdir_p("/tmp/test-exec_workingdirectory", 0755) >= 0);
117
118 test(m, "exec-workingdirectory.service", 0, CLD_EXITED);
119
120 (void) rm_rf("/tmp/test-exec_workingdirectory", REMOVE_ROOT|REMOVE_PHYSICAL);
121 }
122
123 static void test_exec_personality(Manager *m) {
124 #if defined(__x86_64__)
125 test(m, "exec-personality-x86-64.service", 0, CLD_EXITED);
126
127 #elif defined(__s390__)
128 test(m, "exec-personality-s390.service", 0, CLD_EXITED);
129
130 #elif defined(__powerpc64__)
131 # if __BYTE_ORDER == __BIG_ENDIAN
132 test(m, "exec-personality-ppc64.service", 0, CLD_EXITED);
133 # else
134 test(m, "exec-personality-ppc64le.service", 0, CLD_EXITED);
135 # endif
136
137 #elif defined(__aarch64__)
138 test(m, "exec-personality-aarch64.service", 0, CLD_EXITED);
139
140 #elif defined(__i386__)
141 test(m, "exec-personality-x86.service", 0, CLD_EXITED);
142 #else
143 log_notice("Unknown personality, skipping %s", __func__);
144 #endif
145 }
146
147 static void test_exec_ignoresigpipe(Manager *m) {
148 test(m, "exec-ignoresigpipe-yes.service", 0, CLD_EXITED);
149 test(m, "exec-ignoresigpipe-no.service", SIGPIPE, CLD_KILLED);
150 }
151
152 static void test_exec_privatetmp(Manager *m) {
153 assert_se(touch("/tmp/test-exec_privatetmp") >= 0);
154
155 test(m, "exec-privatetmp-yes.service", 0, CLD_EXITED);
156 test(m, "exec-privatetmp-no.service", 0, CLD_EXITED);
157
158 unlink("/tmp/test-exec_privatetmp");
159 }
160
161 static void test_exec_privatedevices(Manager *m) {
162 int r;
163
164 if (detect_container() > 0) {
165 log_notice("Testing in container, skipping %s", __func__);
166 return;
167 }
168 if (!is_inaccessible_available()) {
169 log_notice("Testing without inaccessible, skipping %s", __func__);
170 return;
171 }
172
173 test(m, "exec-privatedevices-yes.service", 0, CLD_EXITED);
174 test(m, "exec-privatedevices-no.service", 0, CLD_EXITED);
175
176 /* We use capsh to test if the capabilities are
177 * properly set, so be sure that it exists */
178 r = find_binary("capsh", NULL);
179 if (r < 0) {
180 log_error_errno(r, "Could not find capsh binary, skipping remaining tests in %s: %m", __func__);
181 return;
182 }
183
184 test(m, "exec-privatedevices-yes-capability-mknod.service", 0, CLD_EXITED);
185 test(m, "exec-privatedevices-no-capability-mknod.service", 0, CLD_EXITED);
186 test(m, "exec-privatedevices-yes-capability-sys-rawio.service", 0, CLD_EXITED);
187 test(m, "exec-privatedevices-no-capability-sys-rawio.service", 0, CLD_EXITED);
188 }
189
190 static void test_exec_protectkernelmodules(Manager *m) {
191 int r;
192
193 if (detect_container() > 0) {
194 log_notice("Testing in container, skipping %s", __func__);
195 return;
196 }
197 if (!is_inaccessible_available()) {
198 log_notice("Testing without inaccessible, skipping %s", __func__);
199 return;
200 }
201
202 r = find_binary("capsh", NULL);
203 if (r < 0) {
204 log_error_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
205 return;
206 }
207
208 test(m, "exec-protectkernelmodules-no-capabilities.service", 0, CLD_EXITED);
209 test(m, "exec-protectkernelmodules-yes-capabilities.service", 0, CLD_EXITED);
210 test(m, "exec-protectkernelmodules-yes-mount-propagation.service", 0, CLD_EXITED);
211 }
212
213 static void test_exec_readonlypaths(Manager *m) {
214
215 test(m, "exec-readonlypaths-simple.service", 0, CLD_EXITED);
216
217 if (path_is_read_only_fs("/var") > 0) {
218 log_notice("Directory /var is readonly, skipping remaining tests in %s", __func__);
219 return;
220 }
221
222 test(m, "exec-readonlypaths.service", 0, CLD_EXITED);
223 test(m, "exec-readonlypaths-mount-propagation.service", 0, CLD_EXITED);
224 test(m, "exec-readonlypaths-with-bindpaths.service", 0, CLD_EXITED);
225 }
226
227 static void test_exec_readwritepaths(Manager *m) {
228
229 if (path_is_read_only_fs("/") > 0) {
230 log_notice("Root directory is readonly, skipping %s", __func__);
231 return;
232 }
233
234 test(m, "exec-readwritepaths-mount-propagation.service", 0, CLD_EXITED);
235 }
236
237 static void test_exec_inaccessiblepaths(Manager *m) {
238
239 if (!is_inaccessible_available()) {
240 log_notice("Testing without inaccessible, skipping %s", __func__);
241 return;
242 }
243
244 test(m, "exec-inaccessiblepaths-proc.service", 0, CLD_EXITED);
245
246 if (path_is_read_only_fs("/") > 0) {
247 log_notice("Root directory is readonly, skipping remaining tests in %s", __func__);
248 return;
249 }
250
251 test(m, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED);
252 }
253
254 static void test_exec_systemcallfilter(Manager *m) {
255 #if HAVE_SECCOMP
256 if (!is_seccomp_available()) {
257 log_notice("Seccomp not available, skipping %s", __func__);
258 return;
259 }
260
261 test(m, "exec-systemcallfilter-not-failing.service", 0, CLD_EXITED);
262 test(m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED);
263 test(m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED);
264 test(m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED);
265 test(m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED);
266 test(m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED);
267 #endif
268 }
269
270 static void test_exec_systemcallerrornumber(Manager *m) {
271 #if HAVE_SECCOMP
272 if (!is_seccomp_available()) {
273 log_notice("Seccomp not available, skipping %s", __func__);
274 return;
275 }
276
277 test(m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED);
278 test(m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED);
279 #endif
280 }
281
282 static void test_exec_restrictnamespaces(Manager *m) {
283 #if HAVE_SECCOMP
284 if (!is_seccomp_available()) {
285 log_notice("Seccomp not available, skipping %s", __func__);
286 return;
287 }
288
289 test(m, "exec-restrictnamespaces-no.service", 0, CLD_EXITED);
290 test(m, "exec-restrictnamespaces-yes.service", 1, CLD_EXITED);
291 test(m, "exec-restrictnamespaces-mnt.service", 0, CLD_EXITED);
292 test(m, "exec-restrictnamespaces-mnt-blacklist.service", 1, CLD_EXITED);
293 #endif
294 }
295
296 static void test_exec_systemcallfilter_system(Manager *m) {
297 #if HAVE_SECCOMP
298 if (!is_seccomp_available()) {
299 log_notice("Seccomp not available, skipping %s", __func__);
300 return;
301 }
302
303 if (getpwnam("nobody"))
304 test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED);
305 else if (getpwnam("nfsnobody"))
306 test(m, "exec-systemcallfilter-system-user-nfsnobody.service", 0, CLD_EXITED);
307 else
308 log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__);
309 #endif
310 }
311
312 static void test_exec_user(Manager *m) {
313 if (getpwnam("nobody"))
314 test(m, "exec-user.service", 0, CLD_EXITED);
315 else if (getpwnam("nfsnobody"))
316 test(m, "exec-user-nfsnobody.service", 0, CLD_EXITED);
317 else
318 log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__);
319 }
320
321 static void test_exec_group(Manager *m) {
322 if (getgrnam("nobody"))
323 test(m, "exec-group.service", 0, CLD_EXITED);
324 else if (getgrnam("nfsnobody"))
325 test(m, "exec-group-nfsnobody.service", 0, CLD_EXITED);
326 else
327 log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__);
328 }
329
330 static void test_exec_supplementarygroups(Manager *m) {
331 test(m, "exec-supplementarygroups.service", 0, CLD_EXITED);
332 test(m, "exec-supplementarygroups-single-group.service", 0, CLD_EXITED);
333 test(m, "exec-supplementarygroups-single-group-user.service", 0, CLD_EXITED);
334 test(m, "exec-supplementarygroups-multiple-groups-default-group-user.service", 0, CLD_EXITED);
335 test(m, "exec-supplementarygroups-multiple-groups-withgid.service", 0, CLD_EXITED);
336 test(m, "exec-supplementarygroups-multiple-groups-withuid.service", 0, CLD_EXITED);
337 }
338
339 static void test_exec_dynamicuser(Manager *m) {
340 test(m, "exec-dynamicuser-fixeduser.service", 0, CLD_EXITED);
341 test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", 0, CLD_EXITED);
342 test(m, "exec-dynamicuser-supplementarygroups.service", 0, CLD_EXITED);
343 test(m, "exec-dynamicuser-statedir.service", 0, CLD_EXITED);
344 }
345
346 static void test_exec_environment(Manager *m) {
347 test(m, "exec-environment.service", 0, CLD_EXITED);
348 test(m, "exec-environment-multiple.service", 0, CLD_EXITED);
349 test(m, "exec-environment-empty.service", 0, CLD_EXITED);
350 }
351
352 static void test_exec_environmentfile(Manager *m) {
353 static const char e[] =
354 "VAR1='word1 word2'\n"
355 "VAR2=word3 \n"
356 "# comment1\n"
357 "\n"
358 "; comment2\n"
359 " ; # comment3\n"
360 "line without an equal\n"
361 "VAR3='$word 5 6'\n";
362 int r;
363
364 r = write_string_file("/tmp/test-exec_environmentfile.conf", e, WRITE_STRING_FILE_CREATE);
365 assert_se(r == 0);
366
367 test(m, "exec-environmentfile.service", 0, CLD_EXITED);
368
369 (void) unlink("/tmp/test-exec_environmentfile.conf");
370 }
371
372 static void test_exec_passenvironment(Manager *m) {
373 /* test-execute runs under MANAGER_USER which, by default, forwards all
374 * variables present in the environment, but only those that are
375 * present _at the time it is created_!
376 *
377 * So these PassEnvironment checks are still expected to work, since we
378 * are ensuring the variables are not present at manager creation (they
379 * are unset explicitly in main) and are only set here.
380 *
381 * This is still a good approximation of how a test for MANAGER_SYSTEM
382 * would work.
383 */
384 assert_se(setenv("VAR1", "word1 word2", 1) == 0);
385 assert_se(setenv("VAR2", "word3", 1) == 0);
386 assert_se(setenv("VAR3", "$word 5 6", 1) == 0);
387 test(m, "exec-passenvironment.service", 0, CLD_EXITED);
388 test(m, "exec-passenvironment-repeated.service", 0, CLD_EXITED);
389 test(m, "exec-passenvironment-empty.service", 0, CLD_EXITED);
390 assert_se(unsetenv("VAR1") == 0);
391 assert_se(unsetenv("VAR2") == 0);
392 assert_se(unsetenv("VAR3") == 0);
393 test(m, "exec-passenvironment-absent.service", 0, CLD_EXITED);
394 }
395
396 static void test_exec_umask(Manager *m) {
397 test(m, "exec-umask-default.service", 0, CLD_EXITED);
398 test(m, "exec-umask-0177.service", 0, CLD_EXITED);
399 }
400
401 static void test_exec_runtimedirectory(Manager *m) {
402 test(m, "exec-runtimedirectory.service", 0, CLD_EXITED);
403 test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED);
404 if (getgrnam("nobody"))
405 test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED);
406 else if (getgrnam("nfsnobody"))
407 test(m, "exec-runtimedirectory-owner-nfsnobody.service", 0, CLD_EXITED);
408 else
409 log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__);
410 }
411
412 static void test_exec_capabilityboundingset(Manager *m) {
413 int r;
414
415 r = find_binary("capsh", NULL);
416 if (r < 0) {
417 log_error_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
418 return;
419 }
420
421 test(m, "exec-capabilityboundingset-simple.service", 0, CLD_EXITED);
422 test(m, "exec-capabilityboundingset-reset.service", 0, CLD_EXITED);
423 test(m, "exec-capabilityboundingset-merge.service", 0, CLD_EXITED);
424 test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED);
425 }
426
427 static void test_exec_capabilityambientset(Manager *m) {
428 int r;
429
430 /* Check if the kernel has support for ambient capabilities. Run
431 * the tests only if that's the case. Clearing all ambient
432 * capabilities is fine, since we are expecting them to be unset
433 * in the first place for the tests. */
434 r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
435 if (r < 0 && IN_SET(errno, EINVAL, EOPNOTSUPP, ENOSYS)) {
436 log_error("Skipping %s, the kernel does not support ambient capabilities", __func__);
437 return;
438 }
439
440 if (getpwnam("nobody")) {
441 test(m, "exec-capabilityambientset.service", 0, CLD_EXITED);
442 test(m, "exec-capabilityambientset-merge.service", 0, CLD_EXITED);
443 } else if (getpwnam("nfsnobody")) {
444 test(m, "exec-capabilityambientset-nfsnobody.service", 0, CLD_EXITED);
445 test(m, "exec-capabilityambientset-merge-nfsnobody.service", 0, CLD_EXITED);
446 } else
447 log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__);
448 }
449
450 static void test_exec_privatenetwork(Manager *m) {
451 int r;
452
453 r = find_binary("ip", NULL);
454 if (r < 0) {
455 log_error_errno(r, "Skipping %s, could not find ip binary: %m", __func__);
456 return;
457 }
458
459 test(m, "exec-privatenetwork-yes.service", 0, CLD_EXITED);
460 }
461
462 static void test_exec_oomscoreadjust(Manager *m) {
463 test(m, "exec-oomscoreadjust-positive.service", 0, CLD_EXITED);
464 test(m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED);
465 }
466
467 static void test_exec_ioschedulingclass(Manager *m) {
468 test(m, "exec-ioschedulingclass-none.service", 0, CLD_EXITED);
469 test(m, "exec-ioschedulingclass-idle.service", 0, CLD_EXITED);
470 test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED);
471 test(m, "exec-ioschedulingclass-best-effort.service", 0, CLD_EXITED);
472 }
473
474 static void test_exec_unsetenvironment(Manager *m) {
475 test(m, "exec-unsetenvironment.service", 0, CLD_EXITED);
476 }
477
478 static void test_exec_specifier(Manager *m) {
479 test(m, "exec-specifier.service", 0, CLD_EXITED);
480 test(m, "exec-specifier@foo-bar.service", 0, CLD_EXITED);
481 test(m, "exec-specifier-interpolation.service", 0, CLD_EXITED);
482 }
483
484 static void test_exec_standardinput(Manager *m) {
485 test(m, "exec-standardinput-data.service", 0, CLD_EXITED);
486 test(m, "exec-standardinput-file.service", 0, CLD_EXITED);
487 }
488
489 static int run_tests(UnitFileScope scope, const test_function_t *tests) {
490 const test_function_t *test = NULL;
491 Manager *m = NULL;
492 int r;
493
494 assert_se(tests);
495
496 r = manager_new(scope, MANAGER_TEST_RUN_MINIMAL, &m);
497 if (MANAGER_SKIP_TEST(r)) {
498 log_notice_errno(r, "Skipping test: manager_new: %m");
499 return EXIT_TEST_SKIP;
500 }
501 assert_se(r >= 0);
502 assert_se(manager_startup(m, NULL, NULL) >= 0);
503
504 for (test = tests; test && *test; test++)
505 (*test)(m);
506
507 manager_free(m);
508
509 return 0;
510 }
511
512 int main(int argc, char *argv[]) {
513 static const test_function_t user_tests[] = {
514 test_exec_bindpaths,
515 test_exec_capabilityambientset,
516 test_exec_capabilityboundingset,
517 test_exec_environment,
518 test_exec_environmentfile,
519 test_exec_group,
520 test_exec_ignoresigpipe,
521 test_exec_inaccessiblepaths,
522 test_exec_ioschedulingclass,
523 test_exec_oomscoreadjust,
524 test_exec_passenvironment,
525 test_exec_personality,
526 test_exec_privatedevices,
527 test_exec_privatenetwork,
528 test_exec_privatetmp,
529 test_exec_protectkernelmodules,
530 test_exec_readonlypaths,
531 test_exec_readwritepaths,
532 test_exec_restrictnamespaces,
533 test_exec_runtimedirectory,
534 test_exec_standardinput,
535 test_exec_supplementarygroups,
536 test_exec_systemcallerrornumber,
537 test_exec_systemcallfilter,
538 test_exec_umask,
539 test_exec_unsetenvironment,
540 test_exec_user,
541 test_exec_workingdirectory,
542 NULL,
543 };
544 static const test_function_t system_tests[] = {
545 test_exec_dynamicuser,
546 test_exec_specifier,
547 test_exec_systemcallfilter_system,
548 NULL,
549 };
550 int r;
551
552 log_set_max_level(LOG_DEBUG);
553 log_parse_environment();
554 log_open();
555
556 (void) unsetenv("USER");
557 (void) unsetenv("LOGNAME");
558
559 /* It is needed otherwise cgroup creation fails */
560 if (getuid() != 0) {
561 puts("Skipping test: not root");
562 return EXIT_TEST_SKIP;
563 }
564
565 r = enter_cgroup_subroot();
566 if (r == -ENOMEDIUM) {
567 puts("Skipping test: cgroupfs not available");
568 return EXIT_TEST_SKIP;
569 }
570
571 assert_se(setenv("XDG_RUNTIME_DIR", "/tmp/", 1) == 0);
572 assert_se(set_unit_path(get_testdata_dir("/test-execute")) >= 0);
573
574 /* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test
575 * cases, otherwise (and if they are present in the environment),
576 * `manager_default_environment` will copy them into the default
577 * environment which is passed to each created job, which will make the
578 * tests that expect those not to be present to fail.
579 */
580 assert_se(unsetenv("VAR1") == 0);
581 assert_se(unsetenv("VAR2") == 0);
582 assert_se(unsetenv("VAR3") == 0);
583
584 r = run_tests(UNIT_FILE_USER, user_tests);
585 if (r != 0)
586 return r;
587
588 return run_tests(UNIT_FILE_SYSTEM, system_tests);
589 }