From 08cd15525450ff2c2ac814a58930f6d82284a1ba Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 11 Dec 2013 03:50:35 +0000 Subject: [PATCH] event: when handling SIGCHLD of a child process only reap after dispatching event source That way the even source callback is run with the zombie process still around so that it can access /proc/$PID/ and similar, and so that it can be sure that the PID has not been reused yet. --- TODO | 9 ++++----- src/libsystemd-bus/sd-event.c | 37 ++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/TODO b/TODO index 2fb9cd3e8..8dc8f63a1 100644 --- a/TODO +++ b/TODO @@ -45,9 +45,11 @@ Features: - add field to transient units that indicate whether systemd or somebody else saves/restores its settings, for integration with libvirt - ensure scope units may be started only a single time -* switch to SipHash for hashmaps/sets? +* code cleanup + - get rid of readdir_r/dirent_storage stuff, it's unnecessary on Linux + - we probably should replace the left-over uses of strv_append() and replace them by strv_push() or strv_extend() -* general: get rid of readdir_r/dirent_storage stuff, it's unnecessary on Linux +* switch to SipHash for hashmaps/sets? * when we detect low battery and no AC on boot, show pretty splash and refuse boot @@ -71,8 +73,6 @@ Features: * Add a new Distribute=$NUMBER key to socket units that makes use of SO_REUSEPORT to distribute network traffic on $NUMBER instances -* we probably should replace the left-over uses of strv_append() and replace them by strv_push() or strv_extend() - * move config_parse_path_strv() out of conf-parser.c * After coming back from hibernation reset hibernation swap partition using the /dev/snapshot ioctl APIs @@ -137,7 +137,6 @@ Features: but do not return anything up to the event loop caller. Instead add parameter to sd_event_request_quit() to take retval. This way errors rippling upwards are the option, not the default - - child pid handling: first invoke waitid(WNOHANG) and call event handler, only afterwards reap the process - native support for watchdog stuff * in the final killing spree, detect processes from the root directory, and diff --git a/src/libsystemd-bus/sd-event.c b/src/libsystemd-bus/sd-event.c index 282e9145e..eb0392300 100644 --- a/src/libsystemd-bus/sd-event.c +++ b/src/libsystemd-bus/sd-event.c @@ -1555,6 +1555,10 @@ static int process_child(sd_event *e) { don't care about. Since this is O(n) this means that if you have a lot of processes you probably want to handle SIGCHLD yourself. + + We do not reap the children here (by using WNOWAIT), this + is only done after the event source is dispatched so that + the callback still sees the process as a zombie. */ HASHMAP_FOREACH(s, e->child_sources, i) { @@ -1567,11 +1571,27 @@ static int process_child(sd_event *e) { continue; zero(s->child.siginfo); - r = waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|s->child.options); + r = waitid(P_PID, s->child.pid, &s->child.siginfo, + WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options); if (r < 0) return -errno; if (s->child.siginfo.si_pid != 0) { + bool zombie = + s->child.siginfo.si_code == CLD_EXITED || + s->child.siginfo.si_code == CLD_KILLED || + s->child.siginfo.si_code == CLD_DUMPED; + + if (!zombie && (s->child.options & WEXITED)) { + /* If the child isn't dead then let's + * immediately remove the state change + * from the queue, since there's no + * benefit in leaving it queued */ + + assert(s->child.options & (WSTOPPED|WCONTINUED)); + waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED))); + } + r = source_set_pending(s, true); if (r < 0) return r; @@ -1625,7 +1645,6 @@ static int process_signal(sd_event *e, uint32_t events) { return r; } - return 0; } @@ -1667,9 +1686,21 @@ static int source_dispatch(sd_event_source *s) { r = s->signal.callback(s, &s->signal.siginfo, s->userdata); break; - case SOURCE_CHILD: + case SOURCE_CHILD: { + bool zombie; + + zombie = s->child.siginfo.si_code == CLD_EXITED || + s->child.siginfo.si_code == CLD_KILLED || + s->child.siginfo.si_code == CLD_DUMPED; + r = s->child.callback(s, &s->child.siginfo, s->userdata); + + /* Now, reap the PID for good. */ + if (zombie) + waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED); + break; + } case SOURCE_DEFER: r = s->defer.callback(s, s->userdata); -- 2.30.2