Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 16 Jul 2014 14:31:28 +0300
From:      Konstantin Belousov <kostikbel@gmail.com>
To:        Baptiste Daroussin <bapt@FreeBSD.org>
Cc:        svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org
Subject:   Re: svn commit: r268745 - in head/usr.bin: . timeout
Message-ID:  <20140716113128.GV93733@kib.kiev.ua>
In-Reply-To: <201407160955.s6G9taro084054@svn.freebsd.org>
References:  <201407160955.s6G9taro084054@svn.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help

--LhGscpP45fjEbabV
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

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 =3D 0;
> +static sig_atomic_t sig_term =3D 0;
> +static sig_atomic_t sig_alrm =3D 0;
> +static sig_atomic_t sig_ign =3D 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 =3D strtod(duration, &end);
> +	if (ret =3D=3D 0 && end =3D=3D duration)
> +		errx(EXIT_FAILURE, "invalid duration");
> +
> +	if (end =3D=3D NULL || *end =3D=3D '\0')
> +		return (ret);
> +
> +	if (end !=3D NULL && *(end + 1) !=3D '\0')
> +		errx(EX_USAGE, "invalid duration");
> +
> +	switch (*end) {
> +	case 's':
> +		break;
> +	case 'm':
> +		ret *=3D 60;
> +		break;
> +	case 'h':
> +		ret *=3D 60 * 60;
> +		break;
> +	case 'd':
> +		ret *=3D 60 * 60 * 24;
> +		break;
> +	default:
> +		errx(EX_USAGE, "invalid duration");
> +	}
> +=09
> +	if (ret < 0 || ret >=3D 100000000UL)
> +		errx(EX_USAGE, "invalid duration");
> +
> +	return (ret);
> +}
> +
> +static int
> +parse_signal(const char *str)
> +{
> +	int sig, i;
> +	const char *err;
> +
> +	sig =3D strtonum(str, 0, sys_nsig, &err);
> +
> +	if (err =3D=3D NULL)
> +		return (sig);
> +	if (strncasecmp(str, "SIG", 3) =3D=3D 0)
> +		str +=3D 3;
> +
> +	for (i =3D 1; i < sys_nsig; i++) {
> +		if (strcasecmp(str, sys_signame[i]) =3D=3D 0)
> +			return (i);
> +	}
> +=09
> +	errx(EX_USAGE, "invalid signal");
> +}
> +
> +static void
> +sig_handler(int signo)
> +{
> +	if (sig_ign !=3D 0 && signo =3D=3D sig_ign) {
> +		sig_ign =3D 0;
> +		return;
> +	}
> +
> +	switch(signo) {
> +	case 0:
> +	case SIGINT:
> +	case SIGHUP:
> +	case SIGQUIT:
> +	case SIGTERM:
> +		sig_term =3D signo;
> +		break;
> +	case SIGCHLD:
> +		sig_chld =3D 1;
> +		break;
> +	case SIGALRM:
> +		sig_alrm =3D 1;
> +		break;
> +	}
> +}
> +
> +static void
> +set_interval(double iv)
> +{
> +	struct itimerval tim;
> +
> +	memset(&tim, 0, sizeof(tim));
> +	tim.it_value.tv_sec =3D (time_t)iv;
> +	iv -=3D (time_t)iv;
> +	tim.it_value.tv_usec =3D (suseconds_t)(iv * 1000000UL);
> +
> +	if (setitimer(ITIMER_REAL, &tim, NULL) =3D=3D -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 =3D SIGTERM;
> +	int killedwith;
> +	pid_t pgid, pid, cpid;
> +	double first_kill;
> +	double second_kill;
> +	bool timedout =3D false;
> +	bool do_second_kill =3D false;
> +	struct sigaction signals;
> +	int signums[] =3D {
> +		-1,
> +		SIGTERM,
> +		SIGINT,
> +		SIGHUP,
> +		SIGCHLD,
> +		SIGALRM,
> +		SIGQUIT,
> +	};
> +
> +	foreground =3D preserve =3D 0;
> +	second_kill =3D 0;
> +	cpid =3D -1;
> +
> +	struct option longopts[] =3D {
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 =3D getopt_long(argc, argv, "+k:s:h", longopts, NULL)) !=3D =
-1) {
> +		switch (ch) {
> +			case 'k':
> +				do_second_kill =3D true;
> +				second_kill =3D parse_duration(optarg);
> +				break;
> +			case 's':
> +				killsig =3D parse_signal(optarg);
> +				break;
> +			case 0:
> +				break;
> +			case 'h':
> +			default:
> +				usage();
> +				break;
> +		}
> +	}
> +
> +	argc -=3D optind;
> +	argv +=3D optind;
> +
> +	if (argc < 2)
> +		usage();
> +
> +	first_kill =3D parse_duration(argv[0]);
> +	argc--;
> +	argv++;
> +
> +	if (!foreground) {
> +		pgid =3D setpgid(0,0);
> +
> +		if (pgid =3D=3D -1)
> +			err(EX_OSERR, "setpgid()");
> +	}
> +
> +	memset(&signals, 0, sizeof(signals));
> +	sigemptyset(&signals.sa_mask);
> +
> +	if (killsig !=3D SIGKILL && killsig !=3D SIGSTOP)
> +		signums[0] =3D killsig;
> +
> +	for (i =3D 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 =3D sig_handler;
> +	signals.sa_flags =3D SA_RESTART;
> +
> +	for (i =3D 0; i < sizeof(signums) / sizeof(signums[0]); i ++)
> +		if (signums[i] !=3D -1 && signums[i] !=3D 0 &&
> +		    sigaction(signums[i], &signals, NULL) =3D=3D -1)
> +			err(EX_OSERR, "sigaction()");
> +
> +	signal(SIGTTIN, SIG_IGN);
> +	signal(SIGTTOU, SIG_IGN);
> +
> +	pid =3D fork();
> +	if (pid =3D=3D -1)
> +		err(EX_OSERR, "fork()");
> +	else if (pid =3D=3D 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 =3D execvp(argv[0], argv);
> +		if (error =3D=3D -1)
> +			err(EX_UNAVAILABLE, "exec()");
> +	}
> +
> +	if (sigprocmask(SIG_BLOCK, &signals.sa_mask, NULL) =3D=3D -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 =3D killsig;
> +		sigemptyset(&signals.sa_mask);
> +		sigsuspend(&signals.sa_mask);
> +
> +		if (sig_chld) {
> +			sig_chld =3D 0;
> +			while (((cpid =3D wait(&status)) < 0) && errno !=3D EINTR)
> +				continue;
Should the test be errno =3D=3D EINTR instead ?

> +
> +			if (cpid =3D=3D pid) {
> +				pstat =3D status;
> +				break;
> +			}
> +		} else if (sig_alrm) {
> +			sig_alrm =3D 0;
> +
> +			timedout =3D true;
> +			if (!foreground)
> +				killpg(pgid, killsig);
> +			else
> +				kill(pid, killsig);
> +
> +			if (do_second_kill) {
> +				set_interval(second_kill);
> +				second_kill =3D 0;
> +				sig_ign =3D killsig;
> +				killsig =3D 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 =3D 0;
> +				sig_ign =3D killsig;
> +				killsig =3D SIGKILL;
> +			} else
> +				break;
> +		}
> +	}
> +
> +	while (cpid !=3D pid  && wait(&pstat) =3D=3D -1) {
> +		if (errno !=3D EINTR)
There, the test looks correct.

> +			err(EX_OSERR, "waitpid()");
> +	}
> +
> +	if (WEXITSTATUS(pstat))
> +		pstat =3D WEXITSTATUS(pstat);
> +	else if(WIFSIGNALED(pstat))
> +		pstat =3D 128 + WTERMSIG(pstat);
> +
> +	if (timedout && !preserve)
> +			pstat =3D EXIT_TIMEOUT;
> +
> +	return (pstat);
> +}

--LhGscpP45fjEbabV
Content-Type: application/pgp-signature

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBAgAGBQJTxmKPAAoJEJDCuSvBvK1BQvIP/RH+KuzDEEd53symNv5dELw8
Mne4wGbjm7rEDV6KXBcHo1cf/dhDk9xWJnlskd99A6ECmwcoGXBigwpVMJJoUE4C
wbz3KhEkmXoK1QiV+7S1XfFHsT1ltHZsglcL/senHN7+sP2L6hC7q00m+OBS1oOa
rOFcIxnnkd/k05fLe8x/b7MQVZ39xCyaHb4gKIwa7mK9QYwFxxHbksl8WM57Q/Ey
5HjtI1vdu8mJWH5AOKgu4rlm97n0v6DQxGOFtiMCM54dJJ12Os/P57FDepMaq6h6
ZULU7wf+zhZGPZ3OXpJGSJt7KZeVViyzNO2dHLD17MvG3i7msarH4VxMXZkgk4av
eIVLDzNFF4BLe9DKmdd6GQ/qyjtD7F5pU6MBxZuhT1LH3hOwFpbBbn8oL+JGlywU
UMb9QGC/wbbpanVC0ZzgLpJtp7eMF9MiFEkItVIJ88OeEyfWBoYy3ZgCCbPKziGg
rcA7JzMWI3TH2uNvlq9NY6zWq+I+pKI0LlRU/dGL8FOaGlSq+djNclSSL9XfEnSd
VbC/kM1KTiAiO7hvr087UFWX2Uu+iHraD2ZmOC3vE/kKJROQBAPuCOGH+O2vPtBs
mTOFmIpSlw+dkhP7JyWIv7TiacTfkI6fePcoK9BtVOEa57o2A/qzH4FPxZpAzJdT
61MkhlIPbie/pOAH1gWJ
=X3yl
-----END PGP SIGNATURE-----

--LhGscpP45fjEbabV--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20140716113128.GV93733>