d117e3605a120f394bfd236eb2920e209eb026ab
[haskell-directory.git] / cbits / runProcess.c
1 /* ----------------------------------------------------------------------------
2    (c) The University of Glasgow 2004
3    
4    Support for System.Process
5    ------------------------------------------------------------------------- */
6
7 #include "HsBase.h"
8
9 #if defined(mingw32_HOST_OS)
10 #include <windows.h>
11 #include <stdlib.h>
12 #endif
13
14 #ifdef HAVE_VFORK_H
15 #include <vfork.h>
16 #endif
17
18 #ifdef HAVE_VFORK
19 #define fork vfork
20 #endif
21
22 #ifdef HAVE_SIGNAL_H
23 #include <signal.h>
24 #endif
25
26 #if !defined(mingw32_HOST_OS) && !defined(__MINGW32__)
27 /* ----------------------------------------------------------------------------
28    UNIX versions
29    ------------------------------------------------------------------------- */
30
31 ProcHandle
32 runProcess (char *const args[], char *workingDirectory, char **environment, 
33             int fdStdInput, int fdStdOutput, int fdStdError,
34             int set_inthandler, long inthandler, 
35             int set_quithandler, long quithandler)
36 {
37     int pid;
38     struct sigaction dfl;
39
40     switch(pid = fork())
41     {
42     case -1:
43         return -1;
44         
45     case 0:
46     {
47         pPrPr_disableITimers();
48         
49         if (workingDirectory) {
50             chdir (workingDirectory);
51         }
52         
53         /* Set the SIGINT/SIGQUIT signal handlers in the child, if requested 
54          */
55         (void)sigemptyset(&dfl.sa_mask);
56         dfl.sa_flags = 0;
57         if (set_inthandler) {
58             dfl.sa_handler = (void *)inthandler;
59             (void)sigaction(SIGINT, &dfl, NULL);
60         }
61         if (set_quithandler) {
62             dfl.sa_handler = (void *)quithandler;
63             (void)sigaction(SIGQUIT,  &dfl, NULL);
64         }
65
66         dup2 (fdStdInput,  STDIN_FILENO);
67         dup2 (fdStdOutput, STDOUT_FILENO);
68         dup2 (fdStdError,  STDERR_FILENO);
69         
70         if (environment) {
71             execvpe(args[0], args, environment);
72         } else {
73             execvp(args[0], args);
74         }
75     }
76     _exit(127);
77     }
78     
79     return pid;
80 }
81
82 ProcHandle
83 runInteractiveProcess (char *const args[], 
84                        char *workingDirectory, char **environment,
85                        int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)
86 {
87     int pid;
88     int fdStdInput[2], fdStdOutput[2], fdStdError[2];
89
90     pipe(fdStdInput);
91     pipe(fdStdOutput);
92     pipe(fdStdError);
93
94     switch(pid = fork())
95     {
96     case -1:
97         close(fdStdInput[0]);
98         close(fdStdInput[1]);
99         close(fdStdOutput[0]);
100         close(fdStdOutput[1]);
101         close(fdStdError[0]);
102         close(fdStdError[1]);
103         return -1;
104         
105     case 0:
106     {
107         pPrPr_disableITimers();
108         
109         if (workingDirectory) {
110             chdir (workingDirectory);
111         }
112         
113         dup2 (fdStdInput[0], STDIN_FILENO);
114         dup2 (fdStdOutput[1], STDOUT_FILENO);
115         dup2 (fdStdError[1], STDERR_FILENO);
116         
117         close(fdStdInput[0]);
118         close(fdStdInput[1]);
119         close(fdStdOutput[0]);
120         close(fdStdOutput[1]);
121         close(fdStdError[0]);
122         close(fdStdError[1]);
123         
124         /* the child */
125         if (environment) {
126             execvpe(args[0], args, environment);
127         } else {
128             execvp(args[0], args);
129         }
130     }
131     _exit(127);
132     
133     default:
134         close(fdStdInput[0]);
135         close(fdStdOutput[1]);
136         close(fdStdError[1]);
137         
138         *pfdStdInput  = fdStdInput[1];
139         *pfdStdOutput = fdStdOutput[0];
140         *pfdStdError  = fdStdError[0];
141         break;
142     }
143     
144     return pid;
145 }
146
147 int
148 terminateProcess (ProcHandle handle)
149 {
150     return (kill(handle, SIGTERM) == 0);
151 }
152
153 int
154 getProcessExitCode (ProcHandle handle, int *pExitCode)
155 {
156     int wstat, res;
157     
158     *pExitCode = 0;
159     
160     if ((res = waitpid(handle, &wstat, WNOHANG)) > 0)
161     {
162         if (WIFEXITED(wstat))
163         {
164             *pExitCode = WEXITSTATUS(wstat);
165             return 1;
166         }
167         else
168             if (WIFSIGNALED(wstat))
169             {
170                 errno = EINTR;
171                 return -1;
172             }
173             else
174             {
175                 /* This should never happen */
176             }
177     }
178     
179     if (res == 0) return 0;
180
181     if (errno == ECHILD) 
182     {
183             *pExitCode = 0;
184             return 1;
185     }
186
187     return -1;
188 }
189
190 int waitForProcess (ProcHandle handle)
191 {
192     int wstat;
193     
194     while (waitpid(handle, &wstat, 0) < 0)
195     {
196         if (errno != EINTR)
197         {
198             return -1;
199         }
200     }
201     
202     if (WIFEXITED(wstat))
203         return WEXITSTATUS(wstat);
204     else
205         if (WIFSIGNALED(wstat))
206         {
207             errno = EINTR;
208         }
209         else
210         {
211             /* This should never happen */
212         }
213     
214     return -1;
215 }
216
217 #else
218 /* ----------------------------------------------------------------------------
219    Win32 versions
220    ------------------------------------------------------------------------- */
221
222 /* -------------------- WINDOWS VERSION --------------------- */
223
224 /* This is the error table that defines the mapping between OS error
225    codes and errno values */
226
227 struct errentry {
228         unsigned long oscode;           /* OS return value */
229         int errnocode;  /* System V error code */
230 };
231
232 static struct errentry errtable[] = {
233         {  ERROR_INVALID_FUNCTION,       EINVAL    },  /* 1 */
234         {  ERROR_FILE_NOT_FOUND,         ENOENT    },  /* 2 */
235         {  ERROR_PATH_NOT_FOUND,         ENOENT    },  /* 3 */
236         {  ERROR_TOO_MANY_OPEN_FILES,    EMFILE    },  /* 4 */
237         {  ERROR_ACCESS_DENIED,          EACCES    },  /* 5 */
238         {  ERROR_INVALID_HANDLE,         EBADF     },  /* 6 */
239         {  ERROR_ARENA_TRASHED,          ENOMEM    },  /* 7 */
240         {  ERROR_NOT_ENOUGH_MEMORY,      ENOMEM    },  /* 8 */
241         {  ERROR_INVALID_BLOCK,          ENOMEM    },  /* 9 */
242         {  ERROR_BAD_ENVIRONMENT,        E2BIG     },  /* 10 */
243         {  ERROR_BAD_FORMAT,             ENOEXEC   },  /* 11 */
244         {  ERROR_INVALID_ACCESS,         EINVAL    },  /* 12 */
245         {  ERROR_INVALID_DATA,           EINVAL    },  /* 13 */
246         {  ERROR_INVALID_DRIVE,          ENOENT    },  /* 15 */
247         {  ERROR_CURRENT_DIRECTORY,      EACCES    },  /* 16 */
248         {  ERROR_NOT_SAME_DEVICE,        EXDEV     },  /* 17 */
249         {  ERROR_NO_MORE_FILES,          ENOENT    },  /* 18 */
250         {  ERROR_LOCK_VIOLATION,         EACCES    },  /* 33 */
251         {  ERROR_BAD_NETPATH,            ENOENT    },  /* 53 */
252         {  ERROR_NETWORK_ACCESS_DENIED,  EACCES    },  /* 65 */
253         {  ERROR_BAD_NET_NAME,           ENOENT    },  /* 67 */
254         {  ERROR_FILE_EXISTS,            EEXIST    },  /* 80 */
255         {  ERROR_CANNOT_MAKE,            EACCES    },  /* 82 */
256         {  ERROR_FAIL_I24,               EACCES    },  /* 83 */
257         {  ERROR_INVALID_PARAMETER,      EINVAL    },  /* 87 */
258         {  ERROR_NO_PROC_SLOTS,          EAGAIN    },  /* 89 */
259         {  ERROR_DRIVE_LOCKED,           EACCES    },  /* 108 */
260         {  ERROR_BROKEN_PIPE,            EPIPE     },  /* 109 */
261         {  ERROR_DISK_FULL,              ENOSPC    },  /* 112 */
262         {  ERROR_INVALID_TARGET_HANDLE,  EBADF     },  /* 114 */
263         {  ERROR_INVALID_HANDLE,         EINVAL    },  /* 124 */
264         {  ERROR_WAIT_NO_CHILDREN,       ECHILD    },  /* 128 */
265         {  ERROR_CHILD_NOT_COMPLETE,     ECHILD    },  /* 129 */
266         {  ERROR_DIRECT_ACCESS_HANDLE,   EBADF     },  /* 130 */
267         {  ERROR_NEGATIVE_SEEK,          EINVAL    },  /* 131 */
268         {  ERROR_SEEK_ON_DEVICE,         EACCES    },  /* 132 */
269         {  ERROR_DIR_NOT_EMPTY,          ENOTEMPTY },  /* 145 */
270         {  ERROR_NOT_LOCKED,             EACCES    },  /* 158 */
271         {  ERROR_BAD_PATHNAME,           ENOENT    },  /* 161 */
272         {  ERROR_MAX_THRDS_REACHED,      EAGAIN    },  /* 164 */
273         {  ERROR_LOCK_FAILED,            EACCES    },  /* 167 */
274         {  ERROR_ALREADY_EXISTS,         EEXIST    },  /* 183 */
275         {  ERROR_FILENAME_EXCED_RANGE,   ENOENT    },  /* 206 */
276         {  ERROR_NESTING_NOT_ALLOWED,    EAGAIN    },  /* 215 */
277         {  ERROR_NOT_ENOUGH_QUOTA,       ENOMEM    }    /* 1816 */
278 };
279
280 /* size of the table */
281 #define ERRTABLESIZE (sizeof(errtable)/sizeof(errtable[0]))
282
283 /* The following two constants must be the minimum and maximum
284    values in the (contiguous) range of Exec Failure errors. */
285 #define MIN_EXEC_ERROR ERROR_INVALID_STARTING_CODESEG
286 #define MAX_EXEC_ERROR ERROR_INFLOOP_IN_RELOC_CHAIN
287
288 /* These are the low and high value in the range of errors that are
289    access violations */
290 #define MIN_EACCES_RANGE ERROR_WRITE_PROTECT
291 #define MAX_EACCES_RANGE ERROR_SHARING_BUFFER_EXCEEDED
292
293 static void maperrno (void)
294 {
295         int i;
296         DWORD dwErrorCode;
297
298         dwErrorCode = GetLastError();
299
300         /* check the table for the OS error code */
301         for (i = 0; i < ERRTABLESIZE; ++i)
302         {
303                 if (dwErrorCode == errtable[i].oscode)
304                 {
305                         errno = errtable[i].errnocode;
306                         return;
307                 }
308         }
309
310         /* The error code wasn't in the table.  We check for a range of */
311         /* EACCES errors or exec failure errors (ENOEXEC).  Otherwise   */
312         /* EINVAL is returned.                                          */
313
314         if (dwErrorCode >= MIN_EACCES_RANGE && dwErrorCode <= MAX_EACCES_RANGE)
315                 errno = EACCES;
316         else
317                 if (dwErrorCode >= MIN_EXEC_ERROR && dwErrorCode <= MAX_EXEC_ERROR)
318                         errno = ENOEXEC;
319                 else
320                         errno = EINVAL;
321 }
322
323 /*
324  * Function: mkAnonPipe
325  *
326  * Purpose:  create an anonymous pipe with read and write ends being
327  *           optionally (non-)inheritable.
328  */
329 static BOOL
330 mkAnonPipe (HANDLE* pHandleIn, BOOL isInheritableIn, 
331             HANDLE* pHandleOut, BOOL isInheritableOut)
332 {
333         HANDLE hTemporaryIn  = NULL;
334         HANDLE hTemporaryOut = NULL;
335         BOOL status;
336         SECURITY_ATTRIBUTES sec_attrs;
337
338         /* Create inheritable security attributes */
339         sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
340         sec_attrs.lpSecurityDescriptor = NULL;
341         sec_attrs.bInheritHandle = TRUE;
342
343         /* Create the anon pipe with both ends inheritable */
344         if (!CreatePipe(&hTemporaryIn, &hTemporaryOut, &sec_attrs, 0))
345         {
346                 maperrno();
347                 *pHandleIn  = NULL;
348                 *pHandleOut = NULL;
349                 return FALSE;
350         }
351
352         if (isInheritableIn)
353                 *pHandleIn = hTemporaryIn;
354         else
355         {
356                 /* Make the read end non-inheritable */
357                 status = DuplicateHandle(GetCurrentProcess(), hTemporaryIn,
358                               GetCurrentProcess(), pHandleIn,
359                               0,
360                               FALSE, /* non-inheritable */
361                               DUPLICATE_SAME_ACCESS);
362                 CloseHandle(hTemporaryIn);
363                 if (!status)
364                 {
365                         maperrno();
366                         *pHandleIn  = NULL;
367                         *pHandleOut = NULL;
368                         CloseHandle(hTemporaryOut);
369                         return FALSE;
370                 }
371         }
372
373         if (isInheritableOut)
374                 *pHandleOut = hTemporaryOut;
375         else
376         {
377                 /* Make the write end non-inheritable */
378                 status = DuplicateHandle(GetCurrentProcess(), hTemporaryOut,
379                               GetCurrentProcess(), pHandleOut,
380                               0,
381                               FALSE, /* non-inheritable */
382                               DUPLICATE_SAME_ACCESS);
383                 CloseHandle(hTemporaryOut);
384                 if (!status)
385                 {
386                         maperrno();
387                         *pHandleIn  = NULL;
388                         *pHandleOut = NULL;
389                         CloseHandle(*pHandleIn);
390                 return FALSE;
391         }
392         }
393
394         return TRUE;
395 }
396
397 ProcHandle
398 runProcess (char *cmd, char *workingDirectory, void *environment,
399             int fdStdInput, int fdStdOutput, int fdStdError)
400 {
401         STARTUPINFO sInfo;
402         PROCESS_INFORMATION pInfo;
403         DWORD flags;
404         char buffer[256];
405
406         ZeroMemory(&sInfo, sizeof(sInfo));
407         sInfo.cb = sizeof(sInfo);       
408         sInfo.hStdInput = (HANDLE) _get_osfhandle(fdStdInput);
409         sInfo.hStdOutput= (HANDLE) _get_osfhandle(fdStdOutput);
410         sInfo.hStdError = (HANDLE) _get_osfhandle(fdStdError);
411
412         if (sInfo.hStdInput == INVALID_HANDLE_VALUE)
413                 sInfo.hStdInput = NULL;
414         if (sInfo.hStdOutput == INVALID_HANDLE_VALUE)
415                 sInfo.hStdOutput = NULL;
416         if (sInfo.hStdError == INVALID_HANDLE_VALUE)
417                 sInfo.hStdError = NULL;
418
419         if (sInfo.hStdInput || sInfo.hStdOutput || sInfo.hStdError)
420                 sInfo.dwFlags = STARTF_USESTDHANDLES;
421
422         if (sInfo.hStdInput  != GetStdHandle(STD_INPUT_HANDLE)  &&
423             sInfo.hStdOutput != GetStdHandle(STD_OUTPUT_HANDLE) &&
424             sInfo.hStdError  != GetStdHandle(STD_ERROR_HANDLE))
425                 flags = CREATE_NO_WINDOW;   // Run without console window only when both output and error are redirected
426         else
427                 flags = 0;
428
429         if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, environment, workingDirectory, &sInfo, &pInfo))
430         {
431                 maperrno();
432                 return -1;
433         }
434
435         CloseHandle(pInfo.hThread);
436         return (ProcHandle)pInfo.hProcess;
437 }
438
439 ProcHandle
440 runInteractiveProcess (char *cmd, char *workingDirectory, void *environment,
441                        int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)
442 {
443         STARTUPINFO sInfo;
444         PROCESS_INFORMATION pInfo;
445         HANDLE hStdInputRead,  hStdInputWrite;
446         HANDLE hStdOutputRead, hStdOutputWrite;
447         HANDLE hStdErrorRead,  hStdErrorWrite;
448
449         if (!mkAnonPipe(&hStdInputRead,  TRUE, &hStdInputWrite,  FALSE))
450                 return -1;
451
452         if (!mkAnonPipe(&hStdOutputRead, FALSE, &hStdOutputWrite, TRUE))
453         {
454                 CloseHandle(hStdInputRead);
455                 CloseHandle(hStdInputWrite);
456                 return -1;
457         }
458
459         if (!mkAnonPipe(&hStdErrorRead,  FALSE, &hStdErrorWrite,  TRUE))
460         {
461                 CloseHandle(hStdInputRead);
462                 CloseHandle(hStdInputWrite);
463                 CloseHandle(hStdOutputRead);
464                 CloseHandle(hStdOutputWrite);
465                 return -1;
466         }
467
468         ZeroMemory(&sInfo, sizeof(sInfo));
469         sInfo.cb = sizeof(sInfo);
470         sInfo.dwFlags = STARTF_USESTDHANDLES;
471         sInfo.hStdInput = hStdInputRead;
472         sInfo.hStdOutput= hStdOutputWrite;
473         sInfo.hStdError = hStdErrorWrite;
474
475         if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, environment, workingDirectory, &sInfo, &pInfo))
476         {
477                 maperrno();
478                 CloseHandle(hStdInputRead);
479                 CloseHandle(hStdInputWrite);
480                 CloseHandle(hStdOutputRead);
481                 CloseHandle(hStdOutputWrite);
482                 CloseHandle(hStdErrorRead);
483                 CloseHandle(hStdErrorWrite);
484                 return -1;
485         }
486         CloseHandle(pInfo.hThread);
487
488         // Close the ends of the pipes that were inherited by the
489         // child process.  This is important, otherwise we won't see
490         // EOF on these pipes when the child process exits.
491         CloseHandle(hStdInputRead);
492         CloseHandle(hStdOutputWrite);
493         CloseHandle(hStdErrorWrite);
494
495         *pfdStdInput  = _open_osfhandle((intptr_t) hStdInputWrite, _O_WRONLY);
496         *pfdStdOutput = _open_osfhandle((intptr_t) hStdOutputRead, _O_RDONLY);
497         *pfdStdError  = _open_osfhandle((intptr_t) hStdErrorRead, _O_RDONLY);
498
499         return (int) pInfo.hProcess;
500 }
501
502 int
503 terminateProcess (ProcHandle handle)
504 {
505     if (!TerminateProcess((HANDLE) handle, 1)) {
506         maperrno();
507         return -1;
508     }
509
510     CloseHandle((HANDLE) handle);
511     return 0;
512 }
513
514 int
515 getProcessExitCode (ProcHandle handle, int *pExitCode)
516 {
517     *pExitCode = 0;
518
519     if (WaitForSingleObject((HANDLE) handle, 1) == WAIT_OBJECT_0)
520     {
521         if (GetExitCodeProcess((HANDLE) handle, (DWORD *) pExitCode) == 0)
522         {
523             maperrno();
524             return -1;
525         }
526         
527         CloseHandle((HANDLE) handle);
528         return 1;
529     }
530     
531     return 0;
532 }
533
534 int
535 waitForProcess (ProcHandle handle)
536 {
537     DWORD retCode;
538
539     if (WaitForSingleObject((HANDLE) handle, INFINITE) == WAIT_OBJECT_0)
540     {
541         if (GetExitCodeProcess((HANDLE) handle, &retCode) == 0)
542         {
543             maperrno();
544             return -1;
545         }
546         
547         CloseHandle((HANDLE) handle);
548         return retCode;
549     }
550     
551     maperrno();
552     return -1;
553 }
554
555 #endif /* Win32 */