aboutsummaryrefslogtreecommitdiff
path: root/win32/popen.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--win32/popen.c316
1 files changed, 316 insertions, 0 deletions
diff --git a/win32/popen.c b/win32/popen.c
new file mode 100644
index 000000000..7cf2b1893
--- /dev/null
+++ b/win32/popen.c
@@ -0,0 +1,316 @@
1#include <fcntl.h>
2#include "libbb.h"
3#include "NUM_APPLETS.h"
4
5typedef struct {
6 PROCESS_INFORMATION piProcInfo;
7 HANDLE pipe[2];
8 int fd;
9} pipe_data;
10
11static pipe_data *pipes = NULL;
12static int num_pipes = 0;
13
14static int mingw_popen_internal(pipe_data *p, const char *exe,
15 const char *cmd, const char *mode, int fd0, pid_t *pid);
16
17static int mingw_pipe(pipe_data *p, int bidi)
18{
19 SECURITY_ATTRIBUTES sa;
20
21 sa.nLength = sizeof(sa); /* Length in bytes */
22 sa.bInheritHandle = 1; /* the child must inherit these handles */
23 sa.lpSecurityDescriptor = NULL;
24
25 if (!bidi) {
26 /* pipe[0] is the read handle, pipe[i] the write handle */
27 if ( !CreatePipe (&p->pipe[0], &p->pipe[1], &sa, 1 << 13) ) {
28 return -1;
29 }
30 }
31 else {
32 char *name;
33 const int ip = 1; /* index of parent end of pipe */
34 const int ic = 0; /* index of child end of pipe */
35 static int count = 0;
36
37 name = xasprintf("\\\\.\\pipe\\bb_pipe.%d.%d", getpid(), ++count);
38
39 p->pipe[ip] = CreateNamedPipe(name,
40 PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
41 PIPE_TYPE_BYTE|PIPE_WAIT,
42 1, 4096, 4096, 0, &sa);
43
44 p->pipe[ic] = CreateFile(name, GENERIC_READ|GENERIC_WRITE, 0, &sa,
45 OPEN_EXISTING,
46 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
47 NULL);
48 free(name);
49 }
50
51 return (p->pipe[0] == INVALID_HANDLE_VALUE ||
52 p->pipe[1] == INVALID_HANDLE_VALUE) ? -1 : 0;
53}
54
55static void clear_pipe_data(pipe_data *p)
56{
57 memset(p, 0, sizeof(pipe_data));
58 p->pipe[0] = INVALID_HANDLE_VALUE;
59 p->pipe[1] = INVALID_HANDLE_VALUE;
60 p->fd = -1;
61}
62
63static void close_pipe_data(pipe_data *p)
64{
65 if (p->pipe[0] != INVALID_HANDLE_VALUE)
66 CloseHandle(p->pipe[0]);
67 if (p->pipe[1] != INVALID_HANDLE_VALUE)
68 CloseHandle(p->pipe[1]);
69 clear_pipe_data(p);
70}
71
72/*
73 * Search for a pipe_data structure with file descriptor fd. If fd is
74 * -1 and no empty slots are available the array is extended. Return
75 * NULL if the file descriptor can't be found or the array can't be
76 * extended.
77 */
78static pipe_data *find_pipe(int fd)
79{
80 int i;
81 pipe_data *p = NULL;
82
83 /* find a matching pipe structure */
84 for ( i=0; i<num_pipes; ++i ) {
85 if (pipes[i].fd == fd) {
86 p = pipes+i;
87 break;
88 }
89 }
90
91 /* if looking for valid file descriptor return now */
92 if (fd != -1)
93 return p;
94
95 if ( p == NULL ) {
96 /* need to extend array */
97 if ( (p=realloc(pipes, sizeof(pipe_data)*(num_pipes+10))) == NULL ) {
98 return NULL;
99 }
100
101 pipes = p;
102 p = pipes + num_pipes;
103 for ( i=0; i<10; ++i ) {
104 clear_pipe_data(p+i);
105 }
106 num_pipes += 10;
107 }
108 clear_pipe_data(p);
109
110 return p;
111}
112
113FILE *mingw_popen(const char *cmd, const char *mode)
114{
115 pipe_data *p;
116 FILE *fptr = NULL;
117 int fd;
118 char *arg, *cmd_buff;
119
120 if ( cmd == NULL || *cmd == '\0' || mode == NULL ||
121 (*mode != 'r' && *mode != 'w') ) {
122 return NULL;
123 }
124
125 /* find an unused pipe structure */
126 if ((p=find_pipe(-1)) == NULL) {
127 return NULL;
128 }
129
130 arg = quote_arg(cmd);
131 cmd_buff = xasprintf("sh -c %s", arg);
132
133 /* Create the pipe */
134 if ((fd=mingw_popen_internal(p, "sh", cmd_buff, mode, -1, NULL)) != -1) {
135 fptr = _fdopen(fd, *mode == 'r' ? "rb" : "wb");
136 }
137
138 free(cmd_buff);
139 free(arg);
140
141 return fptr;
142}
143
144/*
145 * Open a pipe to a command.
146 *
147 * - mode may be "r", "w" or "b" for read-only, write-only or
148 * bidirectional (from the perspective of the parent).
149 * - if fd0 is a valid file descriptor it's used as input to the
150 * command ("r") or as the destination of the output from the
151 * command ("w"). Otherwise (and if not "b") use stdin or stdout.
152 * - the pid of the command is returned in the variable pid, which
153 * can be NULL if the pid is not required.
154 * - mode "w+" forces the use of an external program. This is required
155 * for xz and lzma compression.
156 */
157static int mingw_popen_internal(pipe_data *p, const char *exe,
158 const char *cmd, const char *mode, int fd0, pid_t *pid)
159{
160 pipe_data pd;
161 STARTUPINFO siStartInfo;
162 int success;
163 int fd = -1;
164 int ip, ic, flags;
165 char *freeme = NULL;
166
167 switch (*mode) {
168 case 'r':
169 ip = 0;
170 flags = _O_RDONLY|_O_BINARY;
171 break;
172 case 'w':
173 ip = 1;
174 flags = _O_WRONLY|_O_BINARY;
175 break;
176 case 'b':
177 ip = 1;
178 flags = _O_RDWR|_O_BINARY;
179 break;
180 default:
181 return -1;
182 }
183 ic = !ip;
184
185 if (!p) {
186 /* no struct provided, use a local one */
187 p = &pd;
188 }
189
190 /* Create the pipe */
191 if ( mingw_pipe(p, *mode == 'b') == -1 ) {
192 goto finito;
193 }
194
195#if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1
196 // "w+" mode forces a path lookup
197 if (mode[1] != '+' && find_applet_by_name(exe) >= 0) {
198 exe = bb_busybox_exec_path;
199 } else
200#endif
201 {
202 // Look up executable on PATH
203 freeme = find_first_executable(exe);
204 if (freeme == NULL)
205 bb_perror_msg_and_die("can't execute '%s'", exe);
206 exe = freeme;
207 }
208
209 /* Make the parent end of the pipe non-inheritable */
210 SetHandleInformation(p->pipe[ip], HANDLE_FLAG_INHERIT, 0);
211
212 /* Now create the child process */
213 ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
214 siStartInfo.cb = sizeof(STARTUPINFO);
215 /* default settings for a bidirectional pipe */
216 siStartInfo.hStdInput = p->pipe[ic];
217 siStartInfo.hStdOutput = p->pipe[ic];
218 /* override for read-only or write-only */
219 if ( *mode == 'r' ) {
220 siStartInfo.hStdInput = fd0 >= 0 ? (HANDLE)_get_osfhandle(fd0) :
221 GetStdHandle(STD_INPUT_HANDLE);
222 }
223 else if ( *mode == 'w' ) {
224 siStartInfo.hStdOutput = fd0 >= 0 ? (HANDLE)_get_osfhandle(fd0) :
225 GetStdHandle(STD_OUTPUT_HANDLE);
226 }
227 siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
228 siStartInfo.wShowWindow = SW_HIDE;
229 siStartInfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
230
231 success = CreateProcess((LPCSTR)exe,
232 (LPSTR)cmd, /* command line */
233 NULL, /* process security attributes */
234 NULL, /* primary thread security attributes */
235 TRUE, /* handles are inherited */
236 0, /* creation flags */
237 NULL, /* use parent's environment */
238 NULL, /* use parent's current directory */
239 &siStartInfo, /* STARTUPINFO pointer */
240 &p->piProcInfo); /* receives PROCESS_INFORMATION */
241
242 if ( !success ) {
243 goto finito;
244 }
245
246 /* close child end of pipe */
247 CloseHandle(p->pipe[ic]);
248 p->pipe[ic] = INVALID_HANDLE_VALUE;
249
250 fd = _open_osfhandle((intptr_t)p->pipe[ip], flags);
251
252finito:
253 free(freeme);
254 if ( fd == -1 ) {
255 close_pipe_data(p);
256 }
257 else {
258 p->fd = fd;
259 if ( pid ) {
260 *pid = (pid_t)p->piProcInfo.dwProcessId;
261 }
262 }
263
264 return fd;
265}
266
267int mingw_popen_fd(const char *exe, const char *cmd, const char *mode,
268 int fd0, pid_t *pid)
269{
270 return mingw_popen_internal(NULL, exe, cmd, mode, fd0, pid);
271}
272
273int mingw_pclose(FILE *fp)
274{
275 int fd;
276 pipe_data *p;
277 DWORD ret;
278
279 /* find struct containing fd */
280 if (fp == NULL || (fd=fileno(fp)) == -1 || (p=find_pipe(fd)) == NULL)
281 return -1;
282
283 fclose(fp);
284
285 ret = WaitForSingleObject(p->piProcInfo.hProcess, INFINITE);
286
287 CloseHandle(p->piProcInfo.hProcess);
288 CloseHandle(p->piProcInfo.hThread);
289 close_pipe_data(p);
290
291 return (ret == WAIT_OBJECT_0) ? 0 : -1;
292}
293
294/* Used with mode "w" and a compressor when creating a compressed tar
295 * file; with mode "r" and a decompressor in open_transformer. */
296pid_t mingw_fork_compressor(int fd, const char *compressor, const char *mode)
297{
298 char *cmd;
299 int fd1;
300 pid_t pid;
301
302 cmd = xasprintf("%s -cf -", compressor);
303#if ENABLE_FEATURE_SEAMLESS_XZ || ENABLE_FEATURE_SEAMLESS_LZMA
304 // xz and lzma applets don't support compression, we must use
305 // an external command.
306 if (mode[0] == 'w' && index_in_strings("lzma\0xz\0", compressor) >= 0)
307 mode = "w+";
308#endif
309
310 if ((fd1 = mingw_popen_fd(compressor, cmd, mode, fd, &pid)) == -1)
311 bb_perror_msg_and_die("can't execute '%s'", compressor);
312
313 free(cmd);
314 xmove_fd(fd1, fd);
315 return pid;
316}