chiark / gitweb /
event: when handling SIGCHLD of a child process only reap after dispatching event...
authorLennart Poettering <lennart@poettering.net>
Wed, 11 Dec 2013 03:50:35 +0000 (03:50 +0000)
committerLennart Poettering <lennart@poettering.net>
Wed, 11 Dec 2013 17:20:09 +0000 (18:20 +0100)
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
src/libsystemd-bus/sd-event.c

diff --git a/TODO b/TODO
index 2fb9cd3e89aaf4faebddb26dfa93ca4c15f24d31..8dc8f63a1897d2ae6541d7213b3ba9049fc5b900 100644 (file)
--- 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
index 282e9145e7a2e2cd95de9076e789072001543c7c..eb0392300d0eeddb3b98e2b9eff15e1ea9a54ac1 100644 (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);