1 /* ----------------------------------------------------------------------------
\r
2 (c) The University of Glasgow 2004
\r
4 Support for System.Process
\r
5 ------------------------------------------------------------------------- */
\r
9 #if defined(mingw32_HOST_OS)
\r
10 #include <windows.h>
\r
22 #ifdef HAVE_SIGNAL_H
\r
26 #if !defined(mingw32_HOST_OS) && !defined(__MINGW32__)
\r
27 /* ----------------------------------------------------------------------------
\r
29 ------------------------------------------------------------------------- */
\r
32 runProcess (char *const args[], char *workingDirectory, char **environment,
\r
33 int fdStdInput, int fdStdOutput, int fdStdError,
\r
34 int set_inthandler, long inthandler,
\r
35 int set_quithandler, long quithandler)
\r
38 struct sigaction dfl;
\r
40 switch(pid = fork())
\r
47 pPrPr_disableITimers();
\r
49 if (workingDirectory) {
\r
50 chdir (workingDirectory);
\r
53 /* Set the SIGINT/SIGQUIT signal handlers in the child, if requested
\r
55 (void)sigemptyset(&dfl.sa_mask);
\r
57 if (set_inthandler) {
\r
58 dfl.sa_handler = (void *)inthandler;
\r
59 (void)sigaction(SIGINT, &dfl, NULL);
\r
61 if (set_quithandler) {
\r
62 dfl.sa_handler = (void *)quithandler;
\r
63 (void)sigaction(SIGQUIT, &dfl, NULL);
\r
66 dup2 (fdStdInput, STDIN_FILENO);
\r
67 dup2 (fdStdOutput, STDOUT_FILENO);
\r
68 dup2 (fdStdError, STDERR_FILENO);
\r
71 execvpe(args[0], args, environment);
\r
73 execvp(args[0], args);
\r
83 runInteractiveProcess (char *const args[],
\r
84 char *workingDirectory, char **environment,
\r
85 int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)
\r
88 int fdStdInput[2], fdStdOutput[2], fdStdError[2];
\r
94 switch(pid = fork())
\r
97 close(fdStdInput[0]);
\r
98 close(fdStdInput[1]);
\r
99 close(fdStdOutput[0]);
\r
100 close(fdStdOutput[1]);
\r
101 close(fdStdError[0]);
\r
102 close(fdStdError[1]);
\r
107 pPrPr_disableITimers();
\r
109 if (workingDirectory) {
\r
110 chdir (workingDirectory);
\r
113 dup2 (fdStdInput[0], STDIN_FILENO);
\r
114 dup2 (fdStdOutput[1], STDOUT_FILENO);
\r
115 dup2 (fdStdError[1], STDERR_FILENO);
\r
117 close(fdStdInput[0]);
\r
118 close(fdStdInput[1]);
\r
119 close(fdStdOutput[0]);
\r
120 close(fdStdOutput[1]);
\r
121 close(fdStdError[0]);
\r
122 close(fdStdError[1]);
\r
126 execvpe(args[0], args, environment);
\r
128 execvp(args[0], args);
\r
134 close(fdStdInput[0]);
\r
135 close(fdStdOutput[1]);
\r
136 close(fdStdError[1]);
\r
138 *pfdStdInput = fdStdInput[1];
\r
139 *pfdStdOutput = fdStdOutput[0];
\r
140 *pfdStdError = fdStdError[0];
\r
148 terminateProcess (ProcHandle handle)
\r
150 return (kill(handle, SIGTERM) == 0);
\r
154 getProcessExitCode (ProcHandle handle, int *pExitCode)
\r
160 if (waitpid(handle, &wstat, WNOHANG) > 0)
\r
162 if (WIFEXITED(wstat))
\r
164 *pExitCode = WEXITSTATUS(wstat);
\r
168 if (WIFSIGNALED(wstat))
\r
175 /* This should never happen */
\r
182 int waitForProcess (ProcHandle handle)
\r
186 while (waitpid(handle, &wstat, 0) < 0)
\r
188 if (errno != EINTR)
\r
194 if (WIFEXITED(wstat))
\r
195 return WEXITSTATUS(wstat);
\r
197 if (WIFSIGNALED(wstat))
\r
203 /* This should never happen */
\r
210 /* ----------------------------------------------------------------------------
\r
212 ------------------------------------------------------------------------- */
\r
214 /* -------------------- WINDOWS VERSION --------------------- */
\r
216 /* This is the error table that defines the mapping between OS error
\r
217 codes and errno values */
\r
220 unsigned long oscode; /* OS return value */
\r
221 int errnocode; /* System V error code */
\r
224 static struct errentry errtable[] = {
\r
225 { ERROR_INVALID_FUNCTION, EINVAL }, /* 1 */
\r
226 { ERROR_FILE_NOT_FOUND, ENOENT }, /* 2 */
\r
227 { ERROR_PATH_NOT_FOUND, ENOENT }, /* 3 */
\r
228 { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, /* 4 */
\r
229 { ERROR_ACCESS_DENIED, EACCES }, /* 5 */
\r
230 { ERROR_INVALID_HANDLE, EBADF }, /* 6 */
\r
231 { ERROR_ARENA_TRASHED, ENOMEM }, /* 7 */
\r
232 { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, /* 8 */
\r
233 { ERROR_INVALID_BLOCK, ENOMEM }, /* 9 */
\r
234 { ERROR_BAD_ENVIRONMENT, E2BIG }, /* 10 */
\r
235 { ERROR_BAD_FORMAT, ENOEXEC }, /* 11 */
\r
236 { ERROR_INVALID_ACCESS, EINVAL }, /* 12 */
\r
237 { ERROR_INVALID_DATA, EINVAL }, /* 13 */
\r
238 { ERROR_INVALID_DRIVE, ENOENT }, /* 15 */
\r
239 { ERROR_CURRENT_DIRECTORY, EACCES }, /* 16 */
\r
240 { ERROR_NOT_SAME_DEVICE, EXDEV }, /* 17 */
\r
241 { ERROR_NO_MORE_FILES, ENOENT }, /* 18 */
\r
242 { ERROR_LOCK_VIOLATION, EACCES }, /* 33 */
\r
243 { ERROR_BAD_NETPATH, ENOENT }, /* 53 */
\r
244 { ERROR_NETWORK_ACCESS_DENIED, EACCES }, /* 65 */
\r
245 { ERROR_BAD_NET_NAME, ENOENT }, /* 67 */
\r
246 { ERROR_FILE_EXISTS, EEXIST }, /* 80 */
\r
247 { ERROR_CANNOT_MAKE, EACCES }, /* 82 */
\r
248 { ERROR_FAIL_I24, EACCES }, /* 83 */
\r
249 { ERROR_INVALID_PARAMETER, EINVAL }, /* 87 */
\r
250 { ERROR_NO_PROC_SLOTS, EAGAIN }, /* 89 */
\r
251 { ERROR_DRIVE_LOCKED, EACCES }, /* 108 */
\r
252 { ERROR_BROKEN_PIPE, EPIPE }, /* 109 */
\r
253 { ERROR_DISK_FULL, ENOSPC }, /* 112 */
\r
254 { ERROR_INVALID_TARGET_HANDLE, EBADF }, /* 114 */
\r
255 { ERROR_INVALID_HANDLE, EINVAL }, /* 124 */
\r
256 { ERROR_WAIT_NO_CHILDREN, ECHILD }, /* 128 */
\r
257 { ERROR_CHILD_NOT_COMPLETE, ECHILD }, /* 129 */
\r
258 { ERROR_DIRECT_ACCESS_HANDLE, EBADF }, /* 130 */
\r
259 { ERROR_NEGATIVE_SEEK, EINVAL }, /* 131 */
\r
260 { ERROR_SEEK_ON_DEVICE, EACCES }, /* 132 */
\r
261 { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, /* 145 */
\r
262 { ERROR_NOT_LOCKED, EACCES }, /* 158 */
\r
263 { ERROR_BAD_PATHNAME, ENOENT }, /* 161 */
\r
264 { ERROR_MAX_THRDS_REACHED, EAGAIN }, /* 164 */
\r
265 { ERROR_LOCK_FAILED, EACCES }, /* 167 */
\r
266 { ERROR_ALREADY_EXISTS, EEXIST }, /* 183 */
\r
267 { ERROR_FILENAME_EXCED_RANGE, ENOENT }, /* 206 */
\r
268 { ERROR_NESTING_NOT_ALLOWED, EAGAIN }, /* 215 */
\r
269 { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } /* 1816 */
\r
272 /* size of the table */
\r
273 #define ERRTABLESIZE (sizeof(errtable)/sizeof(errtable[0]))
\r
275 /* The following two constants must be the minimum and maximum
\r
276 values in the (contiguous) range of Exec Failure errors. */
\r
277 #define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG
\r
278 #define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN
\r
280 /* These are the low and high value in the range of errors that are
\r
281 access violations */
\r
282 #define MIN_EACCES_RANGE ERROR_WRITE_PROTECT
\r
283 #define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED
\r
285 static void maperrno (void)
\r
290 dwErrorCode = GetLastError();
\r
292 /* check the table for the OS error code */
\r
293 for (i = 0; i < ERRTABLESIZE; ++i)
\r
295 if (dwErrorCode == errtable[i].oscode)
\r
297 errno = errtable[i].errnocode;
\r
302 /* The error code wasn't in the table. We check for a range of */
\r
303 /* EACCES errors or exec failure errors (ENOEXEC). Otherwise */
\r
304 /* EINVAL is returned. */
\r
306 if (dwErrorCode >= MIN_EACCES_RANGE && dwErrorCode <= MAX_EACCES_RANGE)
\r
309 if (dwErrorCode >= MIN_EXEC_ERROR && dwErrorCode <= MAX_EXEC_ERROR)
\r
316 * Function: mkAnonPipe
\r
318 * Purpose: create an anonymous pipe with read and write ends being
\r
319 * optionally (non-)inheritable.
\r
322 mkAnonPipe (HANDLE* pHandleIn, BOOL isInheritableIn,
\r
323 HANDLE* pHandleOut, BOOL isInheritableOut)
\r
325 HANDLE hTemporaryIn = NULL;
\r
326 HANDLE hTemporaryOut = NULL;
\r
328 SECURITY_ATTRIBUTES sec_attrs;
\r
330 /* Create inheritable security attributes */
\r
331 sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
\r
332 sec_attrs.lpSecurityDescriptor = NULL;
\r
333 sec_attrs.bInheritHandle = TRUE;
\r
335 /* Create the anon pipe with both ends inheritable */
\r
336 if (!CreatePipe(&hTemporaryIn, &hTemporaryOut, &sec_attrs, 0))
\r
340 *pHandleOut = NULL;
\r
344 if (isInheritableIn)
\r
345 *pHandleIn = hTemporaryIn;
\r
348 /* Make the read end non-inheritable */
\r
349 status = DuplicateHandle(GetCurrentProcess(), hTemporaryIn,
\r
350 GetCurrentProcess(), pHandleIn,
\r
352 FALSE, /* non-inheritable */
\r
353 DUPLICATE_SAME_ACCESS);
\r
354 CloseHandle(hTemporaryIn);
\r
359 *pHandleOut = NULL;
\r
360 CloseHandle(hTemporaryOut);
\r
365 if (isInheritableOut)
\r
366 *pHandleOut = hTemporaryOut;
\r
369 /* Make the write end non-inheritable */
\r
370 status = DuplicateHandle(GetCurrentProcess(), hTemporaryOut,
\r
371 GetCurrentProcess(), pHandleOut,
\r
373 FALSE, /* non-inheritable */
\r
374 DUPLICATE_SAME_ACCESS);
\r
375 CloseHandle(hTemporaryOut);
\r
380 *pHandleOut = NULL;
\r
381 CloseHandle(*pHandleIn);
\r
390 runProcess (char *cmd, char *workingDirectory, void *environment,
\r
391 int fdStdInput, int fdStdOutput, int fdStdError)
\r
394 PROCESS_INFORMATION pInfo;
\r
397 ZeroMemory(&sInfo, sizeof(sInfo));
\r
398 sInfo.cb = sizeof(sInfo);
\r
399 sInfo.dwFlags = STARTF_USESTDHANDLES;
\r
400 sInfo.hStdInput = (HANDLE) _get_osfhandle(fdStdInput);
\r
401 sInfo.hStdOutput= (HANDLE) _get_osfhandle(fdStdOutput);
\r
402 sInfo.hStdError = (HANDLE) _get_osfhandle(fdStdError);
\r
404 if (sInfo.hStdOutput != GetStdHandle(STD_OUTPUT_HANDLE) &&
\r
405 sInfo.hStdError != GetStdHandle(STD_ERROR_HANDLE))
\r
406 flags = CREATE_NO_WINDOW; // Run without console window only when both output and error are redirected
\r
410 if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, environment, workingDirectory, &sInfo, &pInfo))
\r
416 CloseHandle(pInfo.hThread);
\r
417 return (ProcHandle)pInfo.hProcess;
\r
421 runInteractiveProcess (char *cmd, char *workingDirectory, void *environment,
\r
422 int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)
\r
425 PROCESS_INFORMATION pInfo;
\r
426 HANDLE hStdInputRead, hStdInputWrite;
\r
427 HANDLE hStdOutputRead, hStdOutputWrite;
\r
428 HANDLE hStdErrorRead, hStdErrorWrite;
\r
430 if (!mkAnonPipe(&hStdInputRead, TRUE, &hStdInputWrite, FALSE))
\r
433 if (!mkAnonPipe(&hStdOutputRead, FALSE, &hStdOutputWrite, TRUE))
\r
435 CloseHandle(hStdInputRead);
\r
436 CloseHandle(hStdInputWrite);
\r
440 if (!mkAnonPipe(&hStdErrorRead, FALSE, &hStdErrorWrite, TRUE))
\r
442 CloseHandle(hStdInputRead);
\r
443 CloseHandle(hStdInputWrite);
\r
444 CloseHandle(hStdOutputRead);
\r
445 CloseHandle(hStdOutputWrite);
\r
449 ZeroMemory(&sInfo, sizeof(sInfo));
\r
450 sInfo.cb = sizeof(sInfo);
\r
451 sInfo.dwFlags = STARTF_USESTDHANDLES;
\r
452 sInfo.hStdInput = hStdInputRead;
\r
453 sInfo.hStdOutput= hStdOutputWrite;
\r
454 sInfo.hStdError = hStdErrorWrite;
\r
456 if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, environment, workingDirectory, &sInfo, &pInfo))
\r
459 CloseHandle(hStdInputRead);
\r
460 CloseHandle(hStdInputWrite);
\r
461 CloseHandle(hStdOutputRead);
\r
462 CloseHandle(hStdOutputWrite);
\r
463 CloseHandle(hStdErrorRead);
\r
464 CloseHandle(hStdErrorWrite);
\r
467 CloseHandle(pInfo.hThread);
\r
469 // Close the ends of the pipes that were inherited by the
\r
470 // child process. This is important, otherwise we won't see
\r
471 // EOF on these pipes when the child process exits.
\r
472 CloseHandle(hStdInputRead);
\r
473 CloseHandle(hStdOutputWrite);
\r
474 CloseHandle(hStdErrorWrite);
\r
476 *pfdStdInput = _open_osfhandle((intptr_t) hStdInputWrite, _O_WRONLY);
\r
477 *pfdStdOutput = _open_osfhandle((intptr_t) hStdOutputRead, _O_RDONLY);
\r
478 *pfdStdError = _open_osfhandle((intptr_t) hStdErrorRead, _O_RDONLY);
\r
480 return (int) pInfo.hProcess;
\r
484 terminateProcess (ProcHandle handle)
\r
486 if (!TerminateProcess((HANDLE) handle, 1)) {
\r
491 CloseHandle((HANDLE) handle);
\r
496 getProcessExitCode (ProcHandle handle, int *pExitCode)
\r
500 if (WaitForSingleObject((HANDLE) handle, 1) == WAIT_OBJECT_0)
\r
502 if (GetExitCodeProcess((HANDLE) handle, (DWORD *) pExitCode) == 0)
\r
508 CloseHandle((HANDLE) handle);
\r
516 waitForProcess (ProcHandle handle)
\r
520 if (WaitForSingleObject((HANDLE) handle, INFINITE) == WAIT_OBJECT_0)
\r
522 if (GetExitCodeProcess((HANDLE) handle, &retCode) == 0)
\r
528 CloseHandle((HANDLE) handle);
\r