]> git.ozlabs.org Git - ccan/blob - ccan/pipecmd/pipecmd.c
671d39198089b424f00e3f5041a7d79291ec55f8
[ccan] / ccan / pipecmd / pipecmd.c
1 /* CC0 license (public domain) - see LICENSE file for details */
2 #include <ccan/pipecmd/pipecmd.h>
3 #include <ccan/noerr/noerr.h>
4 #include <stdlib.h>
5 #include <errno.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8
9 static char **gather_args(const char *arg0, va_list ap)
10 {
11         size_t n = 1;
12         char **arr = calloc(sizeof(char *), n + 1);
13
14         if (!arr)
15                 return NULL;
16         arr[0] = (char *)arg0;
17
18         while ((arr[n++] = va_arg(ap, char *)) != NULL) {
19                 arr = realloc(arr, sizeof(char *) * (n + 1));
20                 if (!arr)
21                         return NULL;
22         }
23         return arr;
24 }
25
26 pid_t pipecmdv(int *fd_fromchild, int *fd_tochild, const char *cmd, va_list ap)
27 {
28         char **arr = gather_args(cmd, ap);
29         pid_t ret;
30
31         if (!arr) {
32                 errno = ENOMEM;
33                 return -1;
34         }
35         ret = pipecmdarr(fd_fromchild, fd_tochild, arr);
36         free_noerr(arr);
37         return ret;
38 }
39
40 pid_t pipecmdarr(int *fd_fromchild, int *fd_tochild, char *const *arr)
41 {
42         int tochild[2], fromchild[2], execfail[2];
43         pid_t childpid;
44         int err;
45
46         if (fd_tochild) {
47                 if (pipe(tochild) != 0)
48                         goto fail;
49         } else {
50                 tochild[0] = open("/dev/null", O_RDONLY);
51                 if (tochild[0] < 0)
52                         goto fail;
53         }
54         if (fd_fromchild) {
55                 if (pipe(fromchild) != 0)
56                         goto close_tochild_fail;
57         } else {
58                 fromchild[1] = open("/dev/null", O_WRONLY);
59                 if (fromchild[1] < 0)
60                         goto close_tochild_fail;
61         }
62         if (pipe(execfail) != 0)
63                 goto close_fromchild_fail;
64
65         if (fcntl(execfail[1], F_SETFD, fcntl(execfail[1], F_GETFD)
66                   | FD_CLOEXEC) < 0)
67                 goto close_execfail_fail;
68
69         childpid = fork();
70         if (childpid < 0)
71                 goto close_execfail_fail;
72
73         if (childpid == 0) {
74                 if (fd_tochild)
75                         close(tochild[1]);
76                 if (fd_fromchild)
77                         close(fromchild[0]);
78                 close(execfail[0]);
79
80                 // Child runs command.
81                 if (tochild[0] != STDIN_FILENO) {
82                         if (dup2(tochild[0], STDIN_FILENO) == -1)
83                                 goto child_errno_fail;
84                         close(tochild[0]);
85                 }
86                 if (fromchild[1] != STDOUT_FILENO) {
87                         if (dup2(fromchild[1], STDOUT_FILENO) == -1)
88                                 goto child_errno_fail;
89                         close(fromchild[1]);
90                 }
91                 execvp(arr[0], arr);
92
93         child_errno_fail:
94                 err = errno;
95                 write(execfail[1], &err, sizeof(err));
96                 exit(127);
97         }
98
99         close(tochild[0]);
100         close(fromchild[1]);
101         close(execfail[1]);
102         /* Child will close this without writing on successful exec. */
103         if (read(execfail[0], &err, sizeof(err)) == sizeof(err)) {
104                 waitpid(childpid, NULL, 0);
105                 errno = err;
106                 return -1;
107         }
108         if (fd_tochild)
109                 *fd_tochild = tochild[1];
110         if (fd_fromchild)
111                 *fd_fromchild = fromchild[0];
112         return childpid;
113
114 close_execfail_fail:
115         close_noerr(execfail[0]);
116         close_noerr(execfail[1]);
117 close_fromchild_fail:
118         if (fd_fromchild)
119                 close_noerr(fromchild[0]);
120         close_noerr(fromchild[1]);
121 close_tochild_fail:
122         close_noerr(tochild[0]);
123         if (fd_tochild)
124                 close_noerr(tochild[1]);
125 fail:
126         return -1;
127 }
128
129 pid_t pipecmd(int *fd_fromchild, int *fd_tochild, const char *cmd, ...)
130 {
131         pid_t childpid;
132
133         va_list ap;
134         va_start(ap, cmd);
135         childpid = pipecmdv(fd_fromchild, fd_tochild, cmd, ap);
136         va_end(ap);
137
138         return childpid;
139 }