X-Git-Url: https://git.distorted.org.uk/~mdw/misc/blobdiff_plain/e33fa75597994805119c4aafecc5d6ef5e9d2939..7bba3c9c3dcd1a959dde4531e8e4a8b3ec0119da:/mtimeout.c diff --git a/mtimeout.c b/mtimeout.c index 1bb4848..9b22fb7 100644 --- a/mtimeout.c +++ b/mtimeout.c @@ -50,6 +50,7 @@ #include #include +#include #include #include #include @@ -250,6 +251,39 @@ static int namesig(const char *p) else return (-1); } +/* --- @strtotime@ --- * + * + * Arguments: @const char *p@ = pointer to string + * @struct timeval *tv@ = where to put the result + * + * Returns: --- + * + * Use: Converts a string representation of a duration into an + * internal version. Understands various time units. + */ + +static void strtotime(const char *p, struct timeval *tv) +{ + char *q = (/*unconst*/ char *)p; + double t, i, f; + + while (isspace((unsigned char)*q)) q++; + t = strtod(q, &q); + while (isspace((unsigned char)*q)) q++; + switch (*q) { + case 'd': case 'D': t *= 24; + case 'h': case 'H': t *= 60; + case 'm': case 'M': t *= 60; + case 's': case 'S': + q++; + while (isspace((unsigned char)*q)) q++; + } + if (*q) die(253, "bad time value `%s'", p); + f = modf(t, &i); + tv->tv_sec = i; + tv->tv_usec = f * 1000000; +} + /*----- Help functions ----------------------------------------------------*/ static void usage(FILE *fp) @@ -280,42 +314,73 @@ Options:\n\ /* --- @timeout@ --- * * - * The first time, we send the signal requested by the caller. Then we wait - * five seconds for the child to die, and send @SIGKILL@. If that still - * doesn't help, then we just give up. It's not like there's anything else - * we can do which is likely to help. And it's not like the process is going - * to be doing anything else in user mode ever again. + * The timeout sequencing stuff is complicated, so here's a simple machine to + * make it work. */ +enum { + TA_MOAN, /* Report message to user */ +#define TAARG_MOAN s + + TA_KILL, /* Send a signal */ +#define TAARG_KILL i + + TA_GOTO, /* Goto different state */ +#define TAARG_GOTO i + + TA_WAIT, /* Wait for more time */ +#define TAARG_WAIT tv + + TA_STATE, /* Alter the internal state */ +#define TAARG_STATE i +}; + +struct tmact { + unsigned act; + union { + const char *s; /* String parameter */ + int i; /* Integer parameter */ + struct timeval tv; /* Time parameter */ + } u; +}; + struct timeout { - sel_timer t; - int panic; - int sig; + sel_timer t; /* Timer intrusion */ + struct tmact *ta; /* Instruction vector */ + int ip; /* Instruction pointer */ pid_t kid; }; static void timeout(struct timeval *now, void *p) { struct timeout *t = p; + struct tmact *ta; struct timeval tv; - switch (t->panic) { - case 0: - moan("timed out: killing child process"); - kill(-t->kid, t->sig); - break; - case 1: - moan("child hasn't responded: killing harder"); - kill(-t->kid, SIGKILL); - break; - case 2: - moan("child still undead: giving up"); - state = ST_ABORT; - break; + for (;;) { + ta = &t->ta[t->ip++]; + switch (ta->act) { + case TA_MOAN: + moan(ta->u.s); + break; + case TA_KILL: + kill(-t->kid, ta->u.i); + break; + case TA_GOTO: + t->ip = ta->u.i; + break; + case TA_STATE: + state = ta->u.i; + return; + case TA_WAIT: + TV_ADD(&tv, now, &ta->u.tv); + sel_addtimer(&sel, &t->t, &tv, timeout, t); + return; + default: + moan("unexpected tmact %u", ta->act); + abort(); + } } - TV_ADDL(&tv, now, 5, 0); - sel_addtimer(&sel, &t->t, &tv, timeout, t); - t->panic++; } /*----- Signal handling ---------------------------------------------------*/ @@ -369,12 +434,35 @@ static void sigpropagate(int sig, void *p) int main(int argc, char *const argv[]) { - char *p; - double t; - int signo = SIGTERM; pid_t kid; - struct timeval tv; struct timeout to; + struct timeval now; + +#define PAIR(x, y) { x, y } +#define TACODE(I) \ + I(sigwait, WAIT, PAIR(0, 0)) \ + I(_a, MOAN, "timed out: killing child process") \ + I(sig, KILL, SIGTERM) \ + I(killwait, WAIT, PAIR(5, 0)) \ + I(_b, MOAN, "child hasn't responded: killing harder") \ + I(_c, KILL, SIGKILL) \ + I(boredwait, WAIT, PAIR(5, 0)) \ + I(_d, MOAN, "child still undead: giving up") \ + I(_e, STATE, ST_ABORT) + + enum { +#define TALBL(label, op, arg) taoff_##label, + TACODE(TALBL) +#undef TALBL + taoff_end + }; + + static struct tmact ta[] = { +#define TAASM(label, op, arg) { TA_##op, { .TAARG_##op = arg } }, + TACODE(TAASM) +#undef TAASM + }; + struct sigchld sc; sig sig_CHLD; #define DEFSIG(tag) sig sig_##tag; @@ -392,18 +480,33 @@ int main(int argc, char *const argv[]) { "help", 0, 0, 'h' }, { "version", 0, 0, 'v' }, { "usage", 0, 0, 'u' }, + { "no-kill", 0, 0, 'K' }, + { "kill-after", OPTF_ARGREQ, 0, 'k' }, + { "bored-after", OPTF_ARGREQ, 0, 'b' }, { "signal", OPTF_ARGREQ, 0, 's' }, { 0, 0, 0, 0 } }; - int i = mdwopt(argc, argv, "+hvus:", opts, 0, 0, 0); + int i = mdwopt(argc, argv, "+hvuKb:k:s:", opts, 0, 0, 0); if (i < 0) break; switch (i) { case 'h': help(stdout); exit(0); case 'v': version(stdout); exit(0); case 'u': usage(stdout); exit(0); + case 'b': + ta[taoff_boredwait].act = TA_WAIT; + strtotime(optarg, &ta[taoff_boredwait].u.tv); + break; + case 'k': + ta[taoff_killwait].act = TA_WAIT; + strtotime(optarg, &ta[taoff_killwait].u.tv); + break; + case 'K': + ta[taoff_killwait].act = TA_GOTO; + ta[taoff_killwait].u.i = taoff_boredwait; + break; case 's': - if ((signo = namesig(optarg)) < 0) + if ((ta[taoff_sig].u.i = namesig(optarg)) < 0) die(253, "bad signal spec `%s'", optarg); break; default: f |= F_BOGUS; break; @@ -411,12 +514,7 @@ int main(int argc, char *const argv[]) } argc -= optind; argv += optind; if ((f & F_BOGUS) || argc < 2) { usage(stderr); exit(253); } - - p = argv[0]; - while (isspace((unsigned char)*p)) p++; - t = strtod(argv[0], &p); - while (isspace((unsigned char)*p)) p++; - if (*p) die(253, "bad time value `%s'", argv[0]); + strtotime(argv[0], &ta[taoff_sigwait].u.tv); /* --- Get things set up --- */ @@ -449,12 +547,11 @@ int main(int argc, char *const argv[]) /* --- Set up the timer --- */ + gettimeofday(&now, 0); to.kid = kid; - to.sig = signo; - to.panic = 0; - gettimeofday(&tv, 0); - TV_ADDL(&tv, &tv, (time_t)t, ((long)(t * 1000000))%1000000); - sel_addtimer(&sel, &to.t, &tv, timeout, &to); + to.ta = ta; + to.ip = 0; + timeout(&now, &to); /* --- Main @select@ loop */