#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