#include #include #include #include #include #include #include struct timeout_command { unsigned int delay; char** argv; struct timeout_command* next; }; static volatile sig_atomic_t alarm_signal = 0; static void alarm_handler(int signal) { alarm_signal = 1; } int main(int argc, char** argv) { char* cmdname = *argv++; --argc; char* usage = "Usage: %s delimiter command " "delimiter { timeout command }\n"; if (argc <= 5) { /* we expect at least two delimiters, a command, a timeout and another command */ fprintf(stderr, usage, cmdname); exit(255); } /* find command enclosed in delimiters */ char* delimiter = *argv++; --argc; char** command = argv++; --argc; while (argc > 0 && strcmp(*argv, delimiter) != 0) { --argc; ++argv; } if (argc == 0) { fprintf(stderr, "%s: second delimiter, i.e. \"%s\" is missing\n", cmdname, delimiter); fprintf(stderr, usage, cmdname); exit(255); } /* replace delimiter by a null pointer and skip it */ *argv++ = 0; --argc; if (argc == 0) { fprintf(stderr, "%s: timeout command is missing\n", cmdname); fprintf(stderr, usage, cmdname); exit(255); } /* collect timeout commands */ struct timeout_command* head = 0; struct timeout_command* tail = 0; while (argc > 0) { char* timeout_string = *argv++; --argc; char* endptr; long int value = strtol(timeout_string, &endptr, 0); if (*endptr || value <= 0) { fprintf(stderr, "%s: invalid timeout value: %s\n", cmdname, timeout_string); fprintf(stderr, usage, cmdname); exit(255); } unsigned int timeout = value; if (argc == 0) { fprintf(stderr, "%s: timeout command is missing\n", cmdname); fprintf(stderr, usage, cmdname); exit(255); } char** tcommand = argv++; --argc; while (argc > 0 && strcmp(*argv, delimiter) != 0) { --argc; ++argv; } if (argc > 0) { /* replace delimiter by a null pointer and skip it */ *argv++ = 0; --argc; } struct timeout_command* cmd = malloc(sizeof(struct timeout_command)); if (!cmd) { perror("malloc"); exit(255); } cmd->delay = timeout; cmd->argv = tcommand; cmd->next = 0; if (tail) { tail->next = cmd; } else { head = cmd; } tail = cmd; } /* fork command */ pid_t pid = fork(); if (pid < 0) { perror("fork"); exit(255); } if (pid == 0) { execvp(command[0], command); perror(command[0]); exit(255); } /* substitute "%" arguments by process id */ size_t pidlen = snprintf(0, 0, "%d", (int) pid); char* pidstr = malloc(pidlen + 1); if (!pidstr) { perror("malloc"); exit(255); } snprintf(pidstr, pidlen + 1, "%d", (int) pid); for (struct timeout_command* tcmd = head; tcmd; tcmd = tcmd->next) { for (char** argp = tcmd->argv; *argp; ++argp) { if (strcmp(*argp, "%") == 0) { *argp = pidstr; } } } /* wait for the process and execute timeout commands */ int stat; pid_t child; do { struct timeout_command* cmd = 0; if (head) { cmd = head; head = head->next; if (signal(SIGALRM, alarm_handler) == SIG_ERR) { perror("alarm"); exit(255); } alarm_signal = 0; alarm(cmd->delay); } while ((child = wait(&stat)) > 0 && child != pid); if (child < 0) { if (errno == EINTR && alarm_signal) { /* execute timeout command */ pid_t timepid = fork(); if (timepid < 0) { perror("fork"); exit(255); } if (timepid == 0) { execvp(cmd->argv[0], cmd->argv); perror(cmd->argv[0]); exit(255); } } else { perror("wait"); exit(255); } } if (cmd) free(cmd); } while (child != pid); if (WIFEXITED(stat)) { exit(WEXITSTATUS(stat)); } else { exit(255); } /* release storage */ free(pidstr); struct timeout_command* tcmd = head; while (tcmd) { struct timeout_command* discard = tcmd; tcmd = tcmd->next; free(discard); } }