1
/* ----------------------------------------------------------------------------
2
(c) The University of Glasgow 2004
4
Support for System.Process
5
------------------------------------------------------------------------- */
7
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32)
11
/* XXX This is a nasty hack; should put everything necessary in this package */
15
#include "runProcess.h"
17
#if !(defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32))
21
/* ----------------------------------------------------------------------------
23
------------------------------------------------------------------------- */
25
static long max_fd = 0;
27
// Rts internal API, not exposed in a public header file:
28
extern void blockUserSignals(void);
29
extern void unblockUserSignals(void);
32
runInteractiveProcess (char *const args[],
33
char *workingDirectory, char **environment,
34
int fdStdIn, int fdStdOut, int fdStdErr,
35
int *pfdStdInput, int *pfdStdOutput, int *pfdStdError,
36
int set_inthandler, long inthandler,
37
int set_quithandler, long quithandler,
41
int fdStdInput[2], fdStdOutput[2], fdStdError[2];
45
// Ordering matters here, see below [Note #431].
49
sysErrorBelch("runInteractiveProcess: pipe");
55
r = pipe(fdStdOutput);
57
sysErrorBelch("runInteractiveProcess: pipe");
64
sysErrorBelch("runInteractiveProcess: pipe");
69
// Block signals with Haskell handlers. The danger here is that
70
// with the threaded RTS, a signal arrives in the child process,
71
// the RTS writes the signal information into the pipe (which is
72
// shared between parent and child), and the parent behaves as if
73
// the signal had been raised.
76
// See #4074. Sometimes fork() gets interrupted by the timer
77
// signal and keeps restarting indefinitely.
84
#if __GLASGOW_HASKELL__ > 612
92
close(fdStdOutput[0]);
93
close(fdStdOutput[1]);
103
// WARNING! we are now in the child of vfork(), so any memory
104
// we modify below will also be seen in the parent process.
106
unblockUserSignals();
108
if (workingDirectory) {
109
if (chdir (workingDirectory) < 0) {
110
// See #1593. The convention for the exit code when
111
// exec() fails seems to be 127 (gleened from C's
112
// system()), but there's no equivalent convention for
113
// chdir(), so I'm picking 126 --SimonM.
118
// [Note #431]: Ordering matters here. If any of the FDs
119
// 0,1,2 were initially closed, then our pipes may have used
120
// these FDs. So when we dup2 the pipe FDs down to 0,1,2, we
121
// must do it in that order, otherwise we could overwrite an
122
// FD that we need later.
125
if (fdStdInput[0] != STDIN_FILENO) {
126
dup2 (fdStdInput[0], STDIN_FILENO);
127
close(fdStdInput[0]);
129
close(fdStdInput[1]);
131
dup2(fdStdIn, STDIN_FILENO);
134
if (fdStdOut == -1) {
135
if (fdStdOutput[1] != STDOUT_FILENO) {
136
dup2 (fdStdOutput[1], STDOUT_FILENO);
137
close(fdStdOutput[1]);
139
close(fdStdOutput[0]);
141
dup2(fdStdOut, STDOUT_FILENO);
144
if (fdStdErr == -1) {
145
if (fdStdError[1] != STDERR_FILENO) {
146
dup2 (fdStdError[1], STDERR_FILENO);
147
close(fdStdError[1]);
149
close(fdStdError[0]);
151
dup2(fdStdErr, STDERR_FILENO);
158
max_fd = sysconf(_SC_OPEN_MAX);
166
for (i = 3; i < max_fd; i++) {
171
/* Set the SIGINT/SIGQUIT signal handlers in the child, if requested
173
(void)sigemptyset(&dfl.sa_mask);
175
if (set_inthandler) {
176
dfl.sa_handler = (void *)inthandler;
177
(void)sigaction(SIGINT, &dfl, NULL);
179
if (set_quithandler) {
180
dfl.sa_handler = (void *)quithandler;
181
(void)sigaction(SIGQUIT, &dfl, NULL);
186
execvpe(args[0], args, environment);
188
execvp(args[0], args);
195
close(fdStdInput[0]);
196
fcntl(fdStdInput[1], F_SETFD, FD_CLOEXEC);
197
*pfdStdInput = fdStdInput[1];
199
if (fdStdOut == -1) {
200
close(fdStdOutput[1]);
201
fcntl(fdStdOutput[0], F_SETFD, FD_CLOEXEC);
202
*pfdStdOutput = fdStdOutput[0];
204
if (fdStdErr == -1) {
205
close(fdStdError[1]);
206
fcntl(fdStdError[0], F_SETFD, FD_CLOEXEC);
207
*pfdStdError = fdStdError[0];
211
unblockUserSignals();
218
terminateProcess (ProcHandle handle)
220
return (kill(handle, SIGTERM) == 0);
224
getProcessExitCode (ProcHandle handle, int *pExitCode)
230
if ((res = waitpid(handle, &wstat, WNOHANG)) > 0)
232
if (WIFEXITED(wstat))
234
*pExitCode = WEXITSTATUS(wstat);
238
if (WIFSIGNALED(wstat))
245
/* This should never happen */
249
if (res == 0) return 0;
260
int waitForProcess (ProcHandle handle, int *pret)
264
while (waitpid(handle, &wstat, 0) < 0)
272
if (WIFEXITED(wstat)) {
273
*pret = WEXITSTATUS(wstat);
277
if (WIFSIGNALED(wstat))
284
/* This should never happen */
291
/* ----------------------------------------------------------------------------
293
------------------------------------------------------------------------- */
295
/* -------------------- WINDOWS VERSION --------------------- */
298
* Function: mkAnonPipe
300
* Purpose: create an anonymous pipe with read and write ends being
301
* optionally (non-)inheritable.
304
mkAnonPipe (HANDLE* pHandleIn, BOOL isInheritableIn,
305
HANDLE* pHandleOut, BOOL isInheritableOut)
307
HANDLE hTemporaryIn = NULL;
308
HANDLE hTemporaryOut = NULL;
310
/* Create the anon pipe with both ends inheritable */
311
if (!CreatePipe(&hTemporaryIn, &hTemporaryOut, NULL, 0))
319
if (isInheritableIn) {
320
// SetHandleInformation requires at least Win2k
321
if (!SetHandleInformation(hTemporaryIn,
323
HANDLE_FLAG_INHERIT))
328
CloseHandle(hTemporaryIn);
329
CloseHandle(hTemporaryOut);
333
*pHandleIn = hTemporaryIn;
335
if (isInheritableOut) {
336
if (!SetHandleInformation(hTemporaryOut,
338
HANDLE_FLAG_INHERIT))
343
CloseHandle(hTemporaryIn);
344
CloseHandle(hTemporaryOut);
348
*pHandleOut = hTemporaryOut;
354
runInteractiveProcess (wchar_t *cmd, wchar_t *workingDirectory,
356
int fdStdIn, int fdStdOut, int fdStdErr,
357
int *pfdStdInput, int *pfdStdOutput, int *pfdStdError,
361
PROCESS_INFORMATION pInfo;
362
HANDLE hStdInputRead = INVALID_HANDLE_VALUE;
363
HANDLE hStdInputWrite = INVALID_HANDLE_VALUE;
364
HANDLE hStdOutputRead = INVALID_HANDLE_VALUE;
365
HANDLE hStdOutputWrite = INVALID_HANDLE_VALUE;
366
HANDLE hStdErrorRead = INVALID_HANDLE_VALUE;
367
HANDLE hStdErrorWrite = INVALID_HANDLE_VALUE;
372
ZeroMemory(&sInfo, sizeof(sInfo));
373
sInfo.cb = sizeof(sInfo);
374
sInfo.dwFlags = STARTF_USESTDHANDLES;
377
if (!mkAnonPipe(&hStdInputRead, TRUE, &hStdInputWrite, FALSE))
379
sInfo.hStdInput = hStdInputRead;
380
} else if (fdStdIn == 0) {
381
// Don't duplicate stdin, as console handles cannot be
382
// duplicated and inherited. urg.
383
sInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
385
// The handle might not be inheritable, so duplicate it
386
status = DuplicateHandle(GetCurrentProcess(),
387
(HANDLE) _get_osfhandle(fdStdIn),
388
GetCurrentProcess(), &hStdInputRead,
390
TRUE, /* inheritable */
391
DUPLICATE_SAME_ACCESS);
392
if (!status) goto cleanup_err;
393
sInfo.hStdInput = hStdInputRead;
396
if (fdStdOut == -1) {
397
if (!mkAnonPipe(&hStdOutputRead, FALSE, &hStdOutputWrite, TRUE))
399
sInfo.hStdOutput = hStdOutputWrite;
400
} else if (fdStdOut == 1) {
401
// Don't duplicate stdout, as console handles cannot be
402
// duplicated and inherited. urg.
403
sInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
405
// The handle might not be inheritable, so duplicate it
406
status = DuplicateHandle(GetCurrentProcess(),
407
(HANDLE) _get_osfhandle(fdStdOut),
408
GetCurrentProcess(), &hStdOutputWrite,
410
TRUE, /* inheritable */
411
DUPLICATE_SAME_ACCESS);
412
if (!status) goto cleanup_err;
413
sInfo.hStdOutput = hStdOutputWrite;
416
if (fdStdErr == -1) {
417
if (!mkAnonPipe(&hStdErrorRead, TRUE, &hStdErrorWrite, TRUE))
419
sInfo.hStdError = hStdErrorWrite;
420
} else if (fdStdErr == 2) {
421
// Don't duplicate stderr, as console handles cannot be
422
// duplicated and inherited. urg.
423
sInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
425
/* The handle might not be inheritable, so duplicate it */
426
status = DuplicateHandle(GetCurrentProcess(),
427
(HANDLE) _get_osfhandle(fdStdErr),
428
GetCurrentProcess(), &hStdErrorWrite,
430
TRUE, /* inheritable */
431
DUPLICATE_SAME_ACCESS);
432
if (!status) goto cleanup_err;
433
sInfo.hStdError = hStdErrorWrite;
436
if (sInfo.hStdInput != GetStdHandle(STD_INPUT_HANDLE) &&
437
sInfo.hStdOutput != GetStdHandle(STD_OUTPUT_HANDLE) &&
438
sInfo.hStdError != GetStdHandle(STD_ERROR_HANDLE))
439
flags = CREATE_NO_WINDOW; // Run without console window only when both output and error are redirected
444
if (close_fds && fdStdIn == 0 && fdStdOut == 1 && fdStdErr == 2) {
450
if (!CreateProcess(NULL, cmd, NULL, NULL, inherit, flags, environment, workingDirectory, &sInfo, &pInfo))
454
CloseHandle(pInfo.hThread);
456
// Close the ends of the pipes that were inherited by the
457
// child process. This is important, otherwise we won't see
458
// EOF on these pipes when the child process exits.
459
if (hStdInputRead != INVALID_HANDLE_VALUE) CloseHandle(hStdInputRead);
460
if (hStdOutputWrite != INVALID_HANDLE_VALUE) CloseHandle(hStdOutputWrite);
461
if (hStdErrorWrite != INVALID_HANDLE_VALUE) CloseHandle(hStdErrorWrite);
463
*pfdStdInput = _open_osfhandle((intptr_t) hStdInputWrite, _O_WRONLY);
464
*pfdStdOutput = _open_osfhandle((intptr_t) hStdOutputRead, _O_RDONLY);
465
*pfdStdError = _open_osfhandle((intptr_t) hStdErrorRead, _O_RDONLY);
467
return (int) pInfo.hProcess;
470
if (hStdInputRead != INVALID_HANDLE_VALUE) CloseHandle(hStdInputRead);
471
if (hStdInputWrite != INVALID_HANDLE_VALUE) CloseHandle(hStdInputWrite);
472
if (hStdOutputRead != INVALID_HANDLE_VALUE) CloseHandle(hStdOutputRead);
473
if (hStdOutputWrite != INVALID_HANDLE_VALUE) CloseHandle(hStdOutputWrite);
474
if (hStdErrorRead != INVALID_HANDLE_VALUE) CloseHandle(hStdErrorRead);
475
if (hStdErrorWrite != INVALID_HANDLE_VALUE) CloseHandle(hStdErrorWrite);
481
terminateProcess (ProcHandle handle)
483
if (!TerminateProcess((HANDLE) handle, 1)) {
491
getProcessExitCode (ProcHandle handle, int *pExitCode)
495
if (WaitForSingleObject((HANDLE) handle, 1) == WAIT_OBJECT_0)
497
if (GetExitCodeProcess((HANDLE) handle, (DWORD *) pExitCode) == 0)
509
waitForProcess (ProcHandle handle, int *pret)
513
if (WaitForSingleObject((HANDLE) handle, INFINITE) == WAIT_OBJECT_0)
515
if (GetExitCodeProcess((HANDLE) handle, &retCode) == 0)