commit 7bd351d98f3310f4f77be3357fbd35cb65bbe493
parent fddce8f77f15dff6716862572d833bd839ebcb07
Author: Roberto E. Vargas Caballero <k0ga@shike2.com>
Date: Thu, 9 Jan 2025 18:16:01 +0100
make/posix: Fix signal handling
The behaviour of signal() in POSIX is underspecified, and it can restart
interrupted syscalls, so the only way to be sure that syscalls will
be interrupted by signals is to use sigaction(). As we don't want to
pollute the code with these weirdness things we just to a lazy signal
initialization in the POSIX case while we keep normal signal for
everyone else.
Diffstat:
3 files changed, 52 insertions(+), 4 deletions(-)
diff --git a/src/cmd/scc-make/main.c b/src/cmd/scc-make/main.c
@@ -87,7 +87,7 @@ estrdup(char *s)
return memcpy(emalloc(len), s, len);
}
-static void
+void
sighandler(int signo)
{
stop = signo;
diff --git a/src/cmd/scc-make/posix.c b/src/cmd/scc-make/posix.c
@@ -1,3 +1,6 @@
+#define _POSIX_C_SOURCE
+
+#include <signal.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -38,7 +41,23 @@ launch(char *cmd, int ignore)
pid_t pid;
char *name, *shell;
char *args[] = {NULL, "-ec" , cmd, NULL};
+ static int initsignals;
extern char **environ;
+ extern void sighandler(int);
+
+
+ if (!initsignals) {
+ struct sigaction act = {
+ .sa_handler = sighandler
+ };
+
+ /* avoid BSD weirdness signal restart handling */
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+ initsignals = 1;
+ }
switch (pid = fork()) {
case -1:
@@ -56,8 +75,8 @@ launch(char *cmd, int ignore)
execve(shell, args, environ);
_exit(127);
default:
- while (waitpid(pid, &st, 0) < 0 && errno == EINTR)
- ;
- return st;
+ if (waitpid(pid, &st, 0) < 0)
+ kill(pid, SIGTERM);
+ return st;
}
}
diff --git a/tests/make/execute/0098-signal.sh b/tests/make/execute/0098-signal.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+trap 'rm -f file.txt; kill -KILL $pid 2>/dev/null' EXIT INT TERM HUP
+
+scc-make -f - <<'EOF' 2>&1 &
+file.txt:
+ @touch $@
+ @test -f $@
+ @while : ; do sleep 1 ; done
+EOF
+
+pid=$!
+
+for i in 1 2 3
+do
+ if test -f file.txt
+ then
+ kill $pid
+ for i in 1 2 3
+ do
+ test -f file.txt || exit 0
+ sleep 1
+ done
+ exit 1
+ fi
+ sleep 1
+done
+
+exit 1