dont attach, start own instance as otherwise it might be blocked via/proc/sys/kernel...
[LibreOffice.git] / dmake / unix / runargv.c
blob4e87862125ae4e84ff9e97714550bf75cac8c3b9
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.
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 int realErr = errno; // fprintf can pollute errno
273 fprintf(stderr, "%s: Internal Error: wait() failed: %d - %s\n",
274 Pname, errno, strerror(errno) );
275 if( realErr != ECHILD ) {
276 /* Wait was interrupted or a child was terminated (SIGCHLD) */
277 return -2;
278 } else {
279 return -1;
282 #else
283 DWORD pEvent;
284 DWORD dwExitCode;
285 int i;
286 int numProc = 0;
288 *status = 0;
290 /* Create a list of possible objects to wait for. */
291 for( i=0; i<Max_proc; i++ ) {
292 if(_procs[i].pr_valid) {
293 _wpList[numProc++] = _procs[i].pr_pid;
296 if( numProc == 0 ) {
297 fprintf(stderr, "%s: Internal Error: dmwaitnext() failed: "
298 "Nothing to wait for.\n", Pname );
299 return -1;
302 /* Wait ... */
303 /* number of objects in array, array of objects,
304 * wait for any object, wait for the next child to finish */
305 pEvent = WaitForMultipleObjects( numProc, _wpList, FALSE, INFINITE);
307 if( pEvent >= 0 && pEvent < WAIT_OBJECT_0 + numProc ) {
308 *wid = _wpList[pEvent - WAIT_OBJECT_0];
309 for( i=0; i<Max_proc && _procs[i].pr_pid != *wid; i++ )
311 if( i == Max_proc )
312 Fatal("Internal Error: Process not in pq !");
314 GetExitCodeProcess(*wid, &dwExitCode);
315 if(dwExitCode == STILL_ACTIVE) {
316 /* Process did not terminate -> force it, with exit code 1. */
317 TerminateProcess(*wid, 1);
318 dwExitCode = 1;
319 fprintf(stderr, "%s: Internal Error: Process still running - "
320 "terminate it!\n", Pname );
323 /* Close process and thread handles. */
324 CloseHandle( *wid );
325 CloseHandle( _procs[i].pr_tid );
326 *status = dwExitCode;
328 else {
329 int err = GetLastError();
330 LPVOID lpMsgBuf;
332 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
333 FORMAT_MESSAGE_FROM_SYSTEM |
334 FORMAT_MESSAGE_IGNORE_INSERTS,
335 NULL,
336 err,
337 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
338 (LPTSTR) &lpMsgBuf,
339 0, NULL );
341 fprintf(stderr, "%s: Internal Error: WaitForMultipleObjects() (%d) failed:"
342 " %d - %s\n", Pname, numProc, err, lpMsgBuf);
343 LocalFree(lpMsgBuf);
345 /* No way to identify something comparable to ECHILD, always return -2.*/
346 return -2;
349 #endif
350 return 1;
354 static int
355 dmwaitpid( pqid, wid, status )
356 int pqid; /* Process queue to wait for. */
357 DMHANDLE *wid; /* Id we waited for. */
358 int *status; /* status of the finished process. */
359 /* return 1 if the process finished, 0 if it didn't finish yet, -1 if there
360 * was nothing to wait for (ECHILD) and -2 for other errors. */
363 #if !defined(USE_CREATEPROCESS)
364 *wid = waitpid(_procs[pqid].pr_pid, status, WNOHANG);
366 /* Process still running. */
367 if( *wid == 0 ) {
368 *status = 0;
369 return 0;
371 /* If ECHILD is set from waitpid/wait then no child was left. */
372 if( *wid == -1 ) {
373 int realErr = errno; // fprintf can pollute errno
374 fprintf(stderr, "%s: Internal Error: waitpid() failed: %d - %s\n",
375 Pname, errno, strerror(errno) );
376 if(realErr != ECHILD) {
377 /* Wait was interrupted or a child was terminated (SIGCHLD) */
378 return -2;
379 } else {
380 return -1;
383 #else
384 DWORD pEvent;
385 DWORD dwExitCode;
387 *wid = _procs[pqid].pr_pid;
388 *status = 0;
390 /* Wait ... (Check status and return) */
391 pEvent = WaitForSingleObject(*wid, 0);
393 if( pEvent == WAIT_OBJECT_0 ) {
394 GetExitCodeProcess(*wid, &dwExitCode);
395 if(dwExitCode == STILL_ACTIVE) {
396 /* Process did not terminate -> force it, with exit code 1. */
397 TerminateProcess(*wid, 1);
398 dwExitCode = 1;
399 fprintf(stderr, "%s: Internal Error: Process still running - "
400 "terminate it!\n", Pname );
403 /* Close process and thread handles. */
404 CloseHandle( *wid );
405 CloseHandle( _procs[pqid].pr_tid );
406 *status = dwExitCode;
408 else if( pEvent == WAIT_TIMEOUT ) {
409 return 0;
411 else {
412 int err = GetLastError();
413 LPVOID lpMsgBuf;
415 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
416 FORMAT_MESSAGE_FROM_SYSTEM |
417 FORMAT_MESSAGE_IGNORE_INSERTS,
418 NULL,
419 err,
420 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
421 (LPTSTR) &lpMsgBuf,
422 0, NULL );
424 fprintf(stderr, "%s: Internal Error: WaitForSingleObject() failed:"
425 " %d - %s\n", Pname, err, lpMsgBuf);
426 LocalFree(lpMsgBuf);
428 /* No way to identify something comparable to ECHILD, always return -2.*/
429 return -2;
431 #endif
433 return 1;
437 #if ! HAVE_STRERROR
438 static char *
439 private_strerror (errnum)
440 int errnum;
442 #ifndef __APPLE__
443 # if defined(arm32) || defined(linux) || defined(__FreeBSD__) || \
444 defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
445 extern const char * const sys_errlist[];
446 # else
447 extern char *sys_errlist[];
448 # endif
449 #endif
450 extern int sys_nerr;
452 if (errnum > 0 && errnum <= sys_nerr)
453 return sys_errlist[errnum];
454 return "Unknown system error";
456 #define strerror private_strerror
457 #endif /* HAVE_STRERROR */
459 PUBLIC int
460 runargv(target, group, last, cmnd_attr, cmd)/*
461 ==============================================
462 Execute the command given by cmd.
464 Return 0 if the command executed and finished or
465 1 if the command started and is running.
467 CELLPTR target;
468 int group;
469 int last;
470 t_attr cmnd_attr; /* Attributes for current cmnd. */
471 char **cmd; /* Simulate a reference to *cmd. */
473 int ignore = (cmnd_attr & A_IGNORE)!= 0; /* Ignore errors ('-'). */
474 int shell = (cmnd_attr & A_SHELL) != 0; /* Use shell ('+'). */
475 int mute = (cmnd_attr & A_MUTE) != 0; /* Mute output ('@@'). */
476 int wfc = (cmnd_attr & A_WFC) != 0; /* Wait for completion. */
478 TPID pid;
479 int st_pq = 0; /* Current _exec_shell target process index */
480 char *tcmd = *cmd; /* For saver/easier string arithmetic on *cmd. */
481 char **argv;
483 int old_stdout = -1; /* For shell escapes and */
484 int old_stderr = -1; /* @@-recipe silencing. */
485 int internal = 0; /* Used to indicate internal command. */
487 DB_ENTER( "runargv" );
489 /* Special handling for the shell function macro is required. If the
490 * currend command is called as part of a shell escape in a recipe make
491 * sure that all previous recipe lines of this target have finished. */
492 if( Is_exec_shell ) {
493 if( (st_pq = _running(Shell_exec_target)) != -1 ) {
494 RCPPTR rp;
495 /* Add WFC to _procs[st_pq]. */
496 _procs[st_pq].pr_wfc = TRUE;
497 /* Set also the A_WFC flag in the recipe attributes. */
498 for( rp = _procs[st_pq].pr_recipe ; rp != NIL(RCP); rp = rp->prp_next )
499 rp->prp_attr |= A_WFC;
501 Wait_for_child(FALSE, st_pq);
503 } else {
504 if( _running(target) != -1 /*&& Max_proc != 1*/ ) {
505 /* The command will be executed when the previous recipe
506 * line completes. */
507 _attach_cmd( *cmd, group, target, cmnd_attr, last );
508 DB_RETURN( 1 );
512 /* If all process array entries are used wait until we get a free
513 * slot. For Max_proc == 1 this forces sequential execution. */
514 while( _proc_cnt == Max_proc ) {
515 Wait_for_child(FALSE, -1);
518 /* Return immediately for empty line or noop command. */
519 if ( !*tcmd || /* empty line */
520 ( strncmp(tcmd, "noop", 4) == 0 && /* noop command */
521 (iswhite(tcmd[4]) || tcmd[4] == '\0')) ) {
522 internal = 1;
524 else if( !shell && /* internal echo only if not in shell */
525 strncmp(tcmd, "echo", 4) == 0 &&
526 (iswhite(tcmd[4]) || tcmd[4] == '\0') ) {
527 int nl = 1;
529 tcmd = tcmd+4;
530 while( iswhite(*tcmd) ) ++tcmd;
531 if ( strncmp(tcmd,"-n",2 ) == 0) {
532 nl = 0;
533 tcmd = tcmd+2;
534 while( iswhite(*tcmd) ) ++tcmd;
537 /* redirect output for _exec_shell / @@-recipes. */
538 if( Is_exec_shell ) {
539 /* Add error checking? */
540 old_stdout = dup(1);
541 dup2( fileno(stdout_redir), 1 );
543 if( mute ) {
544 old_stderr = dup(2);
545 dup2( zerofd, 2 );
547 if( !Is_exec_shell ) {
548 old_stdout = dup(1);
549 dup2( zerofd, 1 );
553 printf("%s%s", tcmd, nl ? "\n" : "");
554 fflush(stdout);
556 /* Restore stdout/stderr if needed. */
557 if( old_stdout != -1 ) {
558 dup2(old_stdout, 1);
559 close(old_stdout);
560 if( old_stderr != -1 ) {
561 dup2(old_stderr, 2);
562 close(old_stderr);
566 internal = 1;
568 if ( internal ) {
569 /* Use _add_child() / _finished_child() with internal command. */
570 int cur_proc = _add_child(DMNOPID, target, ignore, last, FALSE);
571 _finished_child( (DMHANDLE)-cur_proc, 0 );
572 DB_RETURN( 0 );
575 /* Pack cmd in argument vector. */
576 argv = Pack_argv( group, shell, cmd );
578 /* Really spawn or fork a child. */
579 #if defined( USE_SPAWN )
580 /* As no other childs are started while the output is redirected this
581 * is save. */
582 if( Is_exec_shell ) {
583 /* Add error checking? */
584 old_stdout = dup(1);
585 dup2( fileno(stdout_redir), 1 );
587 if( mute ) {
588 old_stderr = dup(2);
589 dup2( zerofd, 2 );
591 if( !Is_exec_shell ) {
592 old_stdout = dup(1);
593 dup2( zerofd, 1 );
597 pid = dmspawn( argv );
598 terrno = errno;
600 if( old_stdout != -1 ) {
601 dup2(old_stdout, 1);
602 close(old_stdout);
603 if( old_stderr != -1 ) {
604 dup2(old_stderr, 2);
605 close(old_stderr);
608 if(pid.pid == (DMHANDLE)-1) {
609 /* spawn failed */
610 int cur_proc;
612 fprintf(stderr, "%s: Error executing '%s': %s",
613 Pname, argv[0], strerror(terrno) );
614 if( ignore||Continue ) {
615 fprintf(stderr, " (Ignored)" );
617 fprintf(stderr, "\n");
619 /* Use _add_child() / _finished_child() to treat the failure
620 * gracefully, if so requested. */
621 cur_proc = _add_child(DMNOPID, target, ignore, last, FALSE);
622 _finished_child((DMHANDLE)cur_proc, SIGTERM);
624 /* _finished_child() aborts dmake if we are not told to
625 * ignore errors. If we reach the this point return 0 as
626 * errors are obviously ignored and indicate that the process
627 * finished. */
628 DB_RETURN( 0 );
629 } else {
630 _add_child(pid, target, ignore, last, wfc);
632 #else /* USE_SPAWN */
634 fflush(stdout);
635 switch( pid.pid = fork() ){
637 case -1: /* fork failed */
638 Fatal("fork failed: %s: %s", argv[0], strerror( errno ));
640 case 0: /* child */
641 /* redirect output for _exec_shell / @@-recipes. */
642 if( Is_exec_shell ) {
643 /* Add error checking? */
644 old_stdout = dup(1);
645 dup2( fileno(stdout_redir), 1 );
647 if( mute ) {
648 old_stderr = dup(2);
649 dup2( zerofd, 2 );
651 if( !Is_exec_shell ) {
652 old_stdout = dup(1);
653 dup2( zerofd, 1 );
656 execvp(argv[0], argv);
657 /* restoring output to catch potential error output if execvp()
658 * failed. */
659 if( old_stdout != -1 ) {
660 dup2(old_stdout, 1);
661 close(old_stdout);
662 if( old_stderr != -1 ) {
663 dup2(old_stderr, 2);
664 close(old_stderr);
667 fprintf(stderr, "%s: Error executing '%s': %s",
668 Pname, argv[0], strerror(errno) );
669 if( ignore||Continue ) {
670 fprintf(stderr, " (Ignored)" );
672 fprintf(stderr, "\n");
674 kill(getpid(), SIGTERM);
675 /*NOTREACHED*/
676 Fatal("\nInternal Error - kill could't kill child %d.\n", getpid());
678 default: /* parent */
679 _add_child(pid, target, ignore, last, wfc);
682 #endif /* USE_SPAWN */
684 /* If wfc is set this command must have been finished. */
685 if( wfc ) {
686 DB_RETURN( 0 );
687 } else {
688 DB_RETURN( 1 );
693 PUBLIC int
694 Wait_for_child( abort_flg, pqid )/*
695 ===================================
696 Wait for the next processes from process queue pqid to finish. All finished
697 processes are handled by calling _finished_child() for each of them.
698 If pqid == -1 wait for the next process to finish.
699 If abort_flg is TRUE no further processes will be added to any process
700 queue. The A_WFC attribute is honored, see the documentation at the top
701 of this file.
702 Return 0 if we successfully waited for a process and -1 if there was nothing
703 to wait for.
705 int abort_flg;
706 int pqid;
708 DMHANDLE pid;
709 DMHANDLE wid;
710 int status;
711 int waitret; /* return value of the dmwait functions. */
712 /* Never wait for internal commands. */
713 int waitchild;
714 int is_exec_shell_status = Is_exec_shell;
716 if( !_procs ) {
717 /* No process was ever created, i.e. _procs is not yet initialized.
718 * Nothing to wait for. */
719 return -1;
722 if( pqid > Max_proc ) Fatal("Internal Error: pqid > Max_proc !");
724 if( pqid == -1 ) {
725 /* Check if there is something to wait for. */
726 int i;
727 for( i=0; i<Max_proc && !_procs[i].pr_valid; i++ )
729 if( i == Max_proc )
730 return(-1);
732 pid = (DMHANDLE)-1;
733 waitchild = FALSE;
735 else {
736 /* Check if pqid is active. */
737 if( !_procs[pqid].pr_valid ) {
738 /* Make this an error? */
739 Warning("Internal Warning: pqid is not active!?");
740 return(-1);
743 pid = _procs[pqid].pr_pid;
744 waitchild = _procs[pqid].pr_wfc;
748 /* It is impossible that processes that were started from _exec_shell
749 * have follow-up commands in its process entry. Unset Is_exec_shell
750 * to prevent piping of child processes that are started from the
751 * _finished_child subroutine and reset to its original value when
752 * leaving this function. */
753 Is_exec_shell = FALSE;
755 do {
756 /* Wait for the next process to finish. */
757 if( (pid != (DMHANDLE)-1) && (waitret = dmwaitpid(pqid, &wid, &status)) != 0 ) {
758 /* if dmwaitpid returns 0 this means that pid didn't finish yet.
759 * In this case just handle the next finished process in the
760 * following "else". If an error is returned (waitret < 0) the else
761 * clause is not evaluated and the error is handled in the following
762 * lines. If a process was waited for (waitret == 0) also proceed to
763 * the following lines. */
766 else {
767 waitret = dmwaitnext(&wid, &status);
768 /* If we get an error tell the error handling routine below that we
769 * were not waiting for a specific pid. */
770 if( waitret < 0 ) {
771 pid = (DMHANDLE)-1;
775 /* If ECHILD is set from waitpid/wait then no child was left. */
776 if( waitret < 0 ) {
777 if(waitret == -2) {
778 /* Wait was interrupted or a child was terminated (SIGCHLD) */
779 if ( in_quit() ) {
780 /* We're already terminating, just continue. */
781 return 0;
782 } else {
783 Fatal( "dmake was interrupted or a child terminated. "
784 "Stopping all childs ..." );
786 } else {
787 /* The child we were waiting for is missing or no child is
788 * left to wait for. */
789 if( pid != (DMHANDLE)-1 ) {
790 /* If we know the pid disable the pq entry. */
791 if( _procs[pqid].pr_valid ) {
792 _procs[pqid].pr_valid = 0;
793 _procs[pqid].pr_recipe = NIL(RCP);
794 _proc_cnt--;
796 } else {
797 /* otherwise disable all remaining pq's. As we don't know
798 * which pid failed there is no gracefull way to terminate. */
799 int i;
800 for( i=0; i<Max_proc; i++ ) {
801 _procs[i].pr_valid = 0;
802 _procs[i].pr_recipe = NIL(RCP);
804 _proc_cnt = 0;
806 /* The pid we were waiting for or any of the remaining childs
807 * (pid == -1) is missing. This should not happen and means
808 * that the process got lost or was treated elsewhere. */
809 Fatal( "Internal Error: Child is missing but still listed in _procs[x] %d: %s\n"
810 "\nTemporary or .ERRREMOVE targets might not have been removed!\n",
811 errno, strerror( errno ) );
815 _abort_flg = abort_flg;
816 _finished_child(wid, status);
817 _abort_flg = FALSE;
818 if( waitchild ) {
819 /* If pid != wid the process we're waiting for might have been
820 * finished from a "Wait_for_child(FALSE, -1)" call from
821 * _finished_child() -> runargv(). */
822 if( pid != wid ) {
823 if( !_procs[pqid].pr_valid || _procs[pqid].pr_pid != pid ) {
824 /* Someone finished pid, no need to wait further. */
825 waitchild = FALSE;
828 else
829 /* We finished pid, no need to wait further. */
830 waitchild = FALSE;
833 while( waitchild );
835 Is_exec_shell = is_exec_shell_status;
836 return(0);
840 PUBLIC void
841 Clean_up_processes()
843 int ret;
845 if( _procs != NIL(PR) )
847 register int i;
848 for( i=0; i<Max_proc; i++ )
849 if( _procs[i].pr_valid )
851 #if !defined(USE_CREATEPROCESS)
852 if( (ret = kill(_procs[i].pr_pid, SIGTERM)) )
854 fprintf(stderr, "Killing of pid %d from pq[%d] failed with: %s - %d ret: %d\n",
855 _procs[i].pr_pid, i, strerror(errno), SIGTERM, ret );
857 #else
858 TerminateProcess(_procs[i].pr_pid, 1);
859 #endif
865 static int
866 _add_child( pid, target, ignore, last, wfc )/*
867 ==============================================
868 Creates/amend a process queue entry and enters the child parameters.
869 The pid == -1 represents an internal command and the function returns
870 the used process array index. For non-internal commands the function
871 returns -1.
872 If wfc (wait for completion) is TRUE the function calls
873 Wait_for_child to wait for the whole process queue to be finished.
875 TPID pid;
876 CELLPTR target;
877 int ignore;
878 int last;
879 int wfc;
881 register int i;
882 register PR *pp;
884 /* Never change MAXPROCESS after _procs is allocated. */
885 if( _procs_size != Max_proc ) {
886 /* If procs was never initialize this is OK, do it now. */
887 if( _procs == NIL(PR) ) {
888 _procs_size = Max_proc;
889 TALLOC( _procs, Max_proc, PR );
890 #if defined(USE_CREATEPROCESS)
891 TALLOC( _wpList, Max_proc, HANDLE );
893 /* Signed int values are cast to DMHANDLE in various places, use this
894 * sanity check to verify that DMHANDLE is large enough. */
895 if( sizeof(int) > sizeof(DMHANDLE) )
896 Fatal( "Internal Error: Check type of DMHANDLE!" );
897 #endif
899 else {
900 Fatal( "MAXPROCESS changed from `%d' to `%d' after a command was executed!", _procs_size, Max_proc );
904 if( Measure & M_RECIPE )
905 Do_profile_output( "s", M_RECIPE, target );
907 /* If _use_i ! =-1 then this function is called by _finished_child() ( through runargv() ),
908 and we re-use the process queue number given by _use_i. */
909 if( (i = _use_i) == -1 ) {
910 for( i=0; i<Max_proc; i++ )
911 if( !_procs[i].pr_valid )
912 break;
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;
925 if( pp->pr_dir != NIL(char) )
926 FREE(pp->pr_dir);
927 pp->pr_dir = DmStrDup(Get_current_dir());
929 Current_target = NIL(CELL);
931 _proc_cnt++;
933 if( pid.pid != (DMHANDLE)-1 ) {
934 /* Wait for each recipe to finish if wfc is TRUE. This
935 * basically forces sequential execution. */
936 if( wfc ) {
937 Wait_for_child( FALSE, i );
940 return -1;
941 } else
942 return i;
946 static void
947 _finished_child(cid, status)/*
948 ==============================
949 Handle process array entry for finished child. This can be a finished
950 process or a finished internal command depending on the content of cid.
951 For cid >= 1 the value of cid is used as the pid to of the finished
952 process and for cid < 1 -cid is used as the process array index of the
953 internal command.
955 DMHANDLE cid;
956 int status;
958 register int i;
959 char *dir;
961 if((int)cid < 1) { /* Force int. */
962 /* internal command */
963 i = -((int)cid);
965 else {
966 for( i=0; i<Max_proc; i++ )
967 if( _procs[i].pr_valid && _procs[i].pr_pid == cid )
968 break;
970 /* Some children we didn't make esp true if using /bin/sh to execute a
971 * a pipe and feed the output as a makefile into dmake. */
972 if( i == Max_proc ) {
973 Warning("Internal Warning: finished pid %d is not in pq!?", cid);
974 return;
978 /* Not a running process anymore, the next runargv() will not use
979 * _attach_cmd(). */
980 _procs[i].pr_valid = 0;
982 if( Measure & M_RECIPE )
983 Do_profile_output( "e", M_RECIPE, _procs[i].pr_target );
985 _proc_cnt--;
986 dir = DmStrDup(Get_current_dir());
987 Set_dir( _procs[i].pr_dir );
989 if( _procs[i].pr_recipe != NIL(RCP) && !_abort_flg ) {
990 RCPPTR rp = _procs[i].pr_recipe;
993 Current_target = _procs[i].pr_target;
994 Handle_result( status, _procs[i].pr_ignore, FALSE, _procs[i].pr_target );
995 Current_target = NIL(CELL);
997 if ( _procs[i].pr_target->ce_attr & A_ERROR ) {
998 _procs[i].pr_last = TRUE;
999 goto ABORT_REMAINDER_OF_RECIPE;
1002 _procs[i].pr_recipe = rp->prp_next;
1004 _use_i = i;
1005 /* Run next recipe line. The rp->prp_attr propagates a possible
1006 * wfc condition. */
1007 runargv( _procs[i].pr_target, rp->prp_group,
1008 rp->prp_last, rp->prp_attr, &rp->prp_cmd );
1009 _use_i = -1;
1011 FREE( rp->prp_cmd );
1012 FREE( rp );
1014 /* If all process queues are used wait for the next process to
1015 * finish. Is this really needed here? */
1016 if( _proc_cnt == Max_proc ) {
1017 Wait_for_child( FALSE, -1 );
1020 else {
1021 /* empty the queue on abort. */
1022 if( _abort_flg )
1023 _procs[i].pr_recipe = NIL(RCP);
1025 Handle_result(status,_procs[i].pr_ignore,_abort_flg,_procs[i].pr_target);
1027 ABORT_REMAINDER_OF_RECIPE:
1028 if( _procs[i].pr_last ) {
1029 FREE(_procs[i].pr_dir ); _procs[i].pr_dir = NIL(char); /* Set in _add_child() */
1031 if( !Doing_bang ) {
1032 /* Update_time_stamp() triggers the deletion of intermediate
1033 * targets. This starts a new process queue, so we have to
1034 * clear the _use_i variable. */
1035 int my_use_i = _use_i;
1037 _use_i = -1;
1038 Update_time_stamp( _procs[i].pr_target );
1039 _use_i = my_use_i;
1044 Set_dir(dir);
1045 FREE(dir);
1049 static int
1050 _running( cp )/*
1051 ================
1052 Check if target exists in process array AND is running. Return its
1053 process array index if it is running, return -1 otherwise.
1055 CELLPTR cp;
1057 register int i;
1059 if( !_procs ) return( -1 );
1061 for( i=0; i<Max_proc; i++ )
1062 if( _procs[i].pr_valid &&
1063 _procs[i].pr_target == cp )
1064 break;
1066 return( i == Max_proc ? -1 : i );
1070 static void
1071 _attach_cmd( cmd, group, cp, cmnd_attr, last )/*
1072 ================================================
1073 Attach to an active process queue. Inherit wfc setting. */
1074 char *cmd;
1075 int group;
1076 CELLPTR cp;
1077 t_attr cmnd_attr;
1078 int last;
1080 register int i;
1081 RCPPTR rp;
1083 for( i=0; i<Max_proc; i++ )
1084 if( _procs[i].pr_valid &&
1085 _procs[i].pr_target == cp )
1086 break;
1088 TALLOC( rp, 1, RCP );
1089 rp->prp_cmd = DmStrDup(cmd);
1090 rp->prp_attr = cmnd_attr;
1091 /* Inherit wfc from process queue. */
1092 if( _procs[i].pr_wfc )
1093 rp->prp_attr |= A_WFC;
1094 rp->prp_group = group;
1095 rp->prp_last = last;
1097 if( _procs[i].pr_recipe == NIL(RCP) )
1098 _procs[i].pr_recipe = _procs[i].pr_recipe_end = rp;
1099 else {
1100 _procs[i].pr_recipe_end->prp_next = rp;
1101 _procs[i].pr_recipe_end = rp;