commit: 142e21a38b1a52929cbf63650778bf3ed74bd3e3
parent 3452bf237ba357bcc76bfda0db4c90ccd5bd4403
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Sun, 9 Nov 2025 19:25:21 +0100
cmd/time: propagate SIGTERM and SIGINT
Diffstat:
5 files changed, 87 insertions(+), 7 deletions(-)
diff --git a/Makefile b/Makefile
@@ -27,7 +27,7 @@ include common.mk
selfcheck: selfcheck-cmds selfcheck-libs
.PHONY: selfcheck-cmds
-TEST_CMDS = test-cmd/pathchk-getlimits test-cmd/getpriority
+TEST_CMDS = test-cmd/pathchk-getlimits test-cmd/getpriority test-cmd/echosig
selfcheck-cmds: $(EXE) $(TEST_CMDS)
LDSTATIC="$(LDSTATIC)" ./check-cmds.sh
@@ -169,6 +169,7 @@ lib/sha256.o: lib/sha256.c lib/sha256.h
lib/sha512.o: lib/sha512.c lib/sha512.h
lib/bytes2hex.o: lib/bytes2hex.c lib/strconv.h
+test-cmd/echosig: test-cmd/echosig.c build/sys_signame.c
test-lib/t_sha1: test-lib/t_sha1.c lib/sha1.c lib/bytes2hex.o
test-lib/t_sha256: test-lib/t_sha256.c lib/sha256.c lib/bytes2hex.o
test-lib/t_sha512: test-lib/t_sha512.c lib/sha512.c lib/bytes2hex.o
diff --git a/cmd/time.1 b/cmd/time.1
@@ -41,6 +41,15 @@ was found but couldn't be invoked
.Ar command
could not be found
.El
+.Sh SIGNALS
+.Nm
+propagates
+.Dv SIGTERM
+and
+.Dv SIGINT
+to it's child process, allowing constructs like
+.Ql timeout 1s time yes
+to work as expected.
.Sh SEE ALSO
.Xr getrusage 3 ,
.Xr times 3
diff --git a/cmd/time.c b/cmd/time.c
@@ -6,6 +6,7 @@
#include "../libutils/getopt_nolong.h"
#include <errno.h>
+#include <signal.h>
#include <spawn.h>
#include <stdio.h> // perror, fprintf
#include <stdlib.h> // abort
@@ -19,6 +20,8 @@ extern char **environ;
const char *argv0 = "time";
+static pid_t child = -1;
+
enum cmd_time_mode
{
CMD_TIME_POSIX = 0,
@@ -26,6 +29,23 @@ enum cmd_time_mode
};
static void
+kill_child(int sig)
+{
+ if(child > 0)
+ {
+ if(kill(child, sig) != 0)
+ {
+ // candicate for sig2str
+ fprintf(stderr,
+ "time: warning: Failed propagating signal number %d to child process (%d): %s\n",
+ sig,
+ child,
+ strerror(errno));
+ }
+ }
+}
+
+static void
usage(void)
{
fprintf(stderr, "Usage: time [-p|-v] command [argument ...]\n");
@@ -83,7 +103,6 @@ main(int argc, char *argv[])
return 1;
}
- pid_t child = -1;
if(posix_spawnp(&child, argv[0], NULL, NULL, argv, environ) != 0)
{
ret = 126 + (errno == ENOENT);
@@ -91,8 +110,20 @@ main(int argc, char *argv[])
return ret;
}
+ struct sigaction sa = {
+ .sa_handler = &kill_child,
+ .sa_flags = 0,
+ };
+
+ if(sigaction(SIGTERM, &sa, NULL) != 0)
+ fprintf(stderr, "time: warning: Failed adding handler for SIGTERM: %s\n", strerror(errno));
+
+ if(sigaction(SIGINT, &sa, NULL) != 0)
+ fprintf(stderr, "time: warning: Failed adding handler for SIGINT: %s\n", strerror(errno));
+
int status = 0;
waitpid(child, &status, 0);
+ child = -1; // reaped
clock_t t1 = times(&tms);
if(t1 == -1)
diff --git a/test-cmd/echosig.c b/test-cmd/echosig.c
@@ -0,0 +1,33 @@
+// utils-std: Collection of commonly available Unix tools
+// SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-License-Identifier: CC0-1.0 OR WTFPL
+
+#define _DEFAULT_SOURCE
+#include "../lib/sys_signame.h"
+
+#include <signal.h>
+#include <stdio.h>
+
+int
+main(void)
+{
+ sigset_t sigset;
+ int sig = -1;
+
+ if(sigfillset(&sigset) != 0) return 1;
+
+ if(sigprocmask(SIG_BLOCK, &sigset, NULL) != 0) return 1;
+
+ if(sigwait(&sigset, &sig) != 0) return 1;
+
+ if(sig > 0 && sig < NSIG && util_sys_signame[sig] != NULL)
+ {
+ printf("%s\n", util_sys_signame[sig]);
+ }
+ else
+ {
+ printf("%d\n", sig);
+ }
+
+ return 0;
+}
diff --git a/test-cmd/time.sh b/test-cmd/time.sh
@@ -2,7 +2,7 @@
# SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
# SPDX-License-Identifier: MPL-2.0
-plans=6
+plans=7
WD="$(dirname "$0")/../"
target="${WD}/cmd/time"
. "${WD}/test-cmd/tap.sh"
@@ -18,7 +18,7 @@ $usage"
time_fmt() {
out=$(mktemp time_fmt.XXXXXX)
- "$target" "$@" 2> "$out"
+ "$@" 2> "$out"
err=$?
cut -f1 -d '.' <"$out"
@@ -32,13 +32,19 @@ real 0
user 0
sys 0
"
-t_cmd --exit=1 false "$time_zeroes" time_fmt false
-t_cmd true "$time_zeroes" time_fmt true
+t_cmd --exit=1 false "$time_zeroes" time_fmt "$target" false
+t_cmd true "$time_zeroes" time_fmt "$target" true
t_cmd sleep_1 'real 1
user 0
sys 0
-' time_fmt "${WD}/cmd/sleep" 1
+' time_fmt "$target" "${WD}/cmd/sleep" 1
+
+t_cmd --exit=124 timeout_sig 'TERM
+real 0
+user 0
+sys 0
+' time_fmt "${WD}/cmd/timeout" 0.1s "${WD}/cmd/time" "${WD}/test-cmd/echosig"
if test "NetBSD" = "$(uname -s)"; then
t_skip "# NetBSD somehow doesn't returns ENOENT there, just errno = 0"