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 /* This utility should compile on any POSIX-conformant implementation. */ 37 #define _POSIX_C_SOURCE 200112L 38 39 #include <sys/types.h> 40 #include <sys/wait.h> 41 #include <errno.h> 42 #include <pthread.h> 43 #include <signal.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 49 pid_t pid_to_monitor; 50 51 void sighandler(int sig) 52 { 53 if (0 < pid_to_monitor) { 54 if (kill(pid_to_monitor, SIGKILL) == -1) { 55 perror("kill(.., SIGKILL) failed"); 56 abort(); /* Something's really screwed up if we get here. */ 57 } 58 waitpid(pid_to_monitor, NULL, WNOHANG); 59 } 60 exit(SIGALRM + 128); 61 } 62 63 int main(int argc, char *argv[]) 64 { 65 int status, timeout; 66 67 /* Special case: t0 0 */ 68 if (argc == 2 && (strncmp(argv[1], "0", 1) == 0)) { 69 kill(getpid(), SIGALRM); 70 exit(1); 71 } 72 73 /* General case */ 74 if (argc < 3) { 75 printf("\nUsage: \n"); 76 printf(" $ %s n exe arglist\n", argv[0]); 77 printf(" $ %s 0\n", argv[0]); 78 printf("\nWhere:\n"); 79 printf(" n is the timeout duration in seconds,\n"); 80 printf(" exe is the executable filename to run,\n"); 81 printf 82 (" arglist is the arguments to be passed to executable.\n\n"); 83 printf 84 (" The second use case will emulate an immediate timeout.\n\n"); 85 exit(1); 86 } 87 88 timeout = atoi(argv[1]); 89 if (timeout < 1) { 90 fprintf(stderr, 91 "Invalid timeout value \"%s\". Timeout must be a positive integer.\n", 92 argv[1]); 93 exit(1); 94 } 95 96 if (signal(SIGALRM, sighandler) == SIG_ERR) { 97 perror("signal failed"); 98 exit(1); 99 } 100 101 alarm(timeout); 102 103 switch (pid_to_monitor = fork()) { 104 case -1: 105 perror("fork failed"); 106 exit(1); 107 case 0: 108 setpgid(0, 0); 109 execvp(argv[2], &argv[2]); 110 perror("execvp failed"); 111 exit(1); 112 default: 113 114 for (;;) { 115 if (waitpid(pid_to_monitor, &status, 0) == 116 pid_to_monitor) 117 break; 118 else if (errno == EINTR) { 119 perror("waitpid failed"); 120 exit(1); 121 } 122 } 123 /* Relay the child's status back to run-tests.sh */ 124 if (WIFEXITED(status)) 125 exit(WEXITSTATUS(status)); 126 else if (WIFSIGNALED(status)) 127 exit(WTERMSIG(status) + 128); 128 129 } 130 131 exit(1); 132 } 133