mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
reaper: monitor SIGCHLD using the FDM instead of via a signalfd
In addition to letting the FDM do the low-level signal watching, this patch also fixes a bug; multiple SIGCHLDs, be it delivered either through a signal, or via a signalfd, can be coalesced, like all signals. This means we need to loop on waitpid() with WNOHANG until there are no more processes to reap. This in turn requires a small change to the way reaper callbacks are implemented. Previously, the callback was allowed to do the wait(). This was signalled back to the reaper through the callback’s return value. Now, since we’ve already wait():ed, the process’ exit status is passed as an argument to the reaper callback. The callback for the client application has been updated accordingly; it sets a flag in the terminal struct, telling term_destroy() that the process has already been wait():ed on, and also stores the exit status.
This commit is contained in:
parent
37220fc189
commit
79e3a46943
4 changed files with 92 additions and 140 deletions
98
reaper.c
98
reaper.c
|
|
@ -3,13 +3,13 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/signalfd.h>
|
#include <tllist.h>
|
||||||
|
|
||||||
#define LOG_MODULE "reaper"
|
#define LOG_MODULE "reaper"
|
||||||
#define LOG_ENABLE_DBG 0
|
#define LOG_ENABLE_DBG 0
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "tllist.h"
|
|
||||||
|
|
||||||
struct child {
|
struct child {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
@ -19,56 +19,32 @@ struct child {
|
||||||
|
|
||||||
struct reaper {
|
struct reaper {
|
||||||
struct fdm *fdm;
|
struct fdm *fdm;
|
||||||
int fd;
|
|
||||||
tll(struct child) children;
|
tll(struct child) children;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool fdm_reap(struct fdm *fdm, int fd, int events, void *data);
|
static bool fdm_reap(struct fdm *fdm, int signo, void *data);
|
||||||
|
|
||||||
struct reaper *
|
struct reaper *
|
||||||
reaper_init(struct fdm *fdm)
|
reaper_init(struct fdm *fdm)
|
||||||
{
|
{
|
||||||
sigset_t mask;
|
|
||||||
sigemptyset(&mask);
|
|
||||||
sigaddset(&mask, SIGCHLD);
|
|
||||||
|
|
||||||
/* Block normal signal handling - we're using a signalfd instead */
|
|
||||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
|
|
||||||
LOG_ERRNO("failed to block SIGCHLD");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
|
|
||||||
if (fd < 0) {
|
|
||||||
LOG_ERRNO("failed to create signal FD");
|
|
||||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct reaper *reaper = malloc(sizeof(*reaper));
|
struct reaper *reaper = malloc(sizeof(*reaper));
|
||||||
if (unlikely(reaper == NULL)) {
|
if (unlikely(reaper == NULL)) {
|
||||||
LOG_ERRNO("malloc() failed");
|
LOG_ERRNO("malloc() failed");
|
||||||
close(fd);
|
|
||||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*reaper = (struct reaper){
|
*reaper = (struct reaper){
|
||||||
.fdm = fdm,
|
.fdm = fdm,
|
||||||
.fd = fd,
|
|
||||||
.children = tll_init(),
|
.children = tll_init(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!fdm_add(fdm, fd, EPOLLIN, &fdm_reap, reaper)){
|
if (!fdm_signal_add(fdm, SIGCHLD, &fdm_reap, reaper))
|
||||||
LOG_ERR("failed to register with the FDM");
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
|
||||||
|
|
||||||
return reaper;
|
return reaper;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
tll_free(reaper->children);
|
tll_free(reaper->children);
|
||||||
close(fd);
|
|
||||||
free(reaper);
|
free(reaper);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -79,14 +55,9 @@ reaper_destroy(struct reaper *reaper)
|
||||||
if (reaper == NULL)
|
if (reaper == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fdm_del(reaper->fdm, reaper->fd);
|
fdm_signal_del(reaper->fdm, SIGCHLD);
|
||||||
tll_free(reaper->children);
|
tll_free(reaper->children);
|
||||||
free(reaper);
|
free(reaper);
|
||||||
|
|
||||||
sigset_t mask;
|
|
||||||
sigemptyset(&mask);
|
|
||||||
sigaddset(&mask, SIGCHLD);
|
|
||||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -110,37 +81,27 @@ reaper_del(struct reaper *reaper, pid_t pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
fdm_reap(struct fdm *fdm, int fd, int events, void *data)
|
fdm_reap(struct fdm *fdm, int signo, void *data)
|
||||||
{
|
{
|
||||||
struct reaper *reaper = data;
|
struct reaper *reaper = data;
|
||||||
|
|
||||||
bool pollin = events & EPOLLIN;
|
while (true) {
|
||||||
bool hup = events & EPOLLHUP;
|
int status;
|
||||||
|
pid_t pid = waitpid(-1, &status, WNOHANG);
|
||||||
|
if (pid <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
if (hup && !pollin)
|
if (WIFEXITED(status))
|
||||||
return false;
|
LOG_DBG("pid=%d: exited with status=%d", pid, WEXITSTATUS(status));
|
||||||
|
else if (WIFSIGNALED(status))
|
||||||
assert(pollin);
|
LOG_DBG("pid=%d: killed by signal=%d", pid, WTERMSIG(status));
|
||||||
|
else
|
||||||
struct signalfd_siginfo info;
|
LOG_DBG("pid=%d: died of unknown resason", pid);
|
||||||
ssize_t amount = read(reaper->fd, &info, sizeof(info));
|
|
||||||
|
|
||||||
if (amount < 0) {
|
|
||||||
LOG_ERRNO("failed to read");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert((size_t)amount >= sizeof(info));
|
|
||||||
|
|
||||||
if (info.ssi_signo != SIGCHLD) {
|
|
||||||
LOG_WARN("got non-SIGCHLD signal: %d", info.ssi_signo);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
tll_foreach(reaper->children, it) {
|
tll_foreach(reaper->children, it) {
|
||||||
struct child *_child = &it->item;
|
struct child *_child = &it->item;
|
||||||
|
|
||||||
if (_child->pid != (pid_t)info.ssi_pid)
|
if (_child->pid != pid)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Make sure we remove it *before* the callback, since it too
|
/* Make sure we remove it *before* the callback, since it too
|
||||||
|
|
@ -148,31 +109,12 @@ fdm_reap(struct fdm *fdm, int fd, int events, void *data)
|
||||||
struct child child = it->item;
|
struct child child = it->item;
|
||||||
tll_remove(reaper->children, it);
|
tll_remove(reaper->children, it);
|
||||||
|
|
||||||
bool reap_ourselves = true;
|
|
||||||
if (child.cb != NULL)
|
if (child.cb != NULL)
|
||||||
reap_ourselves = !child.cb(reaper, child.pid, child.cb_data);
|
child.cb(reaper, child.pid, status, child.cb_data);
|
||||||
|
|
||||||
if (reap_ourselves) {
|
break;
|
||||||
int result;
|
|
||||||
int res = waitpid(child.pid, &result, WNOHANG);
|
|
||||||
|
|
||||||
if (res <= 0) {
|
|
||||||
if (res < 0)
|
|
||||||
LOG_ERRNO("waitpid failed for pid=%d", child.pid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (WIFEXITED(result))
|
|
||||||
LOG_DBG("pid=%d: exited with status=%d", pid, WEXITSTATUS(result));
|
|
||||||
else if (WIFSIGNALED(result))
|
|
||||||
LOG_DBG("pid=%d: killed by signal=%d", pid, WTERMSIG(result));
|
|
||||||
else
|
|
||||||
LOG_DBG("pid=%d: died of unknown resason", pid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hup)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
reaper.h
3
reaper.h
|
|
@ -10,7 +10,8 @@ struct reaper;
|
||||||
struct reaper *reaper_init(struct fdm *fdm);
|
struct reaper *reaper_init(struct fdm *fdm);
|
||||||
void reaper_destroy(struct reaper *reaper);
|
void reaper_destroy(struct reaper *reaper);
|
||||||
|
|
||||||
typedef bool (*reaper_cb)(struct reaper *reaper, pid_t pid, void *data);
|
typedef void (*reaper_cb)(
|
||||||
|
struct reaper *reaper, pid_t pid, int status, void *data);
|
||||||
|
|
||||||
void reaper_add(struct reaper *reaper, pid_t pid, reaper_cb cb, void *cb_data);
|
void reaper_add(struct reaper *reaper, pid_t pid, reaper_cb cb, void *cb_data);
|
||||||
void reaper_del(struct reaper *reaper, pid_t pid);
|
void reaper_del(struct reaper *reaper, pid_t pid);
|
||||||
|
|
|
||||||
32
terminal.c
32
terminal.c
|
|
@ -953,22 +953,25 @@ load_fonts_from_conf(struct terminal *term)
|
||||||
return reload_fonts(term);
|
return reload_fonts(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
slave_died(struct reaper *reaper, pid_t pid, void *data)
|
slave_died(struct reaper *reaper, pid_t pid, int status, void *data)
|
||||||
{
|
{
|
||||||
struct terminal *term = data;
|
struct terminal *term = data;
|
||||||
LOG_DBG("slave (PID=%u) died", pid);
|
LOG_DBG("slave (PID=%u) died", pid);
|
||||||
|
|
||||||
|
term->slave_has_been_reaped = true;
|
||||||
|
term->exit_status = status;
|
||||||
|
|
||||||
if (term->conf->hold_at_exit) {
|
if (term->conf->hold_at_exit) {
|
||||||
/* The PTMX FDM handler may already have closed our end */
|
/* The PTMX FDM handler may already have closed our end */
|
||||||
if (term->ptmx >= 0) {
|
if (term->ptmx >= 0) {
|
||||||
fdm_del(term->fdm, term->ptmx);
|
fdm_del(term->fdm, term->ptmx);
|
||||||
term->ptmx = -1;
|
term->ptmx = -1;
|
||||||
}
|
}
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return term_shutdown(term);
|
term_shutdown(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct terminal *
|
struct terminal *
|
||||||
|
|
@ -1044,7 +1047,6 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
.fdm = fdm,
|
.fdm = fdm,
|
||||||
.reaper = reaper,
|
.reaper = reaper,
|
||||||
.conf = conf,
|
.conf = conf,
|
||||||
.quit = false,
|
|
||||||
.ptmx = ptmx,
|
.ptmx = ptmx,
|
||||||
.ptmx_buffers = tll_init(),
|
.ptmx_buffers = tll_init(),
|
||||||
.ptmx_paste_buffers = tll_init(),
|
.ptmx_paste_buffers = tll_init(),
|
||||||
|
|
@ -1457,6 +1459,11 @@ term_destroy(struct terminal *term)
|
||||||
int ret = EXIT_SUCCESS;
|
int ret = EXIT_SUCCESS;
|
||||||
|
|
||||||
if (term->slave > 0) {
|
if (term->slave > 0) {
|
||||||
|
int exit_status;
|
||||||
|
|
||||||
|
if (term->slave_has_been_reaped)
|
||||||
|
exit_status = term->exit_status;
|
||||||
|
else {
|
||||||
LOG_DBG("waiting for slave (PID=%u) to die", term->slave);
|
LOG_DBG("waiting for slave (PID=%u) to die", term->slave);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1479,11 +1486,10 @@ term_destroy(struct terminal *term)
|
||||||
sigaction(SIGALRM, &(const struct sigaction){.sa_handler = &sig_alarm}, NULL);
|
sigaction(SIGALRM, &(const struct sigaction){.sa_handler = &sig_alarm}, NULL);
|
||||||
alarm(2);
|
alarm(2);
|
||||||
|
|
||||||
int status;
|
|
||||||
int kill_signal = SIGTERM;
|
int kill_signal = SIGTERM;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int r = waitpid(term->slave, &status, 0);
|
int r = waitpid(term->slave, &exit_status, 0);
|
||||||
|
|
||||||
if (r == term->slave)
|
if (r == term->slave)
|
||||||
break;
|
break;
|
||||||
|
|
@ -1511,16 +1517,18 @@ term_destroy(struct terminal *term)
|
||||||
/* Cancel alarm */
|
/* Cancel alarm */
|
||||||
alarm(0);
|
alarm(0);
|
||||||
sigaction(SIGALRM, &(const struct sigaction){.sa_handler = SIG_DFL}, NULL);
|
sigaction(SIGALRM, &(const struct sigaction){.sa_handler = SIG_DFL}, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
ret = EXIT_FAILURE;
|
ret = EXIT_FAILURE;
|
||||||
if (WIFEXITED(status)) {
|
if (WIFEXITED(exit_status)) {
|
||||||
ret = WEXITSTATUS(status);
|
ret = WEXITSTATUS(exit_status);
|
||||||
LOG_DBG("slave exited with code %d", ret);
|
LOG_DBG("slave exited with code %d", ret);
|
||||||
} else if (WIFSIGNALED(status)) {
|
} else if (WIFSIGNALED(exit_status)) {
|
||||||
ret = WTERMSIG(status);
|
ret = WTERMSIG(exit_status);
|
||||||
LOG_WARN("slave exited with signal %d (%s)", ret, strsignal(ret));
|
LOG_WARN("slave exited with signal %d (%s)", ret, strsignal(ret));
|
||||||
} else {
|
} else {
|
||||||
LOG_WARN("slave exited for unknown reason (status = 0x%08x)", status);
|
LOG_WARN("slave exited for unknown reason (status = 0x%08x)",
|
||||||
|
exit_status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -487,8 +487,9 @@ struct terminal {
|
||||||
} ime;
|
} ime;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool quit;
|
|
||||||
bool is_shutting_down;
|
bool is_shutting_down;
|
||||||
|
bool slave_has_been_reaped;
|
||||||
|
int exit_status;
|
||||||
void (*shutdown_cb)(void *data, int exit_code);
|
void (*shutdown_cb)(void *data, int exit_code);
|
||||||
void *shutdown_data;
|
void *shutdown_data;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue