1
0
mirror of https://github.com/systemd/systemd synced 2025-10-05 19:54:46 +02:00

Compare commits

..

No commits in common. "92716a079fb63a13074444b1af73e264145b9b73" and "111e2a106cde7236c53c92a247ae29d0d17c2561" have entirely different histories.

24 changed files with 448 additions and 483 deletions

View File

@ -10,7 +10,6 @@ static const struct errno_name* lookup_errno(register const char *str,
register GPERF_LEN_TYPE len); register GPERF_LEN_TYPE len);
#include "errno-from-name.inc" #include "errno-from-name.inc"
#include "errno-to-name.inc"
int errno_from_name(const char *name) { int errno_from_name(const char *name) {
const struct errno_name *sc; const struct errno_name *sc;
@ -25,24 +24,26 @@ int errno_from_name(const char *name) {
return sc->id; return sc->id;
} }
#if HAVE_STRERRORNAME_NP
const char* errno_to_name(int id) { const char* errno_to_name(int id) {
if (id == 0) /* To stay in line with our own impl */ if (id == 0) /* To stay in line with our own impl */
return NULL; return NULL;
return strerrorname_np(ABS(id));
}
#else
# include "errno-to-name.inc"
const char* errno_to_name(int id) {
if (id < 0) if (id < 0)
id = -id; id = -id;
#if HAVE_STRERRORNAME_NP
const char *n = strerrorname_np(id);
if (n)
return n;
#endif
if ((size_t) id >= ELEMENTSOF(errno_names)) if ((size_t) id >= ELEMENTSOF(errno_names))
return NULL; return NULL;
return errno_names[id]; return errno_names[id];
} }
#endif
const char* errno_name_full(int id, char buf[static ERRNO_NAME_BUF_LEN]) { const char* errno_name_full(int id, char buf[static ERRNO_NAME_BUF_LEN]) {
const char *a = errno_to_name(id); const char *a = errno_to_name(id);

View File

@ -212,13 +212,6 @@ static inline bool ERRNO_IS_NEG_DEVICE_ABSENT(intmax_t r) {
} }
_DEFINE_ABS_WRAPPER(DEVICE_ABSENT); _DEFINE_ABS_WRAPPER(DEVICE_ABSENT);
/* Device is absent or "empty". We get -ENOMEDIUM from CD/DVD devices, also in VMs. */
static inline bool ERRNO_IS_NEG_DEVICE_ABSENT_OR_EMPTY(intmax_t r) {
return ERRNO_IS_NEG_DEVICE_ABSENT(r) ||
r == -ENOMEDIUM;
}
_DEFINE_ABS_WRAPPER(DEVICE_ABSENT_OR_EMPTY);
/* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and /* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and
* where it simply doesn't have the requested xattr the same way */ * where it simply doesn't have the requested xattr the same way */
static inline bool ERRNO_IS_NEG_XATTR_ABSENT(intmax_t r) { static inline bool ERRNO_IS_NEG_XATTR_ABSENT(intmax_t r) {

View File

@ -1931,9 +1931,9 @@ static int socket_coldplug(Unit *u) {
SOCKET_RUNNING)) { SOCKET_RUNNING)) {
/* Originally, we used to simply reopen all sockets here that we didn't have file descriptors /* Originally, we used to simply reopen all sockets here that we didn't have file descriptors
* for. However, this is problematic, as we won't traverse through the SOCKET_START_CHOWN * for. However, this is problematic, as we won't traverse through the SOCKET_START_CHOWN state for
* state for them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we * them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we have all fds open,
* have all fds open, and if there's a mismatch, warn loudly. * and if there's a mismatch, warn loudly.
* *
* Note that SOCKET_START_OPEN requires no special treatment, as it's only intermediate * Note that SOCKET_START_OPEN requires no special treatment, as it's only intermediate
* between SOCKET_START_PRE and SOCKET_START_CHOWN and shall otherwise not be observed. * between SOCKET_START_PRE and SOCKET_START_CHOWN and shall otherwise not be observed.
@ -1942,14 +1942,14 @@ static int socket_coldplug(Unit *u) {
r = socket_check_open(s); r = socket_check_open(s);
if (r == SOCKET_OPEN_NONE) if (r == SOCKET_OPEN_NONE)
log_unit_warning(UNIT(s), log_unit_warning(UNIT(s),
"Unit configuration changed while unit was running, " "Socket unit configuration has changed while unit has been running, "
"and no socket file descriptors are open. " "no open socket file descriptor left. "
"Unit not functional until restarted."); "The socket unit is not functional until restarted.");
else if (r == SOCKET_OPEN_SOME) else if (r == SOCKET_OPEN_SOME)
log_unit_warning(UNIT(s), log_unit_warning(UNIT(s),
"Unit configuration changed while unit was running, " "Socket unit configuration has changed while unit has been running, "
"and some socket file descriptors have not been opened yet. " "and some socket file descriptors have not been opened yet. "
"Unit not fully functional until restarted."); "The socket unit is not fully functional until restarted.");
} }
if (s->deserialized_state == SOCKET_LISTENING) { if (s->deserialized_state == SOCKET_LISTENING) {

View File

@ -344,7 +344,7 @@ static void test_sd_device_enumerator_filter_subsystem_one(
ASSERT_OK(sd_device_enumerator_new(&e)); ASSERT_OK(sd_device_enumerator_new(&e));
ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, subsystem, true)); ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, subsystem, true));
exclude_problematic_devices(e); ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*"));
FOREACH_DEVICE(e, d) { FOREACH_DEVICE(e, d) {
const char *syspath; const char *syspath;
@ -613,9 +613,9 @@ TEST(sd_device_enumerator_add_all_parents) {
/* STEP 1: enumerate all block devices without all_parents() */ /* STEP 1: enumerate all block devices without all_parents() */
ASSERT_OK(sd_device_enumerator_new(&e)); ASSERT_OK(sd_device_enumerator_new(&e));
ASSERT_OK(sd_device_enumerator_allow_uninitialized(e)); ASSERT_OK(sd_device_enumerator_allow_uninitialized(e));
exclude_problematic_devices(e);
/* filter in only a subsystem */ /* filter in only a subsystem */
ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*"));
ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true)); ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true));
ASSERT_OK(sd_device_enumerator_add_match_property(e, "DEVTYPE", "partition")); ASSERT_OK(sd_device_enumerator_add_match_property(e, "DEVTYPE", "partition"));
@ -758,9 +758,8 @@ TEST(sd_device_new_from_path) {
ASSERT_OK(sd_device_enumerator_new(&e)); ASSERT_OK(sd_device_enumerator_new(&e));
ASSERT_OK(sd_device_enumerator_allow_uninitialized(e)); ASSERT_OK(sd_device_enumerator_allow_uninitialized(e));
exclude_problematic_devices(e);
ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true)); ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, "block", true));
ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*"));
ASSERT_OK(sd_device_enumerator_add_match_property(e, "DEVNAME", "*")); ASSERT_OK(sd_device_enumerator_add_match_property(e, "DEVNAME", "*"));
FOREACH_DEVICE(e, dev) { FOREACH_DEVICE(e, dev) {

View File

@ -11,43 +11,23 @@
#include "oomd-util.h" #include "oomd-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "path-util.h" #include "path-util.h"
#include "process-util.h"
#include "set.h" #include "set.h"
#include "tests.h" #include "tests.h"
#include "time-util.h" #include "time-util.h"
#include "tmpfile-util.h" #include "tmpfile-util.h"
static char *cgroup = NULL;
STATIC_DESTRUCTOR_REGISTER(cgroup, freep);
static int enter_cgroup_root_cached(void) {
static int saved_result = 0; /* 0: not entered, 1: success, negative errno: error */
int r;
if (saved_result != 0)
return saved_result;
r = enter_cgroup_root(&cgroup);
if (r < 0) {
log_tests_skipped_errno(r, "Failed to enter a test cgroup scope");
saved_result = r;
} else
saved_result = 1;
return saved_result;
}
static int fork_and_sleep(unsigned sleep_min) { static int fork_and_sleep(unsigned sleep_min) {
pid_t pid; usec_t n, timeout, ts;
int r;
ASSERT_OK(r = safe_fork("(test-oom-child)", /* flags = */ 0, &pid)); pid_t pid = fork();
if (r == 0) { assert_se(pid >= 0);
usec_t timeout = usec_add(now(CLOCK_MONOTONIC), sleep_min * USEC_PER_MINUTE);
if (pid == 0) {
timeout = sleep_min * USEC_PER_MINUTE;
ts = now(CLOCK_MONOTONIC);
for (;;) { for (;;) {
usec_t n = now(CLOCK_MONOTONIC); n = now(CLOCK_MONOTONIC);
if (timeout < n) { if (ts + timeout < n) {
log_error("Child timed out waiting to be killed"); log_error("Child timed out waiting to be killed");
abort(); abort();
} }
@ -58,92 +38,95 @@ static int fork_and_sleep(unsigned sleep_min) {
return pid; return pid;
} }
TEST(oomd_cgroup_kill) { static void test_oomd_cgroup_kill(void) {
_cleanup_free_ char *subcgroup = NULL; _cleanup_free_ char *cgroup_root = NULL, *cgroup = NULL;
int pid[2];
int r; int r;
if (enter_cgroup_root_cached() < 0) if (geteuid() != 0)
return; return (void) log_tests_skipped("not root");
assert_se(cg_pid_get_path(NULL, 0, &cgroup_root) >= 0);
/* Create another cgroup below this one for the pids we forked off. We need this to be managed /* Create another cgroup below this one for the pids we forked off. We need this to be managed
* by the test so that pid1 doesn't delete it before we can read the xattrs. */ * by the test so that pid1 doesn't delete it before we can read the xattrs. */
ASSERT_NOT_NULL(subcgroup = path_join(cgroup, "oomdkilltest")); cgroup = path_join(cgroup_root, "oomdkilltest");
ASSERT_OK(cg_create(subcgroup)); assert_se(cgroup);
assert_se(cg_create(cgroup) >= 0);
/* If we don't have permissions to set xattrs we're likely in a userns or missing capabilities */ /* If we don't have permissions to set xattrs we're likely in a userns or missing capabilities */
r = cg_set_xattr(subcgroup, "user.oomd_test", "test", 4, 0); r = cg_set_xattr(cgroup, "user.oomd_test", "test", 4, 0);
if (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r)) if (ERRNO_IS_PRIVILEGE(r) || ERRNO_IS_NOT_SUPPORTED(r))
return (void) log_tests_skipped("Cannot set user xattrs"); return (void) log_tests_skipped("Cannot set user xattrs");
/* Do this twice to also check the increment behavior on the xattrs */ /* Do this twice to also check the increment behavior on the xattrs */
for (size_t i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
_cleanup_free_ char *v = NULL; _cleanup_free_ char *v = NULL;
pid_t pid[2];
for (size_t j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
pid[j] = fork_and_sleep(5); pid[j] = fork_and_sleep(5);
ASSERT_OK(cg_attach(subcgroup, pid[j])); assert_se(cg_attach(cgroup, pid[j]) >= 0);
} }
ASSERT_OK_POSITIVE(oomd_cgroup_kill(subcgroup, false /* recurse */, false /* dry run */)); r = oomd_cgroup_kill(cgroup, false /* recurse */, false /* dry run */);
if (r <= 0) {
log_debug_errno(r, "Failed to kill processes under %s: %m", cgroup);
abort();
}
ASSERT_OK(cg_get_xattr(subcgroup, "user.oomd_ooms", &v, /* ret_size= */ NULL)); ASSERT_OK(cg_get_xattr(cgroup, "user.oomd_ooms", &v, /* ret_size= */ NULL));
ASSERT_STREQ(v, i == 0 ? "1" : "2"); assert_se(streq(v, i == 0 ? "1" : "2"));
v = mfree(v); v = mfree(v);
/* Wait a bit since processes may take some time to be cleaned up. */ /* Wait a bit since processes may take some time to be cleaned up. */
bool empty = false; sleep(2);
for (size_t t = 0; t < 100; t++) { assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, cgroup) == true);
usleep_safe(100 * USEC_PER_MSEC);
ASSERT_OK(r = cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, subcgroup));
if (r > 0) {
empty = true;
break;
}
}
ASSERT_TRUE(empty);
ASSERT_OK(cg_get_xattr(subcgroup, "user.oomd_kill", &v, /* ret_size= */ NULL)); ASSERT_OK(cg_get_xattr(cgroup, "user.oomd_kill", &v, /* ret_size= */ NULL));
ASSERT_STREQ(v, i == 0 ? "2" : "4"); assert_se(streq(v, i == 0 ? "2" : "4"));
} }
} }
TEST(oomd_cgroup_context_acquire_and_insert) { static void test_oomd_cgroup_context_acquire_and_insert(void) {
_cleanup_hashmap_free_ Hashmap *h1 = NULL, *h2 = NULL; _cleanup_hashmap_free_ Hashmap *h1 = NULL, *h2 = NULL;
_cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *ctx = NULL; _cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *ctx = NULL;
_cleanup_free_ char *cgroup = NULL;
OomdCGroupContext *c1, *c2; OomdCGroupContext *c1, *c2;
CGroupMask mask; CGroupMask mask;
if (geteuid() != 0)
return (void) log_tests_skipped("not root");
if (!is_pressure_supported()) if (!is_pressure_supported())
return (void) log_tests_skipped("system does not support pressure"); return (void) log_tests_skipped("system does not support pressure");
if (enter_cgroup_root_cached() < 0) assert_se(cg_mask_supported(&mask) >= 0);
return;
ASSERT_OK(cg_mask_supported(&mask));
if (!FLAGS_SET(mask, CGROUP_MASK_MEMORY)) if (!FLAGS_SET(mask, CGROUP_MASK_MEMORY))
return (void) log_tests_skipped("cgroup memory controller is not available"); return (void) log_tests_skipped("cgroup memory controller is not available");
ASSERT_OK(oomd_cgroup_context_acquire(cgroup, &ctx)); assert_se(cg_pid_get_path(NULL, 0, &cgroup) >= 0);
assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0);
ASSERT_STREQ(ctx->path, cgroup); assert_se(streq(ctx->path, cgroup));
ASSERT_GT(ctx->current_memory_usage, 0u); assert_se(ctx->current_memory_usage > 0);
ASSERT_EQ(ctx->memory_min, 0u); assert_se(ctx->memory_min == 0);
ASSERT_EQ(ctx->memory_low, 0u); assert_se(ctx->memory_low == 0);
ASSERT_EQ(ctx->swap_usage, 0u); assert_se(ctx->swap_usage == 0);
ASSERT_EQ(ctx->last_pgscan, 0u); assert_se(ctx->last_pgscan == 0);
ASSERT_EQ(ctx->pgscan, 0u); assert_se(ctx->pgscan == 0);
ASSERT_NULL(ctx = oomd_cgroup_context_free(ctx)); ctx = oomd_cgroup_context_free(ctx);
ASSERT_OK(oomd_cgroup_context_acquire("", &ctx)); assert_se(oomd_cgroup_context_acquire("", &ctx) == 0);
ASSERT_STREQ(ctx->path, "/"); assert_se(streq(ctx->path, "/"));
ASSERT_GT(ctx->current_memory_usage, 0u); assert_se(ctx->current_memory_usage > 0);
/* Test hashmap inserts */ /* Test hashmap inserts */
ASSERT_NOT_NULL(h1 = hashmap_new(&oomd_cgroup_ctx_hash_ops)); assert_se(h1 = hashmap_new(&oomd_cgroup_ctx_hash_ops));
ASSERT_OK(oomd_insert_cgroup_context(NULL, h1, cgroup)); assert_se(oomd_insert_cgroup_context(NULL, h1, cgroup) == 0);
ASSERT_NOT_NULL(c1 = hashmap_get(h1, cgroup)); c1 = hashmap_get(h1, cgroup);
ASSERT_ERROR(oomd_insert_cgroup_context(NULL, h1, cgroup), EEXIST); assert_se(c1);
assert_se(oomd_insert_cgroup_context(NULL, h1, cgroup) == -EEXIST);
/* make sure certain values from h1 get updated in h2 */ /* make sure certain values from h1 get updated in h2 */
c1->pgscan = UINT64_MAX; c1->pgscan = UINT64_MAX;
@ -151,19 +134,21 @@ TEST(oomd_cgroup_context_acquire_and_insert) {
c1->mem_pressure_limit_hit_start = 42; c1->mem_pressure_limit_hit_start = 42;
c1->mem_pressure_duration_usec = 1234; c1->mem_pressure_duration_usec = 1234;
c1->last_had_mem_reclaim = 888; c1->last_had_mem_reclaim = 888;
ASSERT_NOT_NULL(h2 = hashmap_new(&oomd_cgroup_ctx_hash_ops)); assert_se(h2 = hashmap_new(&oomd_cgroup_ctx_hash_ops));
ASSERT_OK(oomd_insert_cgroup_context(h1, h2, cgroup)); assert_se(oomd_insert_cgroup_context(h1, h2, cgroup) == 0);
ASSERT_NOT_NULL(c1 = hashmap_get(h1, cgroup)); c1 = hashmap_get(h1, cgroup);
ASSERT_NOT_NULL(c2 = hashmap_get(h2, cgroup)); c2 = hashmap_get(h2, cgroup);
ASSERT_TRUE(c1 != c2); assert_se(c1);
ASSERT_EQ(c2->last_pgscan, UINT64_MAX); assert_se(c2);
ASSERT_EQ(c2->mem_pressure_limit, 6789u); assert_se(c1 != c2);
ASSERT_EQ(c2->mem_pressure_limit_hit_start, 42u); assert_se(c2->last_pgscan == UINT64_MAX);
ASSERT_EQ(c2->mem_pressure_duration_usec, 1234u); assert_se(c2->mem_pressure_limit == 6789);
ASSERT_EQ(c2->last_had_mem_reclaim, 888u); /* assumes the live pgscan is less than UINT64_MAX */ assert_se(c2->mem_pressure_limit_hit_start == 42);
assert_se(c2->mem_pressure_duration_usec == 1234);
assert_se(c2->last_had_mem_reclaim == 888); /* assumes the live pgscan is less than UINT64_MAX */
} }
TEST(oomd_update_cgroup_contexts_between_hashmaps) { static void test_oomd_update_cgroup_contexts_between_hashmaps(void) {
_cleanup_hashmap_free_ Hashmap *h_old = NULL, *h_new = NULL; _cleanup_hashmap_free_ Hashmap *h_old = NULL, *h_new = NULL;
OomdCGroupContext *c_old, *c_new; OomdCGroupContext *c_old, *c_new;
char **paths = STRV_MAKE("/0.slice", char **paths = STRV_MAKE("/0.slice",
@ -191,55 +176,57 @@ TEST(oomd_update_cgroup_contexts_between_hashmaps) {
.pgscan = 101 }, .pgscan = 101 },
}; };
ASSERT_NOT_NULL(h_old = hashmap_new(&string_hash_ops)); assert_se(h_old = hashmap_new(&string_hash_ops));
ASSERT_OK(hashmap_put(h_old, paths[0], &ctx_old[0])); assert_se(hashmap_put(h_old, paths[0], &ctx_old[0]) >= 0);
ASSERT_OK(hashmap_put(h_old, paths[1], &ctx_old[1])); assert_se(hashmap_put(h_old, paths[1], &ctx_old[1]) >= 0);
ASSERT_NOT_NULL(h_new = hashmap_new(&string_hash_ops)); assert_se(h_new = hashmap_new(&string_hash_ops));
ASSERT_OK(hashmap_put(h_new, paths[0], &ctx_new[0])); assert_se(hashmap_put(h_new, paths[0], &ctx_new[0]) >= 0);
ASSERT_OK(hashmap_put(h_new, paths[1], &ctx_new[1])); assert_se(hashmap_put(h_new, paths[1], &ctx_new[1]) >= 0);
oomd_update_cgroup_contexts_between_hashmaps(h_old, h_new); oomd_update_cgroup_contexts_between_hashmaps(h_old, h_new);
ASSERT_NOT_NULL(c_old = hashmap_get(h_old, "/0.slice")); assert_se(c_old = hashmap_get(h_old, "/0.slice"));
ASSERT_NOT_NULL(c_new = hashmap_get(h_new, "/0.slice")); assert_se(c_new = hashmap_get(h_new, "/0.slice"));
ASSERT_EQ(c_old->pgscan, c_new->last_pgscan); assert_se(c_old->pgscan == c_new->last_pgscan);
ASSERT_EQ(c_old->mem_pressure_limit, c_new->mem_pressure_limit); assert_se(c_old->mem_pressure_limit == c_new->mem_pressure_limit);
ASSERT_EQ(c_old->mem_pressure_limit_hit_start, c_new->mem_pressure_limit_hit_start); assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start);
ASSERT_EQ(c_old->mem_pressure_duration_usec, c_new->mem_pressure_duration_usec); assert_se(c_old->mem_pressure_duration_usec == c_new->mem_pressure_duration_usec);
ASSERT_EQ(c_old->last_had_mem_reclaim, c_new->last_had_mem_reclaim); assert_se(c_old->last_had_mem_reclaim == c_new->last_had_mem_reclaim);
ASSERT_NOT_NULL(c_old = hashmap_get(h_old, "/1.slice")); assert_se(c_old = hashmap_get(h_old, "/1.slice"));
ASSERT_NOT_NULL(c_new = hashmap_get(h_new, "/1.slice")); assert_se(c_new = hashmap_get(h_new, "/1.slice"));
ASSERT_EQ(c_old->pgscan, c_new->last_pgscan); assert_se(c_old->pgscan == c_new->last_pgscan);
ASSERT_EQ(c_old->mem_pressure_limit, c_new->mem_pressure_limit); assert_se(c_old->mem_pressure_limit == c_new->mem_pressure_limit);
ASSERT_EQ(c_old->mem_pressure_limit_hit_start, c_new->mem_pressure_limit_hit_start); assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start);
ASSERT_EQ(c_old->mem_pressure_duration_usec, c_new->mem_pressure_duration_usec); assert_se(c_old->mem_pressure_duration_usec == c_new->mem_pressure_duration_usec);
ASSERT_LT(c_old->last_had_mem_reclaim, c_new->last_had_mem_reclaim); assert_se(c_new->last_had_mem_reclaim > c_old->last_had_mem_reclaim);
} }
TEST(oomd_system_context_acquire) { static void test_oomd_system_context_acquire(void) {
_cleanup_(unlink_tempfilep) char path[] = "/tmp/oomdgetsysctxtestXXXXXX"; _cleanup_(unlink_tempfilep) char path[] = "/tmp/oomdgetsysctxtestXXXXXX";
_cleanup_close_ int fd = -EBADF; _cleanup_close_ int fd = -EBADF;
OomdSystemContext ctx; OomdSystemContext ctx;
ASSERT_OK(fd = mkostemp_safe(path)); if (geteuid() != 0)
return (void) log_tests_skipped("not root");
ASSERT_ERROR(oomd_system_context_acquire("/verylikelynonexistentpath", &ctx), ENOENT); assert_se((fd = mkostemp_safe(path)) >= 0);
ASSERT_ERROR(oomd_system_context_acquire(path, &ctx), EINVAL); assert_se(oomd_system_context_acquire("/verylikelynonexistentpath", &ctx) == -ENOENT);
ASSERT_OK(write_string_file(path, "some\nwords\nacross\nmultiple\nlines", WRITE_STRING_FILE_CREATE)); assert_se(oomd_system_context_acquire(path, &ctx) == -EINVAL);
ASSERT_ERROR(oomd_system_context_acquire(path, &ctx), EINVAL);
ASSERT_OK(write_string_file(path, "MemTotal: 32495256 kB trailing\n" assert_se(write_string_file(path, "some\nwords\nacross\nmultiple\nlines", WRITE_STRING_FILE_CREATE) == 0);
assert_se(oomd_system_context_acquire(path, &ctx) == -EINVAL);
assert_se(write_string_file(path, "MemTotal: 32495256 kB trailing\n"
"MemFree: 9880512 kB data\n" "MemFree: 9880512 kB data\n"
"SwapTotal: 8388604 kB is\n" "SwapTotal: 8388604 kB is\n"
"SwapFree: 7604 kB bad\n", "SwapFree: 7604 kB bad\n", WRITE_STRING_FILE_CREATE) == 0);
WRITE_STRING_FILE_CREATE)); assert_se(oomd_system_context_acquire(path, &ctx) == -EINVAL);
ASSERT_ERROR(oomd_system_context_acquire(path, &ctx), EINVAL);
ASSERT_OK(write_string_file(path, "MemTotal: 32495256 kB\n" assert_se(write_string_file(path, "MemTotal: 32495256 kB\n"
"MemFree: 9880512 kB\n" "MemFree: 9880512 kB\n"
"MemAvailable: 21777088 kB\n" "MemAvailable: 21777088 kB\n"
"Buffers: 5968 kB\n" "Buffers: 5968 kB\n"
@ -247,75 +234,74 @@ TEST(oomd_system_context_acquire) {
"Unevictable: 740004 kB\n" "Unevictable: 740004 kB\n"
"Mlocked: 4484 kB\n" "Mlocked: 4484 kB\n"
"SwapTotal: 8388604 kB\n" "SwapTotal: 8388604 kB\n"
"SwapFree: 7604 kB\n", "SwapFree: 7604 kB\n", WRITE_STRING_FILE_CREATE) == 0);
WRITE_STRING_FILE_CREATE)); assert_se(oomd_system_context_acquire(path, &ctx) == 0);
ASSERT_OK(oomd_system_context_acquire(path, &ctx)); assert_se(ctx.mem_total == 33275142144);
ASSERT_EQ(ctx.mem_total, 33275142144u); assert_se(ctx.mem_used == 10975404032);
ASSERT_EQ(ctx.mem_used, 10975404032u); assert_se(ctx.swap_total == 8589930496);
ASSERT_EQ(ctx.swap_total, 8589930496u); assert_se(ctx.swap_used == 8582144000);
ASSERT_EQ(ctx.swap_used, 8582144000u);
} }
TEST(oomd_pressure_above) { static void test_oomd_pressure_above(void) {
_cleanup_hashmap_free_ Hashmap *h1 = NULL, *h2 = NULL; _cleanup_hashmap_free_ Hashmap *h1 = NULL, *h2 = NULL;
_cleanup_set_free_ Set *t1 = NULL, *t2 = NULL, *t3 = NULL; _cleanup_set_free_ Set *t1 = NULL, *t2 = NULL, *t3 = NULL;
OomdCGroupContext ctx[2] = {}, *c; OomdCGroupContext ctx[2] = {}, *c;
loadavg_t threshold; loadavg_t threshold;
ASSERT_OK(store_loadavg_fixed_point(80, 0, &threshold)); assert_se(store_loadavg_fixed_point(80, 0, &threshold) == 0);
/* /herp.slice */ /* /herp.slice */
ASSERT_OK(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg10))); assert_se(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg10)) == 0);
ASSERT_OK(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg60))); assert_se(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg60)) == 0);
ASSERT_OK(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg300))); assert_se(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg300)) == 0);
ctx[0].mem_pressure_limit = threshold; ctx[0].mem_pressure_limit = threshold;
/* Set memory pressure duration to 0 since we use the real system monotonic clock /* Set memory pressure duration to 0 since we use the real system monotonic clock
* in oomd_pressure_above() and we want to avoid this test depending on timing. */ * in oomd_pressure_above() and we want to avoid this test depending on timing. */
ctx[0].mem_pressure_duration_usec = 0; ctx[0].mem_pressure_duration_usec = 0;
/* /derp.slice */ /* /derp.slice */
ASSERT_OK(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg10))); assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg10)) == 0);
ASSERT_OK(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg60))); assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg60)) == 0);
ASSERT_OK(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg300))); assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg300)) == 0);
ctx[1].mem_pressure_limit = threshold; ctx[1].mem_pressure_limit = threshold;
ctx[1].mem_pressure_duration_usec = 0; ctx[1].mem_pressure_duration_usec = 0;
/* High memory pressure */ /* High memory pressure */
ASSERT_NOT_NULL(h1 = hashmap_new(&string_hash_ops)); assert_se(h1 = hashmap_new(&string_hash_ops));
ASSERT_OK(hashmap_put(h1, "/herp.slice", &ctx[0])); assert_se(hashmap_put(h1, "/herp.slice", &ctx[0]) >= 0);
ASSERT_OK_POSITIVE(oomd_pressure_above(h1, &t1)); assert_se(oomd_pressure_above(h1, &t1) == 1);
ASSERT_TRUE(set_contains(t1, &ctx[0])); assert_se(set_contains(t1, &ctx[0]));
ASSERT_NOT_NULL(c = hashmap_get(h1, "/herp.slice")); assert_se(c = hashmap_get(h1, "/herp.slice"));
ASSERT_GT(c->mem_pressure_limit_hit_start, 0u); assert_se(c->mem_pressure_limit_hit_start > 0);
/* Low memory pressure */ /* Low memory pressure */
ASSERT_NOT_NULL(h2 = hashmap_new(&string_hash_ops)); assert_se(h2 = hashmap_new(&string_hash_ops));
ASSERT_OK(hashmap_put(h2, "/derp.slice", &ctx[1])); assert_se(hashmap_put(h2, "/derp.slice", &ctx[1]) >= 0);
ASSERT_OK_ZERO(oomd_pressure_above(h2, &t2)); assert_se(oomd_pressure_above(h2, &t2) == 0);
ASSERT_NULL(t2); assert_se(!t2);
ASSERT_NOT_NULL(c = hashmap_get(h2, "/derp.slice")); assert_se(c = hashmap_get(h2, "/derp.slice"));
ASSERT_EQ(c->mem_pressure_limit_hit_start, 0u); assert_se(c->mem_pressure_limit_hit_start == 0);
/* High memory pressure w/ multiple cgroups */ /* High memory pressure w/ multiple cgroups */
ASSERT_OK(hashmap_put(h1, "/derp.slice", &ctx[1])); assert_se(hashmap_put(h1, "/derp.slice", &ctx[1]) >= 0);
ASSERT_OK_POSITIVE(oomd_pressure_above(h1, &t3)); assert_se(oomd_pressure_above(h1, &t3) == 1);
ASSERT_TRUE(set_contains(t3, &ctx[0])); assert_se(set_contains(t3, &ctx[0]));
ASSERT_EQ(set_size(t3), 1u); assert_se(set_size(t3) == 1);
ASSERT_NOT_NULL(c = hashmap_get(h1, "/herp.slice")); assert_se(c = hashmap_get(h1, "/herp.slice"));
ASSERT_GT(c->mem_pressure_limit_hit_start, 0u); assert_se(c->mem_pressure_limit_hit_start > 0);
ASSERT_NOT_NULL(c = hashmap_get(h1, "/derp.slice")); assert_se(c = hashmap_get(h1, "/derp.slice"));
ASSERT_EQ(c->mem_pressure_limit_hit_start, 0u); assert_se(c->mem_pressure_limit_hit_start == 0);
} }
TEST(oomd_mem_and_swap_free_below) { static void test_oomd_mem_and_swap_free_below(void) {
OomdSystemContext ctx = (OomdSystemContext) { OomdSystemContext ctx = (OomdSystemContext) {
.mem_total = UINT64_C(20971512) * 1024U, .mem_total = UINT64_C(20971512) * 1024U,
.mem_used = UINT64_C(3310136) * 1024U, .mem_used = UINT64_C(3310136) * 1024U,
.swap_total = UINT64_C(20971512) * 1024U, .swap_total = UINT64_C(20971512) * 1024U,
.swap_used = UINT64_C(20971440) * 1024U, .swap_used = UINT64_C(20971440) * 1024U,
}; };
ASSERT_FALSE(oomd_mem_available_below(&ctx, 2000)); assert_se(oomd_mem_available_below(&ctx, 2000) == false);
ASSERT_TRUE(oomd_swap_free_below(&ctx, 2000)); assert_se(oomd_swap_free_below(&ctx, 2000) == true);
ctx = (OomdSystemContext) { ctx = (OomdSystemContext) {
.mem_total = UINT64_C(20971512) * 1024U, .mem_total = UINT64_C(20971512) * 1024U,
@ -323,8 +309,8 @@ TEST(oomd_mem_and_swap_free_below) {
.swap_total = UINT64_C(20971512) * 1024U, .swap_total = UINT64_C(20971512) * 1024U,
.swap_used = UINT64_C(3310136) * 1024U, .swap_used = UINT64_C(3310136) * 1024U,
}; };
ASSERT_TRUE(oomd_mem_available_below(&ctx, 2000)); assert_se(oomd_mem_available_below(&ctx, 2000) == true);
ASSERT_FALSE(oomd_swap_free_below(&ctx, 2000)); assert_se(oomd_swap_free_below(&ctx, 2000) == false);
ctx = (OomdSystemContext) { ctx = (OomdSystemContext) {
.mem_total = 0, .mem_total = 0,
@ -332,11 +318,11 @@ TEST(oomd_mem_and_swap_free_below) {
.swap_total = 0, .swap_total = 0,
.swap_used = 0, .swap_used = 0,
}; };
ASSERT_FALSE(oomd_mem_available_below(&ctx, 2000)); assert_se(oomd_mem_available_below(&ctx, 2000) == false);
ASSERT_FALSE(oomd_swap_free_below(&ctx, 2000)); assert_se(oomd_swap_free_below(&ctx, 2000) == false);
} }
TEST(oomd_sort_cgroups) { static void test_oomd_sort_cgroups(void) {
_cleanup_hashmap_free_ Hashmap *h = NULL; _cleanup_hashmap_free_ Hashmap *h = NULL;
_cleanup_free_ OomdCGroupContext **sorted_cgroups = NULL; _cleanup_free_ OomdCGroupContext **sorted_cgroups = NULL;
char **paths = STRV_MAKE("/herp.slice", char **paths = STRV_MAKE("/herp.slice",
@ -385,37 +371,37 @@ TEST(oomd_sort_cgroups) {
.preference = MANAGED_OOM_PREFERENCE_AVOID }, .preference = MANAGED_OOM_PREFERENCE_AVOID },
}; };
ASSERT_NOT_NULL(h = hashmap_new(&string_hash_ops)); assert_se(h = hashmap_new(&string_hash_ops));
ASSERT_OK(hashmap_put(h, "/herp.slice", &ctx[0])); assert_se(hashmap_put(h, "/herp.slice", &ctx[0]) >= 0);
ASSERT_OK(hashmap_put(h, "/herp.slice/derp.scope", &ctx[1])); assert_se(hashmap_put(h, "/herp.slice/derp.scope", &ctx[1]) >= 0);
ASSERT_OK(hashmap_put(h, "/herp.slice/derp.scope/sheep.service", &ctx[2])); assert_se(hashmap_put(h, "/herp.slice/derp.scope/sheep.service", &ctx[2]) >= 0);
ASSERT_OK(hashmap_put(h, "/zupa.slice", &ctx[3])); assert_se(hashmap_put(h, "/zupa.slice", &ctx[3]) >= 0);
ASSERT_OK(hashmap_put(h, "/boop.slice", &ctx[4])); assert_se(hashmap_put(h, "/boop.slice", &ctx[4]) >= 0);
ASSERT_OK(hashmap_put(h, "/omitted.slice", &ctx[5])); assert_se(hashmap_put(h, "/omitted.slice", &ctx[5]) >= 0);
ASSERT_OK(hashmap_put(h, "/avoid.slice", &ctx[6])); assert_se(hashmap_put(h, "/avoid.slice", &ctx[6]) >= 0);
ASSERT_OK_EQ(oomd_sort_cgroup_contexts(h, compare_swap_usage, NULL, &sorted_cgroups), 6); assert_se(oomd_sort_cgroup_contexts(h, compare_swap_usage, NULL, &sorted_cgroups) == 6);
ASSERT_PTR_EQ(sorted_cgroups[0], &ctx[1]); assert_se(sorted_cgroups[0] == &ctx[1]);
ASSERT_PTR_EQ(sorted_cgroups[1], &ctx[2]); assert_se(sorted_cgroups[1] == &ctx[2]);
ASSERT_PTR_EQ(sorted_cgroups[2], &ctx[0]); assert_se(sorted_cgroups[2] == &ctx[0]);
ASSERT_PTR_EQ(sorted_cgroups[3], &ctx[4]); assert_se(sorted_cgroups[3] == &ctx[4]);
ASSERT_PTR_EQ(sorted_cgroups[4], &ctx[3]); assert_se(sorted_cgroups[4] == &ctx[3]);
ASSERT_PTR_EQ(sorted_cgroups[5], &ctx[6]); assert_se(sorted_cgroups[5] == &ctx[6]);
sorted_cgroups = mfree(sorted_cgroups); sorted_cgroups = mfree(sorted_cgroups);
ASSERT_OK_EQ(oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, NULL, &sorted_cgroups), 6); assert_se(oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, NULL, &sorted_cgroups) == 6);
ASSERT_PTR_EQ(sorted_cgroups[0], &ctx[0]); assert_se(sorted_cgroups[0] == &ctx[0]);
ASSERT_PTR_EQ(sorted_cgroups[1], &ctx[2]); assert_se(sorted_cgroups[1] == &ctx[2]);
ASSERT_PTR_EQ(sorted_cgroups[2], &ctx[3]); assert_se(sorted_cgroups[2] == &ctx[3]);
ASSERT_PTR_EQ(sorted_cgroups[3], &ctx[1]); assert_se(sorted_cgroups[3] == &ctx[1]);
ASSERT_PTR_EQ(sorted_cgroups[4], &ctx[4]); assert_se(sorted_cgroups[4] == &ctx[4]);
ASSERT_PTR_EQ(sorted_cgroups[5], &ctx[6]); assert_se(sorted_cgroups[5] == &ctx[6]);
sorted_cgroups = mfree(sorted_cgroups); sorted_cgroups = mfree(sorted_cgroups);
ASSERT_OK_EQ(oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, "/herp.slice/derp.scope", &sorted_cgroups), 2); assert_se(oomd_sort_cgroup_contexts(h, compare_pgscan_rate_and_memory_usage, "/herp.slice/derp.scope", &sorted_cgroups) == 2);
ASSERT_PTR_EQ(sorted_cgroups[0], &ctx[2]); assert_se(sorted_cgroups[0] == &ctx[2]);
ASSERT_PTR_EQ(sorted_cgroups[1], &ctx[1]); assert_se(sorted_cgroups[1] == &ctx[1]);
ASSERT_NULL(sorted_cgroups[2]); ASSERT_NULL(sorted_cgroups[2]);
ASSERT_NULL(sorted_cgroups[3]); ASSERT_NULL(sorted_cgroups[3]);
ASSERT_NULL(sorted_cgroups[4]); ASSERT_NULL(sorted_cgroups[4]);
@ -423,24 +409,27 @@ TEST(oomd_sort_cgroups) {
ASSERT_NULL(sorted_cgroups[6]); ASSERT_NULL(sorted_cgroups[6]);
} }
TEST(oomd_fetch_cgroup_oom_preference) { static void test_oomd_fetch_cgroup_oom_preference(void) {
_cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *ctx = NULL; _cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *ctx = NULL;
_cleanup_free_ char *cgroup = NULL;
ManagedOOMPreference root_pref; ManagedOOMPreference root_pref;
CGroupMask mask; CGroupMask mask;
bool test_xattrs; bool test_xattrs;
int r; int root_xattrs, r;
if (geteuid() != 0)
return (void) log_tests_skipped("not root");
if (!is_pressure_supported()) if (!is_pressure_supported())
return (void) log_tests_skipped("system does not support pressure"); return (void) log_tests_skipped("system does not support pressure");
if (enter_cgroup_root_cached() < 0) assert_se(cg_mask_supported(&mask) >= 0);
return;
ASSERT_OK(cg_mask_supported(&mask));
if (!FLAGS_SET(mask, CGROUP_MASK_MEMORY)) if (!FLAGS_SET(mask, CGROUP_MASK_MEMORY))
return (void) log_tests_skipped("cgroup memory controller is not available"); return (void) log_tests_skipped("cgroup memory controller is not available");
ASSERT_OK(oomd_cgroup_context_acquire(cgroup, &ctx)); assert_se(cg_pid_get_path(NULL, 0, &cgroup) >= 0);
assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0);
/* If we don't have permissions to set xattrs we're likely in a userns or missing capabilities /* If we don't have permissions to set xattrs we're likely in a userns or missing capabilities
* so skip the xattr portions of the test. */ * so skip the xattr portions of the test. */
@ -448,56 +437,77 @@ TEST(oomd_fetch_cgroup_oom_preference) {
test_xattrs = !ERRNO_IS_PRIVILEGE(r) && !ERRNO_IS_NOT_SUPPORTED(r); test_xattrs = !ERRNO_IS_PRIVILEGE(r) && !ERRNO_IS_NOT_SUPPORTED(r);
if (test_xattrs) { if (test_xattrs) {
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, NULL)); assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0);
ASSERT_OK(cg_set_xattr(cgroup, "user.oomd_omit", "1", 1, 0)); assert_se(cg_set_xattr(cgroup, "user.oomd_omit", "1", 1, 0) >= 0);
ASSERT_OK(cg_set_xattr(cgroup, "user.oomd_avoid", "1", 1, 0)); assert_se(cg_set_xattr(cgroup, "user.oomd_avoid", "1", 1, 0) >= 0);
/* omit takes precedence over avoid when both are set to true */ /* omit takes precedence over avoid when both are set to true */
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, NULL)); assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0);
ASSERT_EQ(ctx->preference, geteuid() == 0 ? MANAGED_OOM_PREFERENCE_OMIT : MANAGED_OOM_PREFERENCE_NONE); assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_OMIT);
} else { } else {
ASSERT_FAIL(oomd_fetch_cgroup_oom_preference(ctx, NULL)); assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) < 0);
ASSERT_EQ(ctx->preference, MANAGED_OOM_PREFERENCE_NONE); assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_NONE);
} }
ctx = oomd_cgroup_context_free(ctx); ctx = oomd_cgroup_context_free(ctx);
/* also check when only avoid is set to true */ /* also check when only avoid is set to true */
if (test_xattrs) { if (test_xattrs) {
ASSERT_OK(cg_set_xattr(cgroup, "user.oomd_omit", "0", 1, 0)); assert_se(cg_set_xattr(cgroup, "user.oomd_omit", "0", 1, 0) >= 0);
ASSERT_OK(cg_set_xattr(cgroup, "user.oomd_avoid", "1", 1, 0)); assert_se(cg_set_xattr(cgroup, "user.oomd_avoid", "1", 1, 0) >= 0);
ASSERT_OK(oomd_cgroup_context_acquire(cgroup, &ctx)); assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0);
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, NULL)); assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0);
ASSERT_EQ(ctx->preference, geteuid() == 0 ? MANAGED_OOM_PREFERENCE_AVOID : MANAGED_OOM_PREFERENCE_NONE); assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_AVOID);
ctx = oomd_cgroup_context_free(ctx); ctx = oomd_cgroup_context_free(ctx);
} }
/* Test the root cgroup */ /* Test the root cgroup */
/* Root cgroup is live and not made on demand like the cgroup the test runs in. It can have varying /* Root cgroup is live and not made on demand like the cgroup the test runs in. It can have varying
* xattrs set already so let's read in the booleans first to get the final preference value. */ * xattrs set already so let's read in the booleans first to get the final preference value. */
ASSERT_OK(oomd_cgroup_context_acquire("", &ctx)); assert_se(oomd_cgroup_context_acquire("", &ctx) == 0);
root_pref = MANAGED_OOM_PREFERENCE_NONE; root_xattrs = cg_get_xattr_bool("", "user.oomd_omit");
if (cg_get_xattr_bool("", "user.oomd_avoid") > 0) root_pref = root_xattrs > 0 ? MANAGED_OOM_PREFERENCE_OMIT : MANAGED_OOM_PREFERENCE_NONE;
root_pref = MANAGED_OOM_PREFERENCE_AVOID; root_xattrs = cg_get_xattr_bool("", "user.oomd_avoid");
if (cg_get_xattr_bool("", "user.oomd_omit") > 0) root_pref = root_xattrs > 0 ? MANAGED_OOM_PREFERENCE_AVOID : MANAGED_OOM_PREFERENCE_NONE;
root_pref = MANAGED_OOM_PREFERENCE_OMIT; assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0);
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, NULL)); assert_se(ctx->preference == root_pref);
ASSERT_EQ(ctx->preference, root_pref);
ASSERT_ERROR(oomd_fetch_cgroup_oom_preference(ctx, "/herp.slice/derp.scope"), EINVAL); assert_se(oomd_fetch_cgroup_oom_preference(ctx, "/herp.slice/derp.scope") == -EINVAL);
/* Assert that avoid/omit are not set if the cgroup and prefix are not /* Assert that avoid/omit are not set if the cgroup and prefix are not
* owned by the same user. */ * owned by the same user. */
if (test_xattrs && !empty_or_root(cgroup) && geteuid() == 0) { if (test_xattrs && !empty_or_root(cgroup)) {
ctx = oomd_cgroup_context_free(ctx); ctx = oomd_cgroup_context_free(ctx);
ASSERT_OK(cg_set_access(cgroup, 61183, 0)); assert_se(cg_set_access(cgroup, 61183, 0) >= 0);
ASSERT_OK(oomd_cgroup_context_acquire(cgroup, &ctx)); assert_se(oomd_cgroup_context_acquire(cgroup, &ctx) == 0);
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, NULL)); assert_se(oomd_fetch_cgroup_oom_preference(ctx, NULL) == 0);
ASSERT_EQ(ctx->preference, MANAGED_OOM_PREFERENCE_NONE); assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_NONE);
ASSERT_OK(oomd_fetch_cgroup_oom_preference(ctx, ctx->path)); assert_se(oomd_fetch_cgroup_oom_preference(ctx, ctx->path) == 0);
ASSERT_EQ(ctx->preference, MANAGED_OOM_PREFERENCE_AVOID); assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_AVOID);
} }
} }
DEFINE_TEST_MAIN(LOG_DEBUG); int main(void) {
int r;
test_setup_logging(LOG_DEBUG);
test_oomd_update_cgroup_contexts_between_hashmaps();
test_oomd_system_context_acquire();
test_oomd_pressure_above();
test_oomd_mem_and_swap_free_below();
test_oomd_sort_cgroups();
/* The following tests operate on live cgroups */
r = enter_cgroup_root(NULL);
if (r < 0)
return log_tests_skipped_errno(r, "failed to enter a test cgroup scope");
test_oomd_cgroup_kill();
test_oomd_cgroup_context_acquire_and_insert();
test_oomd_fetch_cgroup_oom_preference();
return 0;
}

