svn commit: r268745 - in head/usr.bin: . timeout
Konstantin Belousov
kostikbel at gmail.com
Wed Jul 16 11:31:36 UTC 2014
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);
> +}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/svn-src-head/attachments/20140716/c8fc5337/attachment.sig>
More information about the svn-src-head
mailing list