1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
11 /* When we include libgen.h because we need dirname() we immediately
12 * undefine basename() since libgen.h defines it as a macro to the POSIX
13 * version which is really broken. We prefer GNU basename(). */
19 #include "alloc-util.h"
20 #include "bus-error.h"
21 #include "bus-locator.h"
23 #include "bus-wait-for-jobs.h"
24 #include "cgroup-setup.h"
25 #include "cgroup-util.h"
30 #include "namespace-util.h"
31 #include "path-util.h"
32 #include "process-util.h"
33 #include "random-util.h"
37 char* setup_fake_runtime_dir(void) {
38 char t
[] = "/tmp/fake-xdg-runtime-XXXXXX", *p
;
40 assert_se(mkdtemp(t
));
41 assert_se(setenv("XDG_RUNTIME_DIR", t
, 1) >= 0);
42 assert_se(p
= strdup(t
));
47 static void load_testdata_env(void) {
48 static bool called
= false;
49 _cleanup_free_
char *s
= NULL
;
50 _cleanup_free_
char *envpath
= NULL
;
51 _cleanup_strv_free_
char **pairs
= NULL
;
57 assert_se(readlink_and_make_absolute("/proc/self/exe", &s
) >= 0);
60 envpath
= path_join(s
, "systemd-runtest.env");
61 if (load_env_file_pairs(NULL
, envpath
, &pairs
) < 0)
64 STRV_FOREACH_PAIR(k
, v
, pairs
)
68 int get_testdata_dir(const char *suffix
, char **ret
) {
74 /* if the env var is set, use that */
75 dir
= getenv("SYSTEMD_TEST_DATA");
77 dir
= SYSTEMD_TEST_DATA
;
78 if (access(dir
, F_OK
) < 0)
79 return log_error_errno(errno
, "ERROR: $SYSTEMD_TEST_DATA directory [%s] not accessible: %m", dir
);
81 p
= path_join(dir
, suffix
);
89 const char* get_catalog_dir(void) {
94 /* if the env var is set, use that */
95 env
= getenv("SYSTEMD_CATALOG_DIR");
97 env
= SYSTEMD_CATALOG_DIR
;
98 if (access(env
, F_OK
) < 0) {
99 fprintf(stderr
, "ERROR: $SYSTEMD_CATALOG_DIR directory [%s] does not exist\n", env
);
105 bool slow_tests_enabled(void) {
108 r
= getenv_bool("SYSTEMD_SLOW_TESTS");
113 log_warning_errno(r
, "Cannot parse $SYSTEMD_SLOW_TESTS, ignoring.");
114 return SYSTEMD_SLOW_TESTS_DEFAULT
;
117 void test_setup_logging(int level
) {
118 log_set_max_level(level
);
119 log_parse_environment();
123 int log_tests_skipped(const char *message
) {
124 log_notice("%s: %s, skipping tests.",
125 program_invocation_short_name
, message
);
126 return EXIT_TEST_SKIP
;
129 int log_tests_skipped_errno(int r
, const char *message
) {
130 log_notice_errno(r
, "%s: %s, skipping tests: %m",
131 program_invocation_short_name
, message
);
132 return EXIT_TEST_SKIP
;
135 bool have_namespaces(void) {
139 /* Checks whether namespaces are available. In some cases they aren't. We do this by calling unshare(), and we
140 * do so in a child process in order not to affect our own process. */
147 if (detach_mount_namespace() < 0)
153 assert_se(waitid(P_PID
, pid
, &si
, WEXITED
) >= 0);
154 assert_se(si
.si_code
== CLD_EXITED
);
156 if (si
.si_status
== EXIT_SUCCESS
)
159 if (si
.si_status
== EXIT_FAILURE
)
162 assert_not_reached();
165 bool can_memlock(void) {
166 /* Let's see if we can mlock() a larger blob of memory. BPF programs are charged against
167 * RLIMIT_MEMLOCK, hence let's first make sure we can lock memory at all, and skip the test if we
168 * cannot. Why not check RLIMIT_MEMLOCK explicitly? Because in container environments the
169 * RLIMIT_MEMLOCK value we see might not match the RLIMIT_MEMLOCK value actually in effect. */
171 void *p
= mmap(NULL
, CAN_MEMLOCK_SIZE
, PROT_READ
|PROT_WRITE
, MAP_ANONYMOUS
|MAP_SHARED
, -1, 0);
175 bool b
= mlock(p
, CAN_MEMLOCK_SIZE
) >= 0;
177 assert_se(munlock(p
, CAN_MEMLOCK_SIZE
) >= 0);
179 assert_se(munmap(p
, CAN_MEMLOCK_SIZE
) >= 0);
183 static int allocate_scope(void) {
184 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
, *reply
= NULL
;
185 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
186 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*w
= NULL
;
187 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
188 _cleanup_free_
char *scope
= NULL
;
192 /* Let's try to run this test in a scope of its own, with delegation turned on, so that PID 1 doesn't
193 * interfere with our cgroup management. */
195 r
= sd_bus_default_system(&bus
);
197 return log_error_errno(r
, "Failed to connect to system bus: %m");
199 r
= bus_wait_for_jobs_new(bus
, &w
);
203 if (asprintf(&scope
, "%s-%" PRIx64
".scope", program_invocation_short_name
, random_u64()) < 0)
206 r
= bus_message_new_method_call(bus
, &m
, bus_systemd_mgr
, "StartTransientUnit");
208 return bus_log_create_error(r
);
211 r
= sd_bus_message_append(m
, "ss", scope
, "fail");
213 return bus_log_create_error(r
);
216 r
= sd_bus_message_open_container(m
, 'a', "(sv)");
218 return bus_log_create_error(r
);
220 r
= sd_bus_message_append(m
, "(sv)", "PIDs", "au", 1, (uint32_t) getpid_cached());
222 return bus_log_create_error(r
);
224 r
= sd_bus_message_append(m
, "(sv)", "Delegate", "b", 1);
226 return bus_log_create_error(r
);
228 r
= sd_bus_message_append(m
, "(sv)", "CollectMode", "s", "inactive-or-failed");
230 return bus_log_create_error(r
);
232 r
= sd_bus_message_close_container(m
);
234 return bus_log_create_error(r
);
236 /* Auxiliary units */
237 r
= sd_bus_message_append(m
, "a(sa(sv))", 0);
239 return bus_log_create_error(r
);
241 r
= sd_bus_call(bus
, m
, 0, &error
, &reply
);
243 return log_error_errno(r
, "Failed to start transient scope unit: %s", bus_error_message(&error
, r
));
245 r
= sd_bus_message_read(reply
, "o", &object
);
247 return bus_log_parse_error(r
);
249 r
= bus_wait_for_jobs_one(w
, object
, false, NULL
);
256 static int enter_cgroup(char **ret_cgroup
, bool enter_subroot
) {
257 _cleanup_free_
char *cgroup_root
= NULL
, *cgroup_subroot
= NULL
;
258 CGroupMask supported
;
261 r
= allocate_scope();
263 log_warning_errno(r
, "Couldn't allocate a scope unit for this test, proceeding without.");
265 r
= cg_pid_get_path(NULL
, 0, &cgroup_root
);
267 return log_warning_errno(r
, "cg_pid_get_path(NULL, 0, ...) failed: %m");
271 assert_se(asprintf(&cgroup_subroot
, "%s/%" PRIx64
, cgroup_root
, random_u64()) >= 0);
273 cgroup_subroot
= strdup(cgroup_root
);
274 assert_se(cgroup_subroot
!= NULL
);
277 assert_se(cg_mask_supported(&supported
) >= 0);
279 /* If this fails, then we don't mind as the later cgroup operations will fail too, and it's fine if
280 * we handle any errors at that point. */
282 r
= cg_create_everywhere(supported
, _CGROUP_MASK_ALL
, cgroup_subroot
);
286 r
= cg_attach_everywhere(supported
, cgroup_subroot
, 0, NULL
, NULL
);
291 *ret_cgroup
= TAKE_PTR(cgroup_subroot
);
296 int enter_cgroup_subroot(char **ret_cgroup
) {
297 return enter_cgroup(ret_cgroup
, true);
300 int enter_cgroup_root(char **ret_cgroup
) {
301 return enter_cgroup(ret_cgroup
, false);
304 const char *ci_environment(void) {
305 /* We return a string because we might want to provide multiple bits of information later on: not
306 * just the general CI environment type, but also whether we're sanitizing or not, etc. The caller is
307 * expected to use strstr on the returned value. */
308 static const char *ans
= POINTER_MAX
;
312 if (ans
!= POINTER_MAX
)
315 /* We allow specifying the environment with $CITYPE. Nobody uses this so far, but we are ready. */
316 p
= getenv("CITYPE");
320 if (getenv_bool("TRAVIS") > 0)
321 return (ans
= "travis");
322 if (getenv_bool("SEMAPHORE") > 0)
323 return (ans
= "semaphore");
324 if (getenv_bool("GITHUB_ACTIONS") > 0)
325 return (ans
= "github-actions");
326 if (getenv("AUTOPKGTEST_ARTIFACTS") || getenv("AUTOPKGTEST_TMP"))
327 return (ans
= "autopkgtest");
329 FOREACH_STRING(p
, "CI", "CONTINOUS_INTEGRATION") {
330 /* Those vars are booleans according to Semaphore and Travis docs:
331 * https://docs.travis-ci.com/user/environment-variables/#default-environment-variables
332 * https://docs.semaphoreci.com/ci-cd-environment/environment-variables/#ci
336 return (ans
= "unknown"); /* Some other unknown thing */