X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Fpipecmd%2Fpipecmd.c;h=0090275b01554a6654b810c35518a21c59c2a0b5;hp=352677fac1c216240eb38252216f72584793221c;hb=HEAD;hpb=66de67ea48009a086f940288eacf9928515f3aec diff --git a/ccan/pipecmd/pipecmd.c b/ccan/pipecmd/pipecmd.c index 352677fa..0090275b 100644 --- a/ccan/pipecmd/pipecmd.c +++ b/ccan/pipecmd/pipecmd.c @@ -1,4 +1,5 @@ /* CC0 license (public domain) - see LICENSE file for details */ +#include #include #include #include @@ -6,6 +7,8 @@ #include #include +int pipecmd_preserve; + static char **gather_args(const char *arg0, va_list ap) { size_t n = 1; @@ -16,114 +19,188 @@ static char **gather_args(const char *arg0, va_list ap) arr[0] = (char *)arg0; while ((arr[n++] = va_arg(ap, char *)) != NULL) { - arr = realloc(arr, sizeof(char *) * (n + 1)); - if (!arr) + char **narr = realloc(arr, sizeof(char *) * (n + 1)); + if (!narr) { + free(arr); return NULL; + } + arr = narr; } return arr; } -pid_t pipecmdv(int *fd_fromchild, int *fd_tochild, const char *cmd, va_list ap) +pid_t pipecmdv(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild, + const char *cmd, va_list ap) +{ + char **arr = gather_args(cmd, ap); + pid_t ret; + + if (!arr) { + errno = ENOMEM; + return -1; + } + ret = pipecmdarr(fd_tochild, fd_fromchild, fd_errfromchild, arr); + free_noerr(arr); + return ret; +} + +pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild, + char *const *arr) { - int tochild[2], fromchild[2], execfail[2]; + int tochild[2], fromchild[2], errfromchild[2], execfail[2]; + /* fds for parent to close */ + int par_close[4], num_par_close = 0; + /* fds for child to close */ + int child_close[4], num_child_close = 0; pid_t childpid; int err; if (fd_tochild) { - if (pipe(tochild) != 0) + if (fd_tochild == &pipecmd_preserve) { + tochild[0] = STDIN_FILENO; + } else if (pipe(tochild) == 0) { + par_close[num_par_close++] = tochild[0]; + child_close[num_child_close++] = tochild[1]; + } else goto fail; } else { tochild[0] = open("/dev/null", O_RDONLY); if (tochild[0] < 0) goto fail; + par_close[num_par_close++] = tochild[0]; } if (fd_fromchild) { - if (pipe(fromchild) != 0) - goto close_tochild_fail; + if (fd_fromchild == &pipecmd_preserve) { + fromchild[1] = STDOUT_FILENO; + } else if (pipe(fromchild) == 0) { + par_close[num_par_close++] = fromchild[1]; + child_close[num_child_close++] = fromchild[0]; + } else + goto fail; } else { fromchild[1] = open("/dev/null", O_WRONLY); if (fromchild[1] < 0) - goto close_tochild_fail; + goto fail; + par_close[num_par_close++] = fromchild[1]; + } + if (fd_errfromchild) { + if (fd_errfromchild == &pipecmd_preserve) { + errfromchild[1] = STDERR_FILENO; + } else if (fd_errfromchild == fd_fromchild) { + errfromchild[0] = fromchild[0]; + errfromchild[1] = fromchild[1]; + } else if (pipe(errfromchild) == 0) { + par_close[num_par_close++] = errfromchild[1]; + child_close[num_child_close++] = errfromchild[0]; + } else + goto fail; + } else { + errfromchild[1] = open("/dev/null", O_WRONLY); + if (errfromchild[1] < 0) + goto fail; + par_close[num_par_close++] = errfromchild[1]; } + if (pipe(execfail) != 0) - goto close_fromchild_fail; + goto fail; + + par_close[num_par_close++] = execfail[1]; + child_close[num_child_close++] = execfail[0]; if (fcntl(execfail[1], F_SETFD, fcntl(execfail[1], F_GETFD) | FD_CLOEXEC) < 0) - goto close_execfail_fail; + goto fail; childpid = fork(); if (childpid < 0) - goto close_execfail_fail; + goto fail; if (childpid == 0) { - char **args = gather_args(cmd, ap); - - if (fd_tochild) - close(tochild[1]); - if (fd_fromchild) - close(fromchild[0]); - close(execfail[0]); + int i; + for (i = 0; i < num_child_close; i++) + close(child_close[i]); // Child runs command. - if (!args) - err = ENOMEM; - else { - if (tochild[0] != STDIN_FILENO) { - if (dup2(tochild[0], STDIN_FILENO) == -1) - goto child_errno_fail; - close(tochild[0]); - } - if (fromchild[1] != STDOUT_FILENO) { - if (dup2(fromchild[1], STDOUT_FILENO) == -1) - goto child_errno_fail; - close(fromchild[1]); - } - execvp(cmd, args); - child_errno_fail: - err = errno; + if (tochild[0] != STDIN_FILENO) { + if (dup2(tochild[0], STDIN_FILENO) == -1) + goto child_errno_fail; + close(tochild[0]); + } + if (fromchild[1] != STDOUT_FILENO) { + if (dup2(fromchild[1], STDOUT_FILENO) == -1) + goto child_errno_fail; + close(fromchild[1]); + } + if (fd_errfromchild && fd_errfromchild == fd_fromchild) { + if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) + goto child_errno_fail; + } else if (errfromchild[1] != STDERR_FILENO) { + if (dup2(errfromchild[1], STDERR_FILENO) == -1) + goto child_errno_fail; + close(errfromchild[1]); + } + + /* Map execfail[1] to fd 3. */ + if (execfail[1] != 3) { + if (dup2(execfail[1], 3) == -1) + goto child_errno_fail; + /* CLOEXEC is not shared by dup2, so copy the flags + * from execfail[1] to 3. + */ + if (fcntl(3, F_SETFD, fcntl(execfail[1], F_GETFD)) < 0) + goto child_errno_fail; + close(execfail[1]); + execfail[1] = 3; + } + + /* Make (fairly!) sure all other fds are closed. */ + closefrom(4); + + execvp(arr[0], arr); + + child_errno_fail: + err = errno; + /* Gcc's warn-unused-result fail. */ + if (write(execfail[1], &err, sizeof(err))) { + ; } - write(execfail[1], &err, sizeof(err)); exit(127); } - close(tochild[0]); - close(fromchild[1]); - close(execfail[1]); + int i; + for (i = 0; i < num_par_close; i++) + close(par_close[i]); + /* Child will close this without writing on successful exec. */ if (read(execfail[0], &err, sizeof(err)) == sizeof(err)) { + close(execfail[0]); waitpid(childpid, NULL, 0); errno = err; return -1; } - if (fd_tochild) + close(execfail[0]); + if (fd_tochild && fd_tochild != &pipecmd_preserve) *fd_tochild = tochild[1]; - if (fd_fromchild) + if (fd_fromchild && fd_fromchild != &pipecmd_preserve) *fd_fromchild = fromchild[0]; + if (fd_errfromchild && fd_errfromchild != &pipecmd_preserve) + *fd_errfromchild = errfromchild[0]; return childpid; -close_execfail_fail: - close_noerr(execfail[0]); - close_noerr(execfail[1]); -close_fromchild_fail: - if (fd_fromchild) - close_noerr(fromchild[0]); - close_noerr(fromchild[1]); -close_tochild_fail: - close_noerr(tochild[0]); - if (fd_tochild) - close_noerr(tochild[1]); fail: + for (i = 0; i < num_par_close; i++) + close_noerr(par_close[i]); return -1; } -pid_t pipecmd(int *fd_fromchild, int *fd_tochild, const char *cmd, ...) +pid_t pipecmd(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild, + const char *cmd, ...) { pid_t childpid; va_list ap; va_start(ap, cmd); - childpid = pipecmdv(fd_fromchild, fd_tochild, cmd, ap); + childpid = pipecmdv(fd_tochild, fd_fromchild, fd_errfromchild, cmd, ap); va_end(ap); return childpid;