/* --- @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 ---------------------------------------------------*/
int main(int argc, char *const argv[])
{
- int signo = SIGTERM;
pid_t kid;
- struct timeval now, 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;
case 'v': version(stdout); exit(0);
case 'u': usage(stdout); exit(0);
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;
}
argc -= optind; argv += optind;
if ((f & F_BOGUS) || argc < 2) { usage(stderr); exit(253); }
- strtotime(argv[0], &tv);
+ strtotime(argv[0], &ta[taoff_sigwait].u.tv);
/* --- Get things set up --- */
/* --- Set up the timer --- */
- to.kid = kid;
- to.sig = signo;
- to.panic = 0;
gettimeofday(&now, 0);
- TV_ADD(&tv, &now, &tv);
- sel_addtimer(&sel, &to.t, &tv, timeout, &to);
+ to.kid = kid;
+ to.ta = ta;
+ to.ip = 0;
+ timeout(&now, &to);
/* --- Main @select@ loop */