#include <unistd.h>
#include <signal.h>
#include "strerr.h"
#include "pathexec.h"
#include "iopause.h"
#include "taia.h"
#include "wait.h"
#include "sig.h"
#include "coe.h"
#include "ndelay.h"
#include "fd.h"
#include "buffer.h"
#include "error.h"
#include "sgetopt.h"
#include "scan.h"
/* defaults */
#define TIMEOUT 180
#define KTIMEOUT 5
#define TRYMAX 5
#define USAGE " [-vp] [-t seconds] [-k kseconds] [-n tries] prog"
#define WARNING "tryto: warning: "
#define FATAL "tryto: fatal: "
const char *progname;
int selfpipe[2];
int try =0;
void sig_child_handler(void) {
try++;
write(selfpipe[1], "", 1);
}
void usage () {
strerr_die4x(1, "usage: ", progname, USAGE, "\n");
}
int main (int argc, const char * const *argv, const char * const *envp) {
int opt;
struct taia now, deadline;
iopause_fd x[2];
int pid;
int rc =111;
unsigned long timeout =TIMEOUT;
unsigned long ktimeout =KTIMEOUT;
unsigned long trymax =TRYMAX;
int verbose =0;
char ch;
int processor =0;
unsigned int pgroup =0;
int cpipe[2];
progname =*argv;
while ((opt =getopt(argc,argv,"t:k:n:pPvV")) != opteof) {
switch(opt) {
case 'V':
strerr_warn1("$Id: tryto.c,v 1.9 2006/02/26 13:30:37 pape Exp $\n", 0);
case '?':
usage();
case 't':
scan_ulong(optarg, &timeout);
if (timeout <= 0) timeout =TIMEOUT;
break;
case 'k':
scan_ulong(optarg, &ktimeout);
if (ktimeout <= 0) ktimeout =KTIMEOUT;
break;
case 'n':
scan_ulong(optarg, &trymax);
break;
case 'p':
processor =1;
break;
case 'P':
pgroup =1;
break;
case 'v':
verbose =1;
break;
}
}
argv +=optind;
if (!*argv) usage();
/* create selfpipe */
if (pipe(selfpipe) == -1) {
strerr_die2sys(111, FATAL, "unable to create selfpipe: ");
}
coe(selfpipe[0]);
coe(selfpipe[1]);
ndelay_on(selfpipe[0]);
ndelay_on(selfpipe[1]);
ndelay_on(0);
if (processor) ndelay_on(4);
sig_block(sig_pipe);
sig_block(sig_child);
sig_catch(sig_child, sig_child_handler);
/* set timeout */
taia_now(&now);
taia_uint(&deadline, timeout);
taia_add(&deadline, &now, &deadline);
timeout =0;
for (;;) {
int iopausefds;
char buffer_x_space[BUFFER_INSIZE];
buffer buffer_x;
if (processor) {
buffer_init(&buffer_x, buffer_unixread, 4, buffer_x_space,
sizeof buffer_x_space);
} else {
buffer_init(&buffer_x, buffer_unixread, 0, buffer_x_space,
sizeof buffer_x_space);
}
/* start real processor */
if (pipe(cpipe) == -1) {
strerr_die2sys(111, FATAL, "unable to create pipe for child: ");
}
while ((pid =fork()) == -1) {
strerr_warn4(WARNING, "unable to fork for \"", *argv, "\" pausing: ",
&strerr_sys);
sleep(5);
}
if (!pid) {
/* child */
sig_unblock(sig_pipe);
sig_unblock(sig_child);
sig_uncatch(sig_child);
close(cpipe[1]);
fd_move(0, cpipe[0]);
if (processor) {
fd_move(2, 5);
close(4);
}
if (pgroup) setsid();
pathexec_run(*argv, argv, envp);
strerr_die2sys(111, FATAL, "unable to start child: ");
}
close(cpipe[0]);
x[0].fd =selfpipe[0];
x[0].events =IOPAUSE_READ;
if (processor) {
fd_move(2, 5);
x[1].fd =4;
} else {
x[1].fd =0;
}
x[1].events =IOPAUSE_READ;
iopausefds =2;
/* feed + watch child */
for (;;) {
int r;
int i;
char *s;
sig_unblock(sig_child);
iopause(x, iopausefds, &deadline, &now);
sig_block(sig_child);
while (read(selfpipe[0], &ch, 1) == 1) {}
taia_now(&now);
if ((timeout =taia_less(&deadline, &now))) break;
if (wait_nohang(&rc) == pid) break;
rc =111;
r = buffer_feed(&buffer_x);
if (r < 0) {
if ((errno == error_intr) || (errno == error_again)) continue;
}
if (r == 0) {
if (processor && (buffer_x.fd == 4)) {
x[1].fd =0;
buffer_init(&buffer_x, buffer_unixread, 0, buffer_x_space,
sizeof buffer_x_space);
continue;
}
if (iopausefds == 2) {
close(cpipe[1]);
iopausefds =1;
}
continue;
}
s =buffer_peek(&buffer_x);
i =write(cpipe[1], s, r);
if (i == -1) strerr_die2sys(111, FATAL, "unable to write to child: ");
if (i < r)
strerr_die2x(111, FATAL, "unable to write to child: partial write");
buffer_seek(&buffer_x, r);
}
close(cpipe[1]);
if (timeout) {
if (wait_nohang(&rc) == pid) break;
/* child not finished */
strerr_warn4(WARNING,
"child \"", *argv, "\" timed out. sending TERM...", 0);
kill(pgroup ? -pid : pid, SIGTERM);
/* ktimeout sec timeout */
taia_now(&now);
taia_uint(&deadline, ktimeout);
taia_add(&deadline, &now, &deadline);
ktimeout =0;
for (;;) {
sig_unblock(sig_child);
iopause(x, 1, &deadline, &now);
sig_block(sig_child);
while (read(selfpipe[0], &ch, 1) == 1) {}
if (wait_nohang(&rc) == pid) {
strerr_warn2(WARNING, "child terminated.", 0);
break;
}
rc =111;
taia_now(&now);
if ((ktimeout =taia_less(&deadline, &now))) break;
}
if (ktimeout) {
strerr_warn4(WARNING, "child \"", *argv,
"\" not terminated. sending KILL...", 0);
kill(pgroup ? -pid : pid, SIGKILL);
}
break;
}
if (rc == 0) break;
if (verbose) strerr_warn2(WARNING, "child crashed.", 0);
if (lseek(0, 0, SEEK_SET) != 0)
if (verbose) strerr_warn2(WARNING,
"unable to lseek fd 0: ", &strerr_sys);
if (try >= trymax) break;
sleep(1);
}
if (processor && (rc != 0)) {
for (;;) {
int r;
char *s;
r = buffer_feed(buffer_0);
if (r < 0) {
if ((errno == error_intr) || (errno == error_again)) continue;
}
if (r == 0) {
break;
}
s =buffer_peek(buffer_0);
buffer_putflush(buffer_1, s, r);
buffer_seek(buffer_0, r);
}
}
if (timeout) {
if (processor) strerr_die2x(0, FATAL, "child timed out, giving up.");
strerr_die2x(100, FATAL, "child timed out, giving up.");
}
if (try >= trymax) {
if (processor) strerr_die2x(0, FATAL, "child crashed, giving up.");
strerr_die2x(rc >> 8, FATAL, "child crashed, giving up.");
}
_exit(0);
}
syntax highlighted by Code2HTML, v. 0.9.1