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