Use sigaction(SA_NOCLDWAIT) for SIGCHLD handling
authorChris Down <chris@chrisdown.name>
Sat, 28 Jan 2023 11:53:48 +0000 (12:53 +0100)
committerHiltjo Posthuma <hiltjo@codemadness.org>
Sat, 28 Jan 2023 12:34:43 +0000 (13:34 +0100)
signal() semantics are pretty unclearly specified. For example, depending on OS
kernel and libc, the handler may be returned to SIG_DFL (hence the inner call
to read the signal handler). Moving to sigaction() means the behaviour is
consistently defined.

Using SA_NOCLDWAIT also allows us to avoid calling the non-reentrant function
die() in the handler.

Some addditional notes for archival purposes:

* NRK pointed out errno of waitpid could also theoretically get clobbered.
* The original patch was iterated on and modified by NRK and Hiltjo:
  * SIG_DFL was changed to SIG_IGN, this is required, atleast on older systems
    such as tested on Slackware 11.
  * signals are not blocked using sigprocmask, because in theory it would
    briefly for example also ignore a SIGTERM signal. It is OK if waitpid() is (in
    theory interrupted).

POSIX reference:
"Consequences of Process Termination":
https://pubs.opengroup.org/onlinepubs/9699919799/functions/_Exit.html#tag_16_01_03_01

dwm.c

diff --git a/dwm.c b/dwm.c
index 03baf42b350240f1ab7907169f85725f05d6fe70..c2bd8710544eb4b4e7eaa4a1307e1f1dfd8d16ba 100644 (file)
--- a/dwm.c
+++ b/dwm.c
@@ -205,7 +205,6 @@ static void setmfact(const Arg *arg);
 static void setup(void);
 static void seturgent(Client *c, int urg);
 static void showhide(Client *c);
-static void sigchld(int unused);
 static void spawn(const Arg *arg);
 static void tag(const Arg *arg);
 static void tagmon(const Arg *arg);
@@ -1543,9 +1542,16 @@ setup(void)
        int i;
        XSetWindowAttributes wa;
        Atom utf8string;
+       struct sigaction sa;
 
-       /* clean up any zombies immediately */
-       sigchld(0);
+       /* do not transform children into zombies when they terminate */
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+
+       /* clean up any zombies (inherited from .xinitrc etc) immediately */
+       while (waitpid(-1, NULL, WNOHANG) > 0);
 
        /* init screen */
        screen = DefaultScreen(dpy);
@@ -1638,14 +1644,6 @@ showhide(Client *c)
        }
 }
 
-void
-sigchld(int unused)
-{
-       if (signal(SIGCHLD, sigchld) == SIG_ERR)
-               die("can't install SIGCHLD handler:");
-       while (0 < waitpid(-1, NULL, WNOHANG));
-}
-
 void
 spawn(const Arg *arg)
 {