[project @ 2005-03-07 10:40:44 by simonmar]
[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, res;\r
157     \r
158     *pExitCode = 0;\r
159     \r
160     if ((res = 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     if (res == 0) return 0;\r
180 \r
181     if (errno == ECHILD) \r
182     {\r
183             *pExitCode = 0;\r
184             return 1;\r
185     }\r
186 \r
187     return -1;\r
188 }\r
189 \r
190 int waitForProcess (ProcHandle handle)\r
191 {\r
192     int wstat;\r
193     \r
194     while (waitpid(handle, &wstat, 0) < 0)\r
195     {\r
196         if (errno != EINTR)\r
197         {\r
198             return -1;\r
199         }\r
200     }\r
201     \r
202     if (WIFEXITED(wstat))\r
203         return WEXITSTATUS(wstat);\r
204     else\r
205         if (WIFSIGNALED(wstat))\r
206         {\r
207             errno = EINTR;\r
208         }\r
209         else\r
210         {\r
211             /* This should never happen */\r
212         }\r
213     \r
214     return -1;\r
215 }\r
216 \r
217 #else\r
218 /* ----------------------------------------------------------------------------\r
219    Win32 versions\r
220    ------------------------------------------------------------------------- */\r
221 \r
222 /* -------------------- WINDOWS VERSION --------------------- */\r
223 \r
224 /* This is the error table that defines the mapping between OS error\r
225    codes and errno values */\r
226 \r
227 struct errentry {\r
228         unsigned long oscode;           /* OS return value */\r
229         int errnocode;  /* System V error code */\r
230 };\r
231 \r
232 static struct errentry errtable[] = {\r
233         {  ERROR_INVALID_FUNCTION,       EINVAL    },  /* 1 */\r
234         {  ERROR_FILE_NOT_FOUND,         ENOENT    },  /* 2 */\r
235         {  ERROR_PATH_NOT_FOUND,         ENOENT    },  /* 3 */\r
236         {  ERROR_TOO_MANY_OPEN_FILES,    EMFILE    },  /* 4 */\r
237         {  ERROR_ACCESS_DENIED,          EACCES    },  /* 5 */\r
238         {  ERROR_INVALID_HANDLE,         EBADF     },  /* 6 */\r
239         {  ERROR_ARENA_TRASHED,          ENOMEM    },  /* 7 */\r
240         {  ERROR_NOT_ENOUGH_MEMORY,      ENOMEM    },  /* 8 */\r
241         {  ERROR_INVALID_BLOCK,          ENOMEM    },  /* 9 */\r
242         {  ERROR_BAD_ENVIRONMENT,        E2BIG     },  /* 10 */\r
243         {  ERROR_BAD_FORMAT,             ENOEXEC   },  /* 11 */\r
244         {  ERROR_INVALID_ACCESS,         EINVAL    },  /* 12 */\r
245         {  ERROR_INVALID_DATA,           EINVAL    },  /* 13 */\r
246         {  ERROR_INVALID_DRIVE,          ENOENT    },  /* 15 */\r
247         {  ERROR_CURRENT_DIRECTORY,      EACCES    },  /* 16 */\r
248         {  ERROR_NOT_SAME_DEVICE,        EXDEV     },  /* 17 */\r
249         {  ERROR_NO_MORE_FILES,          ENOENT    },  /* 18 */\r
250         {  ERROR_LOCK_VIOLATION,         EACCES    },  /* 33 */\r
251         {  ERROR_BAD_NETPATH,            ENOENT    },  /* 53 */\r
252         {  ERROR_NETWORK_ACCESS_DENIED,  EACCES    },  /* 65 */\r
253         {  ERROR_BAD_NET_NAME,           ENOENT    },  /* 67 */\r
254         {  ERROR_FILE_EXISTS,            EEXIST    },  /* 80 */\r
255         {  ERROR_CANNOT_MAKE,            EACCES    },  /* 82 */\r
256         {  ERROR_FAIL_I24,               EACCES    },  /* 83 */\r
257         {  ERROR_INVALID_PARAMETER,      EINVAL    },  /* 87 */\r
258         {  ERROR_NO_PROC_SLOTS,          EAGAIN    },  /* 89 */\r
259         {  ERROR_DRIVE_LOCKED,           EACCES    },  /* 108 */\r
260         {  ERROR_BROKEN_PIPE,            EPIPE     },  /* 109 */\r
261         {  ERROR_DISK_FULL,              ENOSPC    },  /* 112 */\r
262         {  ERROR_INVALID_TARGET_HANDLE,  EBADF     },  /* 114 */\r
263         {  ERROR_INVALID_HANDLE,         EINVAL    },  /* 124 */\r
264         {  ERROR_WAIT_NO_CHILDREN,       ECHILD    },  /* 128 */\r
265         {  ERROR_CHILD_NOT_COMPLETE,     ECHILD    },  /* 129 */\r
266         {  ERROR_DIRECT_ACCESS_HANDLE,   EBADF     },  /* 130 */\r
267         {  ERROR_NEGATIVE_SEEK,          EINVAL    },  /* 131 */\r
268         {  ERROR_SEEK_ON_DEVICE,         EACCES    },  /* 132 */\r
269         {  ERROR_DIR_NOT_EMPTY,          ENOTEMPTY },  /* 145 */\r
270         {  ERROR_NOT_LOCKED,             EACCES    },  /* 158 */\r
271         {  ERROR_BAD_PATHNAME,           ENOENT    },  /* 161 */\r
272         {  ERROR_MAX_THRDS_REACHED,      EAGAIN    },  /* 164 */\r
273         {  ERROR_LOCK_FAILED,            EACCES    },  /* 167 */\r
274         {  ERROR_ALREADY_EXISTS,         EEXIST    },  /* 183 */\r
275         {  ERROR_FILENAME_EXCED_RANGE,   ENOENT    },  /* 206 */\r
276         {  ERROR_NESTING_NOT_ALLOWED,    EAGAIN    },  /* 215 */\r
277         {  ERROR_NOT_ENOUGH_QUOTA,       ENOMEM    }    /* 1816 */\r
278 };\r
279 \r
280 /* size of the table */\r
281 #define ERRTABLESIZE (sizeof(errtable)/sizeof(errtable[0]))\r
282 \r
283 /* The following two constants must be the minimum and maximum\r
284    values in the (contiguous) range of Exec Failure errors. */\r
285 #define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG\r
286 #define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN\r
287 \r
288 /* These are the low and high value in the range of errors that are\r
289    access violations */\r
290 #define MIN_EACCES_RANGE ERROR_WRITE_PROTECT\r
291 #define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED\r
292 \r
293 static void maperrno (void)\r
294 {\r
295         int i;\r
296         DWORD dwErrorCode;\r
297 \r
298         dwErrorCode = GetLastError();\r
299 \r
300         /* check the table for the OS error code */\r
301         for (i = 0; i < ERRTABLESIZE; ++i)\r
302         {\r
303                 if (dwErrorCode == errtable[i].oscode)\r
304                 {\r
305                         errno = errtable[i].errnocode;\r
306                         return;\r
307                 }\r
308         }\r
309 \r
310         /* The error code wasn't in the table.  We check for a range of */\r
311         /* EACCES errors or exec failure errors (ENOEXEC).  Otherwise   */\r
312         /* EINVAL is returned.                                          */\r
313 \r
314         if (dwErrorCode >= MIN_EACCES_RANGE && dwErrorCode <= MAX_EACCES_RANGE)\r
315                 errno = EACCES;\r
316         else\r
317                 if (dwErrorCode >= MIN_EXEC_ERROR && dwErrorCode <= MAX_EXEC_ERROR)\r
318                         errno = ENOEXEC;\r
319                 else\r
320                         errno = EINVAL;\r
321 }\r
322 \r
323 /*\r
324  * Function: mkAnonPipe\r
325  *\r
326  * Purpose:  create an anonymous pipe with read and write ends being\r
327  *           optionally (non-)inheritable.\r
328  */\r
329 static BOOL\r
330 mkAnonPipe (HANDLE* pHandleIn, BOOL isInheritableIn, \r
331             HANDLE* pHandleOut, BOOL isInheritableOut)\r
332 {\r
333         HANDLE hTemporaryIn  = NULL;\r
334         HANDLE hTemporaryOut = NULL;\r
335         BOOL status;\r
336         SECURITY_ATTRIBUTES sec_attrs;\r
337 \r
338         /* Create inheritable security attributes */\r
339         sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);\r
340         sec_attrs.lpSecurityDescriptor = NULL;\r
341         sec_attrs.bInheritHandle = TRUE;\r
342 \r
343         /* Create the anon pipe with both ends inheritable */\r
344         if (!CreatePipe(&hTemporaryIn, &hTemporaryOut, &sec_attrs, 0))\r
345         {\r
346                 maperrno();\r
347                 *pHandleIn  = NULL;\r
348                 *pHandleOut = NULL;\r
349                 return FALSE;\r
350         }\r
351 \r
352         if (isInheritableIn)\r
353                 *pHandleIn = hTemporaryIn;\r
354         else\r
355         {\r
356                 /* Make the read end non-inheritable */\r
357                 status = DuplicateHandle(GetCurrentProcess(), hTemporaryIn,\r
358                               GetCurrentProcess(), pHandleIn,\r
359                               0,\r
360                               FALSE, /* non-inheritable */\r
361                               DUPLICATE_SAME_ACCESS);\r
362                 CloseHandle(hTemporaryIn);\r
363                 if (!status)\r
364                 {\r
365                         maperrno();\r
366                         *pHandleIn  = NULL;\r
367                         *pHandleOut = NULL;\r
368                         CloseHandle(hTemporaryOut);\r
369                         return FALSE;\r
370                 }\r
371         }\r
372 \r
373         if (isInheritableOut)\r
374                 *pHandleOut = hTemporaryOut;\r
375         else\r
376         {\r
377                 /* Make the write end non-inheritable */\r
378                 status = DuplicateHandle(GetCurrentProcess(), hTemporaryOut,\r
379                               GetCurrentProcess(), pHandleOut,\r
380                               0,\r
381                               FALSE, /* non-inheritable */\r
382                               DUPLICATE_SAME_ACCESS);\r
383                 CloseHandle(hTemporaryOut);\r
384                 if (!status)\r
385                 {\r
386                         maperrno();\r
387                         *pHandleIn  = NULL;\r
388                         *pHandleOut = NULL;\r
389                         CloseHandle(*pHandleIn);\r
390                 return FALSE;\r
391         }\r
392         }\r
393 \r
394         return TRUE;\r
395 }\r
396 \r
397 ProcHandle\r
398 runProcess (char *cmd, char *workingDirectory, void *environment,\r
399             int fdStdInput, int fdStdOutput, int fdStdError)\r
400 {\r
401         STARTUPINFO sInfo;\r
402         PROCESS_INFORMATION pInfo;\r
403         DWORD flags;\r
404 \r
405         ZeroMemory(&sInfo, sizeof(sInfo));\r
406         sInfo.cb = sizeof(sInfo);\r
407         sInfo.dwFlags = STARTF_USESTDHANDLES;\r
408         sInfo.hStdInput = (HANDLE) _get_osfhandle(fdStdInput);\r
409         sInfo.hStdOutput= (HANDLE) _get_osfhandle(fdStdOutput);\r
410         sInfo.hStdError = (HANDLE) _get_osfhandle(fdStdError);\r
411 \r
412         if (sInfo.hStdOutput != GetStdHandle(STD_OUTPUT_HANDLE) &&\r
413             sInfo.hStdError  != GetStdHandle(STD_ERROR_HANDLE))\r
414                 flags = CREATE_NO_WINDOW;   // Run without console window only when both output and error are redirected\r
415         else\r
416                 flags = 0;\r
417 \r
418         if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, environment, workingDirectory, &sInfo, &pInfo))\r
419         {\r
420                 maperrno();\r
421                 return -1;\r
422         }\r
423 \r
424         CloseHandle(pInfo.hThread);\r
425         return (ProcHandle)pInfo.hProcess;\r
426 }\r
427 \r
428 ProcHandle\r
429 runInteractiveProcess (char *cmd, char *workingDirectory, void *environment,\r
430                        int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)\r
431 {\r
432         STARTUPINFO sInfo;\r
433         PROCESS_INFORMATION pInfo;\r
434         HANDLE hStdInputRead,  hStdInputWrite;\r
435         HANDLE hStdOutputRead, hStdOutputWrite;\r
436         HANDLE hStdErrorRead,  hStdErrorWrite;\r
437 \r
438         if (!mkAnonPipe(&hStdInputRead,  TRUE, &hStdInputWrite,  FALSE))\r
439                 return -1;\r
440 \r
441         if (!mkAnonPipe(&hStdOutputRead, FALSE, &hStdOutputWrite, TRUE))\r
442         {\r
443                 CloseHandle(hStdInputRead);\r
444                 CloseHandle(hStdInputWrite);\r
445                 return -1;\r
446         }\r
447 \r
448         if (!mkAnonPipe(&hStdErrorRead,  FALSE, &hStdErrorWrite,  TRUE))\r
449         {\r
450                 CloseHandle(hStdInputRead);\r
451                 CloseHandle(hStdInputWrite);\r
452                 CloseHandle(hStdOutputRead);\r
453                 CloseHandle(hStdOutputWrite);\r
454                 return -1;\r
455         }\r
456 \r
457         ZeroMemory(&sInfo, sizeof(sInfo));\r
458         sInfo.cb = sizeof(sInfo);\r
459         sInfo.dwFlags = STARTF_USESTDHANDLES;\r
460         sInfo.hStdInput = hStdInputRead;\r
461         sInfo.hStdOutput= hStdOutputWrite;\r
462         sInfo.hStdError = hStdErrorWrite;\r
463 \r
464         if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, environment, workingDirectory, &sInfo, &pInfo))\r
465         {\r
466                 maperrno();\r
467                 CloseHandle(hStdInputRead);\r
468                 CloseHandle(hStdInputWrite);\r
469                 CloseHandle(hStdOutputRead);\r
470                 CloseHandle(hStdOutputWrite);\r
471                 CloseHandle(hStdErrorRead);\r
472                 CloseHandle(hStdErrorWrite);\r
473                 return -1;\r
474         }\r
475         CloseHandle(pInfo.hThread);\r
476 \r
477         // Close the ends of the pipes that were inherited by the\r
478         // child process.  This is important, otherwise we won't see\r
479         // EOF on these pipes when the child process exits.\r
480         CloseHandle(hStdInputRead);\r
481         CloseHandle(hStdOutputWrite);\r
482         CloseHandle(hStdErrorWrite);\r
483 \r
484         *pfdStdInput  = _open_osfhandle((intptr_t) hStdInputWrite, _O_WRONLY);\r
485         *pfdStdOutput = _open_osfhandle((intptr_t) hStdOutputRead, _O_RDONLY);\r
486         *pfdStdError  = _open_osfhandle((intptr_t) hStdErrorRead, _O_RDONLY);\r
487 \r
488         return (int) pInfo.hProcess;\r
489 }\r
490 \r
491 int\r
492 terminateProcess (ProcHandle handle)\r
493 {\r
494     if (!TerminateProcess((HANDLE) handle, 1)) {\r
495         maperrno();\r
496         return -1;\r
497     }\r
498 \r
499     CloseHandle((HANDLE) handle);\r
500     return 0;\r
501 }\r
502 \r
503 int\r
504 getProcessExitCode (ProcHandle handle, int *pExitCode)\r
505 {\r
506     *pExitCode = 0;\r
507 \r
508     if (WaitForSingleObject((HANDLE) handle, 1) == WAIT_OBJECT_0)\r
509     {\r
510         if (GetExitCodeProcess((HANDLE) handle, (DWORD *) pExitCode) == 0)\r
511         {\r
512             maperrno();\r
513             return -1;\r
514         }\r
515         \r
516         CloseHandle((HANDLE) handle);\r
517         return 1;\r
518     }\r
519     \r
520     return 0;\r
521 }\r
522 \r
523 int\r
524 waitForProcess (ProcHandle handle)\r
525 {\r
526     DWORD retCode;\r
527 \r
528     if (WaitForSingleObject((HANDLE) handle, INFINITE) == WAIT_OBJECT_0)\r
529     {\r
530         if (GetExitCodeProcess((HANDLE) handle, &retCode) == 0)\r
531         {\r
532             maperrno();\r
533             return -1;\r
534         }\r
535         \r
536         CloseHandle((HANDLE) handle);\r
537         return retCode;\r
538     }\r
539     \r
540     maperrno();\r
541     return -1;\r
542 }\r
543 \r
544 #endif // Win32\r