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