git: 2390cbfe55f5 - main - timeout(1): Fix the inheritance of signal dispositions

From: Baptiste Daroussin <bapt_at_FreeBSD.org>
Date: Wed, 16 Apr 2025 19:46:36 UTC
The branch main has been updated by bapt:

URL: https://cgit.FreeBSD.org/src/commit/?id=2390cbfe55f55916eca25e8ba94a3320535e01c9

commit 2390cbfe55f55916eca25e8ba94a3320535e01c9
Author:     Aaron LI <aly@aaronly.me>
AuthorDate: 2025-04-03 01:07:52 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2025-04-16 19:45:38 +0000

    timeout(1): Fix the inheritance of signal dispositions
    
    POSIX.1-2024 requires that the child process inherit the same signal
    dispositions as the timeout(1) utility inherited, except for the signal
    to be sent upon timeout.
    
    For example, when timeout(1) is run by nohup(1), the command should be
    protected from SIGHUP.
    
    Obtained-from: DragonFly BSD
---
 bin/timeout/timeout.1 | 10 +++++++++-
 bin/timeout/timeout.c | 50 ++++++++++++++++++++++++++++++--------------------
 2 files changed, 39 insertions(+), 21 deletions(-)

diff --git a/bin/timeout/timeout.1 b/bin/timeout/timeout.1
index 14fc19292684..371a167d19f3 100644
--- a/bin/timeout/timeout.1
+++ b/bin/timeout/timeout.1
@@ -24,7 +24,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd April 2, 2025
+.Dd April 3, 2025
 .Dt TIMEOUT 1
 .Os
 .Sh NAME
@@ -65,6 +65,14 @@ Therefore, a signal is never sent if
 .Ar duration
 is 0.
 .Pp
+The signal dispositions inherited by the
+.Ar command
+are the same as the dispositions that
+.Nm
+inherited, except for the signal that will be sent upon timeout,
+which is reset to take the default action and should terminate
+the process.
+.Pp
 The options are as follows:
 .Bl -tag -width indent
 .It Fl f , Fl -foreground
diff --git a/bin/timeout/timeout.c b/bin/timeout/timeout.c
index 8a2f0faecd83..1c4cfa6e017d 100644
--- a/bin/timeout/timeout.c
+++ b/bin/timeout/timeout.c
@@ -224,6 +224,7 @@ main(int argc, char **argv)
 	bool timedout = false;
 	bool do_second_kill = false;
 	bool child_done = false;
+	sigset_t zeromask, allmask, oldmask;
 	struct sigaction signals;
 	struct procctl_reaper_status info;
 	int signums[] = {
@@ -288,6 +289,33 @@ main(int argc, char **argv)
 			err(EXIT_FAILURE, "procctl(PROC_REAP_ACQUIRE)");
 	}
 
+	/* Block all signals to avoid racing against the child. */
+	sigfillset(&allmask);
+	if (sigprocmask(SIG_BLOCK, &allmask, &oldmask) == -1)
+		err(EXIT_FAILURE, "sigprocmask()");
+
+	pid = fork();
+	if (pid == -1) {
+		err(EXIT_FAILURE, "fork()");
+	} else if (pid == 0) {
+		/*
+		 * child process
+		 *
+		 * POSIX.1-2024 requires that the child process inherit the
+		 * same signal dispositions as the timeout(1) utility
+		 * inherited, except for the signal to be sent upon timeout.
+		 */
+		signal(killsig, SIG_DFL);
+		if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1)
+			err(EXIT_FAILURE, "sigprocmask(oldmask)");
+
+		execvp(argv[0], argv);
+		warn("exec(%s)", argv[0]);
+		_exit(errno == ENOENT ? EXIT_CMD_NOENT : EXIT_CMD_ERROR);
+	}
+
+	/* parent continues here */
+
 	memset(&signals, 0, sizeof(signals));
 	sigemptyset(&signals.sa_mask);
 
@@ -310,29 +338,11 @@ main(int argc, char **argv)
 	signal(SIGTTIN, SIG_IGN);
 	signal(SIGTTOU, SIG_IGN);
 
-	pid = fork();
-	if (pid == -1) {
-		err(EXIT_FAILURE, "fork()");
-	} else if (pid == 0) {
-		/* child process */
-		signal(SIGTTIN, SIG_DFL);
-		signal(SIGTTOU, SIG_DFL);
-
-		execvp(argv[0], argv);
-		warn("exec(%s)", argv[0]);
-		_exit(errno == ENOENT ? EXIT_CMD_NOENT : EXIT_CMD_ERROR);
-	}
-
-	/* parent continues here */
-
-	if (sigprocmask(SIG_BLOCK, &signals.sa_mask, NULL) == -1)
-		err(EXIT_FAILURE, "sigprocmask()");
-
 	set_interval(first_kill);
-	sigemptyset(&signals.sa_mask);
+	sigemptyset(&zeromask);
 
 	for (;;) {
-		sigsuspend(&signals.sa_mask);
+		sigsuspend(&zeromask);
 
 		if (sig_chld) {
 			sig_chld = 0;