timeout: Clean up

* Annotate logv() and fix format string bug.

* Don't reinvent str2sig(3).

* Reorganize kill_self() so we unblock signals as late as possible, and
  use raise(2) instead of kill(2).

* Explicitly close unused pipe descriptors.

* Use correct type to collect result of read(2) and write(2).

* Compare return values to 0, not -1.

* Sort local variables according to style(9).

* Reduce unnecessary nesting.

* Reindent.

* Fix typo in manual page.

MFC after:	1 week
Sponsored by:	Klara, Inc.
Reviewed by:	kevans
Differential Revision:	https://reviews.freebsd.org/D55277
This commit is contained in:
Dag-Erling Smørgrav
2026-02-13 21:18:35 +01:00
parent b253243297
commit 08208cd694
2 changed files with 74 additions and 86 deletions
+1 -1
View File
@@ -188,7 +188,7 @@ will terminate itself with the same signal if the
is terminated by a signal.
.Pp
If an error occurred, the following exit values are returned:
.Bl -tag -offset indent with indent -compact
.Bl -tag -offset indent -width indent -compact
.It 125
An error other than the two described below occurred.
For example, an invalid duration or signal was specified.
+73 -85
View File
@@ -26,13 +26,13 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/fcntl.h>
#include <sys/procctl.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <err.h>
#include <fcntl.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>
@@ -60,14 +60,14 @@ static void __dead2
usage(void)
{
fprintf(stderr,
"Usage: %s [-f | --foreground] [-k time | --kill-after time]"
" [-p | --preserve-status] [-s signal | --signal signal] "
" [-v | --verbose] <duration> <command> [arg ...]\n",
getprogname());
"Usage: %s [-f | --foreground] [-k time | --kill-after time]"
" [-p | --preserve-status] [-s signal | --signal signal] "
" [-v | --verbose] <duration> <command> [arg ...]\n",
getprogname());
exit(EXIT_FAILURE);
}
static void
static void __printflike(1, 2)
logv(const char *fmt, ...)
{
va_list ap;
@@ -118,26 +118,6 @@ parse_duration(const char *duration)
return (ret);
}
static int
parse_signal(const char *str)
{
int sig, i;
const char *errstr;
sig = strtonum(str, 1, sys_nsig - 1, &errstr);
if (errstr == NULL)
return (sig);
if (strncasecmp(str, "SIG", 3) == 0)
str += 3;
for (i = 1; i < sys_nsig; i++) {
if (strcasecmp(str, sys_signame[i]) == 0)
return (i);
}
errx(EXIT_INVALID, "invalid signal");
}
static void
sig_handler(int signo)
{
@@ -188,28 +168,22 @@ static void
send_sig(pid_t pid, int signo, bool foreground)
{
struct procctl_reaper_kill rk;
int error;
logv("sending signal %s(%d) to command '%s'",
sys_signame[signo], signo, command);
sys_signame[signo], signo, command);
if (foreground) {
if (kill(pid, signo) == -1) {
if (errno != ESRCH)
warn("kill(%d, %s)", (int)pid,
sys_signame[signo]);
}
if (kill(pid, signo) < 0 && errno != ESRCH)
warn("kill(%d, %s)", (int)pid, sys_signame[signo]);
} else {
memset(&rk, 0, sizeof(rk));
rk.rk_sig = signo;
error = procctl(P_PID, getpid(), PROC_REAP_KILL, &rk);
if (error == 0 || (error == -1 && errno == ESRCH))
;
else if (error == -1) {
if (procctl(P_PID, getpid(), PROC_REAP_KILL, &rk) < 0 &&
errno != ESRCH) {
warn("procctl(PROC_REAP_KILL)");
if (rk.rk_fpid > 0)
warnx(
"failed to signal some processes: first pid=%d",
(int)rk.rk_fpid);
if (rk.rk_fpid > 0) {
warnx("failed to signal some processes:"
" first pid=%d", (int)rk.rk_fpid);
}
}
logv("signaled %u processes", rk.rk_killed);
}
@@ -222,7 +196,7 @@ send_sig(pid_t pid, int signo, bool foreground)
*/
if (signo != SIGKILL && signo != SIGSTOP && signo != SIGCONT) {
logv("sending signal %s(%d) to command '%s'",
sys_signame[SIGCONT], SIGCONT, command);
sys_signame[SIGCONT], SIGCONT, command);
if (foreground) {
kill(pid, SIGCONT);
} else {
@@ -245,7 +219,7 @@ set_interval(double iv)
tim.it_value.tv_usec = (suseconds_t)(iv * 1000000UL);
}
if (setitimer(ITIMER_REAL, &tim, NULL) == -1)
if (setitimer(ITIMER_REAL, &tim, NULL) < 0)
err(EXIT_FAILURE, "setitimer()");
}
@@ -261,20 +235,20 @@ kill_self(int signo)
sigset_t mask;
struct rlimit rl;
logv("killing self with signal %s(%d)", sys_signame[signo], signo);
/* Disable core generation. */
memset(&rl, 0, sizeof(rl));
setrlimit(RLIMIT_CORE, &rl);
/* Reset the signal disposition and make sure it's unblocked. */
signal(signo, SIG_DFL);
sigfillset(&mask);
sigdelset(&mask, signo);
sigprocmask(SIG_SETMASK, &mask, NULL);
/* Disable core generation. */
memset(&rl, 0, sizeof(rl));
setrlimit(RLIMIT_CORE, &rl);
logv("killing self with signal %s(%d)", sys_signame[signo], signo);
kill(getpid(), signo);
err(128 + signo, "signal %s(%d) failed to kill self",
sys_signame[signo], signo);
raise(signo);
err(128 + signo, "raise(%d)", signo);
}
static void
@@ -285,7 +259,7 @@ log_termination(const char *name, const siginfo_t *si)
} else if (si->si_code == CLD_DUMPED || si->si_code == CLD_KILLED) {
logv("%s: pid=%d, sig=%d", name, si->si_pid, si->si_status);
} else {
logv("%s: pid=%d, reason=%d, status=%d", si->si_pid,
logv("%s: pid=%d, reason=%d, status=%d", name, si->si_pid,
si->si_code, si->si_status);
}
}
@@ -293,22 +267,23 @@ log_termination(const char *name, const siginfo_t *si)
int
main(int argc, char **argv)
{
int ch, sig;
int pstat = 0;
pid_t pid;
int pp[2], error;
char c;
struct procctl_reaper_status info;
siginfo_t si, child_si;
struct sigaction sa;
sigset_t zeromask, allmask, oldmask;
double first_kill;
double second_kill = 0;
ssize_t error;
pid_t pid;
int pp[2];
int ch, sig;
int pstat = 0;
char c;
bool foreground = false;
bool preserve = false;
bool timedout = false;
bool do_second_kill = false;
bool child_done = false;
sigset_t zeromask, allmask, oldmask;
struct sigaction sa;
struct procctl_reaper_status info;
siginfo_t si, child_si;
const char optstr[] = "+fhk:ps:v";
const struct option longopts[] = {
@@ -334,7 +309,8 @@ main(int argc, char **argv)
preserve = true;
break;
case 's':
killsig = parse_signal(optarg);
if (str2sig(optarg, &killsig) != 0)
errx(EXIT_INVALID, "invalid signal");
break;
case 'v':
verbose = true;
@@ -358,22 +334,22 @@ main(int argc, char **argv)
if (!foreground) {
/* Acquire a reaper */
if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) == -1)
if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) < 0)
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)
if (sigprocmask(SIG_BLOCK, &allmask, &oldmask) < 0)
err(EXIT_FAILURE, "sigprocmask()");
if (pipe2(pp, O_CLOEXEC) == -1)
if (pipe2(pp, O_CLOEXEC) < 0)
err(EXIT_FAILURE, "pipe2");
pid = fork();
if (pid == -1) {
if (pid < 0)
err(EXIT_FAILURE, "fork()");
} else if (pid == 0) {
if (pid == 0) {
/*
* child process
*
@@ -382,11 +358,12 @@ main(int argc, char **argv)
* inherited, except for the signal to be sent upon timeout.
*/
signal(killsig, SIG_DFL);
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) == -1)
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err(EXIT_FAILURE, "sigprocmask(oldmask)");
(void)close(pp[1]);
error = read(pp[0], &c, 1);
if (error == -1)
if (error < 0)
err(EXIT_FAILURE, "read from control pipe");
if (error == 0)
errx(EXIT_FAILURE, "eof from control pipe");
@@ -396,6 +373,7 @@ main(int argc, char **argv)
}
/* parent continues here */
(void)close(pp[0]);
/* Catch all signals in order to propagate them. */
memset(&sa, 0, sizeof(sa));
@@ -403,25 +381,36 @@ main(int argc, char **argv)
sa.sa_handler = sig_handler;
sa.sa_flags = SA_RESTART;
for (sig = 1; sig < sys_nsig; sig++) {
if (sig == SIGKILL || sig == SIGSTOP || sig == SIGCONT ||
sig == SIGTTIN || sig == SIGTTOU)
continue;
if (sigaction(sig, &sa, NULL) == -1)
err(EXIT_FAILURE, "sigaction(%d)", sig);
switch (sig) {
case SIGTTIN:
case SIGTTOU:
/* Don't stop if background child needs TTY */
if (signal(sig, SIG_IGN) == SIG_ERR)
err(EXIT_FAILURE, "signal(%d)", sig);
break;
case SIGKILL:
case SIGSTOP:
case SIGCONT:
/* These can't be caught or ignored */
break;
default:
if (sigaction(sig, &sa, NULL) < 0)
err(EXIT_FAILURE, "sigaction(%d)", sig);
}
}
/* Don't stop if background child needs TTY */
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
/* Start the timer */
set_interval(first_kill);
/* Let the child know we're ready */
error = write(pp[1], "a", 1);
if (error == -1)
if (error < 0)
err(EXIT_FAILURE, "write to control pipe");
if (error == 0)
errx(EXIT_FAILURE, "short write to control pipe");
sigemptyset(&zeromask);
(void)close(pp[1]);
sigemptyset(&zeromask);
for (;;) {
sigsuspend(&zeromask);
@@ -430,9 +419,8 @@ main(int argc, char **argv)
for (;;) {
memset(&si, 0, sizeof(si));
error = waitid(P_ALL, -1, &si, WEXITED |
WNOHANG);
if (error == -1) {
if (waitid(P_ALL, -1, &si,
WEXITED | WNOHANG) < 0) {
if (errno != EINTR)
break;
} else if (si.si_pid == pid) {
@@ -456,7 +444,7 @@ main(int argc, char **argv)
break;
} else {
procctl(P_PID, getpid(),
PROC_REAP_STATUS, &info);
PROC_REAP_STATUS, &info);
if (info.rs_children == 0)
break;
}
@@ -471,7 +459,7 @@ main(int argc, char **argv)
sig = sig_term;
sig_term = 0;
logv("received terminating signal %s(%d)",
sys_signame[sig], sig);
sys_signame[sig], sig);
}
send_sig(pid, sig, foreground);