udevd: event - port spawn_wait() to sd-event

This allows us to drop the special sigterm handling in spawn_wait()
as this will now be passed directly to the worker event loop.

We now log failing spawend processes at 'warning' level, and timeouts
are in terms of CLOCK_BOOTTIME when available, otherwise the behavior
is unchanged.
This commit is contained in:
Tom Gundersen 2015-05-15 11:35:15 +02:00
parent 693d371d30
commit 8128f2297d
5 changed files with 108 additions and 111 deletions

View file

@ -120,11 +120,6 @@ int main(int argc, char *argv[]) {
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (event->fd_signal < 0) {
fprintf(stderr, "error creating signalfd\n");
goto out;
}
/* do what devtmpfs usually provides us */
if (udev_device_get_devnode(dev) != NULL) {
@ -153,8 +148,6 @@ int main(int argc, char *argv[]) {
3 * USEC_PER_SEC, USEC_PER_SEC,
NULL);
out:
if (event != NULL && event->fd_signal >= 0)
close(event->fd_signal);
mac_selinux_finish();
return err ? EXIT_FAILURE : EXIT_SUCCESS;

View file

@ -32,7 +32,16 @@
#include "udev.h"
#include "rtnl-util.h"
#include "event-util.h"
#include "formats-util.h"
#include "process-util.h"
typedef struct Spawn {
const char *cmd;
pid_t pid;
usec_t timeout_warn;
usec_t timeout;
} Spawn;
struct udev_event *udev_event_new(struct udev_device *dev) {
struct udev *udev = udev_device_get_udev(dev);
@ -45,8 +54,7 @@ struct udev_event *udev_event_new(struct udev_device *dev) {
event->udev = udev;
udev_list_init(udev, &event->run_list, false);
udev_list_init(udev, &event->seclabel_list, false);
event->fd_signal = -1;
event->birth_usec = now(CLOCK_MONOTONIC);
event->birth_usec = clock_boottime_or_monotonic();
return event;
}
@ -467,7 +475,7 @@ static void spawn_read(struct udev_event *event,
if (timeout_usec > 0) {
usec_t age_usec;
age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
age_usec = clock_boottime_or_monotonic() - event->birth_usec;
if (age_usec >= timeout_usec) {
log_error("timeout '%s'", cmd);
return;
@ -540,102 +548,116 @@ static void spawn_read(struct udev_event *event,
result[respos] = '\0';
}
static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
Spawn *spawn = userdata;
char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
assert(spawn);
kill_and_sigcont(spawn->pid, SIGKILL);
log_error("spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid,
format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
return 1;
}
static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
Spawn *spawn = userdata;
char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
assert(spawn);
log_warning("spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid,
format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
return 1;
}
static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
Spawn *spawn = userdata;
assert(spawn);
switch (si->si_code) {
case CLD_EXITED:
if (si->si_status != 0)
log_warning("process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
else {
log_debug("process '%s' succeeded.", spawn->cmd);
sd_event_exit(sd_event_source_get_event(s), 0);
return 1;
}
break;
case CLD_KILLED:
case CLD_DUMPED:
log_warning("process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status));
break;
default:
log_error("process '%s' failed due to unknown reason.", spawn->cmd);
}
sd_event_exit(sd_event_source_get_event(s), -EIO);
return 1;
}
static int spawn_wait(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
const char *cmd, pid_t pid) {
struct pollfd pfd[1];
int err = 0;
Spawn spawn = {
.cmd = cmd,
.pid = pid,
};
_cleanup_event_unref_ sd_event *e = NULL;
int r, ret;
pfd[0].events = POLLIN;
pfd[0].fd = event->fd_signal;
r = sd_event_new(&e);
if (r < 0)
return r;
while (pid > 0) {
int timeout;
int timeout_warn = 0;
int fdcount;
if (timeout_usec > 0) {
usec_t usec, age_usec;
if (timeout_usec > 0) {
usec_t age_usec;
usec = now(clock_boottime_or_monotonic());
age_usec = usec - event->birth_usec;
if (age_usec < timeout_usec) {
if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) {
spawn.timeout_warn = timeout_warn_usec - age_usec;
age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
if (age_usec >= timeout_usec)
timeout = 1000;
else {
if (timeout_warn_usec > 0)
timeout_warn = ((timeout_warn_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC;
timeout = ((timeout_usec - timeout_warn_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC;
r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
usec + spawn.timeout_warn, USEC_PER_SEC,
on_spawn_timeout_warning, &spawn);
if (r < 0)
return r;
}
} else {
timeout = -1;
}
fdcount = poll(pfd, 1, timeout_warn);
if (fdcount < 0) {
if (errno == EINTR)
continue;
err = -errno;
log_error_errno(errno, "failed to poll: %m");
goto out;
}
if (fdcount == 0) {
log_warning("slow: '%s' ["PID_FMT"]", cmd, pid);
spawn.timeout = timeout_usec - age_usec;
fdcount = poll(pfd, 1, timeout);
if (fdcount < 0) {
if (errno == EINTR)
continue;
err = -errno;
log_error_errno(errno, "failed to poll: %m");
goto out;
}
if (fdcount == 0) {
log_error("timeout: killing '%s' ["PID_FMT"]", cmd, pid);
kill(pid, SIGKILL);
}
}
if (pfd[0].revents & POLLIN) {
struct signalfd_siginfo fdsi;
int status;
ssize_t size;
size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
if (size != sizeof(struct signalfd_siginfo))
continue;
switch (fdsi.ssi_signo) {
case SIGTERM:
event->sigterm = true;
break;
case SIGCHLD:
if (waitpid(pid, &status, WNOHANG) < 0)
break;
if (WIFEXITED(status)) {
log_debug("'%s' ["PID_FMT"] exit with return code %i", cmd, pid, WEXITSTATUS(status));
if (WEXITSTATUS(status) != 0)
err = -1;
} else if (WIFSIGNALED(status)) {
log_error("'%s' ["PID_FMT"] terminated by signal %i (%s)", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
err = -1;
} else if (WIFSTOPPED(status)) {
log_error("'%s' ["PID_FMT"] stopped", cmd, pid);
err = -1;
} else if (WIFCONTINUED(status)) {
log_error("'%s' ["PID_FMT"] continued", cmd, pid);
err = -1;
} else {
log_error("'%s' ["PID_FMT"] exit with status 0x%04x", cmd, pid, status);
err = -1;
}
pid = 0;
break;
}
r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn);
if (r < 0)
return r;
}
}
out:
return err;
r = sd_event_add_child(e, NULL, pid, WEXITED, on_spawn_sigchld, &spawn);
if (r < 0)
return r;
r = sd_event_loop(e);
if (r < 0)
return r;
r = sd_event_get_exit_code(e, &ret);
if (r < 0)
return r;
return ret;
}
int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) {

View file

@ -44,11 +44,9 @@ struct udev_event {
struct udev_list run_list;
int exec_delay;
usec_t birth_usec;
int fd_signal;
sd_rtnl *rtnl;
unsigned int builtin_run;
unsigned int builtin_ret;
bool sigterm;
bool inotify_watch;
bool inotify_watch_final;
bool group_set;

View file

@ -131,12 +131,6 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (event->fd_signal < 0) {
fprintf(stderr, "error creating signalfd\n");
rc = 5;
goto out;
}
udev_event_execute_rules(event,
60 * USEC_PER_SEC, 20 * USEC_PER_SEC,
@ -154,8 +148,6 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
printf("run: '%s'\n", program);
}
out:
if (event != NULL && event->fd_signal >= 0)
close(event->fd_signal);
udev_builtin_exit(udev);
return rc;
}

View file

@ -406,9 +406,6 @@ static void worker_spawn(Manager *manager, struct event *event) {
goto out;
}
/* needed for SIGCHLD/SIGTERM in spawn() */
udev_event->fd_signal = fd_signal;
if (arg_exec_delay > 0)
udev_event->exec_delay = arg_exec_delay;
@ -481,11 +478,6 @@ skip:
udev_device_unref(dev);
dev = NULL;
if (udev_event->sigterm) {
udev_event_unref(udev_event);
goto out;
}
udev_event_unref(udev_event);
/* wait for more device messages from main udevd, or term signal */