1 /* CC0 license (public domain) - see LICENSE file for details */
2 #include <ccan/closefrom/closefrom.h>
3 #include <ccan/pipecmd/pipecmd.h>
4 #include <ccan/noerr/noerr.h>
12 static char **gather_args(const char *arg0, va_list ap)
15 char **arr = calloc(sizeof(char *), n + 1);
19 arr[0] = (char *)arg0;
21 while ((arr[n++] = va_arg(ap, char *)) != NULL) {
22 char **narr = realloc(arr, sizeof(char *) * (n + 1));
32 pid_t pipecmdv(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
33 const char *cmd, va_list ap)
35 char **arr = gather_args(cmd, ap);
42 ret = pipecmdarr(fd_tochild, fd_fromchild, fd_errfromchild, arr);
47 pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
50 int tochild[2], fromchild[2], errfromchild[2], execfail[2];
51 /* fds for parent to close */
52 int par_close[4], num_par_close = 0;
53 /* fds for child to close */
54 int child_close[4], num_child_close = 0;
59 if (fd_tochild == &pipecmd_preserve) {
60 tochild[0] = STDIN_FILENO;
61 } else if (pipe(tochild) == 0) {
62 par_close[num_par_close++] = tochild[0];
63 child_close[num_child_close++] = tochild[1];
67 tochild[0] = open("/dev/null", O_RDONLY);
70 par_close[num_par_close++] = tochild[0];
73 if (fd_fromchild == &pipecmd_preserve) {
74 fromchild[1] = STDOUT_FILENO;
75 } else if (pipe(fromchild) == 0) {
76 par_close[num_par_close++] = fromchild[1];
77 child_close[num_child_close++] = fromchild[0];
81 fromchild[1] = open("/dev/null", O_WRONLY);
84 par_close[num_par_close++] = fromchild[1];
86 if (fd_errfromchild) {
87 if (fd_errfromchild == &pipecmd_preserve) {
88 errfromchild[1] = STDERR_FILENO;
89 } else if (fd_errfromchild == fd_fromchild) {
90 errfromchild[0] = fromchild[0];
91 errfromchild[1] = fromchild[1];
92 } else if (pipe(errfromchild) == 0) {
93 par_close[num_par_close++] = errfromchild[1];
94 child_close[num_child_close++] = errfromchild[0];
98 errfromchild[1] = open("/dev/null", O_WRONLY);
99 if (errfromchild[1] < 0)
101 par_close[num_par_close++] = errfromchild[1];
104 if (pipe(execfail) != 0)
107 par_close[num_par_close++] = execfail[1];
108 child_close[num_child_close++] = execfail[0];
110 if (fcntl(execfail[1], F_SETFD, fcntl(execfail[1], F_GETFD)
120 for (i = 0; i < num_child_close; i++)
121 close(child_close[i]);
123 // Child runs command.
124 if (tochild[0] != STDIN_FILENO) {
125 if (dup2(tochild[0], STDIN_FILENO) == -1)
126 goto child_errno_fail;
129 if (fromchild[1] != STDOUT_FILENO) {
130 if (dup2(fromchild[1], STDOUT_FILENO) == -1)
131 goto child_errno_fail;
134 if (fd_errfromchild && fd_errfromchild == fd_fromchild) {
135 if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1)
136 goto child_errno_fail;
137 } else if (errfromchild[1] != STDERR_FILENO) {
138 if (dup2(errfromchild[1], STDERR_FILENO) == -1)
139 goto child_errno_fail;
140 close(errfromchild[1]);
143 /* Map execfail[1] to fd 3. */
144 if (execfail[1] != 3) {
145 if (dup2(execfail[1], 3) == -1)
146 goto child_errno_fail;
147 /* CLOEXEC is not shared by dup2, so copy the flags
148 * from execfail[1] to 3.
150 if (fcntl(3, F_SETFD, fcntl(execfail[1], F_GETFD)) < 0)
151 goto child_errno_fail;
156 /* Make (fairly!) sure all other fds are closed. */
163 /* Gcc's warn-unused-result fail. */
164 if (write(execfail[1], &err, sizeof(err))) {
171 for (i = 0; i < num_par_close; i++)
174 /* Child will close this without writing on successful exec. */
175 if (read(execfail[0], &err, sizeof(err)) == sizeof(err)) {
177 waitpid(childpid, NULL, 0);
182 if (fd_tochild && fd_tochild != &pipecmd_preserve)
183 *fd_tochild = tochild[1];
184 if (fd_fromchild && fd_fromchild != &pipecmd_preserve)
185 *fd_fromchild = fromchild[0];
186 if (fd_errfromchild && fd_errfromchild != &pipecmd_preserve)
187 *fd_errfromchild = errfromchild[0];
191 for (i = 0; i < num_par_close; i++)
192 close_noerr(par_close[i]);
196 pid_t pipecmd(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
197 const char *cmd, ...)
203 childpid = pipecmdv(fd_tochild, fd_fromchild, fd_errfromchild, cmd, ap);