summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/test/test-execute.c351
-rw-r--r--test/test-execute/exec-dynamicuser-statedir-migrate-step1.service16
-rw-r--r--test/test-execute/exec-dynamicuser-statedir-migrate-step2.service32
-rw-r--r--test/test-execute/exec-dynamicuser-statedir.service122
-rw-r--r--test/test-execute/exec-privatenetwork-yes.service1
-rw-r--r--test/test-execute/exec-specifier-system.service11
-rw-r--r--test/test-execute/exec-specifier-user.service11
-rw-r--r--test/test-execute/exec-specifier.service5
-rw-r--r--test/test-execute/exec-specifier@.service5
9 files changed, 333 insertions, 221 deletions
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index 52a38f7c52..ceecf373bf 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <stdio.h>
+#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/types.h>
@@ -18,6 +19,7 @@
#include "manager.h"
#include "missing_prctl.h"
#include "mkdir.h"
+#include "mount-util.h"
#include "path-util.h"
#include "process-util.h"
#include "rm-rf.h"
@@ -34,6 +36,8 @@
#include "user-util.h"
#include "virt.h"
+#define PRIVATE_UNIT_DIR "/run/test-execute-unit-dir"
+
static char *user_runtime_unit_dir = NULL;
static bool can_unshare;
@@ -413,7 +417,7 @@ static void test_exec_privatedevices(Manager *m) {
test(m, "exec-privatedevices-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-privatedevices-no.service", 0, CLD_EXITED);
test(m, "exec-privatedevices-disabled-by-prefix.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
- test(m, "exec-privatedevices-yes-with-group.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
+ test(m, "exec-privatedevices-yes-with-group.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_GROUP, CLD_EXITED);
/* We use capsh to test if the capabilities are
* properly set, so be sure that it exists */
@@ -424,9 +428,9 @@ static void test_exec_privatedevices(Manager *m) {
}
test(m, "exec-privatedevices-yes-capability-mknod.service", 0, CLD_EXITED);
- test(m, "exec-privatedevices-no-capability-mknod.service", 0, CLD_EXITED);
+ test(m, "exec-privatedevices-no-capability-mknod.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-privatedevices-yes-capability-sys-rawio.service", 0, CLD_EXITED);
- test(m, "exec-privatedevices-no-capability-sys-rawio.service", 0, CLD_EXITED);
+ test(m, "exec-privatedevices-no-capability-sys-rawio.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED);
}
static void test_exec_protecthome(Manager *m) {
@@ -456,7 +460,7 @@ static void test_exec_protectkernelmodules(Manager *m) {
return;
}
- test(m, "exec-protectkernelmodules-no-capabilities.service", 0, CLD_EXITED);
+ test(m, "exec-protectkernelmodules-no-capabilities.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-protectkernelmodules-yes-capabilities.service", 0, CLD_EXITED);
test(m, "exec-protectkernelmodules-yes-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
}
@@ -777,7 +781,7 @@ static void test_exec_systemcallfilter_system(Manager *m) {
return;
}
- test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED);
+ test(m, "exec-systemcallfilter-system-user.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
if (!check_nobody_user_and_group()) {
log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
@@ -789,12 +793,12 @@ static void test_exec_systemcallfilter_system(Manager *m) {
return;
}
- test(m, "exec-systemcallfilter-system-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
+ test(m, "exec-systemcallfilter-system-user-" NOBODY_USER_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
#endif
}
static void test_exec_user(Manager *m) {
- test(m, "exec-user.service", 0, CLD_EXITED);
+ test(m, "exec-user.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
if (!check_nobody_user_and_group()) {
log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
@@ -806,11 +810,11 @@ static void test_exec_user(Manager *m) {
return;
}
- test(m, "exec-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
+ test(m, "exec-user-" NOBODY_USER_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
}
static void test_exec_group(Manager *m) {
- test(m, "exec-group.service", 0, CLD_EXITED);
+ test(m, "exec-group.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
if (!check_nobody_user_and_group()) {
log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
@@ -822,16 +826,17 @@ static void test_exec_group(Manager *m) {
return;
}
- test(m, "exec-group-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED);
+ test(m, "exec-group-" NOBODY_GROUP_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
}
static void test_exec_supplementarygroups(Manager *m) {
- test(m, "exec-supplementarygroups.service", 0, CLD_EXITED);
- test(m, "exec-supplementarygroups-single-group.service", 0, CLD_EXITED);
- test(m, "exec-supplementarygroups-single-group-user.service", 0, CLD_EXITED);
- test(m, "exec-supplementarygroups-multiple-groups-default-group-user.service", 0, CLD_EXITED);
- test(m, "exec-supplementarygroups-multiple-groups-withgid.service", 0, CLD_EXITED);
- test(m, "exec-supplementarygroups-multiple-groups-withuid.service", 0, CLD_EXITED);
+ int status = MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP;
+ test(m, "exec-supplementarygroups.service", status, CLD_EXITED);
+ test(m, "exec-supplementarygroups-single-group.service", status, CLD_EXITED);
+ test(m, "exec-supplementarygroups-single-group-user.service", status, CLD_EXITED);
+ test(m, "exec-supplementarygroups-multiple-groups-default-group-user.service", status, CLD_EXITED);
+ test(m, "exec-supplementarygroups-multiple-groups-withgid.service", status, CLD_EXITED);
+ test(m, "exec-supplementarygroups-multiple-groups-withuid.service", status, CLD_EXITED);
}
static char* private_directory_bad(Manager *m) {
@@ -863,14 +868,16 @@ static void test_exec_dynamicuser(Manager *m) {
return;
}
- test(m, "exec-dynamicuser-fixeduser.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ int status = can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_NAMESPACE : EXIT_GROUP;
+
+ test(m, "exec-dynamicuser-fixeduser.service", status, CLD_EXITED);
if (check_user_has_group_with_same_name("adm"))
- test(m, "exec-dynamicuser-fixeduser-adm.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-dynamicuser-fixeduser-adm.service", status, CLD_EXITED);
if (check_user_has_group_with_same_name("games"))
- test(m, "exec-dynamicuser-fixeduser-games.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
- test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
- test(m, "exec-dynamicuser-supplementarygroups.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
- test(m, "exec-dynamicuser-statedir.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-dynamicuser-fixeduser-games.service", status, CLD_EXITED);
+ test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", status, CLD_EXITED);
+ test(m, "exec-dynamicuser-supplementarygroups.service", status, CLD_EXITED);
+ test(m, "exec-dynamicuser-statedir.service", status, CLD_EXITED);
(void) rm_rf("/var/lib/quux", REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
@@ -882,7 +889,7 @@ static void test_exec_dynamicuser(Manager *m) {
(void) rm_rf("/var/lib/private/waldo", REMOVE_ROOT|REMOVE_PHYSICAL);
test(m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
- test(m, "exec-dynamicuser-statedir-migrate-step2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-dynamicuser-statedir-migrate-step2.service", status, CLD_EXITED);
test(m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
(void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
@@ -890,9 +897,9 @@ static void test_exec_dynamicuser(Manager *m) {
(void) rm_rf("/var/lib/private/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf("/var/lib/private/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
- test(m, "exec-dynamicuser-runtimedirectory1.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
- test(m, "exec-dynamicuser-runtimedirectory2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
- test(m, "exec-dynamicuser-runtimedirectory3.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-dynamicuser-runtimedirectory1.service", status, CLD_EXITED);
+ test(m, "exec-dynamicuser-runtimedirectory2.service", status, CLD_EXITED);
+ test(m, "exec-dynamicuser-runtimedirectory3.service", status, CLD_EXITED);
}
static void test_exec_environment(Manager *m) {
@@ -958,9 +965,12 @@ static void test_exec_umask(Manager *m) {
}
static void test_exec_runtimedirectory(Manager *m) {
+ (void) rm_rf("/run/test-exec_runtimedirectory2", REMOVE_ROOT|REMOVE_PHYSICAL);
test(m, "exec-runtimedirectory.service", 0, CLD_EXITED);
+ (void) rm_rf("/run/test-exec_runtimedirectory2", REMOVE_ROOT|REMOVE_PHYSICAL);
+
test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED);
- test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED);
+ test(m, "exec-runtimedirectory-owner.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
if (!check_nobody_user_and_group()) {
log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
@@ -972,7 +982,7 @@ static void test_exec_runtimedirectory(Manager *m) {
return;
}
- test(m, "exec-runtimedirectory-owner-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED);
+ test(m, "exec-runtimedirectory-owner-" NOBODY_GROUP_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
}
static void test_exec_capabilityboundingset(Manager *m) {
@@ -1046,7 +1056,7 @@ static void test_exec_privatenetwork(Manager *m) {
return;
}
- test(m, "exec-privatenetwork-yes.service", can_unshare ? 0 : EXIT_NETWORK, CLD_EXITED);
+ test(m, "exec-privatenetwork-yes.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_NETWORK : EXIT_FAILURE, CLD_EXITED);
}
static void test_exec_oomscoreadjust(Manager *m) {
@@ -1056,7 +1066,7 @@ static void test_exec_oomscoreadjust(Manager *m) {
log_notice("Testing in container, skipping remaining tests in %s", __func__);
return;
}
- test(m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED);
+ test(m, "exec-oomscoreadjust-negative.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED);
}
static void test_exec_ioschedulingclass(Manager *m) {
@@ -1068,7 +1078,7 @@ static void test_exec_ioschedulingclass(Manager *m) {
log_notice("Testing in container, skipping remaining tests in %s", __func__);
return;
}
- test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED);
+ test(m, "exec-ioschedulingclass-realtime.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_IOPRIO, CLD_EXITED);
}
static void test_exec_unsetenvironment(Manager *m) {
@@ -1077,9 +1087,13 @@ static void test_exec_unsetenvironment(Manager *m) {
static void test_exec_specifier(Manager *m) {
test(m, "exec-specifier.service", 0, CLD_EXITED);
+ if (MANAGER_IS_SYSTEM(m))
+ test(m, "exec-specifier-system.service", 0, CLD_EXITED);
+ else
+ test(m, "exec-specifier-user.service", 0, CLD_EXITED);
test(m, "exec-specifier@foo-bar.service", 0, CLD_EXITED);
test(m, "exec-specifier-interpolation.service", 0, CLD_EXITED);
- test(m, "exec-specifier-credentials-dir.service", 0, CLD_EXITED);
+ test(m, "exec-specifier-credentials-dir.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_CREDENTIALS, CLD_EXITED);
}
static void test_exec_standardinput(Manager *m) {
@@ -1112,7 +1126,7 @@ static void test_exec_umask_namespace(Manager *m) {
log_notice("Testing without inaccessible, skipping %s", __func__);
return;
}
- test(m, "exec-umask-namespace.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+ test(m, "exec-umask-namespace.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_NAMESPACE : EXIT_GROUP, CLD_EXITED);
}
typedef struct test_entry {
@@ -1122,40 +1136,27 @@ typedef struct test_entry {
#define entry(x) {x, #x}
-static int run_tests(LookupScope scope, const test_entry tests[], char **patterns) {
+static void run_tests(LookupScope scope, char **patterns) {
+ _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
+ _cleanup_free_ char *unit_paths = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
int r;
- assert_se(tests);
-
- r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m);
- m->default_std_output = EXEC_OUTPUT_NULL; /* don't rely on host journald */
- if (manager_errno_skip_test(r))
- return log_tests_skipped_errno(r, "manager_new");
- assert_se(r >= 0);
- assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
-
- for (const test_entry *test = tests; test->f; test++)
- if (strv_fnmatch_or_empty(patterns, test->name, FNM_NOESCAPE))
- test->f(m);
- else
- log_info("Skipping %s because it does not match any pattern.", test->name);
-
- return 0;
-}
-
-int main(int argc, char *argv[]) {
- _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
-
- static const test_entry user_tests[] = {
+ static const test_entry tests[] = {
entry(test_exec_basic),
entry(test_exec_ambientcapabilities),
entry(test_exec_bindpaths),
entry(test_exec_capabilityboundingset),
entry(test_exec_condition),
entry(test_exec_cpuaffinity),
+ entry(test_exec_dynamicuser),
entry(test_exec_environment),
entry(test_exec_environmentfile),
+ entry(test_exec_execsearchpath),
+ entry(test_exec_execsearchpath_environment),
+ entry(test_exec_execsearchpath_environment_files),
+ entry(test_exec_execsearchpath_passenvironment),
+ entry(test_exec_execsearchpath_specifier),
entry(test_exec_group),
entry(test_exec_ignoresigpipe),
entry(test_exec_inaccessiblepaths),
@@ -1174,6 +1175,7 @@ int main(int argc, char *argv[]) {
entry(test_exec_readwritepaths),
entry(test_exec_restrictnamespaces),
entry(test_exec_runtimedirectory),
+ entry(test_exec_specifier),
entry(test_exec_standardinput),
entry(test_exec_standardoutput),
entry(test_exec_standardoutput_append),
@@ -1181,35 +1183,15 @@ int main(int argc, char *argv[]) {
entry(test_exec_supplementarygroups),
entry(test_exec_systemcallerrornumber),
entry(test_exec_systemcallfilter),
+ entry(test_exec_systemcallfilter_system),
entry(test_exec_temporaryfilesystem),
entry(test_exec_umask),
+ entry(test_exec_umask_namespace),
entry(test_exec_unsetenvironment),
entry(test_exec_user),
entry(test_exec_workingdirectory),
- entry(test_exec_execsearchpath),
- entry(test_exec_execsearchpath_environment),
- entry(test_exec_execsearchpath_environment_files),
- entry(test_exec_execsearchpath_passenvironment),
- {},
- };
- static const test_entry system_tests[] = {
- entry(test_exec_dynamicuser),
- entry(test_exec_specifier),
- entry(test_exec_execsearchpath_specifier),
- entry(test_exec_systemcallfilter_system),
- entry(test_exec_umask_namespace),
{},
};
- int r;
-
- test_setup_logging(LOG_DEBUG);
-
-#if HAS_FEATURE_ADDRESS_SANITIZER
- if (strstr_ptr(ci_environment(), "travis") || strstr_ptr(ci_environment(), "github-actions")) {
- log_notice("Running on Travis CI/GH Actions under ASan, skipping, see https://github.com/systemd/systemd/issues/10696");
- return EXIT_TEST_SKIP;
- }
-#endif
assert_se(unsetenv("USER") == 0);
assert_se(unsetenv("LOGNAME") == 0);
@@ -1217,68 +1199,185 @@ int main(int argc, char *argv[]) {
assert_se(unsetenv("HOME") == 0);
assert_se(unsetenv("TMPDIR") == 0);
- can_unshare = have_namespaces();
-
- /* It is needed otherwise cgroup creation fails */
- if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0)
- return log_tests_skipped("not privileged");
-
- r = enter_cgroup_subroot(NULL);
- if (r == -ENOMEDIUM)
- return log_tests_skipped("cgroupfs not available");
-
- if (path_is_read_only_fs("/sys") > 0)
- return log_tests_skipped("/sys is mounted read-only");
+ /* Unset VARx, especially, VAR1, VAR2 and VAR3, which are used in the PassEnvironment test cases,
+ * otherwise (and if they are present in the environment), `manager_default_environment` will copy
+ * them into the default environment which is passed to each created job, which will make the tests
+ * that expect those not to be present to fail. */
+ assert_se(unsetenv("VAR1") == 0);
+ assert_se(unsetenv("VAR2") == 0);
+ assert_se(unsetenv("VAR3") == 0);
+ assert_se(unsetenv("VAR4") == 0);
+ assert_se(unsetenv("VAR5") == 0);
- _cleanup_free_ char *unit_dir = NULL, *unit_paths = NULL;
- assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
assert_se(runtime_dir = setup_fake_runtime_dir());
assert_se(user_runtime_unit_dir = path_join(runtime_dir, "systemd/user"));
- assert_se(unit_paths = strjoin(unit_dir, ":", user_runtime_unit_dir));
+ assert_se(unit_paths = strjoin(PRIVATE_UNIT_DIR, ":", user_runtime_unit_dir));
assert_se(set_unit_path(unit_paths) >= 0);
- /* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test
- * cases, otherwise (and if they are present in the environment),
- * `manager_default_environment` will copy them into the default
- * environment which is passed to each created job, which will make the
- * tests that expect those not to be present to fail.
- */
- assert_se(unsetenv("VAR1") == 0);
- assert_se(unsetenv("VAR2") == 0);
- assert_se(unsetenv("VAR3") == 0);
+ r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m);
+ if (manager_errno_skip_test(r))
+ return (void) log_tests_skipped_errno(r, "manager_new");
+ assert_se(r >= 0);
+
+ m->default_std_output = EXEC_OUTPUT_NULL; /* don't rely on host journald */
+ assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+
+ /* Uncomment below if you want to make debugging logs stored to journal. */
+ //manager_override_log_target(m, LOG_TARGET_AUTO);
+ //manager_override_log_level(m, LOG_DEBUG);
+
+ for (const test_entry *test = tests; test->f; test++)
+ if (strv_fnmatch_or_empty(patterns, test->name, FNM_NOESCAPE))
+ test->f(m);
+ else
+ log_info("Skipping %s because it does not match any pattern.", test->name);
+}
+
+static int prepare_ns(const char *process_name) {
+ int r;
+
+ r = safe_fork(process_name,
+ FORK_RESET_SIGNALS |
+ FORK_CLOSE_ALL_FDS |
+ FORK_DEATHSIG |
+ FORK_WAIT |
+ FORK_REOPEN_LOG |
+ FORK_LOG |
+ FORK_NEW_MOUNTNS |
+ FORK_MOUNTNS_SLAVE,
+ NULL);
+ assert_se(r >= 0);
+ if (r == 0) {
+ _cleanup_free_ char *unit_dir = NULL;
+
+ /* Make "/" read-only. */
+ assert_se(mount_nofollow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_BIND|MS_REMOUNT, NULL) >= 0);
+
+ /* Creating a new user namespace in the above means all MS_SHARED mounts become MS_SLAVE.
+ * Let's put them back to MS_SHARED here, since that's what we want as defaults. (This will
+ * not reconnect propagation, but simply create new peer groups for all our mounts). */
+ assert_se(mount_follow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_SHARED|MS_REC, NULL) >= 0);
+
+ assert_se(mkdir_p(PRIVATE_UNIT_DIR, 0755) >= 0);
+
+ /* Mount tmpfs on the following directories to make not StateDirectory= or friends disturb the host. */
+ FOREACH_STRING(p, "/root", "/tmp", "/var/tmp", "/var/lib", PRIVATE_UNIT_DIR)
+ assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
- r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1);
- if (r != 0)
- return r;
+ /* Copy unit files to make them accessible even when unprivileged. */
+ assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
+ assert_se(copy_directory(unit_dir, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0);
- r = run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1);
- if (r != 0)
- return r;
+ /* Prepare credstore like tmpfiles.d/credstore.conf for LoadCredential= tests. */
+ FOREACH_STRING(p, "/run/credstore", "/run/credstore.encrypted") {
+ assert_se(mkdir_p(p, 0) >= 0);
+ assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, "mode=0000") >= 0);
+ }
+ }
+
+ return r;
+}
+
+TEST(run_tests_root) {
+ _cleanup_strv_free_ char **filters = NULL;
+
+ if (!have_namespaces())
+ return (void) log_tests_skipped("unshare() is disabled");
+
+ /* safe_fork() clears saved_argv in the child process. Let's copy it. */
+ assert_se(filters = strv_copy(strv_skip(saved_argv, 1)));
+
+ if (prepare_ns("(test-execute-root)") == 0) {
+ can_unshare = true;
+ run_tests(LOOKUP_SCOPE_SYSTEM, filters);
+ _exit(EXIT_SUCCESS);
+ }
+}
+
+TEST(run_tests_without_unshare) {
+ if (!have_namespaces()) {
+ /* unshare() is already filtered. */
+ can_unshare = false;
+ run_tests(LOOKUP_SCOPE_SYSTEM, strv_skip(saved_argv, 1));
+ return;
+ }
#if HAVE_SECCOMP
+ _cleanup_strv_free_ char **filters = NULL;
+ int r;
+
/* The following tests are for 1beab8b0d0ff2d7d1436b52d4a0c3d56dc908962. */
- if (!is_seccomp_available()) {
- log_notice("Seccomp not available, skipping unshare() filtered tests.");
- return 0;
- }
+ if (!is_seccomp_available())
+ return (void) log_tests_skipped("Seccomp not available, cannot run unshare() filtered tests");
+
+ /* safe_fork() clears saved_argv in the child process. Let's copy it. */
+ assert_se(filters = strv_copy(strv_skip(saved_argv, 1)));
- _cleanup_hashmap_free_ Hashmap *s = NULL;
- assert_se(s = hashmap_new(NULL));
- r = seccomp_syscall_resolve_name("unshare");
- assert_se(r != __NR_SCMP_ERROR);
- assert_se(hashmap_put(s, UINT32_TO_PTR(r + 1), INT_TO_PTR(-1)) >= 0);
- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EOPNOTSUPP), true) >= 0);
- assert_se(unshare(CLONE_NEWNS) < 0);
- assert_se(errno == EOPNOTSUPP);
+ if (prepare_ns("(test-execute-without-unshare)") == 0) {
+ _cleanup_hashmap_free_ Hashmap *s = NULL;
- can_unshare = false;
+ r = seccomp_syscall_resolve_name("unshare");
+ assert_se(r != __NR_SCMP_ERROR);
+ assert_se(hashmap_ensure_put(&s, NULL, UINT32_TO_PTR(r + 1), INT_TO_PTR(-1)) >= 0);
+ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EOPNOTSUPP), true) >= 0);
- r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1);
- if (r != 0)
- return r;
+ /* Check unshare() is actually filtered. */
+ assert_se(unshare(CLONE_NEWNS) < 0);
+ assert_se(errno == EOPNOTSUPP);
- return run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1);
+ can_unshare = false;
+ run_tests(LOOKUP_SCOPE_SYSTEM, filters);
+ _exit(EXIT_SUCCESS);
+ }
#else
- return 0;
+ log_tests_skipped("Built without seccomp support, cannot run unshare() filtered tests");
+#endif
+}
+
+TEST(run_tests_unprivileged) {
+ _cleanup_strv_free_ char **filters = NULL;
+
+ if (!have_namespaces())
+ return (void) log_tests_skipped("unshare() is disabled");
+
+ /* safe_fork() clears saved_argv in the child process. Let's copy it. */
+ assert_se(filters = strv_copy(strv_skip(saved_argv, 1)));
+
+ if (prepare_ns("(test-execute-unprivileged)") == 0) {
+ assert_se(capability_bounding_set_drop(0, /* right_now = */ true) >= 0);
+
+ can_unshare = false;
+ run_tests(LOOKUP_SCOPE_USER, filters);
+ _exit(EXIT_SUCCESS);
+ }
+}
+
+static int intro(void) {
+#if HAS_FEATURE_ADDRESS_SANITIZER
+ if (strstr_ptr(ci_environment(), "travis") || strstr_ptr(ci_environment(), "github-actions"))
+ return log_tests_skipped("Running on Travis CI/GH Actions under ASan, see https://github.com/systemd/systemd/issues/10696");
#endif
+ /* It is needed otherwise cgroup creation fails */
+ if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0)
+ return log_tests_skipped("not privileged");
+
+ if (enter_cgroup_subroot(NULL) == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
+
+ if (path_is_read_only_fs("/sys") > 0)
+ return log_tests_skipped("/sys is mounted read-only");
+
+ /* Create dummy network interface for testing PrivateNetwork=yes */
+ (void) system("ip link add dummy-test-exec type dummy");
+
+ return EXIT_SUCCESS;
}
+
+static int outro(void) {
+ (void) system("ip link del dummy-test-exec");
+ (void) rmdir(PRIVATE_UNIT_DIR);
+
+ return EXIT_SUCCESS;
+}
+
+DEFINE_TEST_MAIN_FULL(LOG_DEBUG, intro, outro);
diff --git a/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service b/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service
index 1c79e4f722..2a5a1e1ff3 100644
--- a/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service
+++ b/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service
@@ -3,14 +3,14 @@
Description=Test DynamicUser= migrate StateDirectory= (preparation)
[Service]
-ExecStart=test -w /var/lib/test-dynamicuser-migrate
-ExecStart=test -w /var/lib/test-dynamicuser-migrate2/hoge
-ExecStart=test ! -L /var/lib/test-dynamicuser-migrate
-ExecStart=test ! -L /var/lib/test-dynamicuser-migrate2/hoge
-ExecStart=test -d /var/lib/test-dynamicuser-migrate
-ExecStart=test -d /var/lib/test-dynamicuser-migrate2/hoge
-ExecStart=touch /var/lib/test-dynamicuser-migrate/yay
-ExecStart=touch /var/lib/test-dynamicuser-migrate2/hoge/yayyay
+ExecStart=test -w %S/test-dynamicuser-migrate
+ExecStart=test -w %S/test-dynamicuser-migrate2/hoge
+ExecStart=test ! -L %S/test-dynamicuser-migrate
+ExecStart=test ! -L %S/test-dynamicuser-migrate2/hoge
+ExecStart=test -d %S/test-dynamicuser-migrate
+ExecStart=test -d %S/test-dynamicuser-migrate2/hoge
+ExecStart=touch %S/test-dynamicuser-migrate/yay
+ExecStart=touch %S/test-dynamicuser-migrate2/hoge/yayyay
ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
Type=oneshot
diff --git a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service
index 015b74ce22..e89f0c5aae 100644
--- a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service
+++ b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service
@@ -3,22 +3,22 @@
Description=Test DynamicUser= migrate StateDirectory=
[Service]
-ExecStart=test -w /var/lib/test-dynamicuser-migrate
-ExecStart=test -w /var/lib/test-dynamicuser-migrate2/hoge
-ExecStart=test -L /var/lib/test-dynamicuser-migrate
-ExecStart=test -L /var/lib/test-dynamicuser-migrate2/hoge
-ExecStart=test -d /var/lib/test-dynamicuser-migrate
-ExecStart=test -d /var/lib/test-dynamicuser-migrate2/hoge
-ExecStart=test -f /var/lib/test-dynamicuser-migrate/yay
-ExecStart=test -f /var/lib/test-dynamicuser-migrate2/hoge/yayyay
-ExecStart=test -d /var/lib/private/test-dynamicuser-migrate
-ExecStart=test -d /var/lib/private/test-dynamicuser-migrate2/hoge
-ExecStart=test -f /var/lib/private/test-dynamicuser-migrate/yay
-ExecStart=test -f /var/lib/private/test-dynamicuser-migrate2/hoge/yayyay
-ExecStart=touch /var/lib/test-dynamicuser-migrate/yay
-ExecStart=touch /var/lib/test-dynamicuser-migrate2/hoge/yayyay
-ExecStart=touch /var/lib/private/test-dynamicuser-migrate/yay
-ExecStart=touch /var/lib/private/test-dynamicuser-migrate2/hoge/yayyay
+ExecStart=test -w %S/test-dynamicuser-migrate
+ExecStart=test -w %S/test-dynamicuser-migrate2/hoge
+ExecStart=test -L %S/test-dynamicuser-migrate
+ExecStart=test -L %S/test-dynamicuser-migrate2/hoge
+ExecStart=test -d %S/test-dynamicuser-migrate
+ExecStart=test -d %S/test-dynamicuser-migrate2/hoge
+ExecStart=test -f %S/test-dynamicuser-migrate/yay
+ExecStart=test -f %S/test-dynamicuser-migrate2/hoge/yayyay
+ExecStart=test -d %S/private/test-dynamicuser-migrate
+ExecStart=test -d %S/private/test-dynamicuser-migrate2/hoge
+ExecStart=test -f %S/private/test-dynamicuser-migrate/yay
+ExecStart=test -f %S/private/test-dynamicuser-migrate2/hoge/yayyay
+ExecStart=touch %S/test-dynamicuser-migrate/yay
+ExecStart=touch %S/test-dynamicuser-migrate2/hoge/yayyay
+ExecStart=touch %S/private/test-dynamicuser-migrate/yay
+ExecStart=touch %S/private/test-dynamicuser-migrate2/hoge/yayyay
ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
Type=oneshot
diff --git a/test/test-execute/exec-dynamicuser-statedir.service b/test/test-execute/exec-dynamicuser-statedir.service
index b33b4da74a..b7e36f529e 100644
--- a/test/test-execute/exec-dynamicuser-statedir.service
+++ b/test/test-execute/exec-dynamicuser-statedir.service
@@ -3,71 +3,71 @@
Description=Test DynamicUser= with StateDirectory=
[Service]
-ExecStart=test -w /var/lib/waldo
-ExecStart=test -w /var/lib/quux/pief
-ExecStart=test -w /var/lib/aaa
-ExecStart=test -w /var/lib/aaa/bbb
-ExecStart=test -w /var/lib/aaa/ccc
-ExecStart=test -w /var/lib/xxx
-ExecStart=test -w /var/lib/xxx/yyy
-ExecStart=test -w /var/lib/xxx/zzz
-ExecStart=test -w /var/lib/aaa/111
-ExecStart=test -w /var/lib/aaa/222
-ExecStart=test -w /var/lib/aaa/333
+ExecStart=test -w %S/waldo
+ExecStart=test -w %S/quux/pief
+ExecStart=test -w %S/aaa
+ExecStart=test -w %S/aaa/bbb
+ExecStart=test -w %S/aaa/ccc
+ExecStart=test -w %S/xxx
+ExecStart=test -w %S/xxx/yyy
+ExecStart=test -w %S/xxx/zzz
+ExecStart=test -w %S/aaa/111
+ExecStart=test -w %S/aaa/222
+ExecStart=test -w %S/aaa/333
-ExecStart=test -d /var/lib/waldo
-ExecStart=test -d /var/lib/quux/pief
-ExecStart=test -d /var/lib/aaa
-ExecStart=test -d /var/lib/aaa/bbb
-ExecStart=test -d /var/lib/aaa/ccc
-ExecStart=test -d /var/lib/xxx
-ExecStart=test -d /var/lib/xxx/yyy
-ExecStart=test -d /var/lib/xxx/zzz
-ExecStart=test -L /var/lib/aaa/111
-ExecStart=test -L /var/lib/aaa/222
-ExecStart=test -L /var/lib/aaa/333
+ExecStart=test -d %S/waldo
+ExecStart=test -d %S/quux/pief
+ExecStart=test -d %S/aaa
+ExecStart=test -d %S/aaa/bbb
+ExecStart=test -d %S/aaa/ccc
+ExecStart=test -d %S/xxx
+ExecStart=test -d %S/xxx/yyy
+ExecStart=test -d %S/xxx/zzz
+ExecStart=test -L %S/aaa/111
+ExecStart=test -L %S/aaa/222
+ExecStart=test -L %S/aaa/333
-ExecStart=touch /var/lib/waldo/hoge
-ExecStart=touch /var/lib/quux/pief/hoge
-ExecStart=touch /var/lib/aaa/hoge
-ExecStart=touch /var/lib/aaa/bbb/hoge
-ExecStart=touch /var/lib/aaa/ccc/hoge
-ExecStart=touch /var/lib/xxx/hoge
-ExecStart=touch /var/lib/xxx/yyy/hoge
-ExecStart=touch /var/lib/xxx/zzz/hoge
-ExecStart=touch /var/lib/aaa/111/foo
-ExecStart=touch /var/lib/aaa/222/foo
-ExecStart=touch /var/lib/aaa/333/foo
+ExecStart=touch %S/waldo/hoge
+ExecStart=touch %S/quux/pief/hoge
+ExecStart=touch %S/aaa/hoge
+ExecStart=touch %S/aaa/bbb/hoge
+ExecStart=touch %S/aaa/ccc/hoge
+ExecStart=touch %S/xxx/hoge
+ExecStart=touch %S/xxx/yyy/hoge
+ExecStart=touch %S/xxx/zzz/hoge
+ExecStart=touch %S/aaa/111/foo
+ExecStart=touch %S/aaa/222/foo
+ExecStart=touch %S/aaa/333/foo
-ExecStart=test -f /var/lib/waldo/hoge
-ExecStart=test -f /var/lib/quux/pief/hoge
-ExecStart=test -f /var/lib/aaa/hoge
-ExecStart=test -f /var/lib/aaa/bbb/hoge
-ExecStart=test -f /var/lib/aaa/ccc/hoge
-ExecStart=test -f /var/lib/xxx/hoge
-ExecStart=test -f /var/lib/xxx/yyy/hoge
-ExecStart=test -f /var/lib/xxx/zzz/hoge
-ExecStart=test -f /var/lib/aaa/111/foo
-ExecStart=test -f /var/lib/aaa/222/foo
-ExecStart=test -f /var/lib/aaa/333/foo
-ExecStart=test -f /var/lib/xxx/foo
-ExecStart=test -f /var/lib/xxx/yyy/foo
-ExecStart=test -f /var/lib/xxx/zzz/foo
+ExecStart=test -f %S/waldo/hoge
+ExecStart=test -f %S/quux/pief/hoge
+ExecStart=test -f %S/aaa/hoge
+ExecStart=test -f %S/aaa/bbb/hoge
+ExecStart=test -f %S/aaa/ccc/hoge
+ExecStart=test -f %S/xxx/hoge
+ExecStart=test -f %S/xxx/yyy/hoge
+ExecStart=test -f %S/xxx/zzz/hoge
+ExecStart=test -f %S/aaa/111/foo
+ExecStart=test -f %S/aaa/222/foo
+ExecStart=test -f %S/aaa/333/foo
+ExecStart=test -f %S/xxx/foo
+ExecStart=test -f %S/xxx/yyy/foo
+ExecStart=test -f %S/xxx/zzz/foo
-ExecStart=test -f /var/lib/private/waldo/hoge
-ExecStart=test -f /var/lib/private/quux/pief/hoge
-ExecStart=test -f /var/lib/private/aaa/hoge
-ExecStart=test -f /var/lib/private/aaa/bbb/hoge
-ExecStart=test -f /var/lib/private/aaa/ccc/hoge
-ExecStart=test -f /var/lib/private/xxx/hoge
-ExecStart=test -f /var/lib/private/xxx/yyy/hoge
-ExecStart=test -f /var/lib/private/xxx/zzz/hoge
-ExecStart=test -f /var/lib/private/aaa/111/foo
-ExecStart=test -f /var/lib/private/aaa/222/foo
-ExecStart=test -f /var/lib/private/aaa/333/foo
-ExecStart=test -f /var/lib/private/xxx/foo
-ExecStart=test -f /var/lib/private/xxx/yyy/foo
-ExecStart=test -f /var/lib/private/xxx/zzz/foo
+ExecStart=test -f %S/private/waldo/hoge
+ExecStart=test -f %S/private/quux/pief/hoge
+ExecStart=test -f %S/private/aaa/hoge
+ExecStart=test -f %S/private/aaa/bbb/hoge
+ExecStart=test -f %S/private/aaa/ccc/hoge
+ExecStart=test -f %S/private/xxx/hoge
+ExecStart=test -f %S/private/xxx/yyy/hoge
+ExecStart=test -f %S/private/xxx/zzz/hoge
+ExecStart=test -f %S/private/aaa/111/foo
+ExecStart=test -f %S/private/aaa/222/foo
+ExecStart=test -f %S/private/aaa/333/foo
+ExecStart=test -f %S/private/xxx/foo
+ExecStart=test -f %S/private/xxx/yyy/foo
+ExecStart=test -f %S/private/xxx/zzz/foo
ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/aaa:%S/aaa/bbb:%S/aaa/ccc:%S/quux/pief:%S/waldo:%S/xxx:%S/xxx/yyy:%S/xxx/zzz"'
diff --git a/test/test-execute/exec-privatenetwork-yes.service b/test/test-execute/exec-privatenetwork-yes.service
index 0fff048b94..360099d337 100644
--- a/test/test-execute/exec-privatenetwork-yes.service
+++ b/test/test-execute/exec-privatenetwork-yes.service
@@ -4,5 +4,6 @@ Description=Test for PrivateNetwork
[Service]
ExecStart=/bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -Ev ": (lo|(erspan|gre|gretap|ip_vti|ip6_vti|ip6gre|ip6tnl|sit|tunl)0@.*):"'
+ExecStart=/bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -F ": dummy-test-exec:"'
Type=oneshot
PrivateNetwork=yes
diff --git a/test/test-execute/exec-specifier-system.service b/test/test-execute/exec-specifier-system.service
new file mode 100644
index 0000000000..9e8ee567aa
--- /dev/null
+++ b/test/test-execute/exec-specifier-system.service
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for specifiers (system)
+
+[Service]
+Type=oneshot
+ExecStart=test %t = /run
+ExecStart=test %S = /var/lib
+ExecStart=test %C = /var/cache
+ExecStart=test %L = /var/log
+ExecStart=test %E = /etc
diff --git a/test/test-execute/exec-specifier-user.service b/test/test-execute/exec-specifier-user.service
new file mode 100644
index 0000000000..ee0301a426
--- /dev/null
+++ b/test/test-execute/exec-specifier-user.service
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for specifiers
+
+[Service]
+Type=oneshot
+ExecStart=sh -c 'test %t = $$XDG_RUNTIME_DIR'
+ExecStart=sh -c 'test %S = %h/.config'
+ExecStart=sh -c 'test %C = %h/.cache'
+ExecStart=sh -c 'test %L = %h/.config/log'
+ExecStart=sh -c 'test %E = %h/.config'
diff --git a/test/test-execute/exec-specifier.service b/test/test-execute/exec-specifier.service
index 2b487bae8c..512f786f83 100644
--- a/test/test-execute/exec-specifier.service
+++ b/test/test-execute/exec-specifier.service
@@ -13,11 +13,6 @@ ExecStart=test %I = ""
ExecStart=test %j = specifier
ExecStart=test %J = specifier
ExecStart=test %f = /exec/specifier
-ExecStart=test %t = /run
-ExecStart=test %S = /var/lib
-ExecStart=test %C = /var/cache
-ExecStart=test %L = /var/log
-ExecStart=test %E = /etc
ExecStart=test %T = /tmp
ExecStart=test %V = /var/tmp
ExecStart=test %d = %t/credentials/%n
diff --git a/test/test-execute/exec-specifier@.service b/test/test-execute/exec-specifier@.service
index 69e969f716..cb9d0a182a 100644
--- a/test/test-execute/exec-specifier@.service
+++ b/test/test-execute/exec-specifier@.service
@@ -13,11 +13,6 @@ ExecStart=test %I = foo/bar
ExecStart=test %j = specifier
ExecStart=test %J = specifier
ExecStart=test %f = /foo/bar
-ExecStart=test %t = /run
-ExecStart=test %S = /var/lib
-ExecStart=test %C = /var/cache
-ExecStart=test %L = /var/log
-ExecStart=test %E = /etc
ExecStart=sh -c 'test %u = $$(id -un)'
ExecStart=sh -c 'test %U = $$(id -u)'
ExecStart=sh -c 'test %g = $$(id -gn)'