diff --git a/.gitignore b/.gitignore index 356ef5982a..38b61e99d7 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ doc/protocol-binary.txt /version.m4 /version.num /testapp +/timedrun /doc/doxy diff --git a/Makefile.am b/Makefile.am index 62fd1bf23b..76a6a03be3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,11 +1,13 @@ bin_PROGRAMS = memcached pkginclude_HEADERS = protocol_binary.h -noinst_PROGRAMS = memcached-debug sizes testapp +noinst_PROGRAMS = memcached-debug sizes testapp timedrun BUILT_SOURCES= testapp_SOURCES = testapp.c util.c util.h +timedrun_SOURCES = timedrun.c + memcached_SOURCES = memcached.c memcached.h \ hash.c hash.h \ slabs.c slabs.h \ diff --git a/t/lib/MemcachedTest.pm b/t/lib/MemcachedTest.pm index c95a4157c3..34d1085c6c 100644 --- a/t/lib/MemcachedTest.pm +++ b/t/lib/MemcachedTest.pm @@ -159,7 +159,7 @@ sub new_memcached { croak("memcached binary not executable\n") unless -x _; unless ($childpid) { - exec "$exe $args"; + exec "$builddir/timedrun 600 $exe $args"; exit; # never gets here. } diff --git a/testapp.c b/testapp.c index 7c3d3e1a65..c6b46c64b7 100644 --- a/testapp.c +++ b/testapp.c @@ -263,9 +263,13 @@ static pid_t start_server(in_port_t *port_out, bool daemon) { if (pid == 0) { /* Child */ - char *argv[10]; + char *argv[20]; int arg = 0; putenv(environment); + if (!daemon) { + argv[arg++] = "./timedrun"; + argv[arg++] = "15"; + } argv[arg++] = "./memcached-debug"; argv[arg++] = "-p"; argv[arg++] = "-1"; @@ -277,7 +281,7 @@ static pid_t start_server(in_port_t *port_out, bool daemon) { argv[arg++] = pid_file; } argv[arg++] = NULL; - assert(execv("./memcached-debug", argv) != -1); + assert(execv(argv[0], argv) != -1); } /* Yeah just let us "busy-wait" for the file to be created ;-) */ @@ -447,6 +451,7 @@ int main(int argc, char **argv) for (ii = 0; testcases[ii].description != NULL; ++ii) { fflush(stdout); + alarm(60); enum test_return ret = testcases[ii].function(); if (ret == TEST_SKIP) { fprintf(stdout, "ok # SKIP %d - %s\n", ii + 1, testcases[ii].description); diff --git a/timedrun.c b/timedrun.c new file mode 100644 index 0000000000..5c93ed7516 --- /dev/null +++ b/timedrun.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include + +#include + +static int caught = 0; + +static void caught_signal(int which) +{ + caught = which; +} + +static int wait_for_process(pid_t pid) +{ + int rv = EX_SOFTWARE; + int stats = 0; + int i = 0; + struct sigaction sig_handler; + + sig_handler.sa_handler = caught_signal; + sig_handler.sa_flags = 0; + + sigaction(SIGALRM, &sig_handler, NULL); + sigaction(SIGHUP, &sig_handler, NULL); + sigaction(SIGINT, &sig_handler, NULL); + sigaction(SIGTERM, &sig_handler, NULL); + sigaction(SIGPIPE, &sig_handler, NULL); + + /* Loop forever waiting for the process to quit */ + for (i = 0; ;i++) { + pid_t p = waitpid(pid, &stats, 0); + if (p == pid) { + /* child exited. Let's get out of here */ + rv = WIFEXITED(stats) ? + WEXITSTATUS(stats) : + (0x80 | WTERMSIG(stats)); + break; + } else { + int sig = 0; + switch (i) { + case 0: + /* On the first iteration, pass the signal through */ + sig = caught > 0 ? caught : SIGTERM; + break; + case 1: + sig = SIGTERM; + break; + default: + sig = SIGKILL; + break; + } + if (kill(pid, sig) < 0) { + /* Kill failed. Must have lost the process. :/ */ + perror("lost child when trying to kill"); + } + /* Wait up to 5 seconds for the pid */ + alarm(5); + } + } + return rv; +} + +static int spawn_and_wait(int argc, char **argv) +{ + int rv = EX_SOFTWARE; + pid_t pid = fork(); + + assert(argc > 1); + + switch (pid) { + case -1: + perror("fork"); + rv = EX_OSERR; + break; /* NOTREACHED */ + case 0: + execvp(argv[0], argv); + perror("exec"); + rv = EX_SOFTWARE; + break; /* NOTREACHED */ + default: + rv = wait_for_process(pid); + } + return rv; +} + +int main(int argc, char **argv) +{ + int naptime = 0; + assert(argc > 2); + + naptime = atoi(argv[1]); + assert(naptime > 0 && naptime < 1800); + + alarm(naptime); + + return spawn_and_wait(argc+2, argv+2); +}