Remove Control.Parallel*, now in package parallel
[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(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32)
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(_MSC_VER) || defined(__MINGW32__) || defined(_WIN32))
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             if (chdir (workingDirectory) < 0) {
51                 return -1;
52             }
53         }
54         
55         /* Set the SIGINT/SIGQUIT signal handlers in the child, if requested 
56          */
57         (void)sigemptyset(&dfl.sa_mask);
58         dfl.sa_flags = 0;
59         if (set_inthandler) {
60             dfl.sa_handler = (void *)inthandler;
61             (void)sigaction(SIGINT, &dfl, NULL);
62         }
63         if (set_quithandler) {
64             dfl.sa_handler = (void *)quithandler;
65             (void)sigaction(SIGQUIT,  &dfl, NULL);
66         }
67
68         dup2 (fdStdInput,  STDIN_FILENO);
69         dup2 (fdStdOutput, STDOUT_FILENO);
70         dup2 (fdStdError,  STDERR_FILENO);
71         
72         if (environment) {
73             execvpe(args[0], args, environment);
74         } else {
75             execvp(args[0], args);
76         }
77     }
78     _exit(127);
79     }
80     
81     return pid;
82 }
83
84 ProcHandle
85 runInteractiveProcess (char *const args[], 
86                        char *workingDirectory, char **environment,
87                        int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)
88 {
89     int pid;
90     int fdStdInput[2], fdStdOutput[2], fdStdError[2];
91
92     pipe(fdStdInput);
93     pipe(fdStdOutput);
94     pipe(fdStdError);
95
96     switch(pid = fork())
97     {
98     case -1:
99         close(fdStdInput[0]);
100         close(fdStdInput[1]);
101         close(fdStdOutput[0]);
102         close(fdStdOutput[1]);
103         close(fdStdError[0]);
104         close(fdStdError[1]);
105         return -1;
106         
107     case 0:
108     {
109         pPrPr_disableITimers();
110         
111         if (workingDirectory) {
112             if (chdir (workingDirectory) < 0) {
113                 return -1;
114             }
115         }
116         
117         if (fdStdInput[0] != STDIN_FILENO) {
118             dup2 (fdStdInput[0], STDIN_FILENO);
119             close(fdStdInput[0]);
120         }
121
122         if (fdStdOutput[1] != STDOUT_FILENO) {
123             dup2 (fdStdOutput[1], STDOUT_FILENO);
124             close(fdStdOutput[1]);
125         }
126
127         if (fdStdError[1] != STDERR_FILENO) {
128             dup2 (fdStdError[1], STDERR_FILENO);
129             close(fdStdError[1]);
130         }
131         
132         close(fdStdInput[1]);
133         close(fdStdOutput[0]);
134         close(fdStdError[0]);
135         
136         /* the child */
137         if (environment) {
138             execvpe(args[0], args, environment);
139         } else {
140             execvp(args[0], args);
141         }
142     }
143     _exit(127);
144     
145     default:
146         close(fdStdInput[0]);
147         close(fdStdOutput[1]);
148         close(fdStdError[1]);
149         
150         *pfdStdInput  = fdStdInput[1];
151         *pfdStdOutput = fdStdOutput[0];
152         *pfdStdError  = fdStdError[0];
153         break;
154     }
155     
156     return pid;
157 }
158
159 int
160 terminateProcess (ProcHandle handle)
161 {
162     return (kill(handle, SIGTERM) == 0);
163 }
164
165 int
166 getProcessExitCode (ProcHandle handle, int *pExitCode)
167 {
168     int wstat, res;
169     
170     *pExitCode = 0;
171     
172     if ((res = waitpid(handle, &wstat, WNOHANG)) > 0)
173     {
174         if (WIFEXITED(wstat))
175         {
176             *pExitCode = WEXITSTATUS(wstat);
177             return 1;
178         }
179         else
180             if (WIFSIGNALED(wstat))
181             {
182                 errno = EINTR;
183                 return -1;
184             }
185             else
186             {
187                 /* This should never happen */
188             }
189     }
190     
191     if (res == 0) return 0;
192
193     if (errno == ECHILD) 
194     {
195             *pExitCode = 0;
196             return 1;
197     }
198
199     return -1;
200 }
201
202 int waitForProcess (ProcHandle handle)
203 {
204     int wstat;
205     
206     while (waitpid(handle, &wstat, 0) < 0)
207     {
208         if (errno != EINTR)
209         {
210             return -1;
211         }
212     }
213     
214     if (WIFEXITED(wstat))
215         return WEXITSTATUS(wstat);
216     else
217         if (WIFSIGNALED(wstat))
218         {
219             return wstat;
220         }
221         else
222         {
223             /* This should never happen */
224         }
225     
226     return -1;
227 }
228
229 #else
230 /* ----------------------------------------------------------------------------
231    Win32 versions
232    ------------------------------------------------------------------------- */
233
234 /* -------------------- WINDOWS VERSION --------------------- */
235
236 /*
237  * Function: mkAnonPipe
238  *
239  * Purpose:  create an anonymous pipe with read and write ends being
240  *           optionally (non-)inheritable.
241  */
242 static BOOL
243 mkAnonPipe (HANDLE* pHandleIn, BOOL isInheritableIn, 
244             HANDLE* pHandleOut, BOOL isInheritableOut)
245 {
246         HANDLE hTemporaryIn  = NULL;
247         HANDLE hTemporaryOut = NULL;
248         BOOL status;
249         SECURITY_ATTRIBUTES sec_attrs;
250
251         /* Create inheritable security attributes */
252         sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
253         sec_attrs.lpSecurityDescriptor = NULL;
254         sec_attrs.bInheritHandle = TRUE;
255
256         /* Create the anon pipe with both ends inheritable */
257         if (!CreatePipe(&hTemporaryIn, &hTemporaryOut, &sec_attrs, 0))
258         {
259                 maperrno();
260                 *pHandleIn  = NULL;
261                 *pHandleOut = NULL;
262                 return FALSE;
263         }
264
265         if (isInheritableIn)
266                 *pHandleIn = hTemporaryIn;
267         else
268         {
269                 /* Make the read end non-inheritable */
270                 status = DuplicateHandle(GetCurrentProcess(), hTemporaryIn,
271                               GetCurrentProcess(), pHandleIn,
272                               0,
273                               FALSE, /* non-inheritable */
274                               DUPLICATE_SAME_ACCESS);
275                 CloseHandle(hTemporaryIn);
276                 if (!status)
277                 {
278                         maperrno();
279                         *pHandleIn  = NULL;
280                         *pHandleOut = NULL;
281                         CloseHandle(hTemporaryOut);
282                         return FALSE;
283                 }
284         }
285
286         if (isInheritableOut)
287                 *pHandleOut = hTemporaryOut;
288         else
289         {
290                 /* Make the write end non-inheritable */
291                 status = DuplicateHandle(GetCurrentProcess(), hTemporaryOut,
292                               GetCurrentProcess(), pHandleOut,
293                               0,
294                               FALSE, /* non-inheritable */
295                               DUPLICATE_SAME_ACCESS);
296                 CloseHandle(hTemporaryOut);
297                 if (!status)
298                 {
299                         maperrno();
300                         *pHandleIn  = NULL;
301                         *pHandleOut = NULL;
302                         CloseHandle(*pHandleIn);
303                 return FALSE;
304         }
305         }
306
307         return TRUE;
308 }
309
310 ProcHandle
311 runProcess (char *cmd, char *workingDirectory, void *environment,
312             int fdStdInput, int fdStdOutput, int fdStdError)
313 {
314         STARTUPINFO sInfo;
315         PROCESS_INFORMATION pInfo;
316         DWORD flags;
317
318         ZeroMemory(&sInfo, sizeof(sInfo));
319         sInfo.cb = sizeof(sInfo);       
320         sInfo.hStdInput = (HANDLE) _get_osfhandle(fdStdInput);
321         sInfo.hStdOutput= (HANDLE) _get_osfhandle(fdStdOutput);
322         sInfo.hStdError = (HANDLE) _get_osfhandle(fdStdError);
323
324         if (sInfo.hStdInput == INVALID_HANDLE_VALUE)
325                 sInfo.hStdInput = NULL;
326         if (sInfo.hStdOutput == INVALID_HANDLE_VALUE)
327                 sInfo.hStdOutput = NULL;
328         if (sInfo.hStdError == INVALID_HANDLE_VALUE)
329                 sInfo.hStdError = NULL;
330
331         if (sInfo.hStdInput || sInfo.hStdOutput || sInfo.hStdError)
332                 sInfo.dwFlags = STARTF_USESTDHANDLES;
333
334         if (sInfo.hStdInput  != GetStdHandle(STD_INPUT_HANDLE)  &&
335             sInfo.hStdOutput != GetStdHandle(STD_OUTPUT_HANDLE) &&
336             sInfo.hStdError  != GetStdHandle(STD_ERROR_HANDLE))
337                 flags = CREATE_NO_WINDOW;   // Run without console window only when both output and error are redirected
338         else
339                 flags = 0;
340
341         if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, environment, workingDirectory, &sInfo, &pInfo))
342         {
343                 maperrno();
344                 return -1;
345         }
346
347         CloseHandle(pInfo.hThread);
348         return (ProcHandle)pInfo.hProcess;
349 }
350
351 ProcHandle
352 runInteractiveProcess (char *cmd, char *workingDirectory, void *environment,
353                        int *pfdStdInput, int *pfdStdOutput, int *pfdStdError)
354 {
355         STARTUPINFO sInfo;
356         PROCESS_INFORMATION pInfo;
357         HANDLE hStdInputRead,  hStdInputWrite;
358         HANDLE hStdOutputRead, hStdOutputWrite;
359         HANDLE hStdErrorRead,  hStdErrorWrite;
360
361         if (!mkAnonPipe(&hStdInputRead,  TRUE, &hStdInputWrite,  FALSE))
362                 return -1;
363
364         if (!mkAnonPipe(&hStdOutputRead, FALSE, &hStdOutputWrite, TRUE))
365         {
366                 CloseHandle(hStdInputRead);
367                 CloseHandle(hStdInputWrite);
368                 return -1;
369         }
370
371         if (!mkAnonPipe(&hStdErrorRead,  FALSE, &hStdErrorWrite,  TRUE))
372         {
373                 CloseHandle(hStdInputRead);
374                 CloseHandle(hStdInputWrite);
375                 CloseHandle(hStdOutputRead);
376                 CloseHandle(hStdOutputWrite);
377                 return -1;
378         }
379
380         ZeroMemory(&sInfo, sizeof(sInfo));
381         sInfo.cb = sizeof(sInfo);
382         sInfo.dwFlags = STARTF_USESTDHANDLES;
383         sInfo.hStdInput = hStdInputRead;
384         sInfo.hStdOutput= hStdOutputWrite;
385         sInfo.hStdError = hStdErrorWrite;
386
387         if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, environment, workingDirectory, &sInfo, &pInfo))
388         {
389                 maperrno();
390                 CloseHandle(hStdInputRead);
391                 CloseHandle(hStdInputWrite);
392                 CloseHandle(hStdOutputRead);
393                 CloseHandle(hStdOutputWrite);
394                 CloseHandle(hStdErrorRead);
395                 CloseHandle(hStdErrorWrite);
396                 return -1;
397         }
398         CloseHandle(pInfo.hThread);
399
400         // Close the ends of the pipes that were inherited by the
401         // child process.  This is important, otherwise we won't see
402         // EOF on these pipes when the child process exits.
403         CloseHandle(hStdInputRead);
404         CloseHandle(hStdOutputWrite);
405         CloseHandle(hStdErrorWrite);
406
407         *pfdStdInput  = _open_osfhandle((intptr_t) hStdInputWrite, _O_WRONLY);
408         *pfdStdOutput = _open_osfhandle((intptr_t) hStdOutputRead, _O_RDONLY);
409         *pfdStdError  = _open_osfhandle((intptr_t) hStdErrorRead, _O_RDONLY);
410
411         return (int) pInfo.hProcess;
412 }
413
414 int
415 terminateProcess (ProcHandle handle)
416 {
417     if (!TerminateProcess((HANDLE) handle, 1)) {
418         maperrno();
419         return -1;
420     }
421     return 0;
422 }
423
424 int
425 getProcessExitCode (ProcHandle handle, int *pExitCode)
426 {
427     *pExitCode = 0;
428
429     if (WaitForSingleObject((HANDLE) handle, 1) == WAIT_OBJECT_0)
430     {
431         if (GetExitCodeProcess((HANDLE) handle, (DWORD *) pExitCode) == 0)
432         {
433             maperrno();
434             return -1;
435         }
436         return 1;
437     }
438     
439     return 0;
440 }
441
442 int
443 waitForProcess (ProcHandle handle)
444 {
445     DWORD retCode;
446
447     if (WaitForSingleObject((HANDLE) handle, INFINITE) == WAIT_OBJECT_0)
448     {
449         if (GetExitCodeProcess((HANDLE) handle, &retCode) == 0)
450         {
451             maperrno();
452             return -1;
453         }
454         return retCode;
455     }
456     
457     maperrno();
458     return -1;
459 }
460
461 #endif /* Win32 */