View File

@ -29,7 +29,6 @@
#include "log.h" #include "log.h"
#include "namespace-util.h" #include "namespace-util.h"
#include "path-util.h" #include "path-util.h"
#include "pidref.h"
#include "process-util.h" #include "process-util.h"
#include "random-util.h" #include "random-util.h"
#include "rlimit-util.h" #include "rlimit-util.h"
@ -39,11 +38,13 @@
#include "uid-range.h" #include "uid-range.h"
char* setup_fake_runtime_dir(void) { char* setup_fake_runtime_dir(void) {
char *t; char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p;
ASSERT_OK(mkdtemp_malloc("/tmp/fake-xdg-runtime-XXXXXX", &t)); assert_se(mkdtemp(t));
ASSERT_OK(setenv("XDG_RUNTIME_DIR", t, /* overwrite = */ true)); assert_se(setenv("XDG_RUNTIME_DIR", t, 1) >= 0);
return t; assert_se(p = strdup(t));
return p;
} }
static void load_testdata_env(void) { static void load_testdata_env(void) {
@ -56,18 +57,18 @@ static void load_testdata_env(void) {
return; return;
called = true; called = true;
ASSERT_OK(readlink_and_make_absolute("/proc/self/exe", &s)); assert_se(readlink_and_make_absolute("/proc/self/exe", &s) >= 0);
ASSERT_OK(path_extract_directory(s, &d)); assert_se(path_extract_directory(s, &d) >= 0);
ASSERT_NOT_NULL(envpath = path_join(d, "systemd-runtest.env")); assert_se(envpath = path_join(d, "systemd-runtest.env"));
r = load_env_file_pairs(NULL, envpath, &pairs); r = load_env_file_pairs(NULL, envpath, &pairs);
if (r < 0) { if (r < 0) {
log_debug_errno(r, "Reading %s failed, ignoring: %m", envpath); log_debug_errno(r, "Reading %s failed: %m", envpath);
return; return;
} }
STRV_FOREACH_PAIR(k, v, pairs) STRV_FOREACH_PAIR(k, v, pairs)
ASSERT_OK(setenv(*k, *v, /* overwrite = */ false)); assert_se(setenv(*k, *v, 0) >= 0);
} }
int get_testdata_dir(const char *suffix, char **ret) { int get_testdata_dir(const char *suffix, char **ret) {
@ -143,14 +144,16 @@ int write_tmpfile(char *pattern, const char *contents) {
} }
bool have_namespaces(void) { bool have_namespaces(void) {
_cleanup_(pidref_done) PidRef pid = PIDREF_NULL; siginfo_t si = {};
int r; pid_t pid;
/* Checks whether namespaces are available. In some cases they aren't. We do this by calling unshare(), and we /* Checks whether namespaces are available. In some cases they aren't. We do this by calling unshare(), and we
* do so in a child process in order not to affect our own process. */ * do so in a child process in order not to affect our own process. */
ASSERT_OK(r = pidref_safe_fork("(have_namespace)", /* flags = */ 0, &pid)); pid = fork();
if (r == 0) { assert_se(pid >= 0);
if (pid == 0) {
/* child */ /* child */
if (detach_mount_namespace() < 0) if (detach_mount_namespace() < 0)
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
@ -158,11 +161,13 @@ bool have_namespaces(void) {
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
} }
ASSERT_OK(r = pidref_wait_for_terminate_and_check("(have_namespace)", &pid, /* flags = */ 0)); assert_se(waitid(P_PID, pid, &si, WEXITED) >= 0);
if (r == EXIT_SUCCESS) assert_se(si.si_code == CLD_EXITED);
if (si.si_status == EXIT_SUCCESS)
return true; return true;
if (r == EXIT_FAILURE) if (si.si_status == EXIT_FAILURE)
return false; return false;
assert_not_reached(); assert_not_reached();
@ -198,9 +203,9 @@ bool can_memlock(void) {
bool b = mlock(p, CAN_MEMLOCK_SIZE) >= 0; bool b = mlock(p, CAN_MEMLOCK_SIZE) >= 0;
if (b) if (b)
ASSERT_OK(munlock(p, CAN_MEMLOCK_SIZE)); assert_se(munlock(p, CAN_MEMLOCK_SIZE) >= 0);
ASSERT_OK(munmap(p, CAN_MEMLOCK_SIZE)); assert_se(munmap(p, CAN_MEMLOCK_SIZE) >= 0);
return b; return b;
} }
@ -296,14 +301,16 @@ static int enter_cgroup(char **ret_cgroup, bool enter_subroot) {
r = cg_pid_get_path(NULL, 0, &cgroup_root); r = cg_pid_get_path(NULL, 0, &cgroup_root);
if (IN_SET(r, -ENOMEDIUM, -ENOENT)) if (IN_SET(r, -ENOMEDIUM, -ENOENT))
return log_warning_errno(r, "cg_pid_get_path(NULL, 0, ...) failed: %m"); return log_warning_errno(r, "cg_pid_get_path(NULL, 0, ...) failed: %m");
ASSERT_OK(r); assert(r >= 0);
if (enter_subroot) if (enter_subroot)
ASSERT_OK(asprintf(&cgroup_subroot, "%s/%" PRIx64, cgroup_root, random_u64())); assert_se(asprintf(&cgroup_subroot, "%s/%" PRIx64, cgroup_root, random_u64()) >= 0);
else else {
ASSERT_NOT_NULL(cgroup_subroot = strdup(cgroup_root)); cgroup_subroot = strdup(cgroup_root);
assert_se(cgroup_subroot != NULL);
}
ASSERT_OK(cg_mask_supported(&supported)); assert_se(cg_mask_supported(&supported) >= 0);
/* If this fails, then we don't mind as the later cgroup operations will fail too, and it's fine if /* If this fails, then we don't mind as the later cgroup operations will fail too, and it's fine if
* we handle any errors at that point. */ * we handle any errors at that point. */

View File

@ -82,7 +82,7 @@ int define_hex_ptr_internal(const char *hex, void **name, size_t *name_len);
#define DEFINE_HEX_PTR(name, hex) \ #define DEFINE_HEX_PTR(name, hex) \
_cleanup_free_ void *name = NULL; \ _cleanup_free_ void *name = NULL; \
size_t name##_len = 0; \ size_t name##_len = 0; \
ASSERT_OK(define_hex_ptr_internal(hex, &name, &name##_len)) assert_se(define_hex_ptr_internal(hex, &name, &name##_len) >= 0)
/* Provide a convenient way to check if we're running in CI. */ /* Provide a convenient way to check if we're running in CI. */
const char* ci_environment(void); const char* ci_environment(void);

View File

@ -697,7 +697,7 @@ int resource_resolve_path(
} else if (RESOURCE_IS_FILESYSTEM(rr->type)) { } else if (RESOURCE_IS_FILESYSTEM(rr->type)) {
_cleanup_free_ char *resolved = NULL, *relative_to = NULL; _cleanup_free_ char *resolved = NULL, *relative_to = NULL;
ChaseFlags chase_flags = CHASE_NONEXISTENT | CHASE_PREFIX_ROOT; ChaseFlags chase_flags = CHASE_PREFIX_ROOT;
if (rr->path_relative_to == PATH_RELATIVE_TO_EXPLICIT) { if (rr->path_relative_to == PATH_RELATIVE_TO_EXPLICIT) {
assert(relative_to_directory); assert(relative_to_directory);

View File

@ -413,7 +413,7 @@ static int run(int argc, char *argv[]) {
fd = open(ASSERT_PTR(arg_device), O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); fd = open(ASSERT_PTR(arg_device), O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
if (fd < 0) { if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno); bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno, log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to open device node '%s'%s: %m", "Failed to open device node '%s'%s: %m",
arg_device, ignore ? ", ignoring" : ""); arg_device, ignore ? ", ignoring" : "");

View File

@ -75,7 +75,7 @@ static int run(int argc, char** argv) {
mtd_fd = open(argv[1], O_RDONLY|O_CLOEXEC|O_NOCTTY); mtd_fd = open(argv[1], O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (mtd_fd < 0) { if (mtd_fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno); bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno, log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to open device node '%s'%s: %m", "Failed to open device node '%s'%s: %m",
argv[1], ignore ? ", ignoring" : ""); argv[1], ignore ? ", ignoring" : "");

View File

@ -499,7 +499,7 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) {
fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) { if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd); bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
log_device_debug_errno(dev, fd, "Failed to open block device %s%s: %m", log_device_debug_errno(dev, fd, "Failed to open block device %s%s: %m",
devnode, ignore ? ", ignoring" : ""); devnode, ignore ? ", ignoring" : "");
return ignore ? 0 : fd; return ignore ? 0 : fd;

View File

@ -35,7 +35,7 @@ static int builtin_btrfs(UdevEvent *event, int argc, char *argv[]) {
_cleanup_close_ int fd = open("/dev/btrfs-control", O_RDWR|O_CLOEXEC|O_NOCTTY); _cleanup_close_ int fd = open("/dev/btrfs-control", O_RDWR|O_CLOEXEC|O_NOCTTY);
if (fd < 0) { if (fd < 0) {
if (ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno)) { if (ERRNO_IS_DEVICE_ABSENT(errno)) {
/* Driver not installed? Then we aren't ready. This is useful in initrds that lack /* Driver not installed? Then we aren't ready. This is useful in initrds that lack
* btrfs.ko. After the host transition (where btrfs.ko will hopefully become * btrfs.ko. After the host transition (where btrfs.ko will hopefully become
* available) the device can be retriggered and will then be considered ready. */ * available) the device can be retriggered and will then be considered ready. */

View File

@ -201,7 +201,7 @@ static int builtin_keyboard(UdevEvent *event, int argc, char *argv[]) {
if (fd < 0) { if (fd < 0) {
fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) { if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd); bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd, log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
"Failed to open device '%s'%s: %m", "Failed to open device '%s'%s: %m",
node, ignore ? ", ignoring" : ""); node, ignore ? ", ignoring" : "");
@ -223,7 +223,7 @@ static int builtin_keyboard(UdevEvent *event, int argc, char *argv[]) {
if (fd < 0) { if (fd < 0) {
fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) { if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd); bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd, log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
"Failed to open device '%s'%s: %m", "Failed to open device '%s'%s: %m",
node, ignore ? ", ignoring" : ""); node, ignore ? ", ignoring" : "");

View File

@ -122,7 +122,7 @@ static int builtin_uaccess(UdevEvent *event, int argc, char *argv[]) {
_cleanup_close_ int fd = sd_device_open(dev, O_CLOEXEC|O_PATH); _cleanup_close_ int fd = sd_device_open(dev, O_CLOEXEC|O_PATH);
if (fd < 0) { if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd); bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd, log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
"Failed to open device node%s: %m", "Failed to open device node%s: %m",
ignore ? ", ignoring" : ""); ignore ? ", ignoring" : "");

View File

@ -431,7 +431,7 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
return r; return r;
r = node_get_current(slink, dirfd, &current_id, add ? &current_prio : NULL); r = node_get_current(slink, dirfd, &current_id, add ? &current_prio : NULL);
if (r < 0 && !ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(r)) if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink); return log_device_debug_errno(dev, r, "Failed to get the current device node priority for '%s': %m", slink);
r = stack_directory_update(dev, dirfd, add); r = stack_directory_update(dev, dirfd, add);
@ -702,7 +702,7 @@ int udev_node_apply_permissions(
node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC); node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC);
if (node_fd < 0) { if (node_fd < 0) {
if (ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(node_fd)) { if (ERRNO_IS_DEVICE_ABSENT(node_fd)) {
log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode); log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode);
return 0; /* This is necessarily racey, so ignore missing the device */ return 0; /* This is necessarily racey, so ignore missing the device */
} }
@ -735,7 +735,7 @@ int static_node_apply_permissions(
node_fd = open(devnode, O_PATH|O_CLOEXEC); node_fd = open(devnode, O_PATH|O_CLOEXEC);
if (node_fd < 0) { if (node_fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno); bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno, log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to open device node '%s'%s: %m", "Failed to open device node '%s'%s: %m",
devnode, ignore ? ", ignoring" : ""); devnode, ignore ? ", ignoring" : "");

View File

@ -112,7 +112,7 @@ static int worker_lock_whole_disk(UdevWorker *worker, sd_device *dev, int *ret_f
fd = sd_device_open(dev_whole_disk, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); fd = sd_device_open(dev_whole_disk, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) { if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd); bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
log_device_debug_errno(dev, fd, "Failed to open '%s'%s: %m", whole_disk, ignore ? ", ignoring" : ""); log_device_debug_errno(dev, fd, "Failed to open '%s'%s: %m", whole_disk, ignore ? ", ignoring" : "");
if (!ignore) if (!ignore)
@ -178,7 +178,7 @@ static int worker_mark_block_device_read_only(sd_device *dev) {
_cleanup_close_ int fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); _cleanup_close_ int fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) { if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd); bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd, log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
"Failed to open device node '%s'%s: %m", "Failed to open device node '%s'%s: %m",
node, ignore ? ", ignoring" : ""); node, ignore ? ", ignoring" : "");

View File

@ -61,7 +61,7 @@ static int run(int argc, char *argv[]) {
fd = open(arg_device, O_RDONLY|O_CLOEXEC|O_NOCTTY); fd = open(arg_device, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) { if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno); bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno, log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to open device node '%s'%s: %m", "Failed to open device node '%s'%s: %m",
arg_device, ignore ? ", ignoring" : ""); arg_device, ignore ? ", ignoring" : "");

View File

@ -133,10 +133,6 @@ that make use of `run_testcases`.
`TEST_SKIP_TESTCASE=testcase`: takes a space separated list of testcases to skip. `TEST_SKIP_TESTCASE=testcase`: takes a space separated list of testcases to skip.
`TEST_SAVE_JOURNAL=0|1|fail`: When `0`, journal file will be removed on exit.
When `1`, journal file will be saved at `$BUILD_DIR/test/journal`. When `fail`,
journal file will be saved only when the test is failed. Defaults to `fail`.
`TEST_JOURNAL_USE_TMP=1`: Write test journal to `/tmp` while the test is in `TEST_JOURNAL_USE_TMP=1`: Write test journal to `/tmp` while the test is in
progress and only move the journal to its final location in the build directory progress and only move the journal to its final location in the build directory
(`$BUILD_DIR/test/journal`) when the test is finished. (`$BUILD_DIR/test/journal`) when the test is finished.

View File

@ -1,8 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
integration_tests += [
integration_test_template + {
'name' : fs.name(meson.current_source_dir()),
'vm' : true,
},
]

View File

@ -660,12 +660,7 @@ def main() -> None:
journal_file = Path(shutil.move(journal_file, dst)) journal_file = Path(shutil.move(journal_file, dst))
if shell or (result.returncode in (args.exit_code, 77) and not coredumps and not sanitizer): if shell or (result.returncode in (args.exit_code, 77) and not coredumps and not sanitizer):
exit_code = 0 if shell or result.returncode == args.exit_code else 77 exit(0 if shell or result.returncode == args.exit_code else 77)
exit_str = 'succeeded' if exit_code == 0 else 'skipped'
else:
# 0 also means we failed so translate that to a non-zero exit code to mark the test as failed.
exit_code = result.returncode or 1
exit_str = 'failed'
if journal_file.exists(): if journal_file.exists():
ops = [] ops = []
@ -683,11 +678,10 @@ def main() -> None:
ops += [f'journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info'] ops += [f'journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info']
print( print(f'Test failed, relevant logs can be viewed with: \n\n{(" && ".join(ops))}\n', file=sys.stderr)
f'Test {exit_str}, relevant logs can be viewed with: \n\n{(" && ".join(ops))}\n', file=sys.stderr
)
exit(exit_code) # 0 also means we failed so translate that to a non-zero exit code to mark the test as failed.
exit(result.returncode or 1)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -43,7 +43,6 @@ foreach dirname : [
'TEST-07-PID1', 'TEST-07-PID1',
'TEST-08-INITRD', 'TEST-08-INITRD',
'TEST-09-REBOOT', 'TEST-09-REBOOT',
'TEST-10-MOUNT',
'TEST-13-NSPAWN', 'TEST-13-NSPAWN',
'TEST-15-DROPIN', 'TEST-15-DROPIN',
'TEST-16-EXTEND-TIMEOUT', 'TEST-16-EXTEND-TIMEOUT',

View File

@ -32,11 +32,6 @@ if ! systemd-detect-virt -qc; then
sysctl net.ipv4.ping_group_range="0 2147483647" sysctl net.ipv4.ping_group_range="0 2147483647"
fi fi
# Disable firewalld and friends to make them not disturb test-firewall-util
systemctl disable --now firewalld.service || :
systemctl disable --now iptables.service || :
systemctl disable --now ip6tables.service || :
# Check & report test results # Check & report test results
# Arguments: # Arguments:
# $1: test path # $1: test path

View File

@ -1,169 +0,0 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
# shellcheck source=test/units/test-control.sh
. "$(dirname "$0")"/test-control.sh
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
teardown_test_dependencies() (
set +eux
if mountpoint /tmp/deptest; then
umount /tmp/deptest
fi
if [[ -n "${LOOP}" ]]; then
losetup -d "${LOOP}" || :
fi
if [[ -n "${LOOP_0}" ]]; then
losetup -d "${LOOP_0}" || :
fi
if [[ -n "${LOOP_1}" ]]; then
losetup -d "${LOOP_1}" || :
fi
rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-0.img
rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-1.img
rm -f /run/systemd/system/tmp-deptest.mount
systemctl daemon-reload
return 0
)
setup_loop() {
truncate -s 30m "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img"
sfdisk --wipe=always "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img" <<EOF
label:gpt
name="loop${1?}-part1"
EOF
LOOP=$(losetup -P --show -f "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img")
udevadm wait --settle --timeout=30 "${LOOP}"
udevadm lock --timeout=30 --device="${LOOP}" mkfs.ext4 -L "partname${1?}-1" "${LOOP}p1"
}
check_dependencies() {
local escaped_0 escaped_1 after
escaped_0=$(systemd-escape -p "${LOOP_0}p1")
escaped_1=$(systemd-escape -p "${LOOP_1}p1")
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_not_in "local-fs-pre.target" "$after"
assert_in "remote-fs-pre.target" "$after"
assert_in "network.target" "$after"
fi
# mount LOOP_0
mount -t ext4 "${LOOP_0}p1" /tmp/deptest
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_in "local-fs-pre.target" "$after"
assert_not_in "remote-fs-pre.target" "$after"
assert_not_in "network.target" "$after"
assert_in "${escaped_0}.device" "$after"
assert_in "blockdev@${escaped_0}.target" "$after"
assert_not_in "${escaped_1}.device" "$after"
assert_not_in "blockdev@${escaped_1}.target" "$after"
systemctl stop tmp-deptest.mount
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_not_in "local-fs-pre.target" "$after"
assert_in "remote-fs-pre.target" "$after"
assert_in "network.target" "$after"
fi
# mount LOOP_1 (using fake _netdev option)
mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
# When a device is mounted with userspace options such as _netdev, even when the mount event source is
# triggered, only /proc/self/mountinfo may be updated, and /run/mount/utab may not be updated yet.
# Hence, the mount unit may be created/updated without the userspace options. In that case, the mount
# event source will be retriggered when /run/mount/utab is updated, and the mount unit will be updated
# again with the userspace options. Typically, the window between the two calls is very short, but when
# the mount event source is ratelimited after the first event, processing the second event may be delayed
# about 1 secound. Hence, here we need to wait for a while.
timeout 10 bash -c 'until systemctl show --property=After --value tmp-deptest.mount | grep -q -F remote-fs-pre.target; do sleep .1; done'
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_not_in "local-fs-pre.target" "$after"
assert_in "remote-fs-pre.target" "$after"
assert_in "network.target" "$after"
assert_not_in "${escaped_0}.device" "$after"
assert_not_in "blockdev@${escaped_0}.target" "$after"
assert_in "${escaped_1}.device" "$after"
assert_in "blockdev@${escaped_1}.target" "$after"
systemctl stop tmp-deptest.mount
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_not_in "local-fs-pre.target" "$after"
assert_in "remote-fs-pre.target" "$after"
assert_in "network.target" "$after"
fi
# mount tmpfs
mount -t tmpfs tmpfs /tmp/deptest
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_in "local-fs-pre.target" "$after"
assert_not_in "remote-fs-pre.target" "$after"
assert_not_in "network.target" "$after"
assert_not_in "${escaped_0}.device" "$after"
assert_not_in "blockdev@${escaped_0}.target" "$after"
assert_not_in "${escaped_1}.device" "$after"
assert_not_in "blockdev@${escaped_1}.target" "$after"
systemctl stop tmp-deptest.mount
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_not_in "local-fs-pre.target" "$after"
assert_in "remote-fs-pre.target" "$after"
assert_in "network.target" "$after"
fi
}
testcase_dependencies() {
# test for issue #19983 and #23552.
if systemd-detect-virt --quiet --container; then
echo "Skipping test_dependencies in container"
return
fi
trap teardown_test_dependencies RETURN EXIT ERR INT TERM
setup_loop 0
LOOP_0="${LOOP}"
LOOP=
setup_loop 1
LOOP_1="${LOOP}"
LOOP=
mkdir -p /tmp/deptest
# without .mount file
check_dependencies
# create .mount file
mkdir -p /run/systemd/system
cat >/run/systemd/system/tmp-deptest.mount <<EOF
[Mount]
Where=/tmp/deptest
What=192.168.0.1:/tmp/mnt
Type=nfs
EOF
systemctl daemon-reload
# with .mount file
check_dependencies
}
run_testcases
touch /testok

View File

@ -8,6 +8,154 @@ set -o pipefail
# shellcheck source=test/units/util.sh # shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh . "$(dirname "$0")"/util.sh
teardown_test_dependencies() (
set +eux
if mountpoint /tmp/deptest; then
umount /tmp/deptest
fi
if [[ -n "${LOOP}" ]]; then
losetup -d "${LOOP}" || :
fi
if [[ -n "${LOOP_0}" ]]; then
losetup -d "${LOOP_0}" || :
fi
if [[ -n "${LOOP_1}" ]]; then
losetup -d "${LOOP_1}" || :
fi
rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-0.img
rm -f /tmp/TEST-60-MOUNT-RATELIMIT-dependencies-1.img
rm -f /run/systemd/system/tmp-deptest.mount
systemctl daemon-reload
return 0
)
setup_loop() {
truncate -s 30m "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img"
sfdisk --wipe=always "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img" <<EOF
label:gpt
name="loop${1?}-part1"
EOF
LOOP=$(losetup -P --show -f "/tmp/TEST-60-MOUNT-RATELIMIT-dependencies-${1?}.img")
udevadm wait --settle --timeout=30 "${LOOP}"
udevadm lock --timeout=30 --device="${LOOP}" mkfs.ext4 -L "partname${1?}-1" "${LOOP}p1"
}
check_dependencies() {
local escaped_0 escaped_1 after
escaped_0=$(systemd-escape -p "${LOOP_0}p1")
escaped_1=$(systemd-escape -p "${LOOP_1}p1")
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_not_in "local-fs-pre.target" "$after"
assert_in "remote-fs-pre.target" "$after"
assert_in "network.target" "$after"
fi
# mount LOOP_0
mount -t ext4 "${LOOP_0}p1" /tmp/deptest
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_in "local-fs-pre.target" "$after"
assert_not_in "remote-fs-pre.target" "$after"
assert_not_in "network.target" "$after"
assert_in "${escaped_0}.device" "$after"
assert_in "blockdev@${escaped_0}.target" "$after"
assert_not_in "${escaped_1}.device" "$after"
assert_not_in "blockdev@${escaped_1}.target" "$after"
systemctl stop tmp-deptest.mount
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_not_in "local-fs-pre.target" "$after"
assert_in "remote-fs-pre.target" "$after"
assert_in "network.target" "$after"
fi
# mount LOOP_1 (using fake _netdev option)
mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_not_in "local-fs-pre.target" "$after"
assert_in "remote-fs-pre.target" "$after"
assert_in "network.target" "$after"
assert_not_in "${escaped_0}.device" "$after"
assert_not_in "blockdev@${escaped_0}.target" "$after"
assert_in "${escaped_1}.device" "$after"
assert_in "blockdev@${escaped_1}.target" "$after"
systemctl stop tmp-deptest.mount
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_not_in "local-fs-pre.target" "$after"
assert_in "remote-fs-pre.target" "$after"
assert_in "network.target" "$after"
fi
# mount tmpfs
mount -t tmpfs tmpfs /tmp/deptest
timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_in "local-fs-pre.target" "$after"
assert_not_in "remote-fs-pre.target" "$after"
assert_not_in "network.target" "$after"
assert_not_in "${escaped_0}.device" "$after"
assert_not_in "blockdev@${escaped_0}.target" "$after"
assert_not_in "${escaped_1}.device" "$after"
assert_not_in "blockdev@${escaped_1}.target" "$after"
systemctl stop tmp-deptest.mount
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_not_in "local-fs-pre.target" "$after"
assert_in "remote-fs-pre.target" "$after"
assert_in "network.target" "$after"
fi
}
testcase_dependencies() {
# test for issue #19983 and #23552.
if systemd-detect-virt --quiet --container; then
echo "Skipping test_dependencies in container"
return
fi
trap teardown_test_dependencies RETURN
setup_loop 0
LOOP_0="${LOOP}"
LOOP=
setup_loop 1
LOOP_1="${LOOP}"
LOOP=
mkdir -p /tmp/deptest
# without .mount file
check_dependencies
# create .mount file
mkdir -p /run/systemd/system
cat >/run/systemd/system/tmp-deptest.mount <<EOF
[Mount]
Where=/tmp/deptest
What=192.168.0.1:/tmp/mnt
Type=nfs
EOF
systemctl daemon-reload
# with .mount file
check_dependencies
}
testcase_issue_20329() { testcase_issue_20329() {
# test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited # test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
@ -176,7 +324,7 @@ cat >/run/systemd/journald.conf.d/99-ratelimit.conf <<EOF
[Journal] [Journal]
RateLimitBurst=0 RateLimitBurst=0
EOF EOF
systemctl reload systemd-journald.service systemctl restart systemd-journald.service
run_testcases run_testcases