It needs pledge. > +#include <sys/cdefs.h>
This is wrong, it should be <sys/types.h> > +#include <sys/time.h> > +#include <sys/wait.h> > + > +#include <err.h> > +#include <errno.h> > +#include <getopt.h> > +#include <limits.h> > +#include <signal.h> > +#include <stdbool.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <sysexits.h> > +#include <unistd.h> > + > +#define EXIT_TIMEOUT 124 > + > +static sig_atomic_t sig_chld = 0; > +static sig_atomic_t sig_term = 0; > +static sig_atomic_t sig_alrm = 0; > +static sig_atomic_t sig_ign = 0; > + > +static void __dead > +usage(void) > +{ > + fprintf(stderr, "Usage: %s [--signal sig | -s sig] [--preserve-status]" lower case usage: > + " [--kill-after time | -k time] [--foreground] <duration> <command>" > + " <arg ...>\n", getprogname()); This should not use <>, ingo and jmc are going to scream at you. setprogname/getprogname use is excessive, this could simply be the text "timeout" > + errno = 0; > + sig = strtol(str, &ep, 10); > + > + if (str[0] == '\0' || *ep != '\0') > + goto err; > + if (errno == ERANGE && (sig == LONG_MAX || sig == LONG_MIN)) > + goto err; strtonum will simplify this. > + return; > + } > + > + switch(signo) { > + case 0: I don't understand what the 0 does here. > + case SIGINT: > + case SIGHUP: > + case SIGQUIT: > + case SIGTERM: > + sig_term = signo; > + break; > + case SIGCHLD: > + sig_chld = 1; > + break; > + case SIGALRM: > + sig_alrm = 1; > + break; > + } > +} > +main(int argc, char **argv) > +{ > + int ch; > + unsigned long i; > + int foreground, preserve; > + int error, pstat, status; > + int killsig = SIGTERM; > + pid_t pgid, pid, cpid; > + double first_kill; > + double second_kill; > + bool timedout = false; > + bool do_second_kill = false; > + struct sigaction signals; > + int signums[] = { > + -1, > + SIGTERM, > + SIGINT, > + SIGHUP, > + SIGCHLD, > + SIGALRM, > + SIGQUIT, > + }; Weird indent. > + > + setprogname(argv[0]); setprogname discussion at the top > + > + foreground = preserve = 0; > + second_kill = 0; > + cpid = -1; > + pgid = -1; This defines variables *after* code: > + > + const struct option longopts[] = { > + { "preserve-status", no_argument, &preserve, 1 }, > + { "foreground", no_argument, &foreground, 1 }, > + { "kill-after", required_argument, NULL, 'k'}, > + { "signal", required_argument, NULL, 's'}, > + { "help", no_argument, NULL, 'h'}, > + { NULL, 0, NULL, 0 } > + }; can likely do pledge("stdio proc exec") around here. > + > + while ((ch = getopt_long(argc, argv, "+k:s:h", longopts, NULL)) != -1) { > + switch (ch) { > + case 'k': > + do_second_kill = true; > + second_kill = parse_duration(optarg); > + break; > + case 's': > + killsig = parse_signal(optarg); > + break; > + case 0: > + break; > + case 'h': 'h' is pointless. > + default: > + usage(); > + break; > + } > + } > + > + argc -= optind; > + argv += optind; > + > + if (argc < 2) > + usage(); > + > + first_kill = parse_duration(argv[0]); > + argc--; > + argv++; > + > + if (!foreground) { > + pgid = setpgid(0,0); > + > + if (pgid == -1) > + err(EX_OSERR, "setpgid()"); > + } > + > + memset(&signals, 0, sizeof(signals)); > + sigemptyset(&signals.sa_mask); > + > + if (killsig != SIGKILL && killsig != SIGSTOP) > + signums[0] = killsig; > + > + for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i ++) > + sigaddset(&signals.sa_mask, signums[i]); > + > + signals.sa_handler = sig_handler; > + signals.sa_flags = SA_RESTART; > + > + for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i ++) { > + if (signums[i] != -1 && signums[i] != 0 && > + sigaction(signums[i], &signals, NULL) == -1) > + err(EX_OSERR, "sigaction()"); > + } > + > + signal(SIGTTIN, SIG_IGN); > + signal(SIGTTOU, SIG_IGN); > + > + pid = fork(); > + if (pid == -1) > + err(EX_OSERR, "fork()"); > + else if (pid == 0) { > + /* child process */ > + signal(SIGTTIN, SIG_DFL); > + signal(SIGTTOU, SIG_DFL); > + > + error = execvp(argv[0], argv); > + if (error == -1) > + err(EX_UNAVAILABLE, "exec()"); > + } > + can likely do pledge("stdio") around here. > + if (sigprocmask(SIG_BLOCK, &signals.sa_mask, NULL) == -1) > + err(EX_OSERR, "sigprocmask()"); > + > + /* parent continues here */ > + set_interval(first_kill); > + > + for (;;) { > + sigemptyset(&signals.sa_mask); > + sigsuspend(&signals.sa_mask); > + > + if (sig_chld) { > + sig_chld = 0; > + while (((cpid = wait(&status)) < 0) && errno == EINTR) > + continue; > + > + if (cpid == pid) { > + pstat = status; > + break; > + } > + } else if (sig_alrm) { > + sig_alrm = 0; > + > + timedout = true; > + if (!foreground) > + killpg(pgid, killsig); > + else > + kill(pid, killsig); > + > + if (do_second_kill) { > + set_interval(second_kill); > + second_kill = 0; > + sig_ign = killsig; > + killsig = SIGKILL; > + } else > + break; > + > + } else if (sig_term) { > + if (!foreground) > + killpg(pgid, killsig); > + else > + kill(pid, (int)sig_term); > + > + if (do_second_kill) { > + set_interval(second_kill); > + second_kill = 0; > + sig_ign = killsig; > + killsig = SIGKILL; > + } else > + break; > + } > + } > + > + while (cpid != pid && wait(&pstat) == -1) { > + if (errno != EINTR) > + err(EX_OSERR, "waitpid()"); > + } > + > + if (WEXITSTATUS(pstat)) > + pstat = WEXITSTATUS(pstat); > + else if(WIFSIGNALED(pstat)) > + pstat = 128 + WTERMSIG(pstat); > + > + if (timedout && !preserve) > + pstat = EXIT_TIMEOUT; > + > + return (pstat); > +} > I am surprised it is so long.