* exit(1);
* exit(0);
* }
- * child = pipecmd(&outputfd, NULL, NULL, argv[0], "ignoredarg", NULL);
+ * child = pipecmd(NULL, &outputfd, NULL, argv[0], "ignoredarg", NULL);
* if (child < 0)
* err(1, "Creating child");
* if (read(outputfd, input, sizeof(input)) != sizeof(input))
#include <unistd.h>
#include <fcntl.h>
+int pipecmd_preserve;
+
static char **gather_args(const char *arg0, va_list ap)
{
size_t n = 1;
return arr;
}
-pid_t pipecmdv(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild,
+pid_t pipecmdv(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
const char *cmd, va_list ap)
{
char **arr = gather_args(cmd, ap);
errno = ENOMEM;
return -1;
}
- ret = pipecmdarr(fd_fromchild, fd_tochild, fd_errfromchild, arr);
+ ret = pipecmdarr(fd_tochild, fd_fromchild, fd_errfromchild, arr);
free_noerr(arr);
return ret;
}
-pid_t pipecmdarr(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild,
+pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
char *const *arr)
{
int tochild[2], fromchild[2], errfromchild[2], execfail[2];
int err;
if (fd_tochild) {
- if (pipe(tochild) != 0)
+ if (fd_tochild == &pipecmd_preserve) {
+ tochild[0] = STDIN_FILENO;
+ fd_tochild = NULL;
+ } else if (pipe(tochild) != 0)
goto fail;
} else {
tochild[0] = open("/dev/null", O_RDONLY);
goto fail;
}
if (fd_fromchild) {
- if (pipe(fromchild) != 0)
+ if (fd_fromchild == &pipecmd_preserve) {
+ fromchild[1] = STDOUT_FILENO;
+ fd_fromchild = NULL;
+ } else if (pipe(fromchild) != 0)
goto close_tochild_fail;
} else {
fromchild[1] = open("/dev/null", O_WRONLY);
goto close_tochild_fail;
}
if (fd_errfromchild) {
- if (fd_errfromchild == fd_fromchild) {
+ if (fd_errfromchild == &pipecmd_preserve) {
+ errfromchild[1] = STDERR_FILENO;
+ fd_errfromchild = NULL;
+ } else if (fd_errfromchild == fd_fromchild) {
errfromchild[0] = fromchild[0];
errfromchild[1] = fromchild[1];
} else {
if (errfromchild[1] < 0)
goto close_fromchild_fail;
}
-
+
if (pipe(execfail) != 0)
goto close_errfromchild_fail;
return -1;
}
-pid_t pipecmd(int *fd_fromchild, int *fd_tochild, int *fd_errfromchild,
+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, fd_errfromchild, cmd, ap);
+ childpid = pipecmdv(fd_tochild, fd_fromchild, fd_errfromchild, cmd, ap);
va_end(ap);
return childpid;
* If @errfd is NULL, the child's stderr is (write-only) /dev/null.
*
* If @errfd == @outfd (and non-NULL) they will be shared.
+ * If @infd, @outfd or @errfd is &pipecmd_preserve, it is unchanged.
*
* The return value is the pid of the child, or -1.
*/
* @arr: NULL-terminated array for arguments (first is program to run).
*/
pid_t pipecmdarr(int *infd, int *outfd, int *errfd, char *const *arr);
+
+/**
+ * pipecmd_preserve - special value for fds to indicate it is unchanged
+ */
+extern int pipecmd_preserve;
+
#endif /* CCAN_PIPECMD_H */
/* This is how many tests you plan to run */
plan_tests(13);
- child = pipecmd(&outfd, NULL, NULL, argv[0], "out", NULL);
+ child = pipecmd(NULL, &outfd, NULL, argv[0], "out", NULL);
if (!ok1(child > 0))
exit(1);
ok1(read(outfd, buf, sizeof(buf)) == sizeof(buf));
--- /dev/null
+#include <ccan/pipecmd/pipecmd.h>
+/* Include the C files directly. */
+#include <ccan/pipecmd/pipecmd.c>
+#include <ccan/tap/tap.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+int main(int argc, char *argv[])
+{
+ pid_t child;
+ int fd, oldfd, status;
+ char buf[5] = "test";
+ char template[] = "/tmp/run-preserve.XXXXXX";
+
+ /* We call ourselves, to test pipe. */
+ if (argc == 2) {
+ if (strcmp(argv[1], "out") == 0) {
+ if (write(STDOUT_FILENO, buf, sizeof(buf)) != sizeof(buf))
+ exit(2);
+ } else if (strcmp(argv[1], "in") == 0) {
+ if (read(STDIN_FILENO, buf, sizeof(buf)) != sizeof(buf))
+ exit(3);
+ if (memcmp(buf, "test", sizeof(buf)) != 0)
+ exit(4);
+ } else if (strcmp(argv[1], "err") == 0) {
+ if (write(STDERR_FILENO, buf, sizeof(buf)) != sizeof(buf))
+ exit(5);
+ } else
+ abort();
+ exit(0);
+ }
+
+ /* This is how many tests you plan to run */
+ plan_tests(25);
+
+ /* Preserve stdin test. */
+ fd = mkstemp(template);
+ ok1(write(fd, buf, sizeof(buf)) == sizeof(buf));
+ ok1(fd >= 0);
+ ok1(dup2(fd, STDIN_FILENO) == STDIN_FILENO);
+ ok1(lseek(STDIN_FILENO, 0, SEEK_SET) == 0);
+ child = pipecmd(&pipecmd_preserve, NULL, NULL, argv[0], "in", NULL);
+ if (!ok1(child > 0))
+ exit(1);
+ ok1(waitpid(child, &status, 0) == child);
+ ok1(WIFEXITED(status));
+ ok1(WEXITSTATUS(status) == 0);
+
+ close(STDIN_FILENO);
+
+ /* Preserve stdout test */
+ fd = open(template, O_WRONLY|O_TRUNC);
+ ok1(fd >= 0);
+ oldfd = dup(STDOUT_FILENO);
+ /* Can't use OK after this, since we mug stdout */
+ if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO)
+ exit(1);
+ child = pipecmd(NULL, &pipecmd_preserve, NULL, argv[0], "out", NULL);
+ if (child == -1)
+ exit(1);
+ /* Restore stdout */
+ dup2(oldfd, STDOUT_FILENO);
+ close(oldfd);
+ ok1(waitpid(child, &status, 0) == child);
+ ok1(WIFEXITED(status));
+ ok1(WEXITSTATUS(status) == 0);
+
+ fd = open(template, O_RDONLY);
+ ok1(read(fd, buf, sizeof(buf)) == sizeof(buf));
+ ok1(close(fd) == 0);
+ ok1(memcmp(buf, "test", sizeof(buf)) == 0);
+
+ /* Preserve stderr test. */
+ fd = open(template, O_WRONLY|O_TRUNC);
+ ok1(fd >= 0);
+ oldfd = dup(STDERR_FILENO);
+ ok1(dup2(fd, STDERR_FILENO) == STDERR_FILENO);
+ child = pipecmd(NULL, NULL, &pipecmd_preserve, argv[0], "err", NULL);
+ if (!ok1(child > 0))
+ exit(1);
+
+ /* Restore stderr. */
+ ok1(dup2(oldfd, STDERR_FILENO));
+ ok1(waitpid(child, &status, 0) == child);
+ ok1(WIFEXITED(status));
+ ok1(WEXITSTATUS(status) == 0);
+ close(oldfd);
+
+ fd = open(template, O_RDONLY);
+ ok1(read(fd, buf, sizeof(buf)) == sizeof(buf));
+ ok1(close(fd) == 0);
+ ok1(memcmp(buf, "test", sizeof(buf)) == 0);
+ unlink(template);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}
/* This is how many tests you plan to run */
plan_tests(67);
- child = pipecmd(&outfd, &infd, &errfd, argv[0], "inout", NULL);
+ child = pipecmd(&infd, &outfd, &errfd, argv[0], "inout", NULL);
if (!ok1(child > 0))
exit(1);
ok1(write(infd, buf, sizeof(buf)) == sizeof(buf));
ok1(WIFEXITED(status));
ok1(WEXITSTATUS(status) == 0);
- child = pipecmd(NULL, &infd, NULL, argv[0], "in", NULL);
+ child = pipecmd(&infd, NULL, NULL, argv[0], "in", NULL);
if (!ok1(child > 0))
exit(1);
ok1(write(infd, buf, sizeof(buf)) == sizeof(buf));
ok1(WIFEXITED(status));
ok1(WEXITSTATUS(status) == 0);
- child = pipecmd(&outfd, NULL, NULL, argv[0], "out", NULL);
+ child = pipecmd(NULL, &outfd, NULL, argv[0], "out", NULL);
if (!ok1(child > 0))
exit(1);
ok1(read(outfd, buf, sizeof(buf)) == sizeof(buf));
ok1(WEXITSTATUS(status) == 0);
/* errfd == outfd should work with both. */
- child = pipecmd(&errfd, NULL, &errfd, argv[0], "err", NULL);
+ child = pipecmd(NULL, &errfd, &errfd, argv[0], "err", NULL);
if (!ok1(child > 0))
exit(1);
ok1(read(errfd, buf, sizeof(buf)) == sizeof(buf));
ok1(WIFEXITED(status));
ok1(WEXITSTATUS(status) == 0);
- child = pipecmd(&outfd, NULL, &outfd, argv[0], "out", NULL);
+ child = pipecmd(NULL, &outfd, &outfd, argv[0], "out", NULL);
if (!ok1(child > 0))
exit(1);
ok1(read(outfd, buf, sizeof(buf)) == sizeof(buf));