Bug Summary

File:dmake/unix/runargv.c
Location:line 918, column 4
Description:Access to field 'pr_valid' results in a dereference of a null pointer (loaded from variable 'pp')

Annotated 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/*
26This file (runargv.c) provides all the parallel process handling routines
27for dmake on unix like operating systems. The following text briefly
28describes the process flow.
29
30Exec_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
33Do_cmnd() [sysintf.c] feeds the given command or command group to runargv().
34
35The following flowchart decripes the process flow starting with runargv,
36descriptions 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
65runargv() [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
89Wait_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_H1
114# include <wait.h>
115#else
116# ifdef HAVE_SYS_WAIT_H1
117# include <sys/wait.h>
118# endif
119#endif
120
121#if HAVE_SPAWN_H1 && 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_H1
140# include <errno(*__errno_location ()).h>
141#else
142 extern int errno(*__errno_location ());
143#endif
144
145typedef 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). */
155typedef HANDLE DMHANDLE;
156#else
157typedef int DMHANDLE;
158#endif
159
160typedef 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
173typedef struct tpid {
174 DMHANDLE pid;
175 DMHANDLE tid;
176} TPID;
177
178const TPID DMNOPID = { (DMHANDLE)-1, (DMHANDLE)0 };
179
180static PR *_procs = NIL(PR)((PR*)((void*)0)); /* Array to hold concurrent processes. */
181static int _procs_size = 0; /* Savegard to find MAXPROCESS changes. */
182static int _proc_cnt = 0; /* Number of running processes. */
183static int _abort_flg= FALSE0;
184static int _use_i = -1;
185#if defined(USE_CREATEPROCESS)
186static HANDLE *_wpList = NIL(HANDLE)((HANDLE*)((void*)0)); /* Array to hold pids to wait for. */
187#endif
188
189static int _add_child ANSI((TPID, CELLPTR, int, int, int))(TPID, CELLPTR, int, int, int);
190static void _attach_cmd ANSI((char *, int, CELLPTR, t_attr, int))(char *, int, CELLPTR, t_attr, int);
191static void _finished_child ANSI((DMHANDLE, int))(DMHANDLE, int);
192static int _running ANSI((CELLPTR))(CELLPTR);
193
194/* Machine/OS dependent helpers. */
195static int dmwaitnext ANSI((DMHANDLE *, int *))(DMHANDLE *, int *);
196static int dmwaitpid ANSI((int, DMHANDLE *, int *))(int, DMHANDLE *, int *);
197
198#if defined( USE_SPAWN )
199
200int terrno; /* Temporarily store errno. */
201
202static TPID dmspawn ANSI((char **))(char **);
203
204static TPID
205dmspawn( 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 = FALSE0;
217 PROCESS_INFORMATION pi;
218
219 /* si can be reused. */
220 if( initSTARTUPINFO == FALSE0 ) {
221 initSTARTUPINFO = TRUE1;
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((void*)0), argv[0], NULL((void*)0), NULL((void*)0), TRUE1, 0, NULL((void*)0), NULL((void*)0), &si, &pi) ) {
239 pid.pid = pi.hProcess;
240 pid.tid = pi.hThread;
241 } else {
242 fprintf(stderrstderr, "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((void*)0), NULL((void*)0), argv, (char *)NULL((void*)0)))
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
258static int
259dmwaitnext( 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 *wid = wait(status);
271
272 /* If ECHILD is set from waitpid/wait then no child was left. */
273 if( *wid == -1 ) {
274 int realErr = errno(*__errno_location ()); // fprintf can pollute errno
275 fprintf(stderrstderr, "%s: Internal Error: wait() failed: %d - %s\n",
276 Pname, errno(*__errno_location ()), strerror(errno(*__errno_location ())) );
277 if( realErr != ECHILD10 ) {
278 /* Wait was interrupted or a child was terminated (SIGCHLD) */
279 return -2;
280 } else {
281 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(stderrstderr, "%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, FALSE0, 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(stderrstderr, "%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((void*)0),
338 err,
339 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
340 (LPTSTR) &lpMsgBuf,
341 0, NULL((void*)0) );
342
343 fprintf(stderrstderr, "%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 return 1;
353}
354
355
356static int
357dmwaitpid( 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 *wid = waitpid(_procs[pqid].pr_pid, status, WNOHANG1);
367
368 /* Process still running. */
369 if( *wid == 0 ) {
370 *status = 0;
371 return 0;
372 }
373 /* If ECHILD is set from waitpid/wait then no child was left. */
374 if( *wid == -1 ) {
375 int realErr = errno(*__errno_location ()); // fprintf can pollute errno
376 fprintf(stderrstderr, "%s: Internal Error: waitpid() failed: %d - %s\n",
377 Pname, errno(*__errno_location ()), strerror(errno(*__errno_location ())) );
378 if(realErr != ECHILD10) {
379 /* Wait was interrupted or a child was terminated (SIGCHLD) */
380 return -2;
381 } else {
382 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(stderrstderr, "%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((void*)0),
421 err,
422 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
423 (LPTSTR) &lpMsgBuf,
424 0, NULL((void*)0) );
425
426 fprintf(stderrstderr, "%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 return 1;
436}
437
438
439#if ! HAVE_STRERROR1
440static char *
441private_strerror (errnum)
442 int errnum;
443{
444#ifndef __APPLE__
445# if defined(arm32) || defined(linux1) || 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
461PUBLIC int
462runargv(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 */
469CELLPTR target;
470int group;
471int last;
472t_attr cmnd_attr; /* Attributes for current cmnd. */
473char **cmd; /* Simulate a reference to *cmd. */
474{
475 int ignore = (cmnd_attr & A_IGNORE0x00020)!= 0; /* Ignore errors ('-'). */
476 int shell = (cmnd_attr & A_SHELL0x00800) != 0; /* Use shell ('+'). */
477 int mute = (cmnd_attr & A_MUTE0x80000000) != 0; /* Mute output ('@@'). */
478 int wfc = (cmnd_attr & A_WFC0x00200) != 0; /* Wait for completion. */
479
480 TPID pid;
481 int st_pq = 0; /* Current _exec_shell target process index */
482 char *tcmd = *cmd; /* For saver/easier string arithmetic on *cmd. */
483 char **argv;
484
485 int old_stdout = -1; /* For shell escapes and */
486 int old_stderr = -1; /* @@-recipe silencing. */
487 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 if( Is_exec_shell ) {
38
Taking false branch
495 if( (st_pq = _running(Shell_exec_target)) != -1 ) {
496 RCPPTR rp;
497 /* Add WFC to _procs[st_pq]. */
498 _procs[st_pq].pr_wfc = TRUE1;
499 /* Set also the A_WFC flag in the recipe attributes. */
500 for( rp = _procs[st_pq].pr_recipe ; rp != NIL(RCP)((RCP*)((void*)0)); rp = rp->prp_next )
501 rp->prp_attr |= A_WFC0x00200;
502
503 Wait_for_child(FALSE0, st_pq);
504 }
505 } else {
506 if( _running(target) != -1 /*&& Max_proc != 1*/ ) {
39
Taking false branch
507 /* The command will be executed when the previous recipe
508 * line completes. */
509 _attach_cmd( *cmd, group, target, cmnd_attr, last );
510 DB_RETURN( 1 )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 while( _proc_cnt == Max_proc ) {
40
Assuming '_proc_cnt' is not equal to 'Max_proc'
41
Loop condition is false. Execution continues on line 521
517 Wait_for_child(FALSE0, -1);
518 }
519
520 /* Return immediately for empty line or noop command. */
521 if ( !*tcmd || /* empty line */
522 ( strncmp(tcmd, "noop", 4) == 0 && /* noop command */
523 (iswhite(tcmd[4])((tcmd[4] == ' ') || (tcmd[4] == '\t')) || tcmd[4] == '\0')) ) {
524 internal = 1;
525 }
526 else if( !shell && /* internal echo only if not in shell */
527 strncmp(tcmd, "echo", 4) == 0 &&
528 (iswhite(tcmd[4])((tcmd[4] == ' ') || (tcmd[4] == '\t')) || tcmd[4] == '\0') ) {
529 int nl = 1;
530
531 tcmd = tcmd+4;
532 while( iswhite(*tcmd)((*tcmd == ' ') || (*tcmd == '\t')) ) ++tcmd;
533 if ( strncmp(tcmd,"-n",2 ) == 0) {
534 nl = 0;
535 tcmd = tcmd+2;
536 while( iswhite(*tcmd)((*tcmd == ' ') || (*tcmd == '\t')) ) ++tcmd;
537 }
538
539 /* redirect output for _exec_shell / @@-recipes. */
540 if( Is_exec_shell ) {
541 /* Add error checking? */
542 old_stdout = dup(1);
543 dup2( fileno(stdout_redir), 1 );
544 }
545 if( mute ) {
546 old_stderr = dup(2);
547 dup2( zerofd, 2 );
548
549 if( !Is_exec_shell ) {
550 old_stdout = dup(1);
551 dup2( zerofd, 1 );
552 }
553 }
554
555 printf("%s%s", tcmd, nl ? "\n" : "");
556 fflush(stdoutstdout);
557
558 /* Restore stdout/stderr if needed. */
559 if( old_stdout != -1 ) {
560 dup2(old_stdout, 1);
561 close(old_stdout);
562 if( old_stderr != -1 ) {
563 dup2(old_stderr, 2);
564 close(old_stderr);
565 }
566 }
567
568 internal = 1;
569 }
570 if ( internal ) {
42
Taking false branch
571 /* Use _add_child() / _finished_child() with internal command. */
572 int cur_proc = _add_child(DMNOPID, target, ignore, last, FALSE0);
573 _finished_child( (DMHANDLE)-cur_proc, 0 );
574 DB_RETURN( 0 )return (0);
575 }
576
577 /* Pack cmd in argument vector. */
578 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(*__errno_location ());
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(stderrstderr, "%s: Error executing '%s': %s",
615 Pname, argv[0], strerror(terrno) );
616 if( ignore||Continue ) {
617 fprintf(stderrstderr, " (Ignored)" );
618 }
619 fprintf(stderrstderr, "\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, FALSE0);
624 _finished_child((DMHANDLE)cur_proc, SIGTERM15);
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 )return (0);
631 } else {
632 _add_child(pid, target, ignore, last, wfc);
633 }
634#else /* USE_SPAWN */
635
636 fflush(stdoutstdout);
637 switch( pid.pid = fork() ){
43
Control jumps to the 'default' case at line 680
638
639 case -1: /* fork failed */
640 Fatal("fork failed: %s: %s", argv[0], strerror( errno(*__errno_location ()) ));
641
642 case 0: /* child */
643 /* redirect output for _exec_shell / @@-recipes. */
644 if( Is_exec_shell ) {
645 /* Add error checking? */
646 old_stdout = dup(1);
647 dup2( fileno(stdout_redir), 1 );
648 }
649 if( mute ) {
650 old_stderr = dup(2);
651 dup2( zerofd, 2 );
652
653 if( !Is_exec_shell ) {
654 old_stdout = dup(1);
655 dup2( zerofd, 1 );
656 }
657 }
658 execvp(argv[0], argv);
659 /* restoring output to catch potential error output if execvp()
660 * failed. */
661 if( old_stdout != -1 ) {
662 dup2(old_stdout, 1);
663 close(old_stdout);
664 if( old_stderr != -1 ) {
665 dup2(old_stderr, 2);
666 close(old_stderr);
667 }
668 }
669 fprintf(stderrstderr, "%s: Error executing '%s': %s",
670 Pname, argv[0], strerror(errno(*__errno_location ())) );
671 if( ignore||Continue ) {
672 fprintf(stderrstderr, " (Ignored)" );
673 }
674 fprintf(stderrstderr, "\n");
675
676 kill(getpid(), SIGTERM15);
677 /*NOTREACHED*/
678 Fatal("\nInternal Error - kill could't kill child %d.\n", getpid());
679
680 default: /* parent */
681 _add_child(pid, target, ignore, last, wfc);
44
Calling '_add_child'
682 }
683
684#endif /* USE_SPAWN */
685
686 /* If wfc is set this command must have been finished. */
687 if( wfc ) {
688 DB_RETURN( 0 )return (0);
689 } else {
690 DB_RETURN( 1 )return (1);
691 }
692}
693
694
695PUBLIC int
696Wait_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*/
707int abort_flg;
708int 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 int is_exec_shell_status = Is_exec_shell;
717
718 if( !_procs ) {
1
Assuming '_procs' is non-null
2
Taking false branch
719 /* No process was ever created, i.e. _procs is not yet initialized.
720 * Nothing to wait for. */
721 return -1;
722 }
723
724 if( pqid > Max_proc ) Fatal("Internal Error: pqid > Max_proc !");
3
Taking false branch
725
726 if( pqid == -1 ) {
4
Taking false branch
727 /* Check if there is something to wait for. */
728 int i;
729 for( i=0; i<Max_proc && !_procs[i].pr_valid; i++ )
730 ;
731 if( i == Max_proc )
732 return(-1);
733
734 pid = (DMHANDLE)-1;
735 waitchild = FALSE0;
736 }
737 else {
738 /* Check if pqid is active. */
739 if( !_procs[pqid].pr_valid ) {
5
Taking false branch
740 /* Make this an error? */
741 Warning("Internal Warning: pqid is not active!?");
742 return(-1);
743 }
744
745 pid = _procs[pqid].pr_pid;
746 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 Is_exec_shell = FALSE0;
756
757 do {
12
Loop condition is true. Execution continues on line 759
18
Loop condition is true. Execution continues on line 759
24
Loop condition is true. Execution continues on line 759
758 /* Wait for the next process to finish. */
759 if( (pid != (DMHANDLE)-1) && (waitret = dmwaitpid(pqid, &wid, &status)) != 0 ) {
6
Taking true branch
13
Taking true branch
19
Taking true branch
25
Taking true branch
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 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 if( waitret < 0 ) {
773 pid = (DMHANDLE)-1;
774 }
775 }
776
777 /* If ECHILD is set from waitpid/wait then no child was left. */
778 if( waitret < 0 ) {
7
Taking false branch
14
Taking false branch
20
Taking false branch
26
Taking false branch
779 if(waitret == -2) {
780 /* Wait was interrupted or a child was terminated (SIGCHLD) */
781 if ( in_quit() ) {
782 /* We're already terminating, just continue. */
783 return 0;
784 } else {
785 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 if( pid != (DMHANDLE)-1 ) {
792 /* If we know the pid disable the pq entry. */
793 if( _procs[pqid].pr_valid ) {
794 _procs[pqid].pr_valid = 0;
795 _procs[pqid].pr_recipe = NIL(RCP)((RCP*)((void*)0));
796 _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 for( i=0; i<Max_proc; i++ ) {
803 _procs[i].pr_valid = 0;
804 _procs[i].pr_recipe = NIL(RCP)((RCP*)((void*)0));
805 }
806 _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 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 errno(*__errno_location ()), strerror( errno(*__errno_location ()) ) );
814 }
815 }
816
817 _abort_flg = abort_flg;
818 _finished_child(wid, status);
27
Calling '_finished_child'
819 _abort_flg = FALSE0;
820 if( waitchild ) {
8
Assuming 'waitchild' is not equal to 0
9
Taking true branch
15
Taking true branch
21
Taking true branch
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 if( pid != wid ) {
10
Taking true branch
16
Taking true branch
22
Taking true branch
825 if( !_procs[pqid].pr_valid || _procs[pqid].pr_pid != pid ) {
11
Taking false branch
17
Taking false branch
23
Taking false branch
826 /* Someone finished pid, no need to wait further. */
827 waitchild = FALSE0;
828 }
829 }
830 else
831 /* We finished pid, no need to wait further. */
832 waitchild = FALSE0;
833 }
834 }
835 while( waitchild );
836
837 Is_exec_shell = is_exec_shell_status;
838 return(0);
839}
840
841
842PUBLIC void
843Clean_up_processes()
844{
845 if( _procs != NIL(PR)((PR*)((void*)0)) )
846 {
847 register int i;
848 for( i=0; i<Max_proc; i++ )
849 if( _procs[i].pr_valid )
850 {
851 #if !defined(USE_CREATEPROCESS)
852 int ret;
853 if( (ret = kill(_procs[i].pr_pid, SIGTERM15)) )
854 {
855 fprintf(stderrstderr, "Killing of pid %d from pq[%d] failed with: %s - %d ret: %d\n",
856 _procs[i].pr_pid, i, strerror(errno(*__errno_location ())), SIGTERM15, ret );
857 }
858 #else
859 TerminateProcess(_procs[i].pr_pid, 1);
860 #endif
861 }
862 }
863}
864
865
866static int
867_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*/
876TPID pid;
877CELLPTR target;
878int ignore;
879int last;
880int wfc;
881{
882 register int i;
883 register PR *pp;
884
885 /* Never change MAXPROCESS after _procs is allocated. */
886 if( _procs_size != Max_proc ) {
45
Taking true branch
887 /* If procs was never initialize this is OK, do it now. */
888 if( _procs == NIL(PR)((PR*)((void*)0)) ) {
46
Assuming '_procs' is equal to null
47
Taking true branch
889 _procs_size = Max_proc;
890 TALLOC( _procs, Max_proc, PR )if ((_procs = (PR*) calloc((unsigned int)(Max_proc), (size_t)
sizeof(PR))) == (PR*)0) {No_ram();}
;
891#if defined(USE_CREATEPROCESS)
892 TALLOC( _wpList, Max_proc, HANDLE )if ((_wpList = (HANDLE*) calloc((unsigned int)(Max_proc), (size_t
)sizeof(HANDLE))) == (HANDLE*)0) {No_ram();}
;
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 Fatal( "MAXPROCESS changed from `%d' to `%d' after a command was executed!", _procs_size, Max_proc );
902 }
903 }
904
905 if( Measure & M_RECIPE0x02 )
48
Taking false branch
906 Do_profile_output( "s", M_RECIPE0x02, 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 if( (i = _use_i) == -1 ) {
49
Taking false branch
911 for( i=0; i<Max_proc; i++ )
912 if( !_procs[i].pr_valid )
913 break;
914 }
915
916 pp = &(_procs[i]);
50
Null pointer value stored to 'pp'
917
918 pp->pr_valid = 1;
51
Access to field 'pr_valid' results in a dereference of a null pointer (loaded from variable 'pp')
919 pp->pr_pid = pid.pid;
920 pp->pr_tid = pid.tid;
921 pp->pr_target = target;
922 pp->pr_ignore = ignore;
923 pp->pr_last = last;
924 pp->pr_wfc = wfc;
925
926 if( pp->pr_dir != NIL(char)((char*)((void*)0)) )
927 FREE(pp->pr_dir)free((char*)(pp->pr_dir));
928 pp->pr_dir = DmStrDup(Get_current_dir());
929
930 Current_target = NIL(CELL)((CELL*)((void*)0));
931
932 _proc_cnt++;
933
934 if( pid.pid != (DMHANDLE)-1 ) {
935 /* Wait for each recipe to finish if wfc is TRUE. This
936 * basically forces sequential execution. */
937 if( wfc ) {
938 Wait_for_child( FALSE0, i );
939 }
940
941 return -1;
942 } else
943 return i;
944}
945
946
947static void
948_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*/
956DMHANDLE cid;
957int status;
958{
959 register int i;
960 char *dir;
961
962 if((int)cid < 1) { /* Force int. */
28
Assuming 'cid' is >= 1
29
Taking false branch
963 /* internal command */
964 i = -((int)cid);
965 }
966 else {
967 for( i=0; i<Max_proc; i++ )
30
Assuming 'i' is >= 'Max_proc'
31
Loop condition is false. Execution continues on line 973
968 if( _procs[i].pr_valid && _procs[i].pr_pid == cid )
969 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 if( i == Max_proc ) {
32
Assuming 'i' is not equal to 'Max_proc'
33
Taking false branch
974 Warning("Internal Warning: finished pid %d is not in pq!?", cid);
975 return;
976 }
977 }
978
979 /* Not a running process anymore, the next runargv() will not use
980 * _attach_cmd(). */
981 _procs[i].pr_valid = 0;
982
983 if( Measure & M_RECIPE0x02 )
34
Taking false branch
984 Do_profile_output( "e", M_RECIPE0x02, _procs[i].pr_target );
985
986 _proc_cnt--;
987 dir = DmStrDup(Get_current_dir());
988 Set_dir( _procs[i].pr_dir );
989
990 if( _procs[i].pr_recipe != NIL(RCP)((RCP*)((void*)0)) && !_abort_flg ) {
35
Taking true branch
991 RCPPTR rp = _procs[i].pr_recipe;
992
993
994 Current_target = _procs[i].pr_target;
995 Handle_result( status, _procs[i].pr_ignore, FALSE0, _procs[i].pr_target );
996 Current_target = NIL(CELL)((CELL*)((void*)0));
997
998 if ( _procs[i].pr_target->ce_attr & A_ERROR0x10000000 ) {
36
Taking false branch
999 _procs[i].pr_last = TRUE1;
1000 goto ABORT_REMAINDER_OF_RECIPE;
1001 }
1002
1003 _procs[i].pr_recipe = rp->prp_next;
1004
1005 _use_i = i;
1006 /* Run next recipe line. The rp->prp_attr propagates a possible
1007 * wfc condition. */
1008 runargv( _procs[i].pr_target, rp->prp_group,
37
Calling 'runargv'
1009 rp->prp_last, rp->prp_attr, &rp->prp_cmd );
1010 _use_i = -1;
1011
1012 FREE( rp->prp_cmd )free((char*)(rp->prp_cmd));
1013 FREE( rp )free((char*)(rp));
1014
1015 /* If all process queues are used wait for the next process to
1016 * finish. Is this really needed here? */
1017 if( _proc_cnt == Max_proc ) {
1018 Wait_for_child( FALSE0, -1 );
1019 }
1020 }
1021 else {
1022 /* empty the queue on abort. */
1023 if( _abort_flg )
1024 _procs[i].pr_recipe = NIL(RCP)((RCP*)((void*)0));
1025
1026 Handle_result(status,_procs[i].pr_ignore,_abort_flg,_procs[i].pr_target);
1027
1028 ABORT_REMAINDER_OF_RECIPE:
1029 if( _procs[i].pr_last ) {
1030 FREE(_procs[i].pr_dir )free((char*)(_procs[i].pr_dir)); _procs[i].pr_dir = NIL(char)((char*)((void*)0)); /* Set in _add_child() */
1031
1032 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 int my_use_i = _use_i;
1037
1038 _use_i = -1;
1039 Update_time_stamp( _procs[i].pr_target );
1040 _use_i = my_use_i;
1041 }
1042 }
1043 }
1044
1045 Set_dir(dir);
1046 FREE(dir)free((char*)(dir));
1047}
1048
1049
1050static int
1051_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*/
1056CELLPTR cp;
1057{
1058 register int i;
1059
1060 if( !_procs ) return( -1 );
1061
1062 for( i=0; i<Max_proc; i++ )
1063 if( _procs[i].pr_valid &&
1064 _procs[i].pr_target == cp )
1065 break;
1066
1067 return( i == Max_proc ? -1 : i );
1068}
1069
1070
1071static void
1072_attach_cmd( cmd, group, cp, cmnd_attr, last )/*
1073================================================
1074 Attach to an active process queue. Inherit wfc setting. */
1075char *cmd;
1076int group;
1077CELLPTR cp;
1078t_attr cmnd_attr;
1079int last;
1080{
1081 register int i;
1082 RCPPTR rp;
1083
1084 for( i=0; i<Max_proc; i++ )
1085 if( _procs[i].pr_valid &&
1086 _procs[i].pr_target == cp )
1087 break;
1088
1089 TALLOC( rp, 1, RCP )if ((rp = (RCP*) calloc((unsigned int)(1), (size_t)sizeof(RCP
))) == (RCP*)0) {No_ram();}
;
1090 rp->prp_cmd = DmStrDup(cmd);
1091 rp->prp_attr = cmnd_attr;
1092 /* Inherit wfc from process queue. */
1093 if( _procs[i].pr_wfc )
1094 rp->prp_attr |= A_WFC0x00200;
1095 rp->prp_group = group;
1096 rp->prp_last = last;
1097
1098 if( _procs[i].pr_recipe == NIL(RCP)((RCP*)((void*)0)) )
1099 _procs[i].pr_recipe = _procs[i].pr_recipe_end = rp;
1100 else {
1101 _procs[i].pr_recipe_end->prp_next = rp;
1102 _procs[i].pr_recipe_end = rp;
1103 }
1104}