-/* ----------------------------------------------------------------------------\r
- (c) The University of Glasgow 2004\r
- \r
- Support for System.Process\r
- ------------------------------------------------------------------------- */\r
-\r
-#include "HsBase.h"\r
-\r
-#if defined(mingw32_HOST_OS)\r
-#include <windows.h>\r
-#include <stdlib.h>\r
-#endif\r
-\r
-#ifdef HAVE_VFORK_H\r
-#include <vfork.h>\r
-#endif\r
-\r
-#ifdef HAVE_VFORK\r
-#define fork vfork\r
-#endif\r
-\r
-#ifdef HAVE_SIGNAL_H\r
-#include <signal.h>\r
-#endif\r
-\r
-#if !defined(mingw32_HOST_OS) && !defined(__MINGW32__)\r
-/* ----------------------------------------------------------------------------\r
- UNIX versions\r
- ------------------------------------------------------------------------- */\r
-\r
-ProcHandle\r
-runProcess (char *const args[], char *workingDirectory, char **environment, \r
- int fdStdInput, int fdStdOutput, int fdStdError)\r
-{\r
- int pid;\r
- struct sigaction dfl;\r
-\r
- switch(pid = fork())\r
- {\r
- case -1:\r
- return -1;\r
- \r
- case 0:\r
- {\r
- pPrPr_disableITimers();\r
- \r
- if (workingDirectory) {\r
- chdir (workingDirectory);\r
- }\r
- \r
- /*\r
- * Restore SIGINT and SIGQUIT to default actions\r
- *\r
- * Glyn Clemments writes:\r
- * For your purposes, runProcess + waitForProcess is probably\r
- * the way to go. Except that runProcess appears to be missing\r
- * the usual signal handling. system() ignores SIGINT and\r
- * SIGQUIT in the parent, and resets them to their defaults in\r
- * the child; it also blocks SIGCHLD in the parent. runProcess\r
- * may need to do something similar; it should probably at\r
- * least reset SIGINT and SIGQUIT in the child, in case they\r
- * are ignored in the parent. The parent can set up its own\r
- * signal handling, but the only place it can control the\r
- * child's signal handling is between the fork() and the\r
- * exec(), so if runProcess doesn't do it, it won't get done.\r
- */\r
- dfl.sa_handler = SIG_DFL;\r
- (void)sigemptyset(&dfl.sa_mask);\r
- dfl.sa_flags = 0;\r
- (void)sigaction(SIGINT, &dfl, NULL);\r
- (void)sigaction(SIGQUIT, &dfl, NULL);\r
-\r
- dup2 (fdStdInput, STDIN_FILENO);\r
- dup2 (fdStdOutput, STDOUT_FILENO);\r
- dup2 (fdStdError, STDERR_FILENO);\r
- \r
- if (environment) {\r
- execvpe(args[0], args, environment);\r
- } else {\r
- execvp(args[0], args);\r
- }\r
- }\r
- _exit(127);\r
- }\r
- \r
- return pid;\r
-}\r
-\r
-ProcHandle\r
-runInteractiveProcess (char *const args[], \r
- char *workingDirectory, char **environment,\r
- int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)\r
-{\r
- int pid;\r
- int fdStdInput[2], fdStdOutput[2], fdStdError[2];\r
-\r
- pipe(fdStdInput);\r
- pipe(fdStdOutput);\r
- pipe(fdStdError);\r
-\r
- switch(pid = fork())\r
- {\r
- case -1:\r
- close(fdStdInput[0]);\r
- close(fdStdInput[1]);\r
- close(fdStdOutput[0]);\r
- close(fdStdOutput[1]);\r
- close(fdStdError[0]);\r
- close(fdStdError[1]);\r
- return -1;\r
- \r
- case 0:\r
- {\r
- pPrPr_disableITimers();\r
- \r
- if (workingDirectory) {\r
- chdir (workingDirectory);\r
- }\r
- \r
- dup2 (fdStdInput[0], STDIN_FILENO);\r
- dup2 (fdStdOutput[1], STDOUT_FILENO);\r
- dup2 (fdStdError[1], STDERR_FILENO);\r
- \r
- close(fdStdInput[0]);\r
- close(fdStdInput[1]);\r
- close(fdStdOutput[0]);\r
- close(fdStdOutput[1]);\r
- close(fdStdError[0]);\r
- close(fdStdError[1]);\r
- \r
- /* the child */\r
- if (environment) {\r
- execvpe(args[0], args, environment);\r
- } else {\r
- execvp(args[0], args);\r
- }\r
- }\r
- _exit(127);\r
- \r
- default:\r
- close(fdStdInput[0]);\r
- close(fdStdOutput[1]);\r
- close(fdStdError[1]);\r
- \r
- *pfdStdInput = fdStdInput[1];\r
- *pfdStdOutput = fdStdOutput[0];\r
- *pfdStdError = fdStdError[0];\r
- break;\r
- }\r
- \r
- return pid;\r
-}\r
-\r
-int\r
-terminateProcess (ProcHandle handle)\r
-{\r
- return (kill(handle, SIGTERM) == 0);\r
-}\r
-\r
-int\r
-getProcessExitCode (ProcHandle handle, int *pExitCode)\r
-{\r
- int wstat;\r
- \r
- *pExitCode = 0;\r
- \r
- if (waitpid(handle, &wstat, WNOHANG) > 0)\r
- {\r
- if (WIFEXITED(wstat))\r
- {\r
- *pExitCode = WEXITSTATUS(wstat);\r
- return 1;\r
- }\r
- else\r
- if (WIFSIGNALED(wstat))\r
- {\r
- errno = EINTR;\r
- return -1;\r
- }\r
- else\r
- {\r
- /* This should never happen */\r
- }\r
- }\r
- \r
- return 0;\r
-}\r
-\r
-int waitForProcess (ProcHandle handle)\r
-{\r
- int wstat;\r
- \r
- while (waitpid(handle, &wstat, 0) < 0)\r
- {\r
- if (errno != EINTR)\r
- {\r
- return -1;\r
- }\r
- }\r
- \r
- if (WIFEXITED(wstat))\r
- return WEXITSTATUS(wstat);\r
- else\r
- if (WIFSIGNALED(wstat))\r
- {\r
- errno = EINTR;\r
- }\r
- else\r
- {\r
- /* This should never happen */\r
- }\r
- \r
- return -1;\r
-}\r
-\r
-#else\r
-/* ----------------------------------------------------------------------------\r
- Win32 versions\r
- ------------------------------------------------------------------------- */\r
-\r
-/* -------------------- WINDOWS VERSION --------------------- */\r
-\r
-/* This is the error table that defines the mapping between OS error\r
- codes and errno values */\r
-\r
-struct errentry {\r
- unsigned long oscode; /* OS return value */\r
- int errnocode; /* System V error code */\r
-};\r
-\r
-static struct errentry errtable[] = {\r
- { ERROR_INVALID_FUNCTION, EINVAL }, /* 1 */\r
- { ERROR_FILE_NOT_FOUND, ENOENT }, /* 2 */\r
- { ERROR_PATH_NOT_FOUND, ENOENT }, /* 3 */\r
- { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, /* 4 */\r
- { ERROR_ACCESS_DENIED, EACCES }, /* 5 */\r
- { ERROR_INVALID_HANDLE, EBADF }, /* 6 */\r
- { ERROR_ARENA_TRASHED, ENOMEM }, /* 7 */\r
- { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, /* 8 */\r
- { ERROR_INVALID_BLOCK, ENOMEM }, /* 9 */\r
- { ERROR_BAD_ENVIRONMENT, E2BIG }, /* 10 */\r
- { ERROR_BAD_FORMAT, ENOEXEC }, /* 11 */\r
- { ERROR_INVALID_ACCESS, EINVAL }, /* 12 */\r
- { ERROR_INVALID_DATA, EINVAL }, /* 13 */\r
- { ERROR_INVALID_DRIVE, ENOENT }, /* 15 */\r
- { ERROR_CURRENT_DIRECTORY, EACCES }, /* 16 */\r
- { ERROR_NOT_SAME_DEVICE, EXDEV }, /* 17 */\r
- { ERROR_NO_MORE_FILES, ENOENT }, /* 18 */\r
- { ERROR_LOCK_VIOLATION, EACCES }, /* 33 */\r
- { ERROR_BAD_NETPATH, ENOENT }, /* 53 */\r
- { ERROR_NETWORK_ACCESS_DENIED, EACCES }, /* 65 */\r
- { ERROR_BAD_NET_NAME, ENOENT }, /* 67 */\r
- { ERROR_FILE_EXISTS, EEXIST }, /* 80 */\r
- { ERROR_CANNOT_MAKE, EACCES }, /* 82 */\r
- { ERROR_FAIL_I24, EACCES }, /* 83 */\r
- { ERROR_INVALID_PARAMETER, EINVAL }, /* 87 */\r
- { ERROR_NO_PROC_SLOTS, EAGAIN }, /* 89 */\r
- { ERROR_DRIVE_LOCKED, EACCES }, /* 108 */\r
- { ERROR_BROKEN_PIPE, EPIPE }, /* 109 */\r
- { ERROR_DISK_FULL, ENOSPC }, /* 112 */\r
- { ERROR_INVALID_TARGET_HANDLE, EBADF }, /* 114 */\r
- { ERROR_INVALID_HANDLE, EINVAL }, /* 124 */\r
- { ERROR_WAIT_NO_CHILDREN, ECHILD }, /* 128 */\r
- { ERROR_CHILD_NOT_COMPLETE, ECHILD }, /* 129 */\r
- { ERROR_DIRECT_ACCESS_HANDLE, EBADF }, /* 130 */\r
- { ERROR_NEGATIVE_SEEK, EINVAL }, /* 131 */\r
- { ERROR_SEEK_ON_DEVICE, EACCES }, /* 132 */\r
- { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, /* 145 */\r
- { ERROR_NOT_LOCKED, EACCES }, /* 158 */\r
- { ERROR_BAD_PATHNAME, ENOENT }, /* 161 */\r
- { ERROR_MAX_THRDS_REACHED, EAGAIN }, /* 164 */\r
- { ERROR_LOCK_FAILED, EACCES }, /* 167 */\r
- { ERROR_ALREADY_EXISTS, EEXIST }, /* 183 */\r
- { ERROR_FILENAME_EXCED_RANGE, ENOENT }, /* 206 */\r
- { ERROR_NESTING_NOT_ALLOWED, EAGAIN }, /* 215 */\r
- { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } /* 1816 */\r
-};\r
-\r
-/* size of the table */\r
-#define ERRTABLESIZE (sizeof(errtable)/sizeof(errtable[0]))\r
-\r
-/* The following two constants must be the minimum and maximum\r
- values in the (contiguous) range of Exec Failure errors. */\r
-#define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG\r
-#define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN\r
-\r
-/* These are the low and high value in the range of errors that are\r
- access violations */\r
-#define MIN_EACCES_RANGE ERROR_WRITE_PROTECT\r
-#define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED\r
-\r
-static void maperrno (void)\r
-{\r
- int i;\r
- DWORD dwErrorCode;\r
-\r
- dwErrorCode = GetLastError();\r
-\r
- /* check the table for the OS error code */\r
- for (i = 0; i < ERRTABLESIZE; ++i)\r
- {\r
- if (dwErrorCode == errtable[i].oscode)\r
- {\r
- errno = errtable[i].errnocode;\r
- return;\r
- }\r
- }\r
-\r
- /* The error code wasn't in the table. We check for a range of */\r
- /* EACCES errors or exec failure errors (ENOEXEC). Otherwise */\r
- /* EINVAL is returned. */\r
-\r
- if (dwErrorCode >= MIN_EACCES_RANGE && dwErrorCode <= MAX_EACCES_RANGE)\r
- errno = EACCES;\r
- else\r
- if (dwErrorCode >= MIN_EXEC_ERROR && dwErrorCode <= MAX_EXEC_ERROR)\r
- errno = ENOEXEC;\r
- else\r
- errno = EINVAL;\r
-}\r
-\r
-/*\r
- * Function: mkAnonPipe\r
- *\r
- * Purpose: create an anonymous pipe with read and write ends being\r
- * optionally (non-)inheritable.\r
- */\r
-static BOOL\r
-mkAnonPipe (HANDLE* pHandleIn, BOOL isInheritableIn, \r
- HANDLE* pHandleOut, BOOL isInheritableOut)\r
-{\r
- HANDLE hTemporaryIn = NULL;\r
- HANDLE hTemporaryOut = NULL;\r
- BOOL status;\r
- SECURITY_ATTRIBUTES sec_attrs;\r
-\r
- /* Create inheritable security attributes */\r
- sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);\r
- sec_attrs.lpSecurityDescriptor = NULL;\r
- sec_attrs.bInheritHandle = TRUE;\r
-\r
- /* Create the anon pipe with both ends inheritable */\r
- if (!CreatePipe(&hTemporaryIn, &hTemporaryOut, &sec_attrs, 0))\r
- {\r
- maperrno();\r
- *pHandleIn = NULL;\r
- *pHandleOut = NULL;\r
- return FALSE;\r
- }\r
-\r
- if (isInheritableIn)\r
- *pHandleIn = hTemporaryIn;\r
- else\r
- {\r
- /* Make the read end non-inheritable */\r
- status = DuplicateHandle(GetCurrentProcess(), hTemporaryIn,\r
- GetCurrentProcess(), pHandleIn,\r
- 0,\r
- FALSE, /* non-inheritable */\r
- DUPLICATE_SAME_ACCESS);\r
- CloseHandle(hTemporaryIn);\r
- if (!status)\r
- {\r
- maperrno();\r
- *pHandleIn = NULL;\r
- *pHandleOut = NULL;\r
- CloseHandle(hTemporaryOut);\r
- return FALSE;\r
- }\r
- }\r
-\r
- if (isInheritableOut)\r
- *pHandleOut = hTemporaryOut;\r
- else\r
- {\r
- /* Make the write end non-inheritable */\r
- status = DuplicateHandle(GetCurrentProcess(), hTemporaryOut,\r
- GetCurrentProcess(), pHandleOut,\r
- 0,\r
- FALSE, /* non-inheritable */\r
- DUPLICATE_SAME_ACCESS);\r
- CloseHandle(hTemporaryOut);\r
- if (!status)\r
- {\r
- maperrno();\r
- *pHandleIn = NULL;\r
- *pHandleOut = NULL;\r
- CloseHandle(*pHandleIn);\r
- return FALSE;\r
- }\r
- }\r
-\r
- return TRUE;\r
-}\r
-\r
-ProcHandle\r
-runProcess (char *cmd, char *workingDirectory, void *environment,\r
- int fdStdInput, int fdStdOutput, int fdStdError)\r
-{\r
- STARTUPINFO sInfo;\r
- PROCESS_INFORMATION pInfo;\r
- DWORD flags;\r
-\r
- ZeroMemory(&sInfo, sizeof(sInfo));\r
- sInfo.cb = sizeof(sInfo);\r
- sInfo.dwFlags = STARTF_USESTDHANDLES;\r
- sInfo.hStdInput = (HANDLE) _get_osfhandle(fdStdInput);\r
- sInfo.hStdOutput= (HANDLE) _get_osfhandle(fdStdOutput);\r
- sInfo.hStdError = (HANDLE) _get_osfhandle(fdStdError);\r
-\r
- if (sInfo.hStdOutput != GetStdHandle(STD_OUTPUT_HANDLE) &&\r
- sInfo.hStdError != GetStdHandle(STD_ERROR_HANDLE))\r
- flags = CREATE_NO_WINDOW; // Run without console window only when both output and error are redirected\r
- else\r
- flags = 0;\r
-\r
- if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, environment, workingDirectory, &sInfo, &pInfo))\r
- {\r
- maperrno();\r
- return -1;\r
- }\r
-\r
- CloseHandle(pInfo.hThread);\r
- return (ProcHandle)pInfo.hProcess;\r
-}\r
-\r
-ProcHandle\r
-runInteractiveProcess (char *cmd, char *workingDirectory, void *environment,\r
- int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)\r
-{\r
- STARTUPINFO sInfo;\r
- PROCESS_INFORMATION pInfo;\r
- HANDLE hStdInputRead, hStdInputWrite;\r
- HANDLE hStdOutputRead, hStdOutputWrite;\r
- HANDLE hStdErrorRead, hStdErrorWrite;\r
-\r
- if (!mkAnonPipe(&hStdInputRead, TRUE, &hStdInputWrite, FALSE))\r
- return -1;\r
-\r
- if (!mkAnonPipe(&hStdOutputRead, FALSE, &hStdOutputWrite, TRUE))\r
- {\r
- CloseHandle(hStdInputRead);\r
- CloseHandle(hStdInputWrite);\r
- return -1;\r
- }\r
-\r
- if (!mkAnonPipe(&hStdErrorRead, FALSE, &hStdErrorWrite, TRUE))\r
- {\r
- CloseHandle(hStdInputRead);\r
- CloseHandle(hStdInputWrite);\r
- CloseHandle(hStdOutputRead);\r
- CloseHandle(hStdOutputWrite);\r
- return -1;\r
- }\r
-\r
- ZeroMemory(&sInfo, sizeof(sInfo));\r
- sInfo.cb = sizeof(sInfo);\r
- sInfo.dwFlags = STARTF_USESTDHANDLES;\r
- sInfo.hStdInput = hStdInputRead;\r
- sInfo.hStdOutput= hStdOutputWrite;\r
- sInfo.hStdError = hStdErrorWrite;\r
-\r
- if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, environment, workingDirectory, &sInfo, &pInfo))\r
- {\r
- maperrno();\r
- CloseHandle(hStdInputRead);\r
- CloseHandle(hStdInputWrite);\r
- CloseHandle(hStdOutputRead);\r
- CloseHandle(hStdOutputWrite);\r
- CloseHandle(hStdErrorRead);\r
- CloseHandle(hStdErrorWrite);\r
- return -1;\r
- }\r
- CloseHandle(pInfo.hThread);\r
-\r
- // Close the ends of the pipes that were inherited by the\r
- // child process. This is important, otherwise we won't see\r
- // EOF on these pipes when the child process exits.\r
- CloseHandle(hStdInputRead);\r
- CloseHandle(hStdOutputWrite);\r
- CloseHandle(hStdErrorWrite);\r
-\r
- *pfdStdInput = _open_osfhandle((intptr_t) hStdInputWrite, _O_WRONLY);\r
- *pfdStdOutput = _open_osfhandle((intptr_t) hStdOutputRead, _O_RDONLY);\r
- *pfdStdError = _open_osfhandle((intptr_t) hStdErrorRead, _O_RDONLY);\r
-\r
- return (int) pInfo.hProcess;\r
-}\r
-\r
-int\r
-terminateProcess (ProcHandle handle)\r
-{\r
- if (!TerminateProcess((HANDLE) handle, 1)) {\r
- maperrno();\r
- return -1;\r
- }\r
-\r
- CloseHandle((HANDLE) handle);\r
- return 0;\r
-}\r
-\r
-int\r
-getProcessExitCode (ProcHandle handle, int *pExitCode)\r
-{\r
- *pExitCode = 0;\r
-\r
- if (WaitForSingleObject((HANDLE) handle, 1) == WAIT_OBJECT_0)\r
- {\r
- if (GetExitCodeProcess((HANDLE) handle, (DWORD *) pExitCode) == 0)\r
- {\r
- maperrno();\r
- return -1;\r
- }\r
- \r
- CloseHandle((HANDLE) handle);\r
- return 1;\r
- }\r
- \r
- return 0;\r
-}\r
-\r
-int\r
-waitForProcess (ProcHandle handle)\r
-{\r
- DWORD retCode;\r
-\r
- if (WaitForSingleObject((HANDLE) handle, INFINITE) == WAIT_OBJECT_0)\r
- {\r
- if (GetExitCodeProcess((HANDLE) handle, &retCode) == 0)\r
- {\r
- maperrno();\r
- return -1;\r
- }\r
- \r
- CloseHandle((HANDLE) handle);\r
- return retCode;\r
- }\r
- \r
- maperrno();\r
- return -1;\r
-}\r
-\r
-#endif // Win32\r
+/* ----------------------------------------------------------------------------
+ (c) The University of Glasgow 2004
+
+ Support for System.Process
+ ------------------------------------------------------------------------- */
+
+#include "HsBase.h"
+
+#if defined(mingw32_HOST_OS)
+#include <windows.h>
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_VFORK_H
+#include <vfork.h>
+#endif
+
+#ifdef HAVE_VFORK
+#define fork vfork
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#if !defined(mingw32_HOST_OS) && !defined(__MINGW32__)
+/* ----------------------------------------------------------------------------
+ UNIX versions
+ ------------------------------------------------------------------------- */
+
+ProcHandle
+runProcess (char *const args[], char *workingDirectory, char **environment,
+ int fdStdInput, int fdStdOutput, int fdStdError,
+ int set_inthandler, long inthandler,
+ int set_quithandler, long quithandler)
+{
+ int pid;
+ struct sigaction dfl;
+
+ switch(pid = fork())
+ {
+ case -1:
+ return -1;
+
+ case 0:
+ {
+ pPrPr_disableITimers();
+
+ if (workingDirectory) {
+ if (chdir (workingDirectory) < 0) {
+ return -1;
+ }
+ }
+
+ /* Set the SIGINT/SIGQUIT signal handlers in the child, if requested
+ */
+ (void)sigemptyset(&dfl.sa_mask);
+ dfl.sa_flags = 0;
+ if (set_inthandler) {
+ dfl.sa_handler = (void *)inthandler;
+ (void)sigaction(SIGINT, &dfl, NULL);
+ }
+ if (set_quithandler) {
+ dfl.sa_handler = (void *)quithandler;
+ (void)sigaction(SIGQUIT, &dfl, NULL);
+ }
+
+ dup2 (fdStdInput, STDIN_FILENO);
+ dup2 (fdStdOutput, STDOUT_FILENO);
+ dup2 (fdStdError, STDERR_FILENO);
+
+ if (environment) {
+ execvpe(args[0], args, environment);
+ } else {
+ execvp(args[0], args);
+ }
+ }
+ _exit(127);
+ }
+
+ return pid;
+}
+
+ProcHandle
+runInteractiveProcess (char *const args[],
+ char *workingDirectory, char **environment,
+ int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)
+{
+ int pid;
+ int fdStdInput[2], fdStdOutput[2], fdStdError[2];
+
+ pipe(fdStdInput);
+ pipe(fdStdOutput);
+ pipe(fdStdError);
+
+ switch(pid = fork())
+ {
+ case -1:
+ close(fdStdInput[0]);
+ close(fdStdInput[1]);
+ close(fdStdOutput[0]);
+ close(fdStdOutput[1]);
+ close(fdStdError[0]);
+ close(fdStdError[1]);
+ return -1;
+
+ case 0:
+ {
+ pPrPr_disableITimers();
+
+ if (workingDirectory) {
+ if (chdir (workingDirectory) < 0) {
+ return -1;
+ }
+ }
+
+ if (fdStdInput[0] != STDIN_FILENO) {
+ dup2 (fdStdInput[0], STDIN_FILENO);
+ close(fdStdInput[0]);
+ }
+
+ if (fdStdOutput[1] != STDOUT_FILENO) {
+ dup2 (fdStdOutput[1], STDOUT_FILENO);
+ close(fdStdOutput[1]);
+ }
+
+ if (fdStdError[1] != STDERR_FILENO) {
+ dup2 (fdStdError[1], STDERR_FILENO);
+ close(fdStdError[1]);
+ }
+
+ close(fdStdInput[1]);
+ close(fdStdOutput[0]);
+ close(fdStdError[0]);
+
+ /* the child */
+ if (environment) {
+ execvpe(args[0], args, environment);
+ } else {
+ execvp(args[0], args);
+ }
+ }
+ _exit(127);
+
+ default:
+ close(fdStdInput[0]);
+ close(fdStdOutput[1]);
+ close(fdStdError[1]);
+
+ *pfdStdInput = fdStdInput[1];
+ *pfdStdOutput = fdStdOutput[0];
+ *pfdStdError = fdStdError[0];
+ break;
+ }
+
+ return pid;
+}
+
+int
+terminateProcess (ProcHandle handle)
+{
+ return (kill(handle, SIGTERM) == 0);
+}
+
+int
+getProcessExitCode (ProcHandle handle, int *pExitCode)
+{
+ int wstat, res;
+
+ *pExitCode = 0;
+
+ if ((res = waitpid(handle, &wstat, WNOHANG)) > 0)
+ {
+ if (WIFEXITED(wstat))
+ {
+ *pExitCode = WEXITSTATUS(wstat);
+ return 1;
+ }
+ else
+ if (WIFSIGNALED(wstat))
+ {
+ errno = EINTR;
+ return -1;
+ }
+ else
+ {
+ /* This should never happen */
+ }
+ }
+
+ if (res == 0) return 0;
+
+ if (errno == ECHILD)
+ {
+ *pExitCode = 0;
+ return 1;
+ }
+
+ return -1;
+}
+
+int waitForProcess (ProcHandle handle)
+{
+ int wstat;
+
+ while (waitpid(handle, &wstat, 0) < 0)
+ {
+ if (errno != EINTR)
+ {
+ return -1;
+ }
+ }
+
+ if (WIFEXITED(wstat))
+ return WEXITSTATUS(wstat);
+ else
+ if (WIFSIGNALED(wstat))
+ {
+ return wstat;
+ }
+ else
+ {
+ /* This should never happen */
+ }
+
+ return -1;
+}
+
+#else
+/* ----------------------------------------------------------------------------
+ Win32 versions
+ ------------------------------------------------------------------------- */
+
+/* -------------------- WINDOWS VERSION --------------------- */
+
+/* This is the error table that defines the mapping between OS error
+ codes and errno values */
+
+struct errentry {
+ unsigned long oscode; /* OS return value */
+ int errnocode; /* System V error code */
+};
+
+static struct errentry errtable[] = {
+ { ERROR_INVALID_FUNCTION, EINVAL }, /* 1 */
+ { ERROR_FILE_NOT_FOUND, ENOENT }, /* 2 */
+ { ERROR_PATH_NOT_FOUND, ENOENT }, /* 3 */
+ { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, /* 4 */
+ { ERROR_ACCESS_DENIED, EACCES }, /* 5 */
+ { ERROR_INVALID_HANDLE, EBADF }, /* 6 */
+ { ERROR_ARENA_TRASHED, ENOMEM }, /* 7 */
+ { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, /* 8 */
+ { ERROR_INVALID_BLOCK, ENOMEM }, /* 9 */
+ { ERROR_BAD_ENVIRONMENT, E2BIG }, /* 10 */
+ { ERROR_BAD_FORMAT, ENOEXEC }, /* 11 */
+ { ERROR_INVALID_ACCESS, EINVAL }, /* 12 */
+ { ERROR_INVALID_DATA, EINVAL }, /* 13 */
+ { ERROR_INVALID_DRIVE, ENOENT }, /* 15 */
+ { ERROR_CURRENT_DIRECTORY, EACCES }, /* 16 */
+ { ERROR_NOT_SAME_DEVICE, EXDEV }, /* 17 */
+ { ERROR_NO_MORE_FILES, ENOENT }, /* 18 */
+ { ERROR_LOCK_VIOLATION, EACCES }, /* 33 */
+ { ERROR_BAD_NETPATH, ENOENT }, /* 53 */
+ { ERROR_NETWORK_ACCESS_DENIED, EACCES }, /* 65 */
+ { ERROR_BAD_NET_NAME, ENOENT }, /* 67 */
+ { ERROR_FILE_EXISTS, EEXIST }, /* 80 */
+ { ERROR_CANNOT_MAKE, EACCES }, /* 82 */
+ { ERROR_FAIL_I24, EACCES }, /* 83 */
+ { ERROR_INVALID_PARAMETER, EINVAL }, /* 87 */
+ { ERROR_NO_PROC_SLOTS, EAGAIN }, /* 89 */
+ { ERROR_DRIVE_LOCKED, EACCES }, /* 108 */
+ { ERROR_BROKEN_PIPE, EPIPE }, /* 109 */
+ { ERROR_DISK_FULL, ENOSPC }, /* 112 */
+ { ERROR_INVALID_TARGET_HANDLE, EBADF }, /* 114 */
+ { ERROR_INVALID_HANDLE, EINVAL }, /* 124 */
+ { ERROR_WAIT_NO_CHILDREN, ECHILD }, /* 128 */
+ { ERROR_CHILD_NOT_COMPLETE, ECHILD }, /* 129 */
+ { ERROR_DIRECT_ACCESS_HANDLE, EBADF }, /* 130 */
+ { ERROR_NEGATIVE_SEEK, EINVAL }, /* 131 */
+ { ERROR_SEEK_ON_DEVICE, EACCES }, /* 132 */
+ { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, /* 145 */
+ { ERROR_NOT_LOCKED, EACCES }, /* 158 */
+ { ERROR_BAD_PATHNAME, ENOENT }, /* 161 */
+ { ERROR_MAX_THRDS_REACHED, EAGAIN }, /* 164 */
+ { ERROR_LOCK_FAILED, EACCES }, /* 167 */
+ { ERROR_ALREADY_EXISTS, EEXIST }, /* 183 */
+ { ERROR_FILENAME_EXCED_RANGE, ENOENT }, /* 206 */
+ { ERROR_NESTING_NOT_ALLOWED, EAGAIN }, /* 215 */
+ { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } /* 1816 */
+};
+
+/* size of the table */
+#define ERRTABLESIZE (sizeof(errtable)/sizeof(errtable[0]))
+
+/* The following two constants must be the minimum and maximum
+ values in the (contiguous) range of Exec Failure errors. */
+#define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG
+#define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN
+
+/* These are the low and high value in the range of errors that are
+ access violations */
+#define MIN_EACCES_RANGE ERROR_WRITE_PROTECT
+#define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED
+
+static void maperrno (void)
+{
+ int i;
+ DWORD dwErrorCode;
+
+ dwErrorCode = GetLastError();
+
+ /* check the table for the OS error code */
+ for (i = 0; i < ERRTABLESIZE; ++i)
+ {
+ if (dwErrorCode == errtable[i].oscode)
+ {
+ errno = errtable[i].errnocode;
+ return;
+ }
+ }
+
+ /* The error code wasn't in the table. We check for a range of */
+ /* EACCES errors or exec failure errors (ENOEXEC). Otherwise */
+ /* EINVAL is returned. */
+
+ if (dwErrorCode >= MIN_EACCES_RANGE && dwErrorCode <= MAX_EACCES_RANGE)
+ errno = EACCES;
+ else
+ if (dwErrorCode >= MIN_EXEC_ERROR && dwErrorCode <= MAX_EXEC_ERROR)
+ errno = ENOEXEC;
+ else
+ errno = EINVAL;
+}
+
+/*
+ * Function: mkAnonPipe
+ *
+ * Purpose: create an anonymous pipe with read and write ends being
+ * optionally (non-)inheritable.
+ */
+static BOOL
+mkAnonPipe (HANDLE* pHandleIn, BOOL isInheritableIn,
+ HANDLE* pHandleOut, BOOL isInheritableOut)
+{
+ HANDLE hTemporaryIn = NULL;
+ HANDLE hTemporaryOut = NULL;
+ BOOL status;
+ SECURITY_ATTRIBUTES sec_attrs;
+
+ /* Create inheritable security attributes */
+ sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sec_attrs.lpSecurityDescriptor = NULL;
+ sec_attrs.bInheritHandle = TRUE;
+
+ /* Create the anon pipe with both ends inheritable */
+ if (!CreatePipe(&hTemporaryIn, &hTemporaryOut, &sec_attrs, 0))
+ {
+ maperrno();
+ *pHandleIn = NULL;
+ *pHandleOut = NULL;
+ return FALSE;
+ }
+
+ if (isInheritableIn)
+ *pHandleIn = hTemporaryIn;
+ else
+ {
+ /* Make the read end non-inheritable */
+ status = DuplicateHandle(GetCurrentProcess(), hTemporaryIn,
+ GetCurrentProcess(), pHandleIn,
+ 0,
+ FALSE, /* non-inheritable */
+ DUPLICATE_SAME_ACCESS);
+ CloseHandle(hTemporaryIn);
+ if (!status)
+ {
+ maperrno();
+ *pHandleIn = NULL;
+ *pHandleOut = NULL;
+ CloseHandle(hTemporaryOut);
+ return FALSE;
+ }
+ }
+
+ if (isInheritableOut)
+ *pHandleOut = hTemporaryOut;
+ else
+ {
+ /* Make the write end non-inheritable */
+ status = DuplicateHandle(GetCurrentProcess(), hTemporaryOut,
+ GetCurrentProcess(), pHandleOut,
+ 0,
+ FALSE, /* non-inheritable */
+ DUPLICATE_SAME_ACCESS);
+ CloseHandle(hTemporaryOut);
+ if (!status)
+ {
+ maperrno();
+ *pHandleIn = NULL;
+ *pHandleOut = NULL;
+ CloseHandle(*pHandleIn);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+ProcHandle
+runProcess (char *cmd, char *workingDirectory, void *environment,
+ int fdStdInput, int fdStdOutput, int fdStdError)
+{
+ STARTUPINFO sInfo;
+ PROCESS_INFORMATION pInfo;
+ DWORD flags;
+ char buffer[256];
+
+ ZeroMemory(&sInfo, sizeof(sInfo));
+ sInfo.cb = sizeof(sInfo);
+ sInfo.hStdInput = (HANDLE) _get_osfhandle(fdStdInput);
+ sInfo.hStdOutput= (HANDLE) _get_osfhandle(fdStdOutput);
+ sInfo.hStdError = (HANDLE) _get_osfhandle(fdStdError);
+
+ if (sInfo.hStdInput == INVALID_HANDLE_VALUE)
+ sInfo.hStdInput = NULL;
+ if (sInfo.hStdOutput == INVALID_HANDLE_VALUE)
+ sInfo.hStdOutput = NULL;
+ if (sInfo.hStdError == INVALID_HANDLE_VALUE)
+ sInfo.hStdError = NULL;
+
+ if (sInfo.hStdInput || sInfo.hStdOutput || sInfo.hStdError)
+ sInfo.dwFlags = STARTF_USESTDHANDLES;
+
+ if (sInfo.hStdInput != GetStdHandle(STD_INPUT_HANDLE) &&
+ sInfo.hStdOutput != GetStdHandle(STD_OUTPUT_HANDLE) &&
+ sInfo.hStdError != GetStdHandle(STD_ERROR_HANDLE))
+ flags = CREATE_NO_WINDOW; // Run without console window only when both output and error are redirected
+ else
+ flags = 0;
+
+ if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, environment, workingDirectory, &sInfo, &pInfo))
+ {
+ maperrno();
+ return -1;
+ }
+
+ CloseHandle(pInfo.hThread);
+ return (ProcHandle)pInfo.hProcess;
+}
+
+ProcHandle
+runInteractiveProcess (char *cmd, char *workingDirectory, void *environment,
+ int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)
+{
+ STARTUPINFO sInfo;
+ PROCESS_INFORMATION pInfo;
+ HANDLE hStdInputRead, hStdInputWrite;
+ HANDLE hStdOutputRead, hStdOutputWrite;
+ HANDLE hStdErrorRead, hStdErrorWrite;
+
+ if (!mkAnonPipe(&hStdInputRead, TRUE, &hStdInputWrite, FALSE))
+ return -1;
+
+ if (!mkAnonPipe(&hStdOutputRead, FALSE, &hStdOutputWrite, TRUE))
+ {
+ CloseHandle(hStdInputRead);
+ CloseHandle(hStdInputWrite);
+ return -1;
+ }
+
+ if (!mkAnonPipe(&hStdErrorRead, FALSE, &hStdErrorWrite, TRUE))
+ {
+ CloseHandle(hStdInputRead);
+ CloseHandle(hStdInputWrite);
+ CloseHandle(hStdOutputRead);
+ CloseHandle(hStdOutputWrite);
+ return -1;
+ }
+
+ ZeroMemory(&sInfo, sizeof(sInfo));
+ sInfo.cb = sizeof(sInfo);
+ sInfo.dwFlags = STARTF_USESTDHANDLES;
+ sInfo.hStdInput = hStdInputRead;
+ sInfo.hStdOutput= hStdOutputWrite;
+ sInfo.hStdError = hStdErrorWrite;
+
+ if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, environment, workingDirectory, &sInfo, &pInfo))
+ {
+ maperrno();
+ CloseHandle(hStdInputRead);
+ CloseHandle(hStdInputWrite);
+ CloseHandle(hStdOutputRead);
+ CloseHandle(hStdOutputWrite);
+ CloseHandle(hStdErrorRead);
+ CloseHandle(hStdErrorWrite);
+ return -1;
+ }
+ CloseHandle(pInfo.hThread);
+
+ // Close the ends of the pipes that were inherited by the
+ // child process. This is important, otherwise we won't see
+ // EOF on these pipes when the child process exits.
+ CloseHandle(hStdInputRead);
+ CloseHandle(hStdOutputWrite);
+ CloseHandle(hStdErrorWrite);
+
+ *pfdStdInput = _open_osfhandle((intptr_t) hStdInputWrite, _O_WRONLY);
+ *pfdStdOutput = _open_osfhandle((intptr_t) hStdOutputRead, _O_RDONLY);
+ *pfdStdError = _open_osfhandle((intptr_t) hStdErrorRead, _O_RDONLY);
+
+ return (int) pInfo.hProcess;
+}
+
+int
+terminateProcess (ProcHandle handle)
+{
+ if (!TerminateProcess((HANDLE) handle, 1)) {
+ maperrno();
+ return -1;
+ }
+ return 0;
+}
+
+int
+getProcessExitCode (ProcHandle handle, int *pExitCode)
+{
+ *pExitCode = 0;
+
+ if (WaitForSingleObject((HANDLE) handle, 1) == WAIT_OBJECT_0)
+ {
+ if (GetExitCodeProcess((HANDLE) handle, (DWORD *) pExitCode) == 0)
+ {
+ maperrno();
+ return -1;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+waitForProcess (ProcHandle handle)
+{
+ DWORD retCode;
+
+ if (WaitForSingleObject((HANDLE) handle, INFINITE) == WAIT_OBJECT_0)
+ {
+ if (GetExitCodeProcess((HANDLE) handle, &retCode) == 0)
+ {
+ maperrno();
+ return -1;
+ }
+ return retCode;
+ }
+
+ maperrno();
+ return -1;
+}
+
+#endif /* Win32 */