Compare commits
5 Commits
4dd71a0984
...
3ce1f9281c
Author | SHA1 | Date |
---|---|---|
Yu Watanabe | 3ce1f9281c | |
Yu Watanabe | ba692f2bce | |
Yu Watanabe | 11b00192fb | |
Yu Watanabe | 17933e1b69 | |
Yu Watanabe | 55a5b53b1a |
|
@ -10,17 +10,44 @@
|
||||||
|
|
||||||
#define BUF_SIZE 1024
|
#define BUF_SIZE 1024
|
||||||
|
|
||||||
static void test_event_spawn_core(bool with_pidfd, const char *cmd, char *result_buf, size_t buf_size) {
|
static void test_event_spawn_core(bool with_pidfd, const char *cmd, char *result_buf, size_t buf_size, int expected) {
|
||||||
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
||||||
_cleanup_(udev_event_freep) UdevEvent *event = NULL;
|
_cleanup_(udev_event_freep) UdevEvent *event = NULL;
|
||||||
|
|
||||||
assert_se(setenv("SYSTEMD_PIDFD", yes_no(with_pidfd), 1) >= 0);
|
ASSERT_OK_ERRNO(setenv("SYSTEMD_PIDFD", yes_no(with_pidfd), 1));
|
||||||
|
|
||||||
assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net/lo") >= 0);
|
ASSERT_OK(sd_device_new_from_syspath(&dev, "/sys/class/net/lo"));
|
||||||
assert_se(event = udev_event_new(dev, NULL, EVENT_TEST_SPAWN));
|
ASSERT_NOT_NULL(event = udev_event_new(dev, NULL, EVENT_TEST_SPAWN));
|
||||||
assert_se(udev_event_spawn(event, false, cmd, result_buf, buf_size, NULL) == 0);
|
ASSERT_EQ(udev_event_spawn(event, false, cmd, result_buf, buf_size, NULL), expected);
|
||||||
|
|
||||||
assert_se(unsetenv("SYSTEMD_PIDFD") >= 0);
|
ASSERT_OK_ERRNO(unsetenv("SYSTEMD_PIDFD"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_event_spawn_sleep_child(bool with_pidfd) {
|
||||||
|
_cleanup_free_ char *cmd = NULL;
|
||||||
|
|
||||||
|
log_debug("/* %s(%s) */", __func__, yes_no(with_pidfd));
|
||||||
|
|
||||||
|
ASSERT_OK(find_executable("sleep", &cmd));
|
||||||
|
ASSERT_NOT_NULL(strextend_with_separator(&cmd, " ", "1h"));
|
||||||
|
|
||||||
|
test_event_spawn_core(with_pidfd, cmd, NULL, 0, -EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_event_spawn_sleep(bool with_pidfd) {
|
||||||
|
PidRef pidref;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = pidref_safe_fork("(test-worker)", FORK_LOG, &pidref);
|
||||||
|
ASSERT_OK(r);
|
||||||
|
if (r == 0) {
|
||||||
|
test_event_spawn_sleep_child(with_pidfd);
|
||||||
|
_exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_OK(usleep_safe(USEC_PER_SEC));
|
||||||
|
ASSERT_OK(pidref_kill(&pidref, SIGTERM));
|
||||||
|
ASSERT_OK(wait_for_terminate_with_timeout(pidref.pid, 10 * USEC_PER_SEC));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_event_spawn_cat(bool with_pidfd, size_t buf_size) {
|
static void test_event_spawn_cat(bool with_pidfd, size_t buf_size) {
|
||||||
|
@ -30,18 +57,18 @@ static void test_event_spawn_cat(bool with_pidfd, size_t buf_size) {
|
||||||
|
|
||||||
log_debug("/* %s(%s) */", __func__, yes_no(with_pidfd));
|
log_debug("/* %s(%s) */", __func__, yes_no(with_pidfd));
|
||||||
|
|
||||||
assert_se(find_executable("cat", &cmd) >= 0);
|
ASSERT_OK(find_executable("cat", &cmd));
|
||||||
assert_se(strextend_with_separator(&cmd, " ", "/sys/class/net/lo/uevent"));
|
ASSERT_NOT_NULL(strextend_with_separator(&cmd, " ", "/sys/class/net/lo/uevent"));
|
||||||
|
|
||||||
test_event_spawn_core(with_pidfd, cmd, result_buf,
|
test_event_spawn_core(with_pidfd, cmd, result_buf,
|
||||||
buf_size >= BUF_SIZE ? BUF_SIZE : buf_size);
|
buf_size >= BUF_SIZE ? BUF_SIZE : buf_size, 0);
|
||||||
|
|
||||||
assert_se(lines = strv_split_newlines(result_buf));
|
ASSERT_NOT_NULL(lines = strv_split_newlines(result_buf));
|
||||||
strv_print(lines);
|
strv_print(lines);
|
||||||
|
|
||||||
if (buf_size >= BUF_SIZE) {
|
if (buf_size >= BUF_SIZE) {
|
||||||
assert_se(strv_contains(lines, "INTERFACE=lo"));
|
ASSERT_TRUE(strv_contains(lines, "INTERFACE=lo"));
|
||||||
assert_se(strv_contains(lines, "IFINDEX=1"));
|
ASSERT_TRUE(strv_contains(lines, "IFINDEX=1"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,15 +80,15 @@ static void test_event_spawn_self(const char *self, const char *arg, bool with_p
|
||||||
log_debug("/* %s(%s, %s) */", __func__, arg, yes_no(with_pidfd));
|
log_debug("/* %s(%s, %s) */", __func__, arg, yes_no(with_pidfd));
|
||||||
|
|
||||||
/* 'self' may contain spaces, hence needs to be quoted. */
|
/* 'self' may contain spaces, hence needs to be quoted. */
|
||||||
assert_se(cmd = strjoin("'", self, "' ", arg));
|
ASSERT_NOT_NULL(cmd = strjoin("'", self, "' ", arg));
|
||||||
|
|
||||||
test_event_spawn_core(with_pidfd, cmd, result_buf, BUF_SIZE);
|
test_event_spawn_core(with_pidfd, cmd, result_buf, BUF_SIZE, 0);
|
||||||
|
|
||||||
assert_se(lines = strv_split_newlines(result_buf));
|
ASSERT_NOT_NULL(lines = strv_split_newlines(result_buf));
|
||||||
strv_print(lines);
|
strv_print(lines);
|
||||||
|
|
||||||
assert_se(strv_contains(lines, "aaa"));
|
ASSERT_TRUE(strv_contains(lines, "aaa"));
|
||||||
assert_se(strv_contains(lines, "bbb"));
|
ASSERT_TRUE(strv_contains(lines, "bbb"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test1(void) {
|
static void test1(void) {
|
||||||
|
@ -114,5 +141,7 @@ int main(int argc, char *argv[]) {
|
||||||
test_event_spawn_self(self, "test2", true);
|
test_event_spawn_self(self, "test2", true);
|
||||||
test_event_spawn_self(self, "test2", false);
|
test_event_spawn_self(self, "test2", false);
|
||||||
|
|
||||||
|
test_event_spawn_sleep(true);
|
||||||
|
test_event_spawn_sleep(false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "udev-trace.h"
|
#include "udev-trace.h"
|
||||||
|
|
||||||
typedef struct Spawn {
|
typedef struct Spawn {
|
||||||
|
UdevWorker *worker;
|
||||||
sd_device *device;
|
sd_device *device;
|
||||||
const char *cmd;
|
const char *cmd;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
@ -151,11 +152,27 @@ static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userd
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int on_spawn_sigterm(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
|
||||||
|
Spawn *spawn = ASSERT_PTR(userdata);
|
||||||
|
|
||||||
|
DEVICE_TRACE_POINT(spawn_sigterm, spawn->device, spawn->cmd);
|
||||||
|
|
||||||
|
log_device_error(spawn->device, "Worker process received SIGTERM, killing spawned process '%s' ["PID_FMT"].",
|
||||||
|
spawn->cmd, spawn->pid);
|
||||||
|
|
||||||
|
(void) kill_and_sigcont(spawn->pid, SIGTERM);
|
||||||
|
|
||||||
|
/* terminate the main event source of the worker. Note, the spawn->worker may be NULL in tests. */
|
||||||
|
if (spawn->worker)
|
||||||
|
(void) sd_event_exit(spawn->worker->event, 0);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int spawn_wait(Spawn *spawn) {
|
static int spawn_wait(Spawn *spawn) {
|
||||||
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
||||||
_cleanup_(sd_event_source_disable_unrefp) sd_event_source *sigchld_source = NULL;
|
_cleanup_(sd_event_source_disable_unrefp) sd_event_source
|
||||||
_cleanup_(sd_event_source_disable_unrefp) sd_event_source *stdout_source = NULL;
|
*sigchld_source = NULL, *stdout_source = NULL, *stderr_source = NULL, *sigterm_source = NULL;
|
||||||
_cleanup_(sd_event_source_disable_unrefp) sd_event_source *stderr_source = NULL;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(spawn);
|
assert(spawn);
|
||||||
|
@ -201,11 +218,21 @@ static int spawn_wait(Spawn *spawn) {
|
||||||
r = sd_event_add_child(e, &sigchld_source, spawn->pid, WEXITED, on_spawn_sigchld, spawn);
|
r = sd_event_add_child(e, &sigchld_source, spawn->pid, WEXITED, on_spawn_sigchld, spawn);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_device_debug_errno(spawn->device, r, "Failed to create sigchild event source: %m");
|
return log_device_debug_errno(spawn->device, r, "Failed to create sigchild event source: %m");
|
||||||
|
|
||||||
/* SIGCHLD should be processed after IO is complete */
|
/* SIGCHLD should be processed after IO is complete */
|
||||||
r = sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1);
|
r = sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_device_debug_errno(spawn->device, r, "Failed to set priority to sigchild event source: %m");
|
return log_device_debug_errno(spawn->device, r, "Failed to set priority to sigchild event source: %m");
|
||||||
|
|
||||||
|
r = sd_event_add_signal(e, &sigterm_source, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, on_spawn_sigterm, spawn);
|
||||||
|
if (r < 0)
|
||||||
|
return log_device_debug_errno(spawn->device, r, "Failed to set SIGTERM event: %m");
|
||||||
|
|
||||||
|
/* SIGTERM should be processed with higher priorities with the others. */
|
||||||
|
r = sd_event_source_set_priority(sigterm_source, SD_EVENT_PRIORITY_NORMAL - 1);
|
||||||
|
if (r < 0)
|
||||||
|
return log_device_debug_errno(spawn->device, r, "Failed to set priority to sigchild event source: %m");
|
||||||
|
|
||||||
return sd_event_loop(e);
|
return sd_event_loop(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +266,10 @@ int udev_event_spawn(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event->worker && sd_event_get_exit_code(event->worker->event, NULL) >= 0)
|
||||||
|
return log_device_debug_errno(event->dev, SYNTHETIC_ERRNO(EIO),
|
||||||
|
"The main event loop of the worker process already terminating, skipping execution of '%s'.", cmd);
|
||||||
|
|
||||||
int timeout_signal = event->worker ? event->worker->timeout_signal : SIGKILL;
|
int timeout_signal = event->worker ? event->worker->timeout_signal : SIGKILL;
|
||||||
usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
|
usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
|
||||||
usec_t now_usec = now(CLOCK_MONOTONIC);
|
usec_t now_usec = now(CLOCK_MONOTONIC);
|
||||||
|
@ -304,6 +335,7 @@ int udev_event_spawn(
|
||||||
errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]);
|
errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]);
|
||||||
|
|
||||||
spawn = (Spawn) {
|
spawn = (Spawn) {
|
||||||
|
.worker = event->worker,
|
||||||
.device = event->dev,
|
.device = event->dev,
|
||||||
.cmd = cmd,
|
.cmd = cmd,
|
||||||
.pid = pid,
|
.pid = pid,
|
||||||
|
|
|
@ -15,10 +15,8 @@ at_exit() {
|
||||||
systemctl stop testsleep.service
|
systemctl stop testsleep.service
|
||||||
rm -f /run/udev/udev.conf.d/timeout.conf
|
rm -f /run/udev/udev.conf.d/timeout.conf
|
||||||
rm -f /run/udev/rules.d/99-testsuite.rules
|
rm -f /run/udev/rules.d/99-testsuite.rules
|
||||||
# Forcibly kills sleep command invoked by the udev rule before restarting,
|
# Check if udevd can be restarted within a reasonably short time.
|
||||||
# otherwise systemctl restart below will takes longer.
|
timeout 10 systemctl restart systemd-udevd.service
|
||||||
killall -KILL sleep
|
|
||||||
systemctl restart systemd-udevd.service
|
|
||||||
ip link del "$IFNAME"
|
ip link del "$IFNAME"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue