Home | History | Annotate | Download | only in tools
      1 /*
      2  * Copyright (c) 2004, Bull S.A..  All rights reserved.
      3  * Created by: Sebastien Decugis
      4 
      5  * This program is free software; you can redistribute it and/or modify it
      6  * under the terms of version 2 of the GNU General Public License as
      7  * published by the Free Software Foundation.
      8  *
      9  * This program is distributed in the hope that it would be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     12  *
     13  * You should have received a copy of the GNU General Public License along
     14  * with this program; if not, write the Free Software Foundation, Inc.,
     15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     16 
     17  * This utility software allows to run any executable file with a timeout limit.
     18  * The syntax is:
     19  * $ ./t0 n exe arglist
     20  *  where n is the timeout duration in seconds,
     21  *        exe is the executable filename to run,
     22  *        arglist is the arguments to be passed to executable.
     23  *
     24  * The use of this utility is intended to be "transparent", which means
     25  * everything is as if
     26  * $ exe arglist
     27  *   had been called, and a call to "alarm(n)" had been added inside exe's main.
     28  *
     29  * SPECIAL CASE:
     30  * $ ./t0 0
     31  *  Here another arg is not required. This special case will return immediatly
     32  *  as if it has been timedout. This is usefull to check a timeout return code value.
     33  *
     34  */
     35 
     36 #include <sys/types.h>
     37 #include <sys/wait.h>
     38 #include <errno.h>
     39 #include <pthread.h>
     40 #include <signal.h>
     41 #include <stdio.h>
     42 #include <stdlib.h>
     43 #include <string.h>
     44 #include <unistd.h>
     45 
     46 pid_t pid_to_monitor;
     47 
     48 void sighandler(int sig)
     49 {
     50 	if (0 < pid_to_monitor) {
     51 		if (kill(pid_to_monitor, SIGKILL) == -1) {
     52 			perror("kill(.., SIGKILL) failed");
     53 			abort();	/* Something's really screwed up if we get here. */
     54 		}
     55 		waitpid(pid_to_monitor, NULL, WNOHANG);
     56 	}
     57 	exit(SIGALRM + 128);
     58 }
     59 
     60 int main(int argc, char *argv[])
     61 {
     62 	int status, timeout;
     63 
     64 	/* Special case: t0 0 */
     65 	if (argc == 2 && (strncmp(argv[1], "0", 1) == 0)) {
     66 		kill(getpid(), SIGALRM);
     67 		exit(1);
     68 	}
     69 
     70 	/* General case */
     71 	if (argc < 3) {
     72 		printf("\nUsage: \n");
     73 		printf("  $ %s n exe arglist\n", argv[0]);
     74 		printf("  $ %s 0\n", argv[0]);
     75 		printf("\nWhere:\n");
     76 		printf("  n       is the timeout duration in seconds,\n");
     77 		printf("  exe     is the executable filename to run,\n");
     78 		printf
     79 		    ("  arglist is the arguments to be passed to executable.\n\n");
     80 		printf
     81 		    ("  The second use case will emulate an immediate timeout.\n\n");
     82 		exit(1);
     83 	}
     84 
     85 	timeout = atoi(argv[1]);
     86 	if (timeout < 1) {
     87 		fprintf(stderr,
     88 			"Invalid timeout value \"%s\". Timeout must be a positive integer.\n",
     89 			argv[1]);
     90 		exit(1);
     91 	}
     92 
     93 	if (signal(SIGALRM, sighandler) == SIG_ERR) {
     94 		perror("signal failed");
     95 		exit(1);
     96 	}
     97 
     98 	alarm(timeout);
     99 
    100 	switch (pid_to_monitor = fork()) {
    101 	case -1:
    102 		perror("fork failed");
    103 		exit(1);
    104 	case 0:
    105 		setpgid(0, 0);
    106 		execvp(argv[2], &argv[2]);
    107 		perror("execvp failed");
    108 		exit(1);
    109 	default:
    110 
    111 		for (;;) {
    112 			if (waitpid(pid_to_monitor, &status, 0) ==
    113 			    pid_to_monitor)
    114 				break;
    115 			else if (errno == EINTR) {
    116 				perror("waitpid failed");
    117 				exit(1);
    118 			}
    119 		}
    120 		/* Relay the child's status back to run-tests.sh */
    121 		if (WIFEXITED(status))
    122 			exit(WEXITSTATUS(status));
    123 		else if (WIFSIGNALED(status))
    124 			exit(WTERMSIG(status) + 128);
    125 
    126 	}
    127 
    128 	exit(1);
    129 }
    130