#include #include #include #include #include #include #include #include #include #include #include #include #define NUM_PROCS 2 struct proc { pid_t pid; char *cmd; char **argv; int _stdin; int _stdout; int _stderr; } procs[NUM_PROCS]; int fds[2]; volatile sig_atomic_t terminate = 0; volatile sig_atomic_t sendsignal = 0; void reap_all() { int i; for (i = 0; i < NUM_PROCS; i++) { if (procs[i].pid != -1) kill(procs[i].pid, SIGTERM); } sleep(5); for (i = 0; i < NUM_PROCS; i++) { if (procs[i].pid != -1) kill(procs[i].pid, SIGKILL); } while (wait(NULL) != -1 || errno == EINTR) ; } void fail(int exit, const char *msg, ...) { reap_all(); va_list args; va_start(args, msg); verr(exit, msg, args); va_end(args); // Not reached? } void failx(int exit, const char *msg, ...) { reap_all(); va_list args; va_start(args, msg); verrx(exit, msg, args); va_end(args); // Not reached? } void grow_argv(char ***argv, size_t *len) { #define GROW_SIZE 32 char **ret = realloc(*argv, sizeof(char *) * (*len + GROW_SIZE)); if (ret == NULL) err(1, "realloc"); *argv = ret; *len += GROW_SIZE; #undef GROW_SIZE } void gen_argv(struct proc *proc) { char *saveptr, *tmp, *cmd; char **argv = NULL, **head; size_t len = 0, used = 0; cmd = strdup(proc->cmd); if (cmd == NULL) err(1, "strdup"); grow_argv(&argv, &len); head = argv; tmp = strtok_r(cmd, " \t", &saveptr); while (tmp != NULL) { *(head++) = strdup(tmp); used++; if (used >= len) { grow_argv(&argv, &len); head = argv + used; } tmp = strtok_r(NULL, " \t", &saveptr); } *head = NULL; free(cmd); proc->argv = argv; } void run_cmd(struct proc *proc) { pid_t cpid; cpid = fork(); if (cpid == 0) { if (proc->_stdin != -1) { if (close(0) == -1) fail(1, "close"); if (dup2(proc->_stdin, 0) == -1) fail(1, "dup2"); } if (proc->_stdout != -1) { if (close(1) == -1) fail(1, "close"); if (dup2(proc->_stdout, 1) == -1) fail(1, "dup2"); } if (proc->_stderr != -1) { if (close(2) == -1) fail(1, "close"); if (dup2(proc->_stderr, 2) == -1) fail(1, "dup2"); } close(fds[0]); // These aren't very important if they fail close(fds[1]); execvp(proc->argv[0], proc->argv); perror("execvp"); _exit(128); } else if (cpid == -1) { fail(1, "fork"); } else { proc->pid = cpid; } } int have_children() { int i; for (i = 0; i < NUM_PROCS; i++) { if (procs[i].pid != -1) return 1; } return 0; } void signal_procs() { if (terminate == 1) { if (procs[0].pid != -1) kill(procs[0].pid, sendsignal); else kill(procs[1].pid, sendsignal); } else { int i; for (i = 0; i < NUM_PROCS; i++) kill(procs[i].pid, sendsignal); } sendsignal = 0; } void handle_nonfatal(int sig, siginfo_t *siginfo, void *ucontext) { sendsignal = sig; } void handle_fatal(int sig, siginfo_t *siginfo, void *ucontext) { terminate = 1; sendsignal = sig; } void setup_signals() { struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_sigaction = &handle_fatal; act.sa_flags = SA_SIGINFO; if (sigaction(SIGINT, &act, NULL) == -1) err(1, "sigaction"); if (sigaction(SIGHUP, &act, NULL) == -1) err(1, "sigaction"); if (sigaction(SIGTERM, &act, NULL) == -1) err(1, "sigaction"); act.sa_sigaction = &handle_nonfatal; if (sigaction(SIGUSR1, &act, NULL) == -1) err(1, "sigaction"); if (sigaction(SIGUSR2, &act, NULL) == -1) err(1, "sigaction"); } void start_missing_procs() { int i; for (i = NUM_PROCS - 1; i >= 0; i--) { if (procs[i].pid == -1) run_cmd(&procs[i]); } } void print_argv() { int i; for (i = 0; i < NUM_PROCS; i++) { char **tmp = procs[i].argv; while (*tmp) printf("%s ", *tmp++); printf("\n"); } } int have_nonstarted() { return 0; } int main(int argc, char **argv) { int i; if (argc < 3) { fprintf(stderr, "Usage: piper \n"); return 1; } setup_signals(); for (i = 0; i < NUM_PROCS; i++) { procs[i].cmd = strdup(argv[i + 1]); if (procs[i].cmd == NULL) err(1, "strdup"); procs[i].pid = -1; procs[i]._stdin = -1; procs[i]._stdout = -1; procs[i]._stderr = -1; gen_argv(&procs[i]); } if (pipe(fds) == -1) err(1, "pipe"); procs[0]._stdout = fds[1]; procs[0]._stderr = fds[1]; procs[1]._stdin = fds[0]; while (!terminate || have_children()) { pid_t pid; if (!terminate) start_missing_procs(); if (sendsignal != 0) signal_procs(); pid = waitpid(0, NULL, 0); if (pid == -1) { if (errno == EINTR) { continue; } else fail(1, "wait"); } else if (pid == procs[0].pid) { terminate = 1; //if (sendsignal == 0) // sendsignal = SIGTERM; close(fds[0]); close(fds[1]); procs[0].pid = -1; } else if (pid == procs[1].pid) { procs[1].pid = -1; } } return 0; }