#include "config.h"

#define PARSE_CMDLINE

unsigned long	warntime, killtime;
unsigned long	warnsig, killsig;
volatile int	fdone, falarm, fsig, sigcaught;
int		quiet;

static const char cvs_id[] =
"$Id: timelimit.c,v 1.8 2001/05/22 09:12:48 roam Exp $";

static struct {
	const char	*name, opt;
	unsigned long	*var;
} envopts[] = {
	{"KILLSIG",	'S',	&killsig},
	{"KILLTIME",	'T',	&killtime},
	{"WARNSIG",	's',	&warnsig},
	{"WARNTIME",	't',	&warntime},
	{NULL,		0,	NULL}
};

#ifndef HAVE_ERR
static void	err(int, const char *, ...);
static void	errx(int, const char *, ...);
#endif /* !HAVE_ERR */

static void	usage(void);

static void	init(int, char *[]);
static void	doit(char *[]);
static void	child(char *[]);
static void	terminated(const char *);

#ifndef HAVE_ERR
static void
err(int code, const char *fmt, ...) {
	va_list v;

	va_start(v, fmt);
	vfprintf(stderr, fmt, v);
	va_end(v);

	fprintf(stderr, ": %s\n", strerror(errno));
	exit(code);
}

static void
errx(int code, const char *fmt, ...) {
	va_list v;

	va_start(v, fmt);
	vfprintf(stderr, fmt, v);
	va_end(v);

	fprintf(stderr, "\n");
	exit(code);
}

static void
warn(const char *fmt, ...) {
	va_list v;

	va_start(v, fmt);
	vfprintf(stderr, fmt, v);
	va_end(v);

	fprintf(stderr, ": %s\n", strerror(errno));
}

static void
warnx(const char *fmt, ...) {
	va_list v;

	va_start(v, fmt);
	vfprintf(stderr, fmt, v);
	va_end(v);

	fprintf(stderr, "\n");
}
#endif /* !HAVE_ERR */

static void
usage(void) {
	errx(EX_USAGE, "usage: timelimit [-q] [-S ksig] [-s wsig] "
	    "[-T ktime] [-t wtime] command");
}

static unsigned long
atou_fatal(const char *s) {
	unsigned long v;
	const char *p;

	v = 0;
	for (p = s; (*p >= '0') && (*p <= '9'); p++)
		v = v * 10 + *p - '0';
	if (*p != '\0')
		usage();
	return (v);
}

static void
init(int argc, char *argv[]) {
#ifdef PARSE_CMDLINE
	int ch;
#endif
	int optset;
	unsigned i;
	char *s;
	
	/* defaults */
	quiet = 0;
	warnsig = SIGTERM;
	killsig = SIGKILL;
	warntime = 3600;
	killtime = 120;

	optset = 0;
	
	/* process environment variables first */
	for (i = 0; envopts[i].name != NULL; i++)
		if ((s = getenv(envopts[i].name)) != NULL) {
			*envopts[i].var = atou_fatal(s);
			optset = 1;
		}

#ifdef PARSE_CMDLINE
	while ((ch = getopt(argc, argv, "qS:s:T:t:")) != EOF) {
		switch (ch) {
			case 'q':
				quiet = 1;
				break;
			default:
				/* check if it's a recognized option */
				for (i = 0; envopts[i].name != NULL; i++)
					if (ch == envopts[i].opt) {
						*envopts[i].var =
						    atou_fatal(optarg);
						optset = 1;
						break;
					}
				if (envopts[i].name == NULL)
					usage();
		}
	}
#else
	optind = 1;
#endif

	if (!optset) /* && !quiet? */
		warnx("using defaults: warntime=%lu, warnsig=%lu, "
		    "killtime=%lu, killsig=%lu",
		    warntime, warnsig, killtime, killsig);

	argc -= optind;
	argv += optind;
	if (argc == 0)
		usage();

	/* sanity checks */
	if ((warntime == 0) || (killtime == 0))
		usage();
}

static void
sigchld(int sig __unused) {

	fdone = 1;
}

static void
sigalrm(int sig __unused) {

	falarm = 1;
}

static void
sighandler(int sig) {

	sigcaught = sig;
	fsig = 1;
}

static void
setsig_fatal(int sig, void (*handler)(int)) {
#ifdef HAVE_SIGACTION
	struct sigaction act;

	memset(&act, 0, sizeof(act));
	act.sa_handler = handler;
	act.sa_flags = 0;
#ifdef SA_NOCLDSTOP
	act.sa_flags |= SA_NOCLDSTOP;
#endif /* SA_NOCLDSTOP */
	if (sigaction(sig, &act, NULL) < 0)
		err(EX_OSERR, "setting signal handler for %d", sig);
#else  /* HAVE_SIGACTION */
	if (signal(sig, handler) == SIG_ERR)
		err(EX_OSERR, "setting signal handler for %d", sig);
#endif /* HAVE_SIGACTION */
}
    
static void
doit(char *argv[]) {
	pid_t pid;

	/* install signal handlers */
	fdone = falarm = fsig = sigcaught = 0;
	setsig_fatal(SIGALRM, sigalrm);
	setsig_fatal(SIGCHLD, sigchld);
	setsig_fatal(SIGTERM, sighandler);
	setsig_fatal(SIGHUP, sighandler);
	setsig_fatal(SIGINT, sighandler);
	setsig_fatal(SIGQUIT, sighandler);

	/* fork off the child process */
	if ((pid = fork()) < 0)
		err(EX_OSERR, "fork");
	if (pid == 0)
		child(argv);

	/* sleep for the allowed time */
	alarm(warntime);
	while (!(fdone || falarm || fsig))
		pause();
	alarm(0);

	/* send the warning signal */
	if (fdone)
		return;
	if (fsig)
		terminated("run");
	falarm = 0;
	if (!quiet)
		warnx("sending warning signal %lu", warnsig);
	kill(pid, (int) warnsig);

#ifndef HAVE_SIGACTION
	/* reset our signal handlers, just in case */
	setsig_fatal(SIGALRM, sigalrm);
	setsig_fatal(SIGCHLD, sigchld);
	setsig_fatal(SIGTERM, sighandler);
	setsig_fatal(SIGHUP, sighandler);
	setsig_fatal(SIGINT, sighandler);
	setsig_fatal(SIGQUIT, sighandler);
#endif /* HAVE_SIGACTION */

	/* sleep for the grace time */
	alarm(killtime);
	while (!(fdone || falarm || fsig))
		pause();
	alarm(0);

	/* send the kill signal */
	if (fdone)
		return;
	if (fsig)
		terminated("grace");
	if (!quiet)
		warnx("sending kill signal %lu", killsig);
	kill(pid, (int) killsig);
}

static void
terminated(const char *period) {

	errx(EX_SOFTWARE, "terminated by signal %d during the %s period",
	    sigcaught, period);
}

static void
child(char *argv[]) {

	execvp(argv[0], argv);
	err(EX_OSERR, "executing %s", argv[0]);
}

int
main(int argc, char *argv[]) {

	init(argc, argv);
	argc -= optind;
	argv += optind;
	doit(argv);
	return (EX_OK);
}


syntax highlighted by Code2HTML, v. 0.9.1