update dev300-m58
[ooovba.git] / dmake / unix / runargv.c
blobd74c0d62522810975e89c071f55dcbe9541c749c
1 /* $RCSfile: runargv.c,v $
2 -- $Revision: 1.14 $
3 -- last change: $Author: kz $ $Date: 2008-03-05 18:39:41 $
4 --
5 -- SYNOPSIS
6 -- Invoke a sub process.
7 --
8 -- DESCRIPTION
9 -- Use the standard methods of executing a sub process.
11 -- AUTHOR
12 -- Dennis Vadura, dvadura@dmake.wticorp.com
14 -- WWW
15 -- http://dmake.wticorp.com/
17 -- COPYRIGHT
18 -- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
19 --
20 -- This program is NOT free software; you can redistribute it and/or
21 -- modify it under the terms of the Software License Agreement Provided
22 -- in the file <distribution-root>/readme/license.txt.
24 -- LOG
25 -- Use cvs log to obtain detailed change logs.
28 This file (runargv.c) provides all the parallel process handling routines
29 for dmake on unix like operating systems. The following text briefly
30 describes the process flow.
32 Exec_commands() [make.c] builds the recipes associated to the given target.
33 They are build sequentially in a loop that calls Do_cmnd() for each of them.
35 Do_cmnd() [sysintf.c] feeds the given command or command group to runargv().
37 The following flowchart decripes the process flow starting with runargv,
38 descriptions for each of the functions are following.
40 +--------------------------------+
41 | runargv | <+
42 +--------------------------------+ |
43 | ^ |
44 | | returns if |
45 | calls | wfc is false |
46 v | |
47 +--------------------------------+ |
48 | _add_child | |
49 +--------------------------------+ |
50 | ^ |
51 | calls if | | if another process
52 | wfc is true | returns | is queued:
53 v | | recursive call
54 +--------------------------------+ |
55 | Wait_for_Child | |
56 +--------------------------------+ |
57 | ^ |
58 | | process queue |
59 | calls | is empty |
60 v | |
61 +--------------------------------+ |
62 | _finished_child | -+
63 +--------------------------------+
67 runargv() [unix/runargv] The runargv function manages up to MAXPROCESS
68 process queues (_procs[i]) for parallel process execution and hands
69 the actual commands down to the operating system.
70 Each of the process queues handles the sequential execution of commands
71 that belong to that process queue. Usually this means the sequential
72 execution of the recipe lines that belong to one target.
73 Even in non parallel builds (MAXPROCESS==1) child processes are
74 created and handled.
75 If recipes for a target are currently running attach them to the
76 corresponding process queue (_procs[i]) of that target and return.
77 If the maximum number (MAXPROCESS) of concurrently running queues is
78 reached use Wait_for_child(?, -1) to wait for a process queue to become
79 available.
80 New child processes are started using:
81 spawn: posix_spawnp (POSIX) or spawnvp (cygwin).
82 fork/execvp: Create a client process with fork and run the command
83 with execvp.
84 The parent calls _add_child() to track the child.
86 _add_child(..., wfc) [unix/runargv] creates (or reuses) a process queue
87 and enters the child's parameters.
88 If wfc (wait for completion) is TRUE the function calls
89 Wait_for_child to wait for the whole process queue to be finished.
91 Wait_for_child(abort_flg, pqid) [unix/runargv] waits either for the current
92 process from process queue pqid to finish or if the W_WFC attribute is
93 set for all entries of that process queue (recursively) to finish.
94 All finished processes are handled by calling _finished_child() for each
95 of them.
96 If pqid == -1 wait for the next process to finish but honor the A_WFC
97 attribute of that process (queue) and wait for the whole queue if needed.
98 If abort_flg is TRUE no further processes will be added to any process
99 queue.
100 If a pqid is given but a process from another process queue finishes
101 first that process is handled and A_WFC is also honored.
102 All finished processes are processed until the process from the given pqid
103 is reached or gone (might have been handled while finishing another process
104 queue).
106 _finished_child(pid, status) [unix/runargv] handles the finished child. If
107 there are more commands in the corresponding process queue start the next
108 with runargv().
111 #include <signal.h>
113 #include "extern.h"
115 #ifdef HAVE_WAIT_H
116 # include <wait.h>
117 #else
118 # ifdef HAVE_SYS_WAIT_H
119 # include <sys/wait.h>
120 # endif
121 #endif
123 #if HAVE_SPAWN_H && ENABLE_SPAWN
124 # include <spawn.h>
125 #endif
127 #if __CYGWIN__ && ENABLE_SPAWN
128 # include <process.h>
129 #endif
131 #ifdef __EMX__
132 # include <process.h>
133 #define _P_NOWAIT P_NOWAIT
134 #endif
136 #include "sysintf.h"
137 #if HAVE_ERRNO_H
138 # include <errno.h>
139 #else
140 extern int errno;
141 #endif
143 typedef struct prp {
144 char *prp_cmd;
145 int prp_group;
146 t_attr prp_attr;
147 int prp_last;
148 struct prp *prp_next;
149 } RCP, *RCPPTR;
151 #if defined(USE_CREATEPROCESS)
152 /* MS's HANDLE is basically a (void *) (winnt.h). */
153 typedef HANDLE DMHANDLE;
154 #else
155 typedef int DMHANDLE;
156 #endif
158 typedef struct pr {
159 int pr_valid;
160 DMHANDLE pr_pid;
161 DMHANDLE pr_tid;
162 CELLPTR pr_target;
163 int pr_ignore;
164 int pr_last;
165 int pr_wfc;
166 RCPPTR pr_recipe;
167 RCPPTR pr_recipe_end;
168 char *pr_dir;
169 } PR;
171 typedef struct tpid {
172 DMHANDLE pid;
173 DMHANDLE tid;
174 } TPID;
176 const TPID DMNOPID = { (DMHANDLE)-1, (DMHANDLE)0 };
178 static PR *_procs = NIL(PR); /* Array to hold concurrent processes. */
179 static int _procs_size = 0; /* Savegard to find MAXPROCESS changes. */
180 static int _proc_cnt = 0; /* Number of running processes. */
181 static int _abort_flg= FALSE;
182 static int _use_i = -1;
183 #if defined(USE_CREATEPROCESS)
184 static HANDLE *_wpList = NIL(HANDLE); /* Array to hold pids to wait for. */
185 #endif
187 static int _add_child ANSI((TPID, CELLPTR, int, int, int));
188 static void _attach_cmd ANSI((char *, int, CELLPTR, t_attr, int));
189 static void _finished_child ANSI((DMHANDLE, int));
190 static int _running ANSI((CELLPTR));
192 /* Machine/OS dependent helpers. */
193 static int dmwaitnext ANSI((DMHANDLE *, int *));
194 static int dmwaitpid ANSI((int, DMHANDLE *, int *));
196 #if defined( USE_SPAWN )
198 int terrno; /* Temporarily store errno. */
200 static TPID dmspawn ANSI((char **));
202 static TPID
203 dmspawn( argv )
204 char **argv;
206 TPID pid;
208 /* No error output is done here as stdout/stderr might be redirected. */
209 #if defined( __CYGWIN__) || defined( __EMX__)
210 pid.pid = spawnvp(_P_NOWAIT, argv[0], (const char**) argv);
211 pid.tid = 0;
212 #elif defined(USE_CREATEPROCESS)
213 static STARTUPINFO si;
214 static int initSTARTUPINFO = FALSE;
215 PROCESS_INFORMATION pi;
217 /* si can be reused. */
218 if( initSTARTUPINFO == FALSE ) {
219 initSTARTUPINFO = TRUE;
220 ZeroMemory( &si, sizeof(si) );
221 si.cb = sizeof(si);
223 ZeroMemory( &pi, sizeof(pi) );
225 /* Start the child process. CreateProcess() parameters:
226 * No module name (use command line).
227 * Command line. This fails if the path to the program contains spaces.
228 * Process handle not inheritable.
229 * Thread handle not inheritable.
230 * Set handle inheritance (stdout, stderr, etc.) to TRUE.
231 * No creation flags.
232 * Use parent's environment block.
233 * Use parent's starting directory.
234 * Pointer to STARTUPINFO structure.
235 * Pointer to PROCESS_INFORMATION structure. */
236 if( CreateProcess(NULL, argv[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) ) {
237 pid.pid = pi.hProcess;
238 pid.tid = pi.hThread;
239 } else {
240 fprintf(stderr, "CreateProcess failed (%d).\n", GetLastError() );
241 pid.pid = (DMHANDLE)-1;
243 #else /* Non cygwin, OS/2, MinGW and MSC */
244 int tpid;
245 if (posix_spawnp (&tpid, argv[0], NULL, NULL, argv, (char *)NULL))
246 tpid = -1; /* posix_spawn failed */
248 pid.pid = tpid;
249 pid.tid = 0;
250 #endif /* __CYGWIN__ */
251 return pid;
254 #endif /* USE_SPAWN */
256 static int
257 dmwaitnext( wid, status )
258 DMHANDLE *wid; /* Id we waited for. */
259 int *status; /* status of the finished process. */
260 /* return 1 if a process finished, -1 if there
261 * was nothing to wait for (ECHILD) and -2 for other errors. */
264 #if !defined(USE_CREATEPROCESS)
265 /* Here might be the culprit for the famous OOo build hang. If
266 * cygwin manages to "loose" a process and none else is left the
267 * wait() will wait forever. */
268 *wid = wait(status);
270 /* If ECHILD is set from waitpid/wait then no child was left. */
271 if( *wid == -1 ) {
272 fprintf(stderr, "%s: Internal Error: wait() failed: %d - %s\n",
273 Pname, errno, strerror(errno) );
274 if(errno != ECHILD) {
275 /* Wait was interrupted or a child was terminated (SIGCHLD) */
276 return -2;
277 } else {
278 return -1;
281 #else
282 DWORD pEvent;
283 DWORD dwExitCode;
284 int i;
285 int numProc = 0;
287 *status = 0;
289 /* Create a list of possible objects to wait for. */
290 for( i=0; i<Max_proc; i++ ) {
291 if(_procs[i].pr_valid) {
292 _wpList[numProc++] = _procs[i].pr_pid;
295 if( numProc == 0 ) {
296 fprintf(stderr, "%s: Internal Error: dmwaitnext() failed: "
297 "Nothing to wait for.\n", Pname );
298 return -1;
301 /* Wait ... */
302 /* number of objects in array, array of objects,
303 * wait for any object, wait for the next child to finish */
304 pEvent = WaitForMultipleObjects( numProc, _wpList, FALSE, INFINITE);
306 if( pEvent >= 0 && pEvent < WAIT_OBJECT_0 + numProc ) {
307 *wid = _wpList[pEvent - WAIT_OBJECT_0];
308 for( i=0; i<Max_proc && _procs[i].pr_pid != *wid; i++ )
310 if( i == Max_proc )
311 Fatal("Internal Error: Process not in pq !");
313 GetExitCodeProcess(*wid, &dwExitCode);
314 if(dwExitCode == STILL_ACTIVE) {
315 /* Process did not terminate -> force it, with exit code 1. */
316 TerminateProcess(*wid, 1);
317 dwExitCode = 1;
318 fprintf(stderr, "%s: Internal Error: Process still running - "
319 "terminate it!\n", Pname );
322 /* Close process and thread handles. */
323 CloseHandle( *wid );
324 CloseHandle( _procs[i].pr_tid );
325 *status = dwExitCode;
327 else {
328 int err = GetLastError();
329 LPVOID lpMsgBuf;
331 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
332 FORMAT_MESSAGE_FROM_SYSTEM |
333 FORMAT_MESSAGE_IGNORE_INSERTS,
334 NULL,
335 err,
336 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
337 (LPTSTR) &lpMsgBuf,
338 0, NULL );
340 fprintf(stderr, "%s: Internal Error: WaitForMultipleObjects() (%d) failed:"
341 " %d - %s\n", Pname, numProc, err, lpMsgBuf);
342 LocalFree(lpMsgBuf);
344 /* No way to identify something comparable to ECHILD, always return -2.*/
345 return -2;
348 #endif
349 return 1;
353 static int
354 dmwaitpid( pqid, wid, status )
355 int pqid; /* Process queue to wait for. */
356 DMHANDLE *wid; /* Id we waited for. */
357 int *status; /* status of the finished process. */
358 /* return 1 if the process finished, 0 if it didn't finish yet, -1 if there
359 * was nothing to wait for (ECHILD) and -2 for other errors. */
362 #if !defined(USE_CREATEPROCESS)
363 *wid = waitpid(_procs[pqid].pr_pid, status, WNOHANG);
365 /* Process still running. */
366 if( *wid == 0 ) {
367 *status = 0;
368 return 0;
370 /* If ECHILD is set from waitpid/wait then no child was left. */
371 if( *wid == -1 ) {
372 fprintf(stderr, "%s: Internal Error: waitpid() failed: %d - %s\n",
373 Pname, errno, strerror(errno) );
374 if(errno != ECHILD) {
375 /* Wait was interrupted or a child was terminated (SIGCHLD) */
376 return -2;
377 } else {
378 return -1;
381 #else
382 DWORD pEvent;
383 DWORD dwExitCode;
385 *wid = _procs[pqid].pr_pid;
386 *status = 0;
388 /* Wait ... (Check status and return) */
389 pEvent = WaitForSingleObject(*wid, 0);
391 if( pEvent == WAIT_OBJECT_0 ) {
392 GetExitCodeProcess(*wid, &dwExitCode);
393 if(dwExitCode == STILL_ACTIVE) {
394 /* Process did not terminate -> force it, with exit code 1. */
395 TerminateProcess(*wid, 1);
396 dwExitCode = 1;
397 fprintf(stderr, "%s: Internal Error: Process still running - "
398 "terminate it!\n", Pname );
401 /* Close process and thread handles. */
402 CloseHandle( *wid );
403 CloseHandle( _procs[pqid].pr_tid );
404 *status = dwExitCode;
406 else if( pEvent == WAIT_TIMEOUT ) {
407 return 0;
409 else {
410 int err = GetLastError();
411 LPVOID lpMsgBuf;
413 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
414 FORMAT_MESSAGE_FROM_SYSTEM |
415 FORMAT_MESSAGE_IGNORE_INSERTS,
416 NULL,
417 err,
418 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
419 (LPTSTR) &lpMsgBuf,
420 0, NULL );
422 fprintf(stderr, "%s: Internal Error: WaitForSingleObject() failed:"
423 " %d - %s\n", Pname, err, lpMsgBuf);
424 LocalFree(lpMsgBuf);
426 /* No way to identify something comparable to ECHILD, always return -2.*/
427 return -2;
429 #endif
431 return 1;
435 #if ! HAVE_STRERROR
436 static char *
437 private_strerror (errnum)
438 int errnum;
440 #ifndef __APPLE__
441 # if defined(arm32) || defined(linux) || defined(__FreeBSD__) || defined(__OpenBSD__)
442 extern const char * const sys_errlist[];
443 # else
444 extern char *sys_errlist[];
445 # endif
446 #endif
447 extern int sys_nerr;
449 if (errnum > 0 && errnum <= sys_nerr)
450 return sys_errlist[errnum];
451 return "Unknown system error";
453 #define strerror private_strerror
454 #endif /* HAVE_STRERROR */
456 PUBLIC int
457 runargv(target, group, last, cmnd_attr, cmd)/*
458 ==============================================
459 Execute the command given by cmd.
461 Return 0 if the command executed and finished or
462 1 if the command started and is running.
464 CELLPTR target;
465 int group;
466 int last;
467 t_attr cmnd_attr; /* Attributes for current cmnd. */
468 char **cmd; /* Simulate a reference to *cmd. */
470 int ignore = (cmnd_attr & A_IGNORE)!= 0; /* Ignore errors ('-'). */
471 int shell = (cmnd_attr & A_SHELL) != 0; /* Use shell ('+'). */
472 int mute = (cmnd_attr & A_MUTE) != 0; /* Mute output ('@@'). */
473 int wfc = (cmnd_attr & A_WFC) != 0; /* Wait for completion. */
475 TPID pid;
476 int st_pq = 0; /* Current _exec_shell target process index */
477 char *tcmd = *cmd; /* For saver/easier string arithmetic on *cmd. */
478 char **argv;
480 int old_stdout = -1; /* For shell escapes and */
481 int old_stderr = -1; /* @@-recipe silencing. */
482 int internal = 0; /* Used to indicate internal command. */
484 DB_ENTER( "runargv" );
486 /* Special handling for the shell function macro is required. If the
487 * currend command is called as part of a shell escape in a recipe make
488 * sure that all previous recipe lines of this target have finished. */
489 if( Is_exec_shell ) {
490 if( (st_pq = _running(Shell_exec_target)) != -1 ) {
491 RCPPTR rp;
492 /* Add WFC to _procs[st_pq]. */
493 _procs[st_pq].pr_wfc = TRUE;
494 /* Set also the A_WFC flag in the recipe attributes. */
495 for( rp = _procs[st_pq].pr_recipe ; rp != NIL(RCP); rp = rp->prp_next )
496 rp->prp_attr |= A_WFC;
498 Wait_for_child(FALSE, st_pq);
500 } else {
501 if( _running(target) != -1 /*&& Max_proc != 1*/ ) {
502 /* The command will be executed when the previous recipe
503 * line completes. */
504 _attach_cmd( *cmd, group, target, cmnd_attr, last );
505 DB_RETURN( 1 );
509 /* If all process array entries are used wait until we get a free
510 * slot. For Max_proc == 1 this forces sequential execution. */
511 while( _proc_cnt == Max_proc ) {
512 Wait_for_child(FALSE, -1);
515 /* Return immediately for empty line or noop command. */
516 if ( !*tcmd || /* empty line */
517 ( strncmp(tcmd, "noop", 4) == 0 && /* noop command */
518 (iswhite(tcmd[4]) || tcmd[4] == '\0')) ) {
519 internal = 1;
521 else if( !shell && /* internal echo only if not in shell */
522 strncmp(tcmd, "echo", 4) == 0 &&
523 (iswhite(tcmd[4]) || tcmd[4] == '\0') ) {
524 int nl = 1;
526 tcmd = tcmd+4;
527 while( iswhite(*tcmd) ) ++tcmd;
528 if ( strncmp(tcmd,"-n",2 ) == 0) {
529 nl = 0;
530 tcmd = tcmd+2;
531 while( iswhite(*tcmd) ) ++tcmd;
534 /* redirect output for _exec_shell / @@-recipes. */
535 if( Is_exec_shell ) {
536 /* Add error checking? */
537 old_stdout = dup(1);
538 dup2( fileno(stdout_redir), 1 );
540 if( mute ) {
541 old_stderr = dup(2);
542 dup2( zerofd, 2 );
544 if( !Is_exec_shell ) {
545 old_stdout = dup(1);
546 dup2( zerofd, 1 );
550 printf("%s%s", tcmd, nl ? "\n" : "");
551 fflush(stdout);
553 /* Restore stdout/stderr if needed. */
554 if( old_stdout != -1 ) {
555 dup2(old_stdout, 1);
556 close(old_stdout);
557 if( old_stderr != -1 ) {
558 dup2(old_stderr, 2);
559 close(old_stderr);
563 internal = 1;
565 if ( internal ) {
566 /* Use _add_child() / _finished_child() with internal command. */
567 int cur_proc = _add_child(DMNOPID, target, ignore, last, FALSE);
568 _finished_child( (DMHANDLE)-cur_proc, 0 );
569 DB_RETURN( 0 );
572 /* Pack cmd in argument vector. */
573 argv = Pack_argv( group, shell, cmd );
575 /* Really spawn or fork a child. */
576 #if defined( USE_SPAWN )
577 /* As no other childs are started while the output is redirected this
578 * is save. */
579 if( Is_exec_shell ) {
580 /* Add error checking? */
581 old_stdout = dup(1);
582 dup2( fileno(stdout_redir), 1 );
584 if( mute ) {
585 old_stderr = dup(2);
586 dup2( zerofd, 2 );
588 if( !Is_exec_shell ) {
589 old_stdout = dup(1);
590 dup2( zerofd, 1 );
594 pid = dmspawn( argv );
595 terrno = errno;
597 if( old_stdout != -1 ) {
598 dup2(old_stdout, 1);
599 close(old_stdout);
600 if( old_stderr != -1 ) {
601 dup2(old_stderr, 2);
602 close(old_stderr);
605 if(pid.pid == (DMHANDLE)-1) {
606 /* spawn failed */
607 int cur_proc;
609 fprintf(stderr, "%s: Error executing '%s': %s",
610 Pname, argv[0], strerror(terrno) );
611 if( ignore||Continue ) {
612 fprintf(stderr, " (Ignored)" );
614 fprintf(stderr, "\n");
616 /* Use _add_child() / _finished_child() to treat the failure
617 * gracefully, if so requested. */
618 cur_proc = _add_child(DMNOPID, target, ignore, last, FALSE);
619 _finished_child((DMHANDLE)cur_proc, SIGTERM);
621 /* _finished_child() aborts dmake if we are not told to
622 * ignore errors. If we reach the this point return 0 as
623 * errors are obviously ignored and indicate that the process
624 * finished. */
625 DB_RETURN( 0 );
626 } else {
627 _add_child(pid, target, ignore, last, wfc);
629 #else /* USE_SPAWN */
631 fflush(stdout);
632 switch( pid.pid = fork() ){
634 case -1: /* fork failed */
635 Fatal("fork failed: %s: %s", argv[0], strerror( errno ));
637 case 0: /* child */
638 /* redirect output for _exec_shell / @@-recipes. */
639 if( Is_exec_shell ) {
640 /* Add error checking? */
641 old_stdout = dup(1);
642 dup2( fileno(stdout_redir), 1 );
644 if( mute ) {
645 old_stderr = dup(2);
646 dup2( zerofd, 2 );
648 if( !Is_exec_shell ) {
649 old_stdout = dup(1);
650 dup2( zerofd, 1 );
653 execvp(argv[0], argv);
654 /* restoring output to catch potential error output if execvp()
655 * failed. */
656 if( old_stdout != -1 ) {
657 dup2(old_stdout, 1);
658 close(old_stdout);
659 if( old_stderr != -1 ) {
660 dup2(old_stderr, 2);
661 close(old_stderr);
664 fprintf(stderr, "%s: Error executing '%s': %s",
665 Pname, argv[0], strerror(errno) );
666 if( ignore||Continue ) {
667 fprintf(stderr, " (Ignored)" );
669 fprintf(stderr, "\n");
671 kill(getpid(), SIGTERM);
672 /*NOTREACHED*/
673 Fatal("\nInternal Error - kill could't kill child %d.\n", getpid());
675 default: /* parent */
676 _add_child(pid, target, ignore, last, wfc);
679 #endif /* USE_SPAWN */
681 /* If wfc is set this command must have been finished. */
682 if( wfc ) {
683 DB_RETURN( 0 );
684 } else {
685 DB_RETURN( 1 );
690 PUBLIC int
691 Wait_for_child( abort_flg, pqid )/*
692 ===================================
693 Wait for the next processes from process queue pqid to finish. All finished
694 processes are handled by calling _finished_child() for each of them.
695 If pqid == -1 wait for the next process to finish.
696 If abort_flg is TRUE no further processes will be added to any process
697 queue. The A_WFC attribute is honored, see the documentation at the top
698 of this file.
699 Return 0 if we successfully waited for a process and -1 if there was nothing
700 to wait for.
702 int abort_flg;
703 int pqid;
705 DMHANDLE pid;
706 DMHANDLE wid;
707 int status;
708 int waitret; /* return value of the dmwait functions. */
709 /* Never wait for internal commands. */
710 int waitchild;
711 int is_exec_shell_status = Is_exec_shell;
713 if( !_procs ) {
714 /* No process was ever created, i.e. _procs is not yet initialized.
715 * Nothing to wait for. */
716 return -1;
719 if( pqid > Max_proc ) Fatal("Internal Error: pqid > Max_proc !");
721 if( pqid == -1 ) {
722 /* Check if there is something to wait for. */
723 int i;
724 for( i=0; i<Max_proc && !_procs[i].pr_valid; i++ )
726 if( i == Max_proc )
727 return(-1);
729 pid = (DMHANDLE)-1;
730 waitchild = FALSE;
732 else {
733 /* Check if pqid is active. */
734 if( !_procs[pqid].pr_valid ) {
735 /* Make this an error? */
736 Warning("Internal Warning: pqid is not active!?");
737 return(-1);
740 pid = _procs[pqid].pr_pid;
741 waitchild = _procs[pqid].pr_wfc;
745 /* It is impossible that processes that were started from _exec_shell
746 * have follow-up commands in its process entry. Unset Is_exec_shell
747 * to prevent piping of child processes that are started from the
748 * _finished_child subroutine and reset to its original value when
749 * leaving this function. */
750 Is_exec_shell = FALSE;
752 do {
753 /* Wait for the next process to finish. */
754 if( (pid != (DMHANDLE)-1) && (waitret = dmwaitpid(pqid, &wid, &status)) != 0 ) {
755 /* if dmwaitpid returns 0 this means that pid didn't finish yet.
756 * In this case just handle the next finished process in the
757 * following "else". If an error is returned (waitret < 0) the else
758 * clause is not evaluated and the error is handled in the following
759 * lines. If a process was waited for (waitret == 0) also proceed to
760 * the following lines. */
763 else {
764 waitret = dmwaitnext(&wid, &status);
765 /* If we get an error tell the error handling routine below that we
766 * were not waiting for a specific pid. */
767 if( waitret < 0 ) {
768 pid = (DMHANDLE)-1;
772 /* If ECHILD is set from waitpid/wait then no child was left. */
773 if( waitret < 0 ) {
774 if(waitret == -2) {
775 /* Wait was interrupted or a child was terminated (SIGCHLD) */
776 if ( in_quit() ) {
777 /* We're already terminating, just continue. */
778 return 0;
779 } else {
780 Fatal( "dmake was interrupted or a child terminated. "
781 "Stopping all childs ..." );
783 } else {
784 /* The child we were waiting for is missing or no child is
785 * left to wait for. */
786 if( pid != (DMHANDLE)-1 ) {
787 /* If we know the pid disable the pq entry. */
788 if( _procs[pqid].pr_valid ) {
789 _procs[pqid].pr_valid = 0;
790 _procs[pqid].pr_recipe = NIL(RCP);
791 _proc_cnt--;
793 } else {
794 /* otherwise disable all remaining pq's. As we don't know
795 * which pid failed there is no gracefull way to terminate. */
796 int i;
797 for( i=0; i<Max_proc; i++ ) {
798 _procs[i].pr_valid = 0;
799 _procs[i].pr_recipe = NIL(RCP);
801 _proc_cnt = 0;
803 /* The pid we were waiting for or any of the remaining childs
804 * (pid == -1) is missing. This should not happen and means
805 * that the process got lost or was treated elsewhere. */
806 Fatal( "Internal Error: Child is missing but still listed in _procs[x] %d: %s\n"
807 "\nTemporary or .ERRREMOVE targets might not have been removed!\n",
808 errno, strerror( errno ) );
812 _abort_flg = abort_flg;
813 _finished_child(wid, status);
814 _abort_flg = FALSE;
815 if( waitchild ) {
816 /* If pid != wid the process we're waiting for might have been
817 * finished from a "Wait_for_child(FALSE, -1)" call from
818 * _finished_child() -> runargv(). */
819 if( pid != wid ) {
820 if( !_procs[pqid].pr_valid || _procs[pqid].pr_pid != pid ) {
821 /* Someone finished pid, no need to wait further. */
822 waitchild = FALSE;
825 else
826 /* We finished pid, no need to wait further. */
827 waitchild = FALSE;
830 while( waitchild );
832 Is_exec_shell = is_exec_shell_status;
833 return(0);
837 PUBLIC void
838 Clean_up_processes()
840 register int i;
841 int ret;
843 if( _procs != NIL(PR) ) {
844 for( i=0; i<Max_proc; i++ )
845 if( _procs[i].pr_valid ) {
846 #if !defined(USE_CREATEPROCESS)
847 if( (ret = kill(_procs[i].pr_pid, SIGTERM)) ) {
848 fprintf(stderr, "Killing of pid %d from pq[%d] failed with: %s - %d ret: %d\n",
849 _procs[i].pr_pid, i,
850 strerror(errno), SIGTERM, ret );
852 #else
853 TerminateProcess(_procs[i].pr_pid, 1);
854 #endif
860 static int
861 _add_child( pid, target, ignore, last, wfc )/*
862 ==============================================
863 Creates/amend a process queue entry and enters the child parameters.
864 The pid == -1 represents an internal command and the function returns
865 the used process array index. For non-internal commands the function
866 returns -1.
867 If wfc (wait for completion) is TRUE the function calls
868 Wait_for_child to wait for the whole process queue to be finished.
870 TPID pid;
871 CELLPTR target;
872 int ignore;
873 int last;
874 int wfc;
876 register int i;
877 register PR *pp;
879 /* Never change MAXPROCESS after _procs is allocated. */
880 if( _procs_size != Max_proc ) {
881 /* If procs was never initialize this is OK, do it now. */
882 if( _procs == NIL(PR) ) {
883 _procs_size = Max_proc;
884 TALLOC( _procs, Max_proc, PR );
885 #if defined(USE_CREATEPROCESS)
886 TALLOC( _wpList, Max_proc, HANDLE );
888 /* Signed int values are cast to DMHANDLE in various places, use this
889 * sanity check to verify that DMHANDLE is large enough. */
890 if( sizeof(int) > sizeof(DMHANDLE) )
891 Fatal( "Internal Error: Check type of DMHANDLE!" );
892 #endif
894 else {
895 Fatal( "MAXPROCESS changed from `%d' to `%d' after a command was executed!", _procs_size, Max_proc );
899 if( Measure & M_RECIPE )
900 Do_profile_output( "s", M_RECIPE, target );
902 /* If _use_i!=-1 then this function is called by _finished_child()
903 * ( through runargv() ). */
904 if( (i = _use_i) == -1 ) {
905 for( i=0; i<Max_proc; i++ )
906 if( !_procs[i].pr_valid )
907 break;
909 else {
910 /* Re-use the process queue number given by _use_i.
911 * Free the pointer before using it again below. */
912 FREE( _procs[i].pr_dir );
915 pp = _procs+i;
917 pp->pr_valid = 1;
918 pp->pr_pid = pid.pid;
919 pp->pr_tid = pid.tid;
920 pp->pr_target = target;
921 pp->pr_ignore = ignore;
922 pp->pr_last = last;
923 pp->pr_wfc = wfc;
924 /* Freed above and after the last recipe in _finished child(). */
925 pp->pr_dir = DmStrDup(Get_current_dir());
927 Current_target = NIL(CELL);
929 _proc_cnt++;
931 if( pid.pid != (DMHANDLE)-1 ) {
932 /* Wait for each recipe to finish if wfc is TRUE. This
933 * basically forces sequential execution. */
934 if( wfc ) {
935 Wait_for_child( FALSE, i );
938 return -1;
939 } else
940 return i;
944 static void
945 _finished_child(cid, status)/*
946 ==============================
947 Handle process array entry for finished child. This can be a finished
948 process or a finished internal command depending on the content of cid.
949 For cid >= 1 the value of cid is used as the pid to of the finished
950 process and for cid < 1 -cid is used as the process array index of the
951 internal command.
953 DMHANDLE cid;
954 int status;
956 register int i;
957 char *dir;
959 if((int)cid < 1) { /* Force int. */
960 /* internal command */
961 i = -((int)cid);
963 else {
964 for( i=0; i<Max_proc; i++ )
965 if( _procs[i].pr_valid && _procs[i].pr_pid == cid )
966 break;
968 /* Some children we didn't make esp true if using /bin/sh to execute a
969 * a pipe and feed the output as a makefile into dmake. */
970 if( i == Max_proc ) {
971 Warning("Internal Warning: finished pid %d is not in pq!?", cid);
972 return;
976 /* Not a running process anymore, the next runargv() will not use
977 * _attach_cmd(). */
978 _procs[i].pr_valid = 0;
980 if( Measure & M_RECIPE )
981 Do_profile_output( "e", M_RECIPE, _procs[i].pr_target );
983 _proc_cnt--;
984 dir = DmStrDup(Get_current_dir());
985 Set_dir( _procs[i].pr_dir );
987 if( _procs[i].pr_recipe != NIL(RCP) && !_abort_flg ) {
988 RCPPTR rp = _procs[i].pr_recipe;
991 Current_target = _procs[i].pr_target;
992 Handle_result( status, _procs[i].pr_ignore, FALSE, _procs[i].pr_target );
993 Current_target = NIL(CELL);
995 if ( _procs[i].pr_target->ce_attr & A_ERROR ) {
996 _procs[i].pr_last = TRUE;
997 goto ABORT_REMAINDER_OF_RECIPE;
1000 _procs[i].pr_recipe = rp->prp_next;
1002 _use_i = i;
1003 /* Run next recipe line. The rp->prp_attr propagates a possible
1004 * wfc condition. */
1005 runargv( _procs[i].pr_target, rp->prp_group,
1006 rp->prp_last, rp->prp_attr, &rp->prp_cmd );
1007 _use_i = -1;
1009 FREE( rp->prp_cmd );
1010 FREE( rp );
1012 /* If all process queues are used wait for the next process to
1013 * finish. Is this really needed here? */
1014 if( _proc_cnt == Max_proc ) {
1015 Wait_for_child( FALSE, -1 );
1018 else {
1019 /* empty the queue on abort. */
1020 if( _abort_flg )
1021 _procs[i].pr_recipe = NIL(RCP);
1023 Handle_result(status,_procs[i].pr_ignore,_abort_flg,_procs[i].pr_target);
1025 ABORT_REMAINDER_OF_RECIPE:
1026 if( _procs[i].pr_last ) {
1027 FREE(_procs[i].pr_dir ); /* Set in _add_child() */
1029 if( !Doing_bang ) {
1030 /* Update_time_stamp() triggers the deletion of intermediate
1031 * targets. This starts a new process queue, so we have to
1032 * clear the _use_i variable. */
1033 int my_use_i = _use_i;
1035 _use_i = -1;
1036 Update_time_stamp( _procs[i].pr_target );
1037 _use_i = my_use_i;
1042 Set_dir(dir);
1043 FREE(dir);
1047 static int
1048 _running( cp )/*
1049 ================
1050 Check if target exists in process array AND is running. Return its
1051 process array index if it is running, return -1 otherwise.
1053 CELLPTR cp;
1055 register int i;
1057 if( !_procs ) return( -1 );
1059 for( i=0; i<Max_proc; i++ )
1060 if( _procs[i].pr_valid &&
1061 _procs[i].pr_target == cp )
1062 break;
1064 return( i == Max_proc ? -1 : i );
1068 static void
1069 _attach_cmd( cmd, group, cp, cmnd_attr, last )/*
1070 ================================================
1071 Attach to an active process queue. Inherit wfc setting. */
1072 char *cmd;
1073 int group;
1074 CELLPTR cp;
1075 t_attr cmnd_attr;
1076 int last;
1078 register int i;
1079 RCPPTR rp;
1081 for( i=0; i<Max_proc; i++ )
1082 if( _procs[i].pr_valid &&
1083 _procs[i].pr_target == cp )
1084 break;
1086 TALLOC( rp, 1, RCP );
1087 rp->prp_cmd = DmStrDup(cmd);
1088 rp->prp_attr = cmnd_attr;
1089 /* Inherit wfc from process queue. */
1090 if( _procs[i].pr_wfc )
1091 rp->prp_attr |= A_WFC;
1092 rp->prp_group = group;
1093 rp->prp_last = last;
1095 if( _procs[i].pr_recipe == NIL(RCP) )
1096 _procs[i].pr_recipe = _procs[i].pr_recipe_end = rp;
1097 else {
1098 _procs[i].pr_recipe_end->prp_next = rp;
1099 _procs[i].pr_recipe_end = rp;