Add support for the IO manager thread on Windows
[packages/random.git] / cbits / runProcess.c
1 /* ----------------------------------------------------------------------------
2 (c) The University of Glasgow 2004
3
4 Support for System.Process
5 ------------------------------------------------------------------------- */
6
7 #include "HsBase.h"
8
9 #if defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32)
10 #include <windows.h>
11 #include <stdlib.h>
12 #endif
13
14 #ifdef HAVE_VFORK_H
15 #include <vfork.h>
16 #endif
17
18 #ifdef HAVE_VFORK
19 #define fork vfork
20 #endif
21
22 #ifdef HAVE_SIGNAL_H
23 #include <signal.h>
24 #endif
25
26 #if !(defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32))
27 /* ----------------------------------------------------------------------------
28 UNIX versions
29 ------------------------------------------------------------------------- */
30
31 ProcHandle
32 runProcess (char *const args[], char *workingDirectory, char **environment,
33 int fdStdInput, int fdStdOutput, int fdStdError,
34 int set_inthandler, long inthandler,
35 int set_quithandler, long quithandler)
36 {
37 int pid;
38 struct sigaction dfl;
39
40 switch(pid = fork())
41 {
42 case -1:
43 return -1;
44
45 case 0:
46 {
47 pPrPr_disableITimers();
48
49 if (workingDirectory) {
50 if (chdir (workingDirectory) < 0) {
51 return -1;
52 }
53 }
54
55 /* Set the SIGINT/SIGQUIT signal handlers in the child, if requested
56 */
57 (void)sigemptyset(&dfl.sa_mask);
58 dfl.sa_flags = 0;
59 if (set_inthandler) {
60 dfl.sa_handler = (void *)inthandler;
61 (void)sigaction(SIGINT, &dfl, NULL);
62 }
63 if (set_quithandler) {
64 dfl.sa_handler = (void *)quithandler;
65 (void)sigaction(SIGQUIT, &dfl, NULL);
66 }
67
68 dup2 (fdStdInput, STDIN_FILENO);
69 dup2 (fdStdOutput, STDOUT_FILENO);
70 dup2 (fdStdError, STDERR_FILENO);
71
72 if (environment) {
73 execvpe(args[0], args, environment);
74 } else {
75 execvp(args[0], args);
76 }
77 }
78 _exit(127);
79 }
80
81 return pid;
82 }
83
84 ProcHandle
85 runInteractiveProcess (char *const args[],
86 char *workingDirectory, char **environment,
87 int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)
88 {
89 int pid;
90 int fdStdInput[2], fdStdOutput[2], fdStdError[2];
91
92 pipe(fdStdInput);
93 pipe(fdStdOutput);
94 pipe(fdStdError);
95
96 switch(pid = fork())
97 {
98 case -1:
99 close(fdStdInput[0]);
100 close(fdStdInput[1]);
101 close(fdStdOutput[0]);
102 close(fdStdOutput[1]);
103 close(fdStdError[0]);
104 close(fdStdError[1]);
105 return -1;
106
107 case 0:
108 {
109 pPrPr_disableITimers();
110
111 if (workingDirectory) {
112 if (chdir (workingDirectory) < 0) {
113 return -1;
114 }
115 }
116
117 if (fdStdInput[0] != STDIN_FILENO) {
118 dup2 (fdStdInput[0], STDIN_FILENO);
119 close(fdStdInput[0]);
120 }
121
122 if (fdStdOutput[1] != STDOUT_FILENO) {
123 dup2 (fdStdOutput[1], STDOUT_FILENO);
124 close(fdStdOutput[1]);
125 }
126
127 if (fdStdError[1] != STDERR_FILENO) {
128 dup2 (fdStdError[1], STDERR_FILENO);
129 close(fdStdError[1]);
130 }
131
132 close(fdStdInput[1]);
133 close(fdStdOutput[0]);
134 close(fdStdError[0]);
135
136 /* the child */
137 if (environment) {
138 execvpe(args[0], args, environment);
139 } else {
140 execvp(args[0], args);
141 }
142 }
143 _exit(127);
144
145 default:
146 close(fdStdInput[0]);
147 close(fdStdOutput[1]);
148 close(fdStdError[1]);
149
150 *pfdStdInput = fdStdInput[1];
151 *pfdStdOutput = fdStdOutput[0];
152 *pfdStdError = fdStdError[0];
153 break;
154 }
155
156 return pid;
157 }
158
159 int
160 terminateProcess (ProcHandle handle)
161 {
162 return (kill(handle, SIGTERM) == 0);
163 }
164
165 int
166 getProcessExitCode (ProcHandle handle, int *pExitCode)
167 {
168 int wstat, res;
169
170 *pExitCode = 0;
171
172 if ((res = waitpid(handle, &wstat, WNOHANG)) > 0)
173 {
174 if (WIFEXITED(wstat))
175 {
176 *pExitCode = WEXITSTATUS(wstat);
177 return 1;
178 }
179 else
180 if (WIFSIGNALED(wstat))
181 {
182 errno = EINTR;
183 return -1;
184 }
185 else
186 {
187 /* This should never happen */
188 }
189 }
190
191 if (res == 0) return 0;
192
193 if (errno == ECHILD)
194 {
195 *pExitCode = 0;
196 return 1;
197 }
198
199 return -1;
200 }
201
202 int waitForProcess (ProcHandle handle)
203 {
204 int wstat;
205
206 while (waitpid(handle, &wstat, 0) < 0)
207 {
208 if (errno != EINTR)
209 {
210 return -1;
211 }
212 }
213
214 if (WIFEXITED(wstat))
215 return WEXITSTATUS(wstat);
216 else
217 if (WIFSIGNALED(wstat))
218 {
219 return wstat;
220 }
221 else
222 {
223 /* This should never happen */
224 }
225
226 return -1;
227 }
228
229 #else
230 /* ----------------------------------------------------------------------------
231 Win32 versions
232 ------------------------------------------------------------------------- */
233
234 /* -------------------- WINDOWS VERSION --------------------- */
235
236 /*
237 * Function: mkAnonPipe
238 *
239 * Purpose: create an anonymous pipe with read and write ends being
240 * optionally (non-)inheritable.
241 */
242 static BOOL
243 mkAnonPipe (HANDLE* pHandleIn, BOOL isInheritableIn,
244 HANDLE* pHandleOut, BOOL isInheritableOut)
245 {
246 HANDLE hTemporaryIn = NULL;
247 HANDLE hTemporaryOut = NULL;
248 BOOL status;
249 SECURITY_ATTRIBUTES sec_attrs;
250
251 /* Create inheritable security attributes */
252 sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
253 sec_attrs.lpSecurityDescriptor = NULL;
254 sec_attrs.bInheritHandle = TRUE;
255
256 /* Create the anon pipe with both ends inheritable */
257 if (!CreatePipe(&hTemporaryIn, &hTemporaryOut, &sec_attrs, 0))
258 {
259 maperrno();
260 *pHandleIn = NULL;
261 *pHandleOut = NULL;
262 return FALSE;
263 }
264
265 if (isInheritableIn)
266 *pHandleIn = hTemporaryIn;
267 else
268 {
269 /* Make the read end non-inheritable */
270 status = DuplicateHandle(GetCurrentProcess(), hTemporaryIn,
271 GetCurrentProcess(), pHandleIn,
272 0,
273 FALSE, /* non-inheritable */
274 DUPLICATE_SAME_ACCESS);
275 CloseHandle(hTemporaryIn);
276 if (!status)
277 {
278 maperrno();
279 *pHandleIn = NULL;
280 *pHandleOut = NULL;
281 CloseHandle(hTemporaryOut);
282 return FALSE;
283 }
284 }
285
286 if (isInheritableOut)
287 *pHandleOut = hTemporaryOut;
288 else
289 {
290 /* Make the write end non-inheritable */
291 status = DuplicateHandle(GetCurrentProcess(), hTemporaryOut,
292 GetCurrentProcess(), pHandleOut,
293 0,
294 FALSE, /* non-inheritable */
295 DUPLICATE_SAME_ACCESS);
296 CloseHandle(hTemporaryOut);
297 if (!status)
298 {
299 maperrno();
300 *pHandleIn = NULL;
301 *pHandleOut = NULL;
302 CloseHandle(*pHandleIn);
303 return FALSE;
304 }
305 }
306
307 return TRUE;
308 }
309
310 ProcHandle
311 runProcess (char *cmd, char *workingDirectory, void *environment,
312 int fdStdInput, int fdStdOutput, int fdStdError)
313 {
314 STARTUPINFO sInfo;
315 PROCESS_INFORMATION pInfo;
316 DWORD flags;
317
318 ZeroMemory(&sInfo, sizeof(sInfo));
319 sInfo.cb = sizeof(sInfo);
320 sInfo.hStdInput = (HANDLE) _get_osfhandle(fdStdInput);
321 sInfo.hStdOutput= (HANDLE) _get_osfhandle(fdStdOutput);
322 sInfo.hStdError = (HANDLE) _get_osfhandle(fdStdError);
323
324 if (sInfo.hStdInput == INVALID_HANDLE_VALUE)
325 sInfo.hStdInput = NULL;
326 if (sInfo.hStdOutput == INVALID_HANDLE_VALUE)
327 sInfo.hStdOutput = NULL;
328 if (sInfo.hStdError == INVALID_HANDLE_VALUE)
329 sInfo.hStdError = NULL;
330
331 if (sInfo.hStdInput || sInfo.hStdOutput || sInfo.hStdError)
332 sInfo.dwFlags = STARTF_USESTDHANDLES;
333
334 if (sInfo.hStdInput != GetStdHandle(STD_INPUT_HANDLE) &&
335 sInfo.hStdOutput != GetStdHandle(STD_OUTPUT_HANDLE) &&
336 sInfo.hStdError != GetStdHandle(STD_ERROR_HANDLE))
337 flags = CREATE_NO_WINDOW; // Run without console window only when both output and error are redirected
338 else
339 flags = 0;
340
341 if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, environment, workingDirectory, &sInfo, &pInfo))
342 {
343 maperrno();
344 return -1;
345 }
346
347 CloseHandle(pInfo.hThread);
348 return (ProcHandle)pInfo.hProcess;
349 }
350
351 ProcHandle
352 runInteractiveProcess (char *cmd, char *workingDirectory, void *environment,
353 int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)
354 {
355 STARTUPINFO sInfo;
356 PROCESS_INFORMATION pInfo;
357 HANDLE hStdInputRead, hStdInputWrite;
358 HANDLE hStdOutputRead, hStdOutputWrite;
359 HANDLE hStdErrorRead, hStdErrorWrite;
360
361 if (!mkAnonPipe(&hStdInputRead, TRUE, &hStdInputWrite, FALSE))
362 return -1;
363
364 if (!mkAnonPipe(&hStdOutputRead, FALSE, &hStdOutputWrite, TRUE))
365 {
366 CloseHandle(hStdInputRead);
367 CloseHandle(hStdInputWrite);
368 return -1;
369 }
370
371 if (!mkAnonPipe(&hStdErrorRead, FALSE, &hStdErrorWrite, TRUE))
372 {
373 CloseHandle(hStdInputRead);
374 CloseHandle(hStdInputWrite);
375 CloseHandle(hStdOutputRead);
376 CloseHandle(hStdOutputWrite);
377 return -1;
378 }
379
380 ZeroMemory(&sInfo, sizeof(sInfo));
381 sInfo.cb = sizeof(sInfo);
382 sInfo.dwFlags = STARTF_USESTDHANDLES;
383 sInfo.hStdInput = hStdInputRead;
384 sInfo.hStdOutput= hStdOutputWrite;
385 sInfo.hStdError = hStdErrorWrite;
386
387 if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, environment, workingDirectory, &sInfo, &pInfo))
388 {
389 maperrno();
390 CloseHandle(hStdInputRead);
391 CloseHandle(hStdInputWrite);
392 CloseHandle(hStdOutputRead);
393 CloseHandle(hStdOutputWrite);
394 CloseHandle(hStdErrorRead);
395 CloseHandle(hStdErrorWrite);
396 return -1;
397 }
398 CloseHandle(pInfo.hThread);
399
400 // Close the ends of the pipes that were inherited by the
401 // child process. This is important, otherwise we won't see
402 // EOF on these pipes when the child process exits.
403 CloseHandle(hStdInputRead);
404 CloseHandle(hStdOutputWrite);
405 CloseHandle(hStdErrorWrite);
406
407 *pfdStdInput = _open_osfhandle((intptr_t) hStdInputWrite, _O_WRONLY);
408 *pfdStdOutput = _open_osfhandle((intptr_t) hStdOutputRead, _O_RDONLY);
409 *pfdStdError = _open_osfhandle((intptr_t) hStdErrorRead, _O_RDONLY);
410
411 return (int) pInfo.hProcess;
412 }
413
414 int
415 terminateProcess (ProcHandle handle)
416 {
417 if (!TerminateProcess((HANDLE) handle, 1)) {
418 maperrno();
419 return -1;
420 }
421 return 0;
422 }
423
424 int
425 getProcessExitCode (ProcHandle handle, int *pExitCode)
426 {
427 *pExitCode = 0;
428
429 if (WaitForSingleObject((HANDLE) handle, 1) == WAIT_OBJECT_0)
430 {
431 if (GetExitCodeProcess((HANDLE) handle, (DWORD *) pExitCode) == 0)
432 {
433 maperrno();
434 return -1;
435 }
436 return 1;
437 }
438
439 return 0;
440 }
441
442 int
443 waitForProcess (ProcHandle handle)
444 {
445 DWORD retCode;
446
447 if (WaitForSingleObject((HANDLE) handle, INFINITE) == WAIT_OBJECT_0)
448 {
449 if (GetExitCodeProcess((HANDLE) handle, &retCode) == 0)
450 {
451 maperrno();
452 return -1;
453 }
454 return retCode;
455 }
456
457 maperrno();
458 return -1;
459 }
460
461 #endif /* Win32 */