#include #include #include #include #include #include #include #include #include #include #include #include #include char * this_cmd = NULL; void parse_args(int argc, char * argv[]); int run(); void printcmd(); int main(int argc, char * argv[]) { parse_args(argc, argv); int retcode = run(); if( !WIFEXITED(retcode) ) { printf("Child Error : %d ", retcode); printcmd(); return retcode; } printf("Child exited normally "); printcmd(); return 0; } void usage( FILE * out, int code ) { fprintf(out, "%s [OPTION] [--] N CMD\n", this_cmd); fprintf(out, "Run command, killing it if it doesn't print for more than 5 continuous seconds\n\n"); fprintf(out, "\t-h,--help\tprint this usage message\n"); exit(code); } char ** cmd_to_run = NULL; pid_t child_pid = 0; int pipe_fds[2]; void arg_error(void) { fprintf(stderr,"\n"); usage(stderr, 1); } void parse_args(int argc, char * argv[]) { this_cmd = argv[0]; enum { Help, }; static struct option long_opts[] = { { "help", no_argument, 0, Help }, { 0, 0, 0, 0 } }; // long_opts int long_index; int c; while ( (c = getopt_long( argc, argv, "h", long_opts, &long_index)) != -1 ) { switch ( c ) { case Help: case 'h': usage(stdout, 0); break; default: arg_error(); break; } // switch } // while if( argc < optind + 1 ) { fprintf(stderr, "Too few arguments\n"); arg_error(); } cmd_to_run = argv + optind; } static void exit_handler (__attribute__((unused)) int a, __attribute__((unused)) void * b) { close(pipe_fds[0]); if(child_pid != 0) kill(child_pid, SIGKILL); } #define checked(call, ...) ({int ret = call(__VA_ARGS__); if (ret == -1) { perror(#call); exit(1); } ret;}) void run_child(); void make_noblock(int fd); void sink(int fd); int waitfd(int fd); int run() { on_exit(exit_handler, NULL); checked(pipe, pipe_fds); printf("Watching command: "); printcmd(); child_pid = checked(fork); if (child_pid == 0) { run_child(); } close(pipe_fds[1]); make_noblock(pipe_fds[0]); int status; while(waitpid(child_pid, &status, WNOHANG) == 0) { if(waitfd(pipe_fds[0]) == 0) { printf("Child Deadlocked "); printcmd(); exit(127); } sink(pipe_fds[0]); } child_pid = 0; return status; return 0; } void make_noblock(int fd) { int flags = fcntl(fd, F_GETFL); flags |= O_NONBLOCK; fcntl(fd, F_SETFL, flags ); } void sink(int fd) { char buff[100]; int len = 100; int rv; do { rv = read( fd, buff, len ); } while(rv > 0); } int waitfd(int fd) { fd_set set; FD_ZERO(&set); FD_SET(fd, &set); struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec = 0; int rv = select(fd + 1, &set, NULL, NULL, &timeout); if(rv == -1) { perror("select\n"); exit(1); } return rv; } void run_child() { /* This is the child process. */ while ((dup2(pipe_fds[1], STDOUT_FILENO) == -1) && (errno == EINTR)); close(pipe_fds[1]); close(pipe_fds[0]); execvp ( *cmd_to_run, cmd_to_run); /* The execvp function returns only if an error occurs. */ fprintf(stderr, "an error occurred in execvp\n"); abort (); } void printcmd() { if(!cmd_to_run) return; char ** cmd = cmd_to_run; while (*cmd) { printf("%s ", *cmd); cmd++; } printf("\n"); }