diff mbox series

AT&T Unix PC : 13-wait3-replacement

Message ID c2ac05a9-c038-46e4-99fc-52bb803421cd@knaff.lu (mailing list archive)
State New
Headers show
Series AT&T Unix PC : 13-wait3-replacement | expand

Commit Message

Alain Knaff Nov. 17, 2024, 6:45 p.m. UTC
Hi,

The 13th patch caters to unavailability of wait3() system call on older
SYSV systems such as UnixPC.

On these systems, only an always blocking wait() is available. In
order to get non-blocking wait, you have to use a SIGCLD handler (not
a typo, child signal handling on SYSV was subtly different from what
we are used to now). This handler sets a flag (gotsigchld) when a
child dies.

When upper layers request a non-blocking wait, we test for this
flag. If the gotsigchld flag is set, we can wait(), confident in the
knowledge that this call will return right away. Then clear the flag,
and re-instore SIGCLD handler.
If the gotsigchld flag is not set, we return right away with 0 ("process
does have children, but none have exited so far")


The "subtle difference" between SIGCLD and SIGCHLD is that SIGCLD is
"level triggered": i.e. the system continues raising the signal as long
as there is a terminated child around on which the parent has not yet
waited. Thus, SIGCLD should not be re-enabled in the handler, but only
after wait()

https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh_chap02.html

"Some implementations (System V, for example) will deliver a SIGCLD
signal immediately when a process establishes a signal-catching
function for SIGCLD when that process has a child that has already
terminated. Other implementations, such as 4.3 BSD, do not generate a
new SIGCHLD signal in this way. In general, a process should not
attempt to alter the signal action for the SIGCHLD signal while it has
any outstanding children. However, it is not always possible for a
process to avoid this; for example, shells sometimes start up
processes in pipelines with other processes from the pipeline as
children. Processes that cannot ensure that they have no children when
altering the signal action for SIGCHLD thus need to be prepared for,
but not depend on, generation of an immediate SIGCHLD signal."

Actually, in a way we do "depend on" this peculiarity to implement
non-blocking wait(). However, if it is not available, the only thing
that breaks is "monitor mode" (notification of terminated children
when pressing return in an interactive shell).

Edit: contrarily to what I had announced this morning, there's no amendment 
needed here after all. I was misled by a wrong configure invocation line 
for dietlibc, which somehow led to #include files being included out of
order

Regards,

Alain
diff mbox series

Patch

diff -X ../exclude.txt -urN dash-0.5.12+12-sigaction/configure.ac dash-0.5.12+13-wait3-replacement/configure.ac
--- dash-0.5.12+12-sigaction/configure.ac	2024-11-10 17:08:01.155025888 +0000
+++ dash-0.5.12+13-wait3-replacement/configure.ac	2024-11-10 20:26:19.105474215 +0000
@@ -117,7 +117,7 @@ 
 	       mempcpy memmove \
 	       sigsetmask stpcpy strchrnul strsignal strtod strtoimax \
 	       strtoumax sysconf \
-	       vfork sigaction sigprocmask lstat dup2 getgroups \
+	       vfork wait3 sigaction sigprocmask lstat dup2 getgroups \
 	       strstr stpncpy strcasecmp strerror strdup strtoul vsnprintf \
 	       readdir)
 
diff -X ../exclude.txt -urN dash-0.5.12+12-sigaction/src/jobs.c dash-0.5.12+13-wait3-replacement/src/jobs.c
--- dash-0.5.12+12-sigaction/src/jobs.c	2024-10-27 20:19:13.625231580 +0000
+++ dash-0.5.12+13-wait3-replacement/src/jobs.c	2024-11-10 21:51:55.911519696 +0000
@@ -1154,6 +1154,50 @@ 
 	return rpid;
 }
 
