On Wed, Jul 16, 2014 at 09:55:36AM +0000, Baptiste Daroussin wrote:
> +#include <sys/types.h>
> +#include <sys/time.h>
> +#include <sys/wait.h>
> +#include <signal.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sysexits.h>
> +#include <unistd.h>
> +#include <getopt.h>
> +#include <err.h>
> +#include <spawn.h>
Is this header needed ?
The headers are unordered.

> +#include <errno.h>
> +#include <stdbool.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
> +usage(void)
> +{
There should be empty line after '{' if no local vars are declared.

> +     fprintf(stderr, "Usage: %s [--signal sig | -s sig] [--preserve-status]"
> +         " [--kill-after time | -k time] [--foreground] <duration> <command>"
> +         " <arg ...>\n", getprogname());
> +
> +     exit(EX_USAGE);
> +}
> +
> +static double
> +parse_duration(const char *duration)
> +{
> +     double ret;
> +     char *end;
> +
> +     ret = strtod(duration, &end);
> +     if (ret == 0 && end == duration)
> +             errx(EXIT_FAILURE, "invalid duration");
> +
> +     if (end == NULL || *end == '\0')
> +             return (ret);
> +
> +     if (end != NULL && *(end + 1) != '\0')
> +             errx(EX_USAGE, "invalid duration");
> +
> +     switch (*end) {
> +     case 's':
> +             break;
> +     case 'm':
> +             ret *= 60;
> +             break;
> +     case 'h':
> +             ret *= 60 * 60;
> +             break;
> +     case 'd':
> +             ret *= 60 * 60 * 24;
> +             break;
> +     default:
> +             errx(EX_USAGE, "invalid duration");
> +     }
> +     
> +     if (ret < 0 || ret >= 100000000UL)
> +             errx(EX_USAGE, "invalid duration");
> +
> +     return (ret);
> +}
> +
> +static int
> +parse_signal(const char *str)
> +{
> +     int sig, i;
> +     const char *err;
> +
> +     sig = strtonum(str, 0, sys_nsig, &err);
> +
> +     if (err == 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(EX_USAGE, "invalid signal");
> +}
> +
> +static void
> +sig_handler(int signo)
> +{
> +     if (sig_ign != 0 && signo == sig_ign) {
> +             sig_ign = 0;
> +             return;
> +     }
> +
> +     switch(signo) {
> +     case 0:
> +     case SIGINT:
> +     case SIGHUP:
> +     case SIGQUIT:
> +     case SIGTERM:
> +             sig_term = signo;
> +             break;
> +     case SIGCHLD:
> +             sig_chld = 1;
> +             break;
> +     case SIGALRM:
> +             sig_alrm = 1;
> +             break;
> +     }
> +}
> +
> +static void
> +set_interval(double iv)
> +{
> +     struct itimerval tim;
> +
> +     memset(&tim, 0, sizeof(tim));
> +     tim.it_value.tv_sec = (time_t)iv;
> +     iv -= (time_t)iv;
> +     tim.it_value.tv_usec = (suseconds_t)(iv * 1000000UL);
> +
> +     if (setitimer(ITIMER_REAL, &tim, NULL) == -1)
> +             err(EX_OSERR, "setitimer()");
> +}
> +
> +int
> +main(int argc, char **argv)
> +{
> +     int ch;
> +     unsigned long i;
> +     int foreground, preserve;
> +     int error, pstat, status;
> +     int killsig = SIGTERM;
> +     int killedwith;
> +     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,
> +     };
> +
> +     foreground = preserve = 0;
> +     second_kill = 0;
> +     cpid = -1;
> +
> +     struct option longopts[] = {
This should be static const.

> +             { "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 }
> +     };
> +
> +     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':
> +                     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]);
Calling sigaddset(..., -1); relies both on the quality of implementation,
and on absense of the implementation extensions, which could define
unexpected semantic for the undefined call.

> +
> +     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);
Shouldn't you preserve the signal disposition for all intercepted signals
and restore it for the child ?

> +
> +             error = execvp(argv[0], argv);
> +             if (error == -1)
> +                     err(EX_UNAVAILABLE, "exec()");
> +     }
> +
> +     if (sigprocmask(SIG_BLOCK, &signals.sa_mask, NULL) == -1)
> +             err(EX_OSERR, "sigprocmask()");
> +
> +     /* parent continues here */
> +     set_interval(first_kill);
> +
> +     sigemptyset(&signals.sa_mask);
This statement is not needed, you repeat it inside for() loop below.

> +
> +     for (;;) {
> +             killedwith = killsig;
> +             sigemptyset(&signals.sa_mask);
> +             sigsuspend(&signals.sa_mask);
> +
> +             if (sig_chld) {
> +                     sig_chld = 0;
> +                     while (((cpid = wait(&status)) < 0) && errno != EINTR)
> +                             continue;
Should the test be errno == EINTR instead ?

> +
> +                     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, 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)
There, the test looks correct.

> +                     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);
> +}

Attachment: pgpYWqvsLkfuL.pgp
Description: PGP signature

Reply via email to