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