update ooo310-m15
[ooovba.git] / dmake / unix / runargv.c
blobed442e8df6e5662d66b4c3db3b6ab1de86a3bfcb
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 if( old_stderr != -1 )
557 dup2(old_stderr, 2);
560 internal = 1;
562 if ( internal ) {
563 /* Use _add_child() / _finished_child() with internal command. */
564 int cur_proc = _add_child(DMNOPID, target, ignore, last, FALSE);
565 _finished_child( (DMHANDLE)-cur_proc, 0 );
566 DB_RETURN( 0 );
569 /* Pack cmd in argument vector. */
570 argv = Pack_argv( group, shell, cmd );
572 /* Really spawn or fork a child. */
573 #if defined( USE_SPAWN )
574 /* As no other childs are started while the output is redirected this
575 * is save. */
576 if( Is_exec_shell ) {
577 /* Add error checking? */
578 old_stdout = dup(1);
579 dup2( fileno(stdout_redir), 1 );
581 if( mute ) {
582 old_stderr = dup(2);
583 dup2( zerofd, 2 );
585 if( !Is_exec_shell ) {
586 old_stdout = dup(1);
587 dup2( zerofd, 1 );
591 pid = dmspawn( argv );
592 terrno = errno;
594 if( old_stdout != -1 ) {
595 dup2(old_stdout, 1);
596 if( old_stderr != -1 )
597 dup2(old_stderr, 2);
599 if(pid.pid == (DMHANDLE)-1) {
600 /* spawn failed */
601 int cur_proc;
603 fprintf(stderr, "%s: Error executing '%s': %s",
604 Pname, argv[0], strerror(terrno) );
605 if( ignore||Continue ) {
606 fprintf(stderr, " (Ignored)" );
608 fprintf(stderr, "\n");
610 /* Use _add_child() / _finished_child() to treat the failure
611 * gracefully, if so requested. */
612 cur_proc = _add_child(DMNOPID, target, ignore, last, FALSE);
613 _finished_child((DMHANDLE)cur_proc, SIGTERM);
615 /* _finished_child() aborts dmake if we are not told to
616 * ignore errors. If we reach the this point return 0 as
617 * errors are obviously ignored and indicate that the process
618 * finished. */
619 DB_RETURN( 0 );
620 } else {
621 _add_child(pid, target, ignore, last, wfc);
623 #else /* USE_SPAWN */
625 fflush(stdout);
626 switch( pid.pid = fork() ){
628 case -1: /* fork failed */
629 Fatal("fork failed: %s: %s", argv[0], strerror( errno ));
631 case 0: /* child */
632 /* redirect output for _exec_shell / @@-recipes. */
633 if( Is_exec_shell ) {
634 /* Add error checking? */
635 old_stdout = dup(1);
636 dup2( fileno(stdout_redir), 1 );
638 if( mute ) {
639 old_stderr = dup(2);
640 dup2( zerofd, 2 );
642 if( !Is_exec_shell ) {
643 old_stdout = dup(1);
644 dup2( zerofd, 1 );
647 execvp(argv[0], argv);
648 /* restoring output to catch potential error output if execvp()
649 * failed. */
650 if( old_stdout != -1 ) {
651 dup2(old_stdout, 1);
652 if( old_stderr != -1 )
653 dup2(old_stderr, 2);
655 fprintf(stderr, "%s: Error executing '%s': %s",
656 Pname, argv[0], strerror(errno) );
657 if( ignore||Continue ) {
658 fprintf(stderr, " (Ignored)" );
660 fprintf(stderr, "\n");
662 kill(getpid(), SIGTERM);
663 /*NOTREACHED*/
664 Fatal("\nInternal Error - kill could't kill child %d.\n", getpid());
666 default: /* parent */
667 _add_child(pid, target, ignore, last, wfc);
670 #endif /* USE_SPAWN */
672 /* If wfc is set this command must have been finished. */
673 if( wfc ) {
674 DB_RETURN( 0 );
675 } else {
676 DB_RETURN( 1 );
681 PUBLIC int
682 Wait_for_child( abort_flg, pqid )/*
683 ===================================
684 Wait for the next processes from process queue pqid to finish. All finished
685 processes are handled by calling _finished_child() for each of them.
686 If pqid == -1 wait for the next process to finish.
687 If abort_flg is TRUE no further processes will be added to any process
688 queue. The A_WFC attribute is honored, see the documentation at the top
689 of this file.
690 Return 0 if we successfully waited for a process and -1 if there was nothing
691 to wait for.
693 int abort_flg;
694 int pqid;
696 DMHANDLE pid;
697 DMHANDLE wid;
698 int status;
699 int waitret; /* return value of the dmwait functions. */
700 /* Never wait for internal commands. */
701 int waitchild;
702 int is_exec_shell_status = Is_exec_shell;
704 if( !_procs ) {
705 /* No process was ever created, i.e. _procs is not yet initialized.
706 * Nothing to wait for. */
707 return -1;
710 if( pqid > Max_proc ) Fatal("Internal Error: pqid > Max_proc !");
712 if( pqid == -1 ) {
713 /* Check if there is something to wait for. */
714 int i;
715 for( i=0; i<Max_proc && !_procs[i].pr_valid; i++ )
717 if( i == Max_proc )
718 return(-1);
720 pid = (DMHANDLE)-1;
721 waitchild = FALSE;
723 else {
724 /* Check if pqid is active. */
725 if( !_procs[pqid].pr_valid ) {
726 /* Make this an error? */
727 Warning("Internal Warning: pqid is not active!?");
728 return(-1);
731 pid = _procs[pqid].pr_pid;
732 waitchild = _procs[pqid].pr_wfc;
736 /* It is impossible that processes that were started from _exec_shell
737 * have follow-up commands in its process entry. Unset Is_exec_shell
738 * to prevent piping of child processes that are started from the
739 * _finished_child subroutine and reset to its original value when
740 * leaving this function. */
741 Is_exec_shell = FALSE;
743 do {
744 /* Wait for the next process to finish. */
745 if( (pid != (DMHANDLE)-1) && (waitret = dmwaitpid(pqid, &wid, &status)) != 0 ) {
746 /* if dmwaitpid returns 0 this means that pid didn't finish yet.
747 * In this case just handle the next finished process in the
748 * following "else". If an error is returned (waitret < 0) the else
749 * clause is not evaluated and the error is handled in the following
750 * lines. If a process was waited for (waitret == 0) also proceed to
751 * the following lines. */
754 else {
755 waitret = dmwaitnext(&wid, &status);
756 /* If we get an error tell the error handling routine below that we
757 * were not waiting for a specific pid. */
758 if( waitret < 0 ) {
759 pid = (DMHANDLE)-1;
763 /* If ECHILD is set from waitpid/wait then no child was left. */
764 if( waitret < 0 ) {
765 if(waitret == -2) {
766 /* Wait was interrupted or a child was terminated (SIGCHLD) */
767 if ( in_quit() ) {
768 /* We're already terminating, just continue. */
769 return 0;
770 } else {
771 Fatal( "dmake was interrupted or a child terminated. "
772 "Stopping all childs ..." );
774 } else {
775 /* The child we were waiting for is missing or no child is
776 * left to wait for. */
777 if( pid != (DMHANDLE)-1 ) {
778 /* If we know the pid disable the pq entry. */
779 if( _procs[pqid].pr_valid ) {
780 _procs[pqid].pr_valid = 0;
781 _procs[pqid].pr_recipe = NIL(RCP);
782 _proc_cnt--;
784 } else {
785 /* otherwise disable all remaining pq's. As we don't know
786 * which pid failed there is no gracefull way to terminate. */
787 int i;
788 for( i=0; i<Max_proc; i++ ) {
789 _procs[i].pr_valid = 0;
790 _procs[i].pr_recipe = NIL(RCP);
792 _proc_cnt = 0;
794 /* The pid we were waiting for or any of the remaining childs
795 * (pid == -1) is missing. This should not happen and means
796 * that the process got lost or was treated elsewhere. */
797 Fatal( "Internal Error: Child is missing but still listed in _procs[x] %d: %s\n"
798 "\nTemporary or .ERRREMOVE targets might not have been removed!\n",
799 errno, strerror( errno ) );
803 _abort_flg = abort_flg;
804 _finished_child(wid, status);
805 _abort_flg = FALSE;
806 if( waitchild ) {
807 /* If pid != wid the process we're waiting for might have been
808 * finished from a "Wait_for_child(FALSE, -1)" call from
809 * _finished_child() -> runargv(). */
810 if( pid != wid ) {
811 if( !_procs[pqid].pr_valid || _procs[pqid].pr_pid != pid ) {
812 /* Someone finished pid, no need to wait further. */
813 waitchild = FALSE;
816 else
817 /* We finished pid, no need to wait further. */
818 waitchild = FALSE;
821 while( waitchild );
823 Is_exec_shell = is_exec_shell_status;
824 return(0);
828 PUBLIC void
829 Clean_up_processes()
831 register int i;
832 int ret;
834 if( _procs != NIL(PR) ) {
835 for( i=0; i<Max_proc; i++ )
836 if( _procs[i].pr_valid ) {
837 #if !defined(USE_CREATEPROCESS)
838 if( (ret = kill(_procs[i].pr_pid, SIGTERM)) ) {
839 fprintf(stderr, "Killing of pid %d from pq[%d] failed with: %s - %d ret: %d\n",
840 _procs[i].pr_pid, i,
841 strerror(errno), SIGTERM, ret );
843 #else
844 TerminateProcess(_procs[i].pr_pid, 1);
845 #endif
851 static int
852 _add_child( pid, target, ignore, last, wfc )/*
853 ==============================================
854 Creates/amend a process queue entry and enters the child parameters.
855 The pid == -1 represents an internal command and the function returns
856 the used process array index. For non-internal commands the function
857 returns -1.
858 If wfc (wait for completion) is TRUE the function calls
859 Wait_for_child to wait for the whole process queue to be finished.
861 TPID pid;
862 CELLPTR target;
863 int ignore;
864 int last;
865 int wfc;
867 register int i;
868 register PR *pp;
870 /* Never change MAXPROCESS after _procs is allocated. */
871 if( _procs_size != Max_proc ) {
872 /* If procs was never initialize this is OK, do it now. */
873 if( _procs == NIL(PR) ) {
874 _procs_size = Max_proc;
875 TALLOC( _procs, Max_proc, PR );
876 #if defined(USE_CREATEPROCESS)
877 TALLOC( _wpList, Max_proc, HANDLE );
879 /* Signed int values are cast to DMHANDLE in various places, use this
880 * sanity check to verify that DMHANDLE is large enough. */
881 if( sizeof(int) > sizeof(DMHANDLE) )
882 Fatal( "Internal Error: Check type of DMHANDLE!" );
883 #endif
885 else {
886 Fatal( "MAXPROCESS changed from `%d' to `%d' after a command was executed!", _procs_size, Max_proc );
890 if( Measure & M_RECIPE )
891 Do_profile_output( "s", M_RECIPE, target );
893 /* If _use_i!=-1 then this function is called by _finished_child()
894 * ( through runargv() ). */
895 if( (i = _use_i) == -1 ) {
896 for( i=0; i<Max_proc; i++ )
897 if( !_procs[i].pr_valid )
898 break;
900 else {
901 /* Re-use the process queue number given by _use_i.
902 * Free the pointer before using it again below. */
903 FREE( _procs[i].pr_dir );
906 pp = _procs+i;
908 pp->pr_valid = 1;
909 pp->pr_pid = pid.pid;
910 pp->pr_tid = pid.tid;
911 pp->pr_target = target;
912 pp->pr_ignore = ignore;
913 pp->pr_last = last;
914 pp->pr_wfc = wfc;
915 /* Freed above and after the last recipe in _finished child(). */
916 pp->pr_dir = DmStrDup(Get_current_dir());
918 Current_target = NIL(CELL);
920 _proc_cnt++;
922 if( pid.pid != (DMHANDLE)-1 ) {
923 /* Wait for each recipe to finish if wfc is TRUE. This
924 * basically forces sequential execution. */
925 if( wfc ) {
926 Wait_for_child( FALSE, i );
929 return -1;
930 } else
931 return i;
935 static void
936 _finished_child(cid, status)/*
937 ==============================
938 Handle process array entry for finished child. This can be a finished
939 process or a finished internal command depending on the content of cid.
940 For cid >= 1 the value of cid is used as the pid to of the finished
941 process and for cid < 1 -cid is used as the process array index of the
942 internal command.
944 DMHANDLE cid;
945 int status;
947 register int i;
948 char *dir;
950 if((int)cid < 1) { /* Force int. */
951 /* internal command */
952 i = -((int)cid);
954 else {
955 for( i=0; i<Max_proc; i++ )
956 if( _procs[i].pr_valid && _procs[i].pr_pid == cid )
957 break;
959 /* Some children we didn't make esp true if using /bin/sh to execute a
960 * a pipe and feed the output as a makefile into dmake. */
961 if( i == Max_proc ) {
962 Warning("Internal Warning: finished pid %d is not in pq!?", cid);
963 return;
967 /* Not a running process anymore, the next runargv() will not use
968 * _attach_cmd(). */
969 _procs[i].pr_valid = 0;
971 if( Measure & M_RECIPE )
972 Do_profile_output( "e", M_RECIPE, _procs[i].pr_target );
974 _proc_cnt--;
975 dir = DmStrDup(Get_current_dir());
976 Set_dir( _procs[i].pr_dir );
978 if( _procs[i].pr_recipe != NIL(RCP) && !_abort_flg ) {
979 RCPPTR rp = _procs[i].pr_recipe;
982 Current_target = _procs[i].pr_target;
983 Handle_result( status, _procs[i].pr_ignore, FALSE, _procs[i].pr_target );
984 Current_target = NIL(CELL);
986 if ( _procs[i].pr_target->ce_attr & A_ERROR ) {
987 _procs[i].pr_last = TRUE;
988 goto ABORT_REMAINDER_OF_RECIPE;
991 _procs[i].pr_recipe = rp->prp_next;
993 _use_i = i;
994 /* Run next recipe line. The rp->prp_attr propagates a possible
995 * wfc condition. */
996 runargv( _procs[i].pr_target, rp->prp_group,
997 rp->prp_last, rp->prp_attr, &rp->prp_cmd );
998 _use_i = -1;
1000 FREE( rp->prp_cmd );
1001 FREE( rp );
1003 /* If all process queues are used wait for the next process to
1004 * finish. Is this really needed here? */
1005 if( _proc_cnt == Max_proc ) {
1006 Wait_for_child( FALSE, -1 );
1009 else {
1010 /* empty the queue on abort. */
1011 if( _abort_flg )
1012 _procs[i].pr_recipe = NIL(RCP);
1014 Handle_result(status,_procs[i].pr_ignore,_abort_flg,_procs[i].pr_target);
1016 ABORT_REMAINDER_OF_RECIPE:
1017 if( _procs[i].pr_last ) {
1018 FREE(_procs[i].pr_dir ); /* Set in _add_child() */
1020 if( !Doing_bang ) {
1021 /* Update_time_stamp() triggers the deletion of intermediate
1022 * targets. This starts a new process queue, so we have to
1023 * clear the _use_i variable. */
1024 int my_use_i = _use_i;
1026 _use_i = -1;
1027 Update_time_stamp( _procs[i].pr_target );
1028 _use_i = my_use_i;
1033 Set_dir(dir);
1034 FREE(dir);
1038 static int
1039 _running( cp )/*
1040 ================
1041 Check if target exists in process array AND is running. Return its
1042 process array index if it is running, return -1 otherwise.
1044 CELLPTR cp;
1046 register int i;
1048 if( !_procs ) return( -1 );
1050 for( i=0; i<Max_proc; i++ )
1051 if( _procs[i].pr_valid &&
1052 _procs[i].pr_target == cp )
1053 break;
1055 return( i == Max_proc ? -1 : i );
1059 static void
1060 _attach_cmd( cmd, group, cp, cmnd_attr, last )/*
1061 ================================================
1062 Attach to an active process queue. Inherit wfc setting. */
1063 char *cmd;
1064 int group;
1065 CELLPTR cp;
1066 t_attr cmnd_attr;
1067 int last;
1069 register int i;
1070 RCPPTR rp;
1072 for( i=0; i<Max_proc; i++ )
1073 if( _procs[i].pr_valid &&
1074 _procs[i].pr_target == cp )
1075 break;
1077 TALLOC( rp, 1, RCP );
1078 rp->prp_cmd = DmStrDup(cmd);
1079 rp->prp_attr = cmnd_attr;
1080 /* Inherit wfc from process queue. */
1081 if( _procs[i].pr_wfc )
1082 rp->prp_attr |= A_WFC;
1083 rp->prp_group = group;
1084 rp->prp_last = last;
1086 if( _procs[i].pr_recipe == NIL(RCP) )
1087 _procs[i].pr_recipe = _procs[i].pr_recipe_end = rp;
1088 else {
1089 _procs[i].pr_recipe_end->prp_next = rp;
1090 _procs[i].pr_recipe_end = rp;