| [7bdcac1] | 1 | #include <errno.h> | 
|---|
|  | 2 | #include <fcntl.h> | 
|---|
|  | 3 | #include <getopt.h> | 
|---|
|  | 4 | #include <signal.h> | 
|---|
|  | 5 | #include <stdbool.h> | 
|---|
|  | 6 | #include <stdio.h> | 
|---|
|  | 7 | #include <stdlib.h> | 
|---|
|  | 8 | #include <string.h> | 
|---|
|  | 9 | #include <sys/select.h> | 
|---|
|  | 10 | #include <sys/stat.h> | 
|---|
|  | 11 | #include <sys/types.h> | 
|---|
|  | 12 | #include <sys/wait.h> | 
|---|
|  | 13 | #include <unistd.h> | 
|---|
|  | 14 |  | 
|---|
|  | 15 | char * this_cmd = NULL; | 
|---|
|  | 16 |  | 
|---|
|  | 17 | void parse_args(int argc, char * argv[]); | 
|---|
|  | 18 | int run(); | 
|---|
|  | 19 | void printcmd(); | 
|---|
|  | 20 |  | 
|---|
|  | 21 | int main(int argc, char * argv[]) { | 
|---|
|  | 22 | parse_args(argc, argv); | 
|---|
|  | 23 |  | 
|---|
|  | 24 | int retcode = run(); | 
|---|
|  | 25 | if( !WIFEXITED(retcode) ) { | 
|---|
|  | 26 | printf("Child Error : %d ", retcode); printcmd(); | 
|---|
|  | 27 | return retcode; | 
|---|
|  | 28 | } | 
|---|
|  | 29 |  | 
|---|
|  | 30 | printf("Child exited normally "); printcmd(); | 
|---|
|  | 31 | return 0; | 
|---|
|  | 32 | } | 
|---|
|  | 33 |  | 
|---|
|  | 34 | void usage( FILE * out, int code ) { | 
|---|
|  | 35 | fprintf(out, "%s [OPTION] [--] N CMD\n", this_cmd); | 
|---|
|  | 36 | fprintf(out, "Run command, killing it if it doesn't print for more than 5 continuous seconds\n\n"); | 
|---|
|  | 37 | fprintf(out, "\t-h,--help\tprint this usage message\n"); | 
|---|
|  | 38 | exit(code); | 
|---|
|  | 39 | } | 
|---|
|  | 40 |  | 
|---|
|  | 41 | char ** cmd_to_run = NULL; | 
|---|
|  | 42 | pid_t child_pid = 0; | 
|---|
|  | 43 | int pipe_fds[2]; | 
|---|
|  | 44 |  | 
|---|
|  | 45 | void arg_error(void) { | 
|---|
|  | 46 | fprintf(stderr,"\n"); | 
|---|
|  | 47 | usage(stderr, 1); | 
|---|
|  | 48 | } | 
|---|
|  | 49 |  | 
|---|
|  | 50 | void parse_args(int argc, char * argv[]) { | 
|---|
|  | 51 | this_cmd = argv[0]; | 
|---|
|  | 52 |  | 
|---|
|  | 53 | enum { Help, }; | 
|---|
|  | 54 | static struct option long_opts[] = { | 
|---|
|  | 55 | { "help", no_argument, 0, Help }, | 
|---|
|  | 56 | { 0, 0, 0, 0 } | 
|---|
|  | 57 | }; // long_opts | 
|---|
|  | 58 | int long_index; | 
|---|
|  | 59 |  | 
|---|
|  | 60 | int c; | 
|---|
|  | 61 | while ( (c = getopt_long( argc, argv, "h", long_opts, &long_index)) != -1 ) { | 
|---|
|  | 62 | switch ( c ) { | 
|---|
|  | 63 | case Help: | 
|---|
|  | 64 | case 'h': | 
|---|
|  | 65 | usage(stdout, 0); | 
|---|
|  | 66 | break; | 
|---|
|  | 67 | default: | 
|---|
|  | 68 | arg_error(); | 
|---|
|  | 69 | break; | 
|---|
|  | 70 | } // switch | 
|---|
|  | 71 | } // while | 
|---|
|  | 72 |  | 
|---|
|  | 73 | if( argc < optind + 1 ) { | 
|---|
|  | 74 | fprintf(stderr, "Too few arguments\n"); | 
|---|
|  | 75 | arg_error(); | 
|---|
|  | 76 | } | 
|---|
|  | 77 |  | 
|---|
|  | 78 | cmd_to_run = argv + optind; | 
|---|
|  | 79 | } | 
|---|
|  | 80 |  | 
|---|
|  | 81 | static void exit_handler (__attribute__((unused)) int a, __attribute__((unused)) void * b) { | 
|---|
|  | 82 | close(pipe_fds[0]); | 
|---|
|  | 83 | if(child_pid != 0) kill(child_pid, SIGKILL); | 
|---|
|  | 84 | } | 
|---|
|  | 85 |  | 
|---|
|  | 86 | #define checked(call, ...) ({int ret = call(__VA_ARGS__); if (ret == -1) { perror(#call); exit(1); } ret;}) | 
|---|
|  | 87 |  | 
|---|
|  | 88 | void run_child(); | 
|---|
|  | 89 | void make_noblock(int fd); | 
|---|
|  | 90 | void sink(int fd); | 
|---|
|  | 91 | int waitfd(int fd); | 
|---|
|  | 92 |  | 
|---|
|  | 93 | int run() { | 
|---|
|  | 94 | on_exit(exit_handler, NULL); | 
|---|
|  | 95 | checked(pipe, pipe_fds); | 
|---|
|  | 96 |  | 
|---|
|  | 97 | printf("Watching command: "); printcmd(); | 
|---|
|  | 98 |  | 
|---|
|  | 99 | child_pid = checked(fork); | 
|---|
|  | 100 | if (child_pid == 0) { run_child(); } | 
|---|
|  | 101 |  | 
|---|
|  | 102 | close(pipe_fds[1]); | 
|---|
|  | 103 | make_noblock(pipe_fds[0]); | 
|---|
|  | 104 |  | 
|---|
|  | 105 | int status; | 
|---|
|  | 106 | while(waitpid(child_pid, &status, WNOHANG) == 0) { | 
|---|
|  | 107 | if(waitfd(pipe_fds[0]) == 0) { | 
|---|
|  | 108 | printf("Child Deadlocked "); printcmd(); | 
|---|
|  | 109 | exit(127); | 
|---|
|  | 110 | } | 
|---|
|  | 111 | sink(pipe_fds[0]); | 
|---|
|  | 112 | } | 
|---|
|  | 113 |  | 
|---|
|  | 114 | child_pid = 0; | 
|---|
|  | 115 | return status; | 
|---|
|  | 116 |  | 
|---|
|  | 117 | return 0; | 
|---|
|  | 118 | } | 
|---|
|  | 119 |  | 
|---|
|  | 120 | void make_noblock(int fd) { | 
|---|
|  | 121 | int flags = fcntl(fd, F_GETFL); | 
|---|
|  | 122 | flags |= O_NONBLOCK; | 
|---|
|  | 123 | fcntl(fd, F_SETFL, flags ); | 
|---|
|  | 124 | } | 
|---|
|  | 125 |  | 
|---|
|  | 126 | void sink(int fd) { | 
|---|
|  | 127 | char buff[100]; | 
|---|
|  | 128 | int len = 100; | 
|---|
|  | 129 | int rv; | 
|---|
|  | 130 | do { | 
|---|
|  | 131 | rv = read( fd, buff, len ); | 
|---|
|  | 132 | } | 
|---|
|  | 133 | while(rv > 0); | 
|---|
|  | 134 | } | 
|---|
|  | 135 |  | 
|---|
|  | 136 | int waitfd(int fd) { | 
|---|
|  | 137 | fd_set set; | 
|---|
|  | 138 | FD_ZERO(&set); | 
|---|
|  | 139 | FD_SET(fd, &set); | 
|---|
|  | 140 |  | 
|---|
|  | 141 | struct timeval timeout; | 
|---|
|  | 142 | timeout.tv_sec = 5; | 
|---|
|  | 143 | timeout.tv_usec = 0; | 
|---|
|  | 144 |  | 
|---|
|  | 145 | int rv = select(fd + 1, &set, NULL, NULL, &timeout); | 
|---|
|  | 146 | if(rv == -1) { | 
|---|
|  | 147 | perror("select\n"); | 
|---|
|  | 148 | exit(1); | 
|---|
|  | 149 | } | 
|---|
|  | 150 | return rv; | 
|---|
|  | 151 | } | 
|---|
|  | 152 |  | 
|---|
|  | 153 | void run_child() { | 
|---|
|  | 154 | /* This is the child process. */ | 
|---|
|  | 155 | while ((dup2(pipe_fds[1], STDOUT_FILENO) == -1) && (errno == EINTR)); | 
|---|
|  | 156 |  | 
|---|
|  | 157 | close(pipe_fds[1]); | 
|---|
|  | 158 | close(pipe_fds[0]); | 
|---|
|  | 159 |  | 
|---|
|  | 160 | execvp ( *cmd_to_run, cmd_to_run); | 
|---|
|  | 161 |  | 
|---|
|  | 162 | /* The execvp  function returns only if an error occurs.  */ | 
|---|
|  | 163 | fprintf(stderr, "an error occurred in execvp\n"); | 
|---|
|  | 164 | abort (); | 
|---|
|  | 165 | } | 
|---|
|  | 166 |  | 
|---|
|  | 167 | void printcmd() { | 
|---|
|  | 168 | if(!cmd_to_run) return; | 
|---|
|  | 169 | char ** cmd = cmd_to_run; | 
|---|
|  | 170 | while (*cmd) { | 
|---|
|  | 171 | printf("%s ", *cmd); | 
|---|
|  | 172 | cmd++; | 
|---|
|  | 173 | } | 
|---|
|  | 174 | printf("\n"); | 
|---|
|  | 175 | } | 
|---|