Blob Blame History Raw
From 788faac41b56f8b08e60f3456ad56c5a36fffa4c Mon Sep 17 00:00:00 2001
From: Pavel Raiskup <praiskup@redhat.com>
Date: Tue, 27 Jan 2015 07:05:26 +0100
Subject: [PATCH 16/16] tcsh: fix 'wait' hang

Make sure that SIGCHLD is blocked before we call
handle_pending_signals() for the first time and before we actually
check for pp->p_flags & PRUNNING to make sure that the SIGCHLD is
not leaked meanwhile.

Resolves: rhbz#1181685

---
 Fixes     |  4 ++++
 sh.proc.c | 26 +++++++++++++++++++++++++-
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/Fixes b/Fixes
index 8eac9d4..56915a9 100644
--- a/Fixes
+++ b/Fixes
@@ -1,3 +1,7 @@
+ 29. Pavel Raiskup fix hang with:
+        while (1)
+            ( date & ; wait )
+        end
   6. V6.18.01 - 20120214
   5. fix interruptible wait again
   4. ignore bogus compiler overflow message
diff --git a/sh.proc.c b/sh.proc.c
index e32ebda..0c5fc25 100644
--- a/sh.proc.c
+++ b/sh.proc.c
@@ -593,22 +593,44 @@ void
 dowait(Char **v, struct command *c)
 {
     struct process *pp;
+
+    /* the current block mask to be able to restore */
+    sigset_t old_mask;
+
+    /* block mask for critical section: OLD_MASK U {SIGCHLD} */
+    sigset_t block_mask;
+
+    /* ignore those during blocking sigsuspend:
+       OLD_MASK / {SIGCHLD, possibly(SIGINT)} */
     sigset_t pause_mask;
+
     int opintr_disabled, gotsig;
 
     USE(c);
     USE(v);
     pjobs++;
+
     sigprocmask(SIG_BLOCK, NULL, &pause_mask);
     sigdelset(&pause_mask, SIGCHLD);
     if (setintr)
 	sigdelset(&pause_mask, SIGINT);
+
+    /* critical section, block also SIGCHLD */
+    sigprocmask(SIG_BLOCK, NULL, &block_mask);
+    sigaddset(&block_mask, SIGCHLD);
+    sigprocmask(SIG_BLOCK, &block_mask, &old_mask);
+
+    /* detect older SIGCHLDs and remove PRUNNING flag from proclist */
+    (void)handle_pending_signals();
+
 loop:
     for (pp = proclist.p_next; pp; pp = pp->p_next)
 	if (pp->p_procid &&	/* pp->p_procid == pp->p_jobid && */
 	    pp->p_flags & PRUNNING) {
-	    (void)handle_pending_signals();
+	    /* wait for (or pick up alredy blocked) SIGCHLD */
 	    sigsuspend(&pause_mask);
+
+	    /* make the 'wait' interuptable by CTRL-C */
 	    opintr_disabled = pintr_disabled;
 	    pintr_disabled = 0;
 	    gotsig = handle_pending_signals();
@@ -618,6 +640,8 @@ loop:
 	    goto loop;
 	}
     pjobs = 0;
+
+    sigprocmask(SIG_SETMASK, &old_mask, NULL);
 }
 
 /*
-- 
2.1.0