+/* ----------------------------------------------------------------------------\r
+ (c) The University of Glasgow 2004\r
+ \r
+ Support for System.Process\r
+ ------------------------------------------------------------------------- */\r
+\r
+#include "HsBase.h"\r
+\r
+#if defined(mingw32_TARGET_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_TARGET_OS) && !defined(__MINGW32__)\r
+/* ----------------------------------------------------------------------------\r
+ UNIX versions\r
+ ------------------------------------------------------------------------- */\r
+\r
+int\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