/* * Run something, with a time limit * * invoked as: * * ./run [-] prog [args...] * * for example: * * ./run -10 gdb -x ./gdb.batch -batch ./p1 * * where - optionally specifies the time limit (default 30 seconds) * * R. Perry, Jan. 2003 * 28 Aug. 2003 - improved child wait status handling * 28 Aug. 2009 - more improved child wait status handling, using waitpid() now * June 2012 - updated for Linux * 12 Jan 2021 - always call cleanup(), to prevent persistent background processes */ #include #include #include #include #include #include #include #include static char *progname; /* my name */ static pid_t pid; /* child pid */ static int limit = 30; /* time limit */ void report_signal( const char *what, int sig) { const char *desc = ""; switch( sig) { case SIGHUP: desc = "SIGHUP - Hangup"; break; case SIGINT: desc = "SIGINT - Interrupt"; break; case SIGQUIT: desc = "SIGQUIT - Quit"; break; case SIGILL: desc = "SIGILL - Illegal instruction"; break; case SIGTRAP: desc = "SIGTRAP - Trace trap"; break; case SIGABRT: desc = "SIGABRT - Abort"; break; case SIGBUS: desc = "SIGBUS - BUS error"; break; case SIGFPE: desc = "SIGFPE - Floating-point exception"; break; case SIGKILL: desc = "SIGKILL - Kill"; break; case SIGUSR1: desc = "SIGUSR1 - User-defined signal 1"; break; case SIGSEGV: desc = "SIGSEGV - Segmentation violation"; break; case SIGUSR2: desc = "SIGUSR2 - User-defined signal 2"; break; case SIGPIPE: desc = "SIGPIPE - Broken pipe"; break; case SIGALRM: desc = "SIGALRM - Alarm clock"; break; case SIGTERM: desc = "SIGTERM - Termination"; break; case SIGSTKFLT: desc = "SIGSTKFLT - Stack fault"; break; case SIGCHLD: desc = "SIGCHLD - Child status has changed"; break; case SIGCONT: desc = "SIGCONT - Continue"; break; case SIGSTOP: desc = "SIGSTOP - Stop"; break; case SIGTSTP: desc = "SIGTSTP - Keyboard stop"; break; case SIGTTIN: desc = "SIGTTIN - Background read from tty"; break; case SIGTTOU: desc = "SIGTTOU - Background write to tty"; break; case SIGURG: desc = "SIGURG - Urgent condition on socket"; break; case SIGXCPU: desc = "SIGXCPU - CPU limit exceeded"; break; case SIGXFSZ: desc = "SIGXFSZ - File size limit exceeded"; break; case SIGVTALRM: desc = "SIGVTALRM - Virtual alarm clock"; break; case SIGPROF: desc = "SIGPROF - Profiling alarm clock"; break; case SIGWINCH: desc = "SIGWINCH - Window size change"; break; case SIGPOLL: desc = "SIGPOLL - Pollable event occurred"; break; case SIGPWR: desc = "SIGPWR - Power failure restart"; break; case SIGSYS: desc = "SIGSYS - Bad system call"; break; } fprintf( stderr, "\n%s by signal %d - %s\n", what, sig, desc); } void cleanup( int sig) { if( sig == SIGALRM) { fprintf( stderr, "\n---\n%s: time limit of %d seconds has been exceeded\n", progname, limit); fflush( stderr); } if( geteuid() != 0) kill( -1, SIGKILL); /* kill all, we better not be running as root! */ else kill( pid, SIGKILL); exit(1); } int main( int argc, char *argv[]) { /* get my name */ progname = strrchr( argv[0], '/'); progname = progname ? progname + 1 : argv[0]; /* check for - option */ if( argc > 1 && argv[1][0] == '-') { limit = atoi( argv[1]+1); --argc, ++argv; } /* check usage */ if( argc < 2) { fprintf( stderr, "Usage: %s [-] prog [args...]\n", progname); return 1; } /* reset errno */ errno = 0; /* try to fork */ if( (pid = fork()) < 0) { fprintf( stderr, "%s: fork: %s\n", progname, strerror(errno)); return 1; } if( pid == 0) /* child */ { execvp( argv[1], argv+1); /* should not return */ fprintf( stderr, "%s: execvp: %s: %s\n", progname, argv[1], strerror(errno)); fflush( stderr); _exit(0); } /* parent */ int status; pid_t wait_result; signal( SIGALRM, cleanup); /* set alarm */ alarm( limit); /* wait for child to exit */ wait_result = waitpid( pid, &status, WUNTRACED); alarm(0); /* cancel alarm */ if( wait_result == (pid_t) -1) { fprintf( stderr, "%s: pid = %d, wait_result = %d, error = %s\n", progname, (int) pid, (int) wait_result, strerror(errno)); fflush( stderr); cleanup(0); return 1; } /* wait_result == pid (of some child) */ if( WIFSTOPPED(status)) /* child has stopped */ { report_signal( "stopped", WSTOPSIG(status)); fflush( stderr); cleanup( WSTOPSIG(status)); return 1; } if( WIFSIGNALED(status)) /* child was terminated by a signal */ { report_signal( "terminated", WTERMSIG(status)); fflush( stderr); cleanup(0); return 1; } if( WIFEXITED(status)) /* child has exited */ { cleanup(0); return WEXITSTATUS(status); /* return child exit status */ } cleanup(0); /* shouldn't get here */ return 0; }