1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Ronny Chevalier
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.
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.
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/>.
24 #include <sys/prctl.h>
25 #include <sys/types.h>
27 #include "errno-list.h"
33 #include "path-util.h"
36 #include "seccomp-util.h"
38 #include "stat-util.h"
39 #include "test-helper.h"
45 typedef void (*test_function_t
)(Manager
*m
);
47 static void check(Manager
*m
, Unit
*unit
, int status_expected
, int code_expected
) {
48 Service
*service
= NULL
;
50 usec_t timeout
= 2 * USEC_PER_MINUTE
;
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
)) {
63 r
= sd_event_run(m
->event
, 100 * USEC_PER_MSEC
);
66 n
= now(CLOCK_MONOTONIC
);
67 if (ts
+ timeout
< n
) {
68 log_error("Test timeout when testing %s", unit
->id
);
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
);
77 static bool is_inaccessible_available(void) {
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"
88 if (access(p
, F_OK
) < 0)
95 static void test(Manager
*m
, const char *unit_name
, int status_expected
, int code_expected
) {
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
);
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);
109 test(m
, "exec-bindpaths.service", 0, CLD_EXITED
);
111 (void) rm_rf("/tmp/test-exec-bindpaths", REMOVE_ROOT
|REMOVE_PHYSICAL
);
112 (void) rm_rf("/tmp/test-exec-bindreadonlypaths", REMOVE_ROOT
|REMOVE_PHYSICAL
);
115 static void test_exec_workingdirectory(Manager
*m
) {
116 assert_se(mkdir_p("/tmp/test-exec_workingdirectory", 0755) >= 0);
118 test(m
, "exec-workingdirectory.service", 0, CLD_EXITED
);
120 (void) rm_rf("/tmp/test-exec_workingdirectory", REMOVE_ROOT
|REMOVE_PHYSICAL
);
123 static void test_exec_personality(Manager
*m
) {
124 #if defined(__x86_64__)
125 test(m
, "exec-personality-x86-64.service", 0, CLD_EXITED
);
127 #elif defined(__s390__)
128 test(m
, "exec-personality-s390.service", 0, CLD_EXITED
);
130 #elif defined(__powerpc64__)
131 # if __BYTE_ORDER == __BIG_ENDIAN
132 test(m
, "exec-personality-ppc64.service", 0, CLD_EXITED
);
134 test(m
, "exec-personality-ppc64le.service", 0, CLD_EXITED
);
137 #elif defined(__aarch64__)
138 test(m
, "exec-personality-aarch64.service", 0, CLD_EXITED
);
140 #elif defined(__i386__)
141 test(m
, "exec-personality-x86.service", 0, CLD_EXITED
);
143 log_notice("Unknown personality, skipping %s", __func__
);
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
);
152 static void test_exec_privatetmp(Manager
*m
) {
153 assert_se(touch("/tmp/test-exec_privatetmp") >= 0);
155 test(m
, "exec-privatetmp-yes.service", 0, CLD_EXITED
);
156 test(m
, "exec-privatetmp-no.service", 0, CLD_EXITED
);
158 unlink("/tmp/test-exec_privatetmp");
161 static void test_exec_privatedevices(Manager
*m
) {
164 if (detect_container() > 0) {
165 log_notice("Testing in container, skipping %s", __func__
);
168 if (!is_inaccessible_available()) {
169 log_notice("Testing without inaccessible, skipping %s", __func__
);
173 test(m
, "exec-privatedevices-yes.service", 0, CLD_EXITED
);
174 test(m
, "exec-privatedevices-no.service", 0, CLD_EXITED
);
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
);
180 log_error_errno(r
, "Could not find capsh binary, skipping remaining tests in %s: %m", __func__
);
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
);
190 static void test_exec_protectkernelmodules(Manager
*m
) {
193 if (detect_container() > 0) {
194 log_notice("Testing in container, skipping %s", __func__
);
197 if (!is_inaccessible_available()) {
198 log_notice("Testing without inaccessible, skipping %s", __func__
);
202 r
= find_binary("capsh", NULL
);
204 log_error_errno(r
, "Skipping %s, could not find capsh binary: %m", __func__
);
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
);
213 static void test_exec_readonlypaths(Manager
*m
) {
215 test(m
, "exec-readonlypaths-simple.service", 0, CLD_EXITED
);
217 if (path_is_read_only_fs("/var") > 0) {
218 log_notice("Directory /var is readonly, skipping remaining tests in %s", __func__
);
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
);
227 static void test_exec_readwritepaths(Manager
*m
) {
229 if (path_is_read_only_fs("/") > 0) {
230 log_notice("Root directory is readonly, skipping %s", __func__
);
234 test(m
, "exec-readwritepaths-mount-propagation.service", 0, CLD_EXITED
);
237 static void test_exec_inaccessiblepaths(Manager
*m
) {
239 if (!is_inaccessible_available()) {
240 log_notice("Testing without inaccessible, skipping %s", __func__
);
244 test(m
, "exec-inaccessiblepaths-proc.service", 0, CLD_EXITED
);
246 if (path_is_read_only_fs("/") > 0) {
247 log_notice("Root directory is readonly, skipping remaining tests in %s", __func__
);
251 test(m
, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED
);
254 static void test_exec_systemcallfilter(Manager
*m
) {
256 if (!is_seccomp_available()) {
257 log_notice("Seccomp not available, skipping %s", __func__
);
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
);
270 static void test_exec_systemcallerrornumber(Manager
*m
) {
272 if (!is_seccomp_available()) {
273 log_notice("Seccomp not available, skipping %s", __func__
);
277 test(m
, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED
);
278 test(m
, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED
);
282 static void test_exec_restrictnamespaces(Manager
*m
) {
284 if (!is_seccomp_available()) {
285 log_notice("Seccomp not available, skipping %s", __func__
);
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
);
296 static void test_exec_systemcallfilter_system(Manager
*m
) {
298 if (!is_seccomp_available()) {
299 log_notice("Seccomp not available, skipping %s", __func__
);
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
);
308 log_error_errno(errno
, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__
);
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
);
318 log_error_errno(errno
, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__
);
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
);
327 log_error_errno(errno
, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__
);
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
);
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
);
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
);
352 static void test_exec_environmentfile(Manager
*m
) {
353 static const char e
[] =
354 "VAR1='word1 word2'\n"
360 "line without an equal\n"
361 "VAR3='$word 5 6'\n";
364 r
= write_string_file("/tmp/test-exec_environmentfile.conf", e
, WRITE_STRING_FILE_CREATE
);
367 test(m
, "exec-environmentfile.service", 0, CLD_EXITED
);
369 (void) unlink("/tmp/test-exec_environmentfile.conf");
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_!
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.
381 * This is still a good approximation of how a test for MANAGER_SYSTEM
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
);
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
);
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
);
409 log_error_errno(errno
, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__
);
412 static void test_exec_capabilityboundingset(Manager
*m
) {
415 r
= find_binary("capsh", NULL
);
417 log_error_errno(r
, "Skipping %s, could not find capsh binary: %m", __func__
);
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
);
427 static void test_exec_capabilityambientset(Manager
*m
) {
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__
);
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
);
447 log_error_errno(errno
, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__
);
450 static void test_exec_privatenetwork(Manager
*m
) {
453 r
= find_binary("ip", NULL
);
455 log_error_errno(r
, "Skipping %s, could not find ip binary: %m", __func__
);
459 test(m
, "exec-privatenetwork-yes.service", 0, CLD_EXITED
);
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
);
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
);
474 static void test_exec_unsetenvironment(Manager
*m
) {
475 test(m
, "exec-unsetenvironment.service", 0, CLD_EXITED
);
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
);
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
);
489 static int run_tests(UnitFileScope scope
, const test_function_t
*tests
) {
490 const test_function_t
*test
= NULL
;
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
;
502 assert_se(manager_startup(m
, NULL
, NULL
) >= 0);
504 for (test
= tests
; test
&& *test
; test
++)
512 int main(int argc
, char *argv
[]) {
513 static const test_function_t user_tests
[] = {
515 test_exec_capabilityambientset
,
516 test_exec_capabilityboundingset
,
517 test_exec_environment
,
518 test_exec_environmentfile
,
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
,
539 test_exec_unsetenvironment
,
541 test_exec_workingdirectory
,
544 static const test_function_t system_tests
[] = {
545 test_exec_dynamicuser
,
547 test_exec_systemcallfilter_system
,
552 log_set_max_level(LOG_DEBUG
);
553 log_parse_environment();
556 (void) unsetenv("USER");
557 (void) unsetenv("LOGNAME");
559 /* It is needed otherwise cgroup creation fails */
561 puts("Skipping test: not root");
562 return EXIT_TEST_SKIP
;
565 r
= enter_cgroup_subroot();
566 if (r
== -ENOMEDIUM
) {
567 puts("Skipping test: cgroupfs not available");
568 return EXIT_TEST_SKIP
;
571 assert_se(setenv("XDG_RUNTIME_DIR", "/tmp/", 1) == 0);
572 assert_se(set_unit_path(get_testdata_dir("/test-execute")) >= 0);
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.
580 assert_se(unsetenv("VAR1") == 0);
581 assert_se(unsetenv("VAR2") == 0);
582 assert_se(unsetenv("VAR3") == 0);
584 r
= run_tests(UNIT_FILE_USER
, user_tests
);
588 return run_tests(UNIT_FILE_SYSTEM
, system_tests
);