117 lines
		
	
	
	
		
			2.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			117 lines
		
	
	
	
		
			2.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <stdbool.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| #include <signal.h>
 | |
| #include <sys/wait.h>
 | |
| #include <setjmp.h>
 | |
| #include <ctype.h>
 | |
| #include "diagnostics.h"
 | |
| 
 | |
| static bool timed_out = false;
 | |
| static bool child_exited = false;
 | |
| static char* const* command = NULL;
 | |
| static int timeout = 0;
 | |
| static int pid = 0;
 | |
| 
 | |
| static void parse_arguments(int argc, char* const argv[])
 | |
| {
 | |
|     program_name = argv[0];
 | |
|     for (;;)
 | |
|     {
 | |
|         int c = getopt(argc, argv, "t:");
 | |
|         if (c == -1)
 | |
|             break;
 | |
| 
 | |
|         switch (c)
 | |
|         {
 | |
|             case 't':
 | |
|                 timeout = atoi(optarg);
 | |
|                 break;
 | |
| 
 | |
|             default:
 | |
|                 fatal("syntax: testrunner <timeout in secs> -- <command>\n");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     command = &argv[optind];
 | |
|     if (!command[0])
 | |
|         fatal("you must supply a command");
 | |
|     if (timeout <= 0)
 | |
|         fatal("timeout missing or invalid");
 | |
| }
 | |
| 
 | |
| static void sigalrm_cb(int sigraised)
 | |
| {
 | |
|     timed_out = true;
 | |
|     if (pid)
 | |
|         kill(pid, SIGKILL);
 | |
| }
 | |
| 
 | |
| int main(int argc, char* const argv[])
 | |
| {
 | |
|     const int READ = 0;
 | |
|     const int WRITE = 1;
 | |
| 
 | |
|     int fds[2];
 | |
|     FILE* childin;
 | |
|     int wstatus;
 | |
|     char buffer[4096];
 | |
|     char* p;
 | |
| 
 | |
|     parse_arguments(argc, argv);
 | |
| 
 | |
|     pipe(fds);
 | |
|     pid = fork();
 | |
|     if (pid == 0)
 | |
|     {
 | |
|         /* Child */
 | |
|         close(fds[READ]);
 | |
|         close(0);
 | |
|         dup2(fds[WRITE], 1);
 | |
|         dup2(fds[WRITE], 2);
 | |
|         execvp(command[0], command);
 | |
|         _exit(1);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         /* Parent */
 | |
|         close(fds[WRITE]);
 | |
|         signal(SIGALRM, sigalrm_cb);
 | |
|         alarm(timeout);
 | |
|     }
 | |
| 
 | |
|     childin = fdopen(fds[READ], "r");
 | |
|     if (!childin)
 | |
|         fatal("cannot open pipe");
 | |
| 
 | |
|     while (!timed_out)
 | |
|     {
 | |
|         if (!fgets(buffer, sizeof(buffer), childin))
 | |
|             break;
 | |
|         fputs(buffer, stdout);
 | |
| 
 | |
|         p = buffer;
 | |
|         while (isspace(*p))
 | |
|             p++;
 | |
| 
 | |
|         if (strcmp(p, "@@FINISHED\n") == 0)
 | |
|             break;
 | |
|         if (strcmp(p, "@@FINISHED\r\n") == 0)
 | |
|             break;
 | |
|     }
 | |
| 
 | |
|     /* Reached via EOF or seeing a @@FINISHED. */
 | |
| 
 | |
|     alarm(0);
 | |
| 
 | |
|     kill(pid, SIGKILL);
 | |
|     waitpid(pid, &wstatus, 0);
 | |
|     if (timed_out)
 | |
|     {
 | |
|         fprintf(stderr, "@@TIMEDOUT\n");
 | |
|         exit(1);
 | |
|     }
 | |
|     exit(WEXITSTATUS(wstatus));
 | |
| }
 |