83586056f6ccf2ffc18942f842a7b7f87b02438a
[ghc-base.git] / cbits / runProcess.c
1 /* ----------------------------------------------------------------------------\r
2    (c) The University of Glasgow 2004\r
3    \r
4    Support for System.Process\r
5    ------------------------------------------------------------------------- */\r
6 \r
7 #include "HsBase.h"\r
8 \r
9 #if defined(mingw32_HOST_OS)\r
10 #include <windows.h>\r
11 #include <stdlib.h>\r
12 #endif\r
13 \r
14 #ifdef HAVE_VFORK_H\r
15 #include <vfork.h>\r
16 #endif\r
17 \r
18 #ifdef HAVE_VFORK\r
19 #define fork vfork\r
20 #endif\r
21 \r
22 #ifdef HAVE_SIGNAL_H\r
23 #include <signal.h>\r
24 #endif\r
25 \r
26 #if !defined(mingw32_HOST_OS) && !defined(__MINGW32__)\r
27 /* ----------------------------------------------------------------------------\r
28    UNIX versions\r
29    ------------------------------------------------------------------------- */\r
30 \r
31 ProcHandle\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
36 {\r
37     int pid;\r
38     struct sigaction dfl;\r
39 \r
40     switch(pid = fork())\r
41     {\r
42     case -1:\r
43         return -1;\r
44         \r
45     case 0:\r
46     {\r
47         pPrPr_disableITimers();\r
48         \r
49         if (workingDirectory) {\r
50             chdir (workingDirectory);\r
51         }\r
52         \r
53         /* Set the SIGINT/SIGQUIT signal handlers in the child, if requested \r
54          */\r
55         (void)sigemptyset(&dfl.sa_mask);\r
56         dfl.sa_flags = 0;\r
57         if (set_inthandler) {\r
58             dfl.sa_handler = (void *)inthandler;\r
59             (void)sigaction(SIGINT, &dfl, NULL);\r
60         }\r
61         if (set_quithandler) {\r
62             dfl.sa_handler = (void *)quithandler;\r
63             (void)sigaction(SIGQUIT,  &dfl, NULL);\r
64         }\r
65 \r
66         dup2 (fdStdInput,  STDIN_FILENO);\r
67         dup2 (fdStdOutput, STDOUT_FILENO);\r
68         dup2 (fdStdError,  STDERR_FILENO);\r
69         \r
70         if (environment) {\r
71             execvpe(args[0], args, environment);\r
72         } else {\r
73             execvp(args[0], args);\r
74         }\r
75     }\r
76     _exit(127);\r
77     }\r
78     \r
79     return pid;\r
80 }\r
81 \r
82 ProcHandle\r
83 runInteractiveProcess (char *const args[], \r
84                        char *workingDirectory, char **environment,\r
85                        int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)\r
86 {\r
87     int pid;\r
88     int fdStdInput[2], fdStdOutput[2], fdStdError[2];\r
89 \r
90     pipe(fdStdInput);\r
91     pipe(fdStdOutput);\r
92     pipe(fdStdError);\r
93 \r
94     switch(pid = fork())\r
95     {\r
96     case -1:\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
103         return -1;\r
104         \r
105     case 0:\r
106     {\r
107         pPrPr_disableITimers();\r
108         \r
109         if (workingDirectory) {\r
110             chdir (workingDirectory);\r
111         }\r
112         \r
113         dup2 (fdStdInput[0], STDIN_FILENO);\r
114         dup2 (fdStdOutput[1], STDOUT_FILENO);\r
115         dup2 (fdStdError[1], STDERR_FILENO);\r
116         \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
123         \r
124         /* the child */\r
125         if (environment) {\r
126             execvpe(args[0], args, environment);\r
127         } else {\r
128             execvp(args[0], args);\r
129         }\r
130     }\r
131     _exit(127);\r
132     \r
133     default:\r
134         close(fdStdInput[0]);\r
135         close(fdStdOutput[1]);\r
136         close(fdStdError[1]);\r
137         \r
138         *pfdStdInput  = fdStdInput[1];\r
139         *pfdStdOutput = fdStdOutput[0];\r
140         *pfdStdError  = fdStdError[0];\r
141         break;\r
142     }\r
143     \r
144     return pid;\r
145 }\r
146 \r
147 int\r
148 terminateProcess (ProcHandle handle)\r
149 {\r
150     return (kill(handle, SIGTERM) == 0);\r
151 }\r
152 \r
153 int\r
154 getProcessExitCode (ProcHandle handle, int *pExitCode)\r
155 {\r
156     int wstat;\r
157     \r
158     *pExitCode = 0;\r
159     \r
160     if (waitpid(handle, &wstat, WNOHANG) > 0)\r
161     {\r
162         if (WIFEXITED(wstat))\r
163         {\r
164             *pExitCode = WEXITSTATUS(wstat);\r
165             return 1;\r
166         }\r
167         else\r
168             if (WIFSIGNALED(wstat))\r
169             {\r
170                 errno = EINTR;\r
171                 return -1;\r
172             }\r
173             else\r
174             {\r
175                 /* This should never happen */\r
176             }\r
177     }\r
178     \r
179     return 0;\r
180 }\r
181 \r
182 int waitForProcess (ProcHandle handle)\r
183 {\r
184     int wstat;\r
185     \r
186     while (waitpid(handle, &wstat, 0) < 0)\r
187     {\r
188         if (errno != EINTR)\r
189         {\r
190             return -1;\r
191         }\r
192     }\r
193     \r
194     if (WIFEXITED(wstat))\r
195         return WEXITSTATUS(wstat);\r
196     else\r
197         if (WIFSIGNALED(wstat))\r
198         {\r
199             errno = EINTR;\r
200         }\r
201         else\r
202         {\r
203             /* This should never happen */\r
204         }\r
205     \r
206     return -1;\r
207 }\r
208 \r
209 #else\r
210 /* ----------------------------------------------------------------------------\r
211    Win32 versions\r
212    ------------------------------------------------------------------------- */\r
213 \r
214 /* -------------------- WINDOWS VERSION --------------------- */\r
215 \r
216 /* This is the error table that defines the mapping between OS error\r
217    codes and errno values */\r
218 \r
219 struct errentry {\r
220         unsigned long oscode;           /* OS return value */\r
221         int errnocode;  /* System V error code */\r
222 };\r
223 \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
270 };\r
271 \r
272 /* size of the table */\r
273 #define ERRTABLESIZE (sizeof(errtable)/sizeof(errtable[0]))\r
274 \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
279 \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
284 \r
285 static void maperrno (void)\r
286 {\r
287         int i;\r
288         DWORD dwErrorCode;\r
289 \r
290         dwErrorCode = GetLastError();\r
291 \r
292         /* check the table for the OS error code */\r
293         for (i = 0; i < ERRTABLESIZE; ++i)\r
294         {\r
295                 if (dwErrorCode == errtable[i].oscode)\r
296                 {\r
297                         errno = errtable[i].errnocode;\r
298                         return;\r
299                 }\r
300         }\r
301 \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
305 \r
306         if (dwErrorCode >= MIN_EACCES_RANGE && dwErrorCode <= MAX_EACCES_RANGE)\r
307                 errno = EACCES;\r
308         else\r
309                 if (dwErrorCode >= MIN_EXEC_ERROR && dwErrorCode <= MAX_EXEC_ERROR)\r
310                         errno = ENOEXEC;\r
311                 else\r
312                         errno = EINVAL;\r
313 }\r
314 \r
315 /*\r
316  * Function: mkAnonPipe\r
317  *\r
318  * Purpose:  create an anonymous pipe with read and write ends being\r
319  *           optionally (non-)inheritable.\r
320  */\r
321 static BOOL\r
322 mkAnonPipe (HANDLE* pHandleIn, BOOL isInheritableIn, \r
323             HANDLE* pHandleOut, BOOL isInheritableOut)\r
324 {\r
325         HANDLE hTemporaryIn  = NULL;\r
326         HANDLE hTemporaryOut = NULL;\r
327         BOOL status;\r
328         SECURITY_ATTRIBUTES sec_attrs;\r
329 \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
334 \r
335         /* Create the anon pipe with both ends inheritable */\r
336         if (!CreatePipe(&hTemporaryIn, &hTemporaryOut, &sec_attrs, 0))\r
337         {\r
338                 maperrno();\r
339                 *pHandleIn  = NULL;\r
340                 *pHandleOut = NULL;\r
341                 return FALSE;\r
342         }\r
343 \r
344         if (isInheritableIn)\r
345                 *pHandleIn = hTemporaryIn;\r
346         else\r
347         {\r
348                 /* Make the read end non-inheritable */\r
349                 status = DuplicateHandle(GetCurrentProcess(), hTemporaryIn,\r
350                               GetCurrentProcess(), pHandleIn,\r
351                               0,\r
352                               FALSE, /* non-inheritable */\r
353                               DUPLICATE_SAME_ACCESS);\r
354                 CloseHandle(hTemporaryIn);\r
355                 if (!status)\r
356                 {\r
357                         maperrno();\r
358                         *pHandleIn  = NULL;\r
359                         *pHandleOut = NULL;\r
360                         CloseHandle(hTemporaryOut);\r
361                         return FALSE;\r
362                 }\r
363         }\r
364 \r
365         if (isInheritableOut)\r
366                 *pHandleOut = hTemporaryOut;\r
367         else\r
368         {\r
369                 /* Make the write end non-inheritable */\r
370                 status = DuplicateHandle(GetCurrentProcess(), hTemporaryOut,\r
371                               GetCurrentProcess(), pHandleOut,\r
372                               0,\r
373                               FALSE, /* non-inheritable */\r
374                               DUPLICATE_SAME_ACCESS);\r
375                 CloseHandle(hTemporaryOut);\r
376                 if (!status)\r
377                 {\r
378                         maperrno();\r
379                         *pHandleIn  = NULL;\r
380                         *pHandleOut = NULL;\r
381                         CloseHandle(*pHandleIn);\r
382                 return FALSE;\r
383         }\r
384         }\r
385 \r
386         return TRUE;\r
387 }\r
388 \r
389 ProcHandle\r
390 runProcess (char *cmd, char *workingDirectory, void *environment,\r
391             int fdStdInput, int fdStdOutput, int fdStdError)\r
392 {\r
393         STARTUPINFO sInfo;\r
394         PROCESS_INFORMATION pInfo;\r
395         DWORD flags;\r
396 \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
403 \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
407         else\r
408                 flags = 0;\r
409 \r
410         if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, environment, workingDirectory, &sInfo, &pInfo))\r
411         {\r
412                 maperrno();\r
413                 return -1;\r
414         }\r
415 \r
416         CloseHandle(pInfo.hThread);\r
417         return (ProcHandle)pInfo.hProcess;\r
418 }\r
419 \r
420 ProcHandle\r
421 runInteractiveProcess (char *cmd, char *workingDirectory, void *environment,\r
422                        int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)\r
423 {\r
424         STARTUPINFO sInfo;\r
425         PROCESS_INFORMATION pInfo;\r
426         HANDLE hStdInputRead,  hStdInputWrite;\r
427         HANDLE hStdOutputRead, hStdOutputWrite;\r
428         HANDLE hStdErrorRead,  hStdErrorWrite;\r
429 \r
430         if (!mkAnonPipe(&hStdInputRead,  TRUE, &hStdInputWrite,  FALSE))\r
431                 return -1;\r
432 \r
433         if (!mkAnonPipe(&hStdOutputRead, FALSE, &hStdOutputWrite, TRUE))\r
434         {\r
435                 CloseHandle(hStdInputRead);\r
436                 CloseHandle(hStdInputWrite);\r
437                 return -1;\r
438         }\r
439 \r
440         if (!mkAnonPipe(&hStdErrorRead,  FALSE, &hStdErrorWrite,  TRUE))\r
441         {\r
442                 CloseHandle(hStdInputRead);\r
443                 CloseHandle(hStdInputWrite);\r
444                 CloseHandle(hStdOutputRead);\r
445                 CloseHandle(hStdOutputWrite);\r
446                 return -1;\r
447         }\r
448 \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
455 \r
456         if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, environment, workingDirectory, &sInfo, &pInfo))\r
457         {\r
458                 maperrno();\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
465                 return -1;\r
466         }\r
467         CloseHandle(pInfo.hThread);\r
468 \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
475 \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
479 \r
480         return (int) pInfo.hProcess;\r
481 }\r
482 \r
483 int\r
484 terminateProcess (ProcHandle handle)\r
485 {\r
486     if (!TerminateProcess((HANDLE) handle, 1)) {\r
487         maperrno();\r
488         return -1;\r
489     }\r
490 \r
491     CloseHandle((HANDLE) handle);\r
492     return 0;\r
493 }\r
494 \r
495 int\r
496 getProcessExitCode (ProcHandle handle, int *pExitCode)\r
497 {\r
498     *pExitCode = 0;\r
499 \r
500     if (WaitForSingleObject((HANDLE) handle, 1) == WAIT_OBJECT_0)\r
501     {\r
502         if (GetExitCodeProcess((HANDLE) handle, (DWORD *) pExitCode) == 0)\r
503         {\r
504             maperrno();\r
505             return -1;\r
506         }\r
507         \r
508         CloseHandle((HANDLE) handle);\r
509         return 1;\r
510     }\r
511     \r
512     return 0;\r
513 }\r
514 \r
515 int\r
516 waitForProcess (ProcHandle handle)\r
517 {\r
518     DWORD retCode;\r
519 \r
520     if (WaitForSingleObject((HANDLE) handle, INFINITE) == WAIT_OBJECT_0)\r
521     {\r
522         if (GetExitCodeProcess((HANDLE) handle, &retCode) == 0)\r
523         {\r
524             maperrno();\r
525             return -1;\r
526         }\r
527         \r
528         CloseHandle((HANDLE) handle);\r
529         return retCode;\r
530     }\r
531     \r
532     maperrno();\r
533     return -1;\r
534 }\r
535 \r
536 #endif // Win32\r