+#ifndef HAVE_WAIT3
+
+# ifndef WNOHANG
+#  define WNOHANG 1
+# endif
+
+/* Replacement wait3 */
+STATIC inline pid_t
+wait3(int *wstatus, int options, void * rusage)
+{
+	pid_t pid;
+
+	/* if we are called as non-blocking, and have no SIGCLD
+	   pending, return */
+	if((options & WNOHANG) && !gotsigchld)
+		return 0; /* 0 is returned by wait3 if the process does have
+			   * children, but none have exited so far */
+	pid = wait(wstatus);
+	if(pid <= 0)
+		/* no process reaped, or error */
+		return pid;
+
+	
+	/* if we come here, we did indeed reap a process.  =>
+	 * clear gotsigchld, and re-instore SIGCLD handler.
+	 *
+	 * On old SYSV systems where there is no wait3 call,
+	 * signal(SIGCLD) is level-triggered, and will trigger as
+	 * long as there are still unwaited-for children
+	 * around. However, it is, like all other signals, disabled as
+	 * soon as its signal handler is invoked, which prevents storm
+	 * :-). So we need to re-enable it after wait(), in order to
+	 * be notified of further children dying.
+	 *
+	 * If more than one child had died since last reset of
+	 * gotsigchld, a signal will be delivered immediately after
+	 * re-activation of the signal, because it is level-triggered.
+	 */
+	gotsigchld = 0;
+	setsignal(SIGCLD);
+	return pid;
+}
+#endif
+
 /*
  * Do a wait system call.  If block is zero, we return -1 rather than
  * blocking.  If block is DOWAIT_WAITCMD, we return 0 when a signal
@@ -1182,7 +1226,15 @@ 
 #endif
 
 	do {
+#ifdef HAVE_WAIT3
+		/* reset gotsigchld *only* if we are not using a
+		 * replacement wait3(). Indeed, the replacement
+		 * wait3() needs original value of gotsigchld in order
+		 * to decide whether it does indeed need to
+		 * wait. Replacement wait3 will then reset gotsigchld
+		 * when done with it */
 		gotsigchld = 0;
+#endif
 		do
 			err = wait3(status, flags, NULL);
 		while (err < 0 && errno == EINTR && !pending_sig);
diff -X ../exclude.txt -urN dash-0.5.12+12-sigaction/src/system.h dash-0.5.12+13-wait3-replacement/src/system.h
--- dash-0.5.12+12-sigaction/src/system.h	2024-11-10 16:26:47.464166999 +0000
+++ dash-0.5.12+13-wait3-replacement/src/system.h	2024-11-10 14:53:08.418618090 +0000
@@ -159,6 +159,10 @@ 
 # define O_NONBLOCK O_NDELAY
 #endif
 
+#if !defined SIGCHLD && defined SIGCLD
+# define SIGCHLD SIGCLD
+#endif
+
 #ifndef FD_CLOEXEC
 # define FD_CLOEXEC 1
 #endif
diff -X ../exclude.txt -urN dash-0.5.12+12-sigaction/src/trap.c dash-0.5.12+13-wait3-replacement/src/trap.c
--- dash-0.5.12+12-sigaction/src/trap.c	2024-11-10 20:26:44.222071158 +0000
+++ dash-0.5.12+13-wait3-replacement/src/trap.c	2024-11-10 20:27:41.055421903 +0000
@@ -53,6 +53,7 @@ 
 #include "error.h"
 #include "trap.h"
 #include "mystring.h"
+#include "system.h"
 
 /*
  * Sigmode records the current value of the signal handlers for the various
@@ -87,6 +88,7 @@ 
 #ifdef mkinit
 INCLUDE "memalloc.h"
 INCLUDE "trap.h"
+INCLUDE "system.h"
 
 INIT {
 	sigmode[SIGCHLD - 1] = S_DFL;
@@ -302,7 +304,10 @@ 
 onsig(int signo)
 {
 #ifndef HAVE_SIGACTION
-	signal(signo, onsig);
+#ifdef SIGCLD
+	if(signo != SIGCLD)
+#endif
+		signal(signo, onsig);
 #endif
 	if (vforked)
 		return;