Blob Blame History Raw
From 64d0115dcda445a5b1c069f696a363730f654425 Mon Sep 17 00:00:00 2001
From: David Tardon <dtardon@redhat.com>
Date: Thu, 2 May 2019 12:55:04 +0200
Subject: [PATCH] avoid possible hang if our child process hangs

If there is one or more unexpected child processes that terminate, but
the "main" child process hangs, we will loop through the terminated
children and then, eventually, get stuck in the waitpid() call. Let's
repeat the main cycle if that situation happens, as that allows us to
finish after timeout.

Related: #1697909
---
 src/udev/udev-event.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index 5550ec93de..79b8614ec2 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -605,6 +605,7 @@ static int spawn_wait(struct udev_event *event,
                         struct signalfd_siginfo fdsi;
                         int status, r;
                         ssize_t size;
+                        bool wait_again = false;
 
                         size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
                         if (size != sizeof(struct signalfd_siginfo))
@@ -622,15 +623,21 @@ static int spawn_wait(struct udev_event *event,
                                            In case the PID we wait for also exited the kernel could coalesce SIGCHLDs and we won't get second SIGCHLD
                                            on the signalfd. We can't know if coalescing happened or not, hence we need to call waitpid() in a loop until
                                            the PID we care for exits, we if it haven't already. */
-                                        while (child_exited < 0) {
-                                                r = waitpid(-1, &status, 0);
+                                        while (child_exited < 0 && !wait_again) {
+                                                r = waitpid(-1, &status, WNOHANG);
                                                 if (r < 0 && errno == EINTR)
                                                         continue;
                                                 else if (r < 0)
                                                         break;
                                                 else if (r == pid)
                                                         child_exited = 0;
+                                                else if (r == 0)
+                                                        wait_again = true;
                                         }
+
+                                        /* Only unexpected process(es) have been terminated. Continue waiting */
+                                        if (wait_again)
+                                                continue;
                                 }
 
                                 /* We didn't wait for child yet, let's do that now */