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