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.
This commit is contained in:
Lennart Poettering 2013-12-11 03:50:35 +00:00
parent e599ba01f5
commit 08cd155254
2 changed files with 38 additions and 8 deletions

9
TODO
View File

@ -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

View File

@ -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);