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

Compare commits

..

17 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek
92716a079f
Logging fixlets (#38313) 2025-07-25 12:27:24 +02:00
Yu Watanabe
dda9eeb081 test-sd-device: filter out dm block devices at several more places
Follow-up for 911a52b769e477fd13fbc84d9ac098b8c0298eaf.

Fixes #38332.
2025-07-25 11:03:31 +01:00
Luca Boccassi
9263c6f57e
test: modernize test-oomd-util (#38329) 2025-07-25 11:01:32 +01:00
Yu Watanabe
7bef072ca9 test: stop firewalld and friends to make them not disturb test-firewall-util
Hopefully fixes #35526.
2025-07-25 10:58:17 +01:00
Luca Boccassi
c8082ac09c
test: wait for userspace mount options applied (#38327)
Hopefully fixes #32712.
2025-07-25 10:57:13 +01:00
Nick Labich
85e9805903 sysupdate: Prevent unnecessary failure when a transfer Path is not present
Fixes https://github.com/systemd/systemd/issues/38007
2025-07-25 10:53:48 +01:00
Yu Watanabe
c4ffd0a020 errno-list: fallback to use our errno name table
Some architecture specific errno may not be known by glibc.
Let's fallback to our table when strerrorname_np() returns NULL.

Fixes the following error in mips:
```
src/test/test-errno-list.c:14: Assertion failed: Expected "errno_to_name(i) == errno_names[i]", got "(null) != EINIT"
```

Follow-up for 03ccee19396ae604d9ac1570dddb8b41ec0e7253.
2025-07-25 10:50:45 +01:00
Yu Watanabe
b089f65676 test: modernize tests.c and tests.h
- use ASSERT_OK() and friends,
- use mkdtemp_malloc(),
- use pidref_safe_fork() and pidref_wait_for_terminate_and_check().
2025-07-25 09:10:25 +09:00
Yu Watanabe
468b10b188 test: modernize test-oomd-util
- use DEFINE_TEST_MAIN() macro,
- use ASSERT_OK() and friends,
- drop restriction that the test must run as root,
- replace sleep(2) with loop of shorter sleep and cg_is_empty(),
- applied several coding style cleanups.
2025-07-25 09:09:20 +09:00
Yu Watanabe
3f6e7ac27a TEST-10-MOUNT: wait for userspace mount options being loaded
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.

By adding a debugging logs in mount_setup_unit(), the userspace mount is
not obtained in the first event, and the second event is delayed by the ratelimit.
```
[   20.023086] H TEST-10-MOUNT.sh[446]: + mount -t ext4 -o _netdev /dev/loop1p1 /tmp/deptest
[   20.026255] H kernel: EXT4-fs (loop1p1): mounted filesystem c1fa00ea-2ba8-46b2-9002-2ac997f4cda9 r/w with ordered data mode. Quota mode: none.
[   20.026537] H TEST-10-MOUNT.sh[446]: + timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
[   20.032293] H systemd[1]: tmp-deptest.mount: mount_setup_unit: proc: yes, netdev: no
[   20.035978] H systemd[1]: Unit blockdev@dev-loop1p1.target has alias blockdev@.target.
[   20.039765] H systemd[1]: tmp-deptest.mount: Changed dead -> mounted
[   20.046598] H systemd[1]: Event source 0x7c73093e05e0 (mount-monitor-dispatch) entered rate limit state.
```

Hopefully fixes #32712.
2025-07-25 04:54:42 +09:00
Yu Watanabe
1edc81aa13 TEST-10-MOUNT: make cleanup function run on error 2025-07-25 04:54:42 +09:00
Yu Watanabe
8002f0cf27 TEST-60-MOUNT-RATELIMIT: use reload to make new config applied 2025-07-25 04:54:42 +09:00
Yu Watanabe
62c3f42deb test: move testcase_dependencies() to TEST-10-MOUNT
TEST-60-MOUNT_RATELIMIT is run on nspawn by default, and currently run
on vm only on arch mkosi. Let's move the test case to new TEST-10-MOUNT,
which always run on vm.
2025-07-25 04:54:42 +09:00
Yu Watanabe
1227795362 integration-test: mention TEST_SAVE_JOURNAL in README 2025-07-25 04:54:42 +09:00
Yu Watanabe
8269396078 integration-test: show journalctl command for showing saved journal when TEST_SAVE_JOURNAL=1 2025-07-25 04:54:38 +09:00
Zbigniew Jędrzejewski-Szmek
56631ac8df udev: downgrade ENOMEDIUM warnings
I noticed similar warnings in many VM boots:
  fedora (udev-worker)[475]: sr0: Failed to open device node: No medium found
This particular one is from src/udev/udev-builtin-uaccess.c:125, but I think the
same principle should be used in all places: if we are ignoring errors that the
device has been removed, we should also ignore ENOMEDIUM.
2025-07-24 10:42:50 +02:00
Zbigniew Jędrzejewski-Szmek
b0ca060e05 core/socket: shorten/reword message about restarted sockets
log_unit_warning() already prints the full unit name, so we don't need to say
that it is a socket unit. And in technical messages articles like "the" are
generally skipped to make the messages shorter. Use simpler tense and simpler
wording.

Example from log: fedora.local systemd[1]: systemd-initctl.socket: Socket unit
        configuration has changed while unit has been running, no open socket file
        descriptor left. The socket unit is not functional until restarted.
This is just too wordy and says "socket" four times.
2025-07-24 10:29:23 +02:00
24 changed files with 483 additions and 448 deletions

View File

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

View File

@ -212,6 +212,13 @@ static inline bool ERRNO_IS_NEG_DEVICE_ABSENT(intmax_t r) {
}
_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
* where it simply doesn't have the requested xattr the same way */
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)) {
/* 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 state for
* them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we have all fds open,
* and if there's a mismatch, warn loudly.
* for. However, this is problematic, as we won't traverse through the SOCKET_START_CHOWN
* state for them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we
* have all fds open, and if there's a mismatch, warn loudly.
*
* 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.
@ -1942,14 +1942,14 @@ static int socket_coldplug(Unit *u) {
r = socket_check_open(s);
if (r == SOCKET_OPEN_NONE)
log_unit_warning(UNIT(s),
"Socket unit configuration has changed while unit has been running, "
"no open socket file descriptor left. "
"The socket unit is not functional until restarted.");
"Unit configuration changed while unit was running, "
"and no socket file descriptors are open. "
"Unit not functional until restarted.");
else if (r == SOCKET_OPEN_SOME)
log_unit_warning(UNIT(s),
"Socket unit configuration has changed while unit has been running, "
"Unit configuration changed while unit was running, "
"and some socket file descriptors have not been opened yet. "
"The socket unit is not fully functional until restarted.");
"Unit not fully functional until restarted.");
}
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_add_match_subsystem(e, subsystem, true));
ASSERT_OK(sd_device_enumerator_add_nomatch_sysname(e, "loop*"));
exclude_problematic_devices(e);
FOREACH_DEVICE(e, d) {
const char *syspath;
@ -613,9 +613,9 @@ TEST(sd_device_enumerator_add_all_parents) {
/* STEP 1: enumerate all block devices without all_parents() */
ASSERT_OK(sd_device_enumerator_new(&e));
ASSERT_OK(sd_device_enumerator_allow_uninitialized(e));
exclude_problematic_devices(e);
/* 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_property(e, "DEVTYPE", "partition"));
@ -758,8 +758,9 @@ TEST(sd_device_new_from_path) {
ASSERT_OK(sd_device_enumerator_new(&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_nomatch_sysname(e, "loop*"));
ASSERT_OK(sd_device_enumerator_add_match_property(e, "DEVNAME", "*"));
FOREACH_DEVICE(e, dev) {

View File

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

View File

@ -29,6 +29,7 @@
#include "log.h"
#include "namespace-util.h"
#include "path-util.h"
#include "pidref.h"
#include "process-util.h"
#include "random-util.h"
#include "rlimit-util.h"
@ -38,13 +39,11 @@
#include "uid-range.h"
char* setup_fake_runtime_dir(void) {
char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p;
char *t;
assert_se(mkdtemp(t));
assert_se(setenv("XDG_RUNTIME_DIR", t, 1) >= 0);
assert_se(p = strdup(t));
return p;
ASSERT_OK(mkdtemp_malloc("/tmp/fake-xdg-runtime-XXXXXX", &t));
ASSERT_OK(setenv("XDG_RUNTIME_DIR", t, /* overwrite = */ true));
return t;
}
static void load_testdata_env(void) {
@ -57,18 +56,18 @@ static void load_testdata_env(void) {
return;
called = true;
assert_se(readlink_and_make_absolute("/proc/self/exe", &s) >= 0);
assert_se(path_extract_directory(s, &d) >= 0);
assert_se(envpath = path_join(d, "systemd-runtest.env"));
ASSERT_OK(readlink_and_make_absolute("/proc/self/exe", &s));
ASSERT_OK(path_extract_directory(s, &d));
ASSERT_NOT_NULL(envpath = path_join(d, "systemd-runtest.env"));
r = load_env_file_pairs(NULL, envpath, &pairs);
if (r < 0) {
log_debug_errno(r, "Reading %s failed: %m", envpath);
log_debug_errno(r, "Reading %s failed, ignoring: %m", envpath);
return;
}
STRV_FOREACH_PAIR(k, v, pairs)
assert_se(setenv(*k, *v, 0) >= 0);
ASSERT_OK(setenv(*k, *v, /* overwrite = */ false));
}
int get_testdata_dir(const char *suffix, char **ret) {
@ -144,16 +143,14 @@ int write_tmpfile(char *pattern, const char *contents) {
}
bool have_namespaces(void) {
siginfo_t si = {};
pid_t pid;
_cleanup_(pidref_done) PidRef pid = PIDREF_NULL;
int r;
/* 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. */
pid = fork();
assert_se(pid >= 0);
if (pid == 0) {
ASSERT_OK(r = pidref_safe_fork("(have_namespace)", /* flags = */ 0, &pid));
if (r == 0) {
/* child */
if (detach_mount_namespace() < 0)
_exit(EXIT_FAILURE);
@ -161,13 +158,11 @@ bool have_namespaces(void) {
_exit(EXIT_SUCCESS);
}
assert_se(waitid(P_PID, pid, &si, WEXITED) >= 0);
assert_se(si.si_code == CLD_EXITED);
if (si.si_status == EXIT_SUCCESS)
ASSERT_OK(r = pidref_wait_for_terminate_and_check("(have_namespace)", &pid, /* flags = */ 0));
if (r == EXIT_SUCCESS)
return true;
if (si.si_status == EXIT_FAILURE)
if (r == EXIT_FAILURE)
return false;
assert_not_reached();
@ -203,9 +198,9 @@ bool can_memlock(void) {
bool b = mlock(p, CAN_MEMLOCK_SIZE) >= 0;
if (b)
assert_se(munlock(p, CAN_MEMLOCK_SIZE) >= 0);
ASSERT_OK(munlock(p, CAN_MEMLOCK_SIZE));
assert_se(munmap(p, CAN_MEMLOCK_SIZE) >= 0);
ASSERT_OK(munmap(p, CAN_MEMLOCK_SIZE));
return b;
}
@ -301,16 +296,14 @@ static int enter_cgroup(char **ret_cgroup, bool enter_subroot) {
r = cg_pid_get_path(NULL, 0, &cgroup_root);
if (IN_SET(r, -ENOMEDIUM, -ENOENT))
return log_warning_errno(r, "cg_pid_get_path(NULL, 0, ...) failed: %m");
assert(r >= 0);
ASSERT_OK(r);
if (enter_subroot)
assert_se(asprintf(&cgroup_subroot, "%s/%" PRIx64, cgroup_root, random_u64()) >= 0);
else {
cgroup_subroot = strdup(cgroup_root);
assert_se(cgroup_subroot != NULL);
}
ASSERT_OK(asprintf(&cgroup_subroot, "%s/%" PRIx64, cgroup_root, random_u64()));
else
ASSERT_NOT_NULL(cgroup_subroot = strdup(cgroup_root));
assert_se(cg_mask_supported(&supported) >= 0);
ASSERT_OK(cg_mask_supported(&supported));
/* 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. */

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) \
_cleanup_free_ void *name = NULL; \
size_t name##_len = 0; \
assert_se(define_hex_ptr_internal(hex, &name, &name##_len) >= 0)
ASSERT_OK(define_hex_ptr_internal(hex, &name, &name##_len))
/* Provide a convenient way to check if we're running in CI. */
const char* ci_environment(void);

View File

@ -697,7 +697,7 @@ int resource_resolve_path(
} else if (RESOURCE_IS_FILESYSTEM(rr->type)) {
_cleanup_free_ char *resolved = NULL, *relative_to = NULL;
ChaseFlags chase_flags = CHASE_PREFIX_ROOT;
ChaseFlags chase_flags = CHASE_NONEXISTENT | CHASE_PREFIX_ROOT;
if (rr->path_relative_to == PATH_RELATIVE_TO_EXPLICIT) {
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);
if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno);
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to open device node '%s'%s: %m",
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);
if (mtd_fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno);
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to open device node '%s'%s: %m",
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);
if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
log_device_debug_errno(dev, fd, "Failed to open block device %s%s: %m",
devnode, ignore ? ", ignoring" : "");
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);
if (fd < 0) {
if (ERRNO_IS_DEVICE_ABSENT(errno)) {
if (ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno)) {
/* 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
* 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) {
fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
"Failed to open device '%s'%s: %m",
node, ignore ? ", ignoring" : "");
@ -223,7 +223,7 @@ static int builtin_keyboard(UdevEvent *event, int argc, char *argv[]) {
if (fd < 0) {
fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
"Failed to open device '%s'%s: %m",
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);
if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
"Failed to open device node%s: %m",
ignore ? ", ignoring" : "");

View File

@ -431,7 +431,7 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
return r;
r = node_get_current(slink, dirfd, &current_id, add ? &current_prio : NULL);
if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
if (r < 0 && !ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(r))
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);
@ -702,7 +702,7 @@ int udev_node_apply_permissions(
node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC);
if (node_fd < 0) {
if (ERRNO_IS_DEVICE_ABSENT(node_fd)) {
if (ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(node_fd)) {
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 */
}
@ -735,7 +735,7 @@ int static_node_apply_permissions(
node_fd = open(devnode, O_PATH|O_CLOEXEC);
if (node_fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno);
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to open device node '%s'%s: %m",
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);
if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
log_device_debug_errno(dev, fd, "Failed to open '%s'%s: %m", whole_disk, ignore ? ", ignoring" : "");
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);
if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(fd);
log_device_full_errno(dev, ignore ? LOG_DEBUG : LOG_WARNING, fd,
"Failed to open device node '%s'%s: %m",
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);
if (fd < 0) {
bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
bool ignore = ERRNO_IS_DEVICE_ABSENT_OR_EMPTY(errno);
log_full_errno(ignore ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to open device node '%s'%s: %m",
arg_device, ignore ? ", ignoring" : "");

View File

@ -133,6 +133,10 @@ that make use of `run_testcases`.
`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
progress and only move the journal to its final location in the build directory
(`$BUILD_DIR/test/journal`) when the test is finished.

View File

@ -0,0 +1,8 @@
# 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,7 +660,12 @@ def main() -> None:
journal_file = Path(shutil.move(journal_file, dst))
if shell or (result.returncode in (args.exit_code, 77) and not coredumps and not sanitizer):
exit(0 if shell or result.returncode == args.exit_code else 77)
exit_code = 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():
ops = []
@ -678,10 +683,11 @@ def main() -> None:
ops += [f'journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info']
print(f'Test failed, relevant logs can be viewed with: \n\n{(" && ".join(ops))}\n', file=sys.stderr)
print(
f'Test {exit_str}, relevant logs can be viewed with: \n\n{(" && ".join(ops))}\n', file=sys.stderr
)
# 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)
exit(exit_code)
if __name__ == '__main__':

View File

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

View File

@ -32,6 +32,11 @@ if ! systemd-detect-virt -qc; then
sysctl net.ipv4.ping_group_range="0 2147483647"
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
# Arguments:
# $1: test path

169
test/units/TEST-10-MOUNT.sh Executable file
View File

@ -0,0 +1,169 @@
#!/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,154 +8,6 @@ set -o pipefail
# 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'
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() {
# test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
@ -324,7 +176,7 @@ cat >/run/systemd/journald.conf.d/99-ratelimit.conf <<EOF
[Journal]
RateLimitBurst=0
EOF
systemctl restart systemd-journald.service
systemctl reload systemd-journald.service
run_testcases