/* ---------------------------------------------------------------------------- (c) The University of Glasgow 2004 Support for System.Process ------------------------------------------------------------------------- */ #include "HsBase.h" #if defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32) #include #include #endif #ifdef HAVE_VFORK_H #include #endif #ifdef HAVE_VFORK #define fork vfork #endif #ifdef HAVE_SIGNAL_H #include #endif #if !(defined(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32)) /* ---------------------------------------------------------------------------- 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 */