1 /*
2  * CDDL HEADER START
3  *
4  * This file and its contents are supplied under the terms of the
5  * Common Development and Distribution License ("CDDL"), version 1.0.
6  * You may use this file only in accordance with the terms of version
7  * 1.0 of the CDDL.
8  *
9  * A full copy of the text of the CDDL should have accompanied this
10  * source.  A copy of the CDDL is also available via the Internet at
11  * http://www.opensource.org/licenses/cddl1.txt
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 /*
24  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
25  * Use is subject to license terms.
26  */
27 /*
28  * @(#)parallel.cc 1.75 06/12/12
29  */
30 
31 #pragma	ident	"@(#)parallel.cc	1.75	06/12/12"
32 
33 /*
34  * Copyright 2017-2021 J. Schilling
35  *
36  * @(#)parallel.cc	1.19 21/08/13 2017-2021 J. Schilling
37  */
38 #include <schily/mconfig.h>
39 #ifndef lint
40 static	UConst char sccsid[] =
41 	"@(#)parallel.cc	1.19 21/08/13 2017-2021 J. Schilling";
42 #endif
43 
44 /*
45  *	parallel.cc
46  *
47  *	Deal with the parallel processing
48  */
49 
50 /*
51  * Included files
52  */
53 #ifdef DISTRIBUTED
54 #include <avo/strings.h>	/* AVO_STRDUP() */
55 #include <dm/Avo_DoJobMsg.h>
56 #include <dm/Avo_MToolJobResultMsg.h>
57 #endif
58 
59 #ifdef TEAMWARE_MAKE_CMN
60 #include <avo/util.h>		/* avo_get_user(), avo_hostname() */
61 #endif
62 #include <mk/defs.h>		/* May #undef PMAKE on old platforms */
63 
64 #if defined(TEAMWARE_MAKE_CMN) || defined(PMAKE)
65 #include <mksh/dosys.h>		/* redirect_io() */
66 #include <mksh/macro.h>		/* expand_value() */
67 #include <mksh/misc.h>		/* getmem() */
68 #if defined(SCHILY_BUILD) || defined(SCHILY_INCLUDES)
69 #include <schily/utsname.h>
70 #include <schily/wait.h>
71 #else
72 #include <sys/utsname.h>
73 #include <sys/wait.h>
74 #define	WAIT_T	int
75 #endif
76 
77 #ifdef SGE_SUPPORT
78 #include <dmthread/Avo_PathNames.h>
79 #endif
80 
81 
82 /*
83  * Defined macros
84  */
85 #define MAXRULES		100
86 
87 #if !defined(SCHILY_BUILD) && !defined(SCHILY_INCLUDES)
88 #ifdef	sun
89 #ifndef	HAVE_GETLOADAVG
90 #define	HAVE_GETLOADAVG
91 #endif
92 #endif
93 #endif
94 
95 /*
96  * This const should be in avo_dms/include/AvoDmakeCommand.h
97  */
98 const int local_host_mask = 0x20;
99 
100 
101 /*
102  * typedefs & structs
103  */
104 
105 /*
106  * Static variables
107  */
108 #ifdef TEAMWARE_MAKE_CMN
109 static	Boolean		just_did_subtree = false;
110 static	char		local_host[MAXNAMELEN] = "";
111 static	char		user_name[MAXNAMELEN] = "";
112 #else
113 static	char		local_host[MAXNAMELEN] = "";
114 #endif
115 static	int		pmake_max_jobs = 0;
116 static	pid_t		process_running = -1;
117 static	Running		*running_tail = &running_list;
118 static	Name		subtree_conflict;
119 static	Name		subtree_conflict2;
120 
121 
122 /*
123  * File table of contents
124  */
125 #ifdef DISTRIBUTED
126 static	void		append_dmake_cmd(Avo_DoJobMsg *dmake_job_msg, char *orig_cmd_line, int cmd_options);
127 static	void		append_job_result_msg(Avo_MToolJobResultMsg *msg, char *outFn, char *errFn);
128 static	void		send_job_result_msg(Running rp);
129 #endif
130 static	void		delete_running_struct(Running rp);
131 static	Boolean		dependency_conflict(Name target);
132 static	Doname		distribute_process(char **commands, Property line);
133 static	void		doname_subtree(Name target, Boolean do_get, Boolean implicit);
134 static	void		dump_out_file(char *filename, Boolean err);
135 static	void		finish_doname(Running rp);
136 static	void		maybe_reread_make_state(void);
137 static	void		process_next(void);
138 static	void		reset_conditionals(int cnt, Name *targets, Property *locals);
139 static	pid_t           run_rule_commands(char *host, char **commands);
140 static	Property	*set_conditionals(int cnt, Name *targets);
141 static	void		store_conditionals(Running rp);
142 
143 
144 /*
145  *	execute_parallel(line, waitflg)
146  *
147  *	DMake 2.x:
148  *	parallel mode: spawns a parallel process to execute the command group.
149  *	distributed mode: sends the command group down the pipe to rxm.
150  *
151  *	Return value:
152  *				The result of the execution
153  *
154  *	Parameters:
155  *		line		The command group to execute
156  */
157 Doname
execute_parallel(Property line,Boolean waitflg,Boolean local)158 execute_parallel(Property line, Boolean waitflg, Boolean local)
159 {
160 	int			argcnt;
161 	int			cmd_options = 0;
162 	char			*commands[MAXRULES + 5];
163 	char			*cp;
164 #ifdef DISTRIBUTED
165 	Avo_DoJobMsg		*dmake_job_msg = NULL;
166 #endif
167 	Name			dmake_name;
168 	Name			dmake_value;
169 	int			ignore;
170 	Name			make_machines_name;
171 	char			**p;
172 	Property		prop;
173 	Doname			result = build_ok;
174 	Cmd_line		rule;
175 	Boolean			silent_flag;
176 	Name			target = line->body.line.target;
177 	Boolean			wrote_state_file = false;
178 
179 	if ((pmake_max_jobs == 0) &&
180 	    (dmake_mode_type == parallel_mode)) {
181 #ifdef	TEAMWARE_MAKE_CMN
182 		if (user_name[0] == '\0') {
183 			avo_get_user(user_name, NULL);
184 		}
185 		if (local_host[0] == '\0') {
186 			strcpy(local_host, avo_hostname());
187 		}
188 #else
189 		if (local_host[0] == '\0') {
190 			(void) gethostname(local_host, MAXNAMELEN);
191 		}
192 #endif
193 		MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MAX_JOBS"));
194 		dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
195 		if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
196 		    ((dmake_value = prop->body.macro.value) != NULL)) {
197 			pmake_max_jobs = atoi(dmake_value->string_mb);
198 			if (pmake_max_jobs <= 0) {
199 				warning(gettext("DMAKE_MAX_JOBS cannot be less than or equal to zero."));
200 				warning(gettext("setting DMAKE_MAX_JOBS to %d."), PMAKE_DEF_MAX_JOBS);
201 				pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
202 			}
203 		} else {
204 			/*
205 			 * For backwards compatibility w/ PMake 1.x, when
206 			 * DMake 2.x is being run in parallel mode, DMake
207 			 * should parse the PMake startup file
208 			 * $(HOME)/.make.machines to get the pmake_max_jobs.
209 			 */
210 			MBSTOWCS(wcs_buffer, NOCATGETS("PMAKE_MACHINESFILE"));
211 			dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
212 			if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
213 			    ((dmake_value = prop->body.macro.value) != NULL)) {
214 				make_machines_name = dmake_value;
215 			} else {
216 				make_machines_name = NULL;
217 			}
218 			if ((pmake_max_jobs = read_make_machines(make_machines_name)) <= 0) {
219 				pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
220 			}
221 		}
222 #ifdef DISTRIBUTED
223 		if (send_mtool_msgs) {
224 			send_rsrc_info_msg(pmake_max_jobs, local_host, user_name);
225 		}
226 #endif
227 	}
228 
229 	if ((dmake_mode_type == serial_mode) ||
230 	    ((dmake_mode_type == parallel_mode) && (waitflg))) {
231 		return (execute_serial(line));
232 	}
233 
234 #ifdef DISTRIBUTED
235 	if (dmake_mode_type == distributed_mode) {
236 		if(local) {
237 //			return (execute_serial(line));
238 			waitflg = true;
239 		}
240 		dmake_job_msg = new Avo_DoJobMsg();
241 		dmake_job_msg->setJobId(++job_msg_id);
242 		dmake_job_msg->setTarget(target->string_mb);
243 		dmake_job_msg->setImmediateOutput(0);
244 		called_make = false;
245 	} else
246 #endif
247 	{
248 		p = commands;
249 	}
250 
251 	argcnt = 0;
252 	for (rule = line->body.line.command_used;
253 	     rule != NULL;
254 	     rule = rule->next) {
255 		if (posix && (touch || quest) && !rule->always_exec) {
256 			continue;
257 		}
258 		if (vpath_defined) {
259 			rule->command_line =
260 			  vpath_translation(rule->command_line);
261 		}
262 		if (dmake_mode_type == distributed_mode) {
263 			cmd_options = 0;
264 			if(local) {
265 				cmd_options |= local_host_mask;
266 			}
267 		} else {
268 			silent_flag = false;
269 			ignore = 0;
270 		}
271 		if (rule->command_line->hash.length > 0) {
272 			if (++argcnt == MAXRULES) {
273 				if (dmake_mode_type == distributed_mode) {
274 					/* XXX - tell rxm to execute on local host. */
275 					/* I WAS HERE!!! */
276 				} else {
277 					/* Too many rules, run serially instead. */
278 					return build_serial;
279 				}
280 			}
281 #ifdef DISTRIBUTED
282 			if (dmake_mode_type == distributed_mode) {
283 				/*
284 				 * XXX - set assign_mask to tell rxm
285 				 * to do the following.
286 				 */
287 /* From execute_serial():
288 				if (rule->assign) {
289 					result = build_ok;
290 					do_assign(rule->command_line, target);
291  */
292 				if (0) {
293 				} else if (report_dependencies_level == 0) {
294 					if (rule->ignore_error) {
295 						cmd_options |= ignore_mask;
296 					}
297 					if (rule->silent) {
298 						cmd_options |= silent_mask;
299 					}
300 					if (rule->command_line->meta) {
301 						cmd_options |= meta_mask;
302 					}
303 					if (rule->make_refd) {
304 						cmd_options |= make_refd_mask;
305 					}
306 					if (do_not_exec_rule) {
307 						cmd_options |= do_not_exec_mask;
308 					}
309 					append_dmake_cmd(dmake_job_msg,
310 					                 rule->command_line->string_mb,
311 					                 cmd_options);
312 					/* Copying dosys()... */
313 					if (rule->make_refd) {
314 						if (waitflg) {
315 							dmake_job_msg->setImmediateOutput(1);
316 						}
317 						called_make = true;
318 						if (command_changed &&
319 						    !wrote_state_file) {
320 							write_state_file(0, false);
321 							wrote_state_file = true;
322 						}
323 					}
324 				}
325 			} else
326 #endif
327 			{
328 				if (rule->silent && !silent) {
329 					silent_flag = true;
330 				}
331 				if (rule->ignore_error) {
332 					ignore++;
333 				}
334 				/* XXX - need to add support for + prefix */
335 				if (silent_flag || ignore) {
336 					*p = getmem((silent_flag ? 1 : 0) +
337 						    ignore +
338 						    (strlen(rule->
339 						           command_line->
340 						           string_mb)) +
341 						    1);
342 					cp = *p++;
343 					if (silent_flag) {
344 						*cp++ = (int) at_char;
345 					}
346 					if (ignore) {
347 						*cp++ = (int) hyphen_char;
348 					}
349 					(void) strcpy(cp, rule->command_line->string_mb);
350 				} else {
351 					*p++ = rule->command_line->string_mb;
352 				}
353 			}
354 		}
355 	}
356 	if ((argcnt == 0) ||
357 	    (report_dependencies_level > 0)) {
358 #ifdef DISTRIBUTED
359 		if (dmake_job_msg) {
360 			delete dmake_job_msg;
361 		}
362 #endif
363 		return build_ok;
364 	}
365 #ifdef DISTRIBUTED
366 	if (dmake_mode_type == distributed_mode) {
367 		// Send  a DoJob message to the rxm process.
368 		distribute_rxm(dmake_job_msg);
369 
370 		// Wait for an acknowledgement.
371 		Avo_AcknowledgeMsg *ackMsg = getAcknowledgeMsg();
372 		if (ackMsg) {
373 			delete ackMsg;
374 		}
375 
376 		if (waitflg) {
377 			// Wait for, and process a job result.
378 			result = await_dist(waitflg);
379 			if (called_make) {
380 				maybe_reread_make_state();
381 			}
382 			check_state(temp_file_name);
383 			if (result == build_failed) {
384 				if (!continue_after_error) {
385 
386 #ifdef PRINT_EXIT_STATUS
387 					warning(NOCATGETS("I'm in execute_parallel. await_dist() returned build_failed"));
388 #endif
389 
390 					fatal(gettext("Command failed for target `%s'"),
391 					      target->string_mb);
392 				}
393 				/*
394 				 * Make sure a failing command is not
395 				 * saved in .make.state.
396 				 */
397 				line->body.line.command_used = NULL;
398 			}
399 			if (temp_file_name != NULL) {
400 				free_name(temp_file_name);
401 			}
402 			temp_file_name = NULL;
403 			Property spro = get_prop(sunpro_dependencies->prop, macro_prop);
404 			if(spro != NULL) {
405 				Name val = spro->body.macro.value;
406 				if(val != NULL) {
407 					free_name(val);
408 					spro->body.macro.value = NULL;
409 				}
410 			}
411 			spro = get_prop(sunpro_dependencies->prop, env_mem_prop);
412 			if(spro) {
413 				char *val = spro->body.env_mem.value;
414 				if(val != NULL) {
415 					retmem_mb(val);
416 					spro->body.env_mem.value = NULL;
417 				}
418 			}
419 			return result;
420 		} else {
421 			parallel_process_cnt++;
422 			return build_running;
423 		}
424 	} else
425 #endif
426 	{
427 		*p = NULL;
428 
429 		Doname res = distribute_process(commands, line);
430 		if (res == build_running) {
431 			parallel_process_cnt++;
432 		}
433 
434 		/*
435 		 * Return only those memory that were specially allocated
436 		 * for part of commands.
437 		 */
438 		for (int i = 0; commands[i] != NULL; i++) {
439 			if ((commands[i][0] == (int) at_char) ||
440 			    (commands[i][0] == (int) hyphen_char)) {
441 				retmem_mb(commands[i]);
442 			}
443 		}
444 		return res;
445 	}
446 }
447 
448 #ifdef DISTRIBUTED
449 /*
450  *	append_dmake_cmd()
451  *
452  *	Replaces all escaped newline's (\<cr>)
453  *	  in the original command line with space's,
454  *	  then append the new command line to the DoJobMsg object.
455  */
456 static void
append_dmake_cmd(Avo_DoJobMsg * dmake_job_msg,char * orig_cmd_line,int cmd_options)457 append_dmake_cmd(Avo_DoJobMsg *dmake_job_msg,
458                  char *orig_cmd_line,
459                  int cmd_options)
460 {
461 /*
462 	Avo_DmakeCommand	*tmp_dmake_command;
463 
464 	tmp_dmake_command = new Avo_DmakeCommand(orig_cmd_line, cmd_options);
465 	dmake_job_msg->appendCmd(tmp_dmake_command);
466 	delete tmp_dmake_command;
467  */
468 	dmake_job_msg->appendCmd(new Avo_DmakeCommand(orig_cmd_line, cmd_options));
469 }
470 #endif
471 
472 #if defined(TEAMWARE_MAKE_CMN) || defined(PMAKE)
473 #define MAXJOBS_ADJUST_RFE4694000
474 
475 #ifdef MAXJOBS_ADJUST_RFE4694000
476 
477 #include <unistd.h>	/* sysconf(_SC_NPROCESSORS_ONLN) */
478 #ifdef	HAVE_SYS_IPC_H
479 #include <sys/ipc.h>		/* ftok() */
480 #endif
481 #ifdef	HAVE_SYS_SHM_H
482 #include <sys/shm.h>		/* shmget(), shmat(), shmdt(), shmctl() */
483 #else
484 #include <sys/mman.h>		/* mmap(), munmap() */
485 #endif
486 #ifdef	HAVE_SEMAPHORE_H
487 #include <semaphore.h>		/* sem_init(), sem_trywait(), sem_post(), sem_destroy() */
488 #endif
489 
490 #ifdef	HAVE_SYS_LOADAVG_H
491 #include <sys/loadavg.h>	/* getloadavg() */
492 #endif
493 #ifndef	LOADAVG_1MIN
494 #define	LOADAVG_1MIN	0
495 #endif
496 
497 /*
498  *	adjust_pmake_max_jobs (int pmake_max_jobs)
499  *
500  *	Parameters:
501  * 		pmake_max_jobs	- max jobs limit set by user
502  *
503  *	External functions used:
504  *		sysconf()
505  * 		getloadavg()
506  */
507 static int
adjust_pmake_max_jobs(int pmake_max_jobs)508 adjust_pmake_max_jobs (int pmake_max_jobs)
509 {
510 	static int	ncpu = 0;
511 	double		loadavg[3];
512 	int		adjustment;
513 	int		adjusted_max_jobs;
514 
515 #ifdef	_SC_NPROCESSORS_ONLN
516 	if (ncpu <= 0) {
517 		if ((ncpu = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) {
518 			ncpu = 1;
519 		}
520 	}
521 #else
522 	ncpu = 1;
523 #endif
524 #ifndef	HAVE_GETLOADAVG
525 	return(pmake_max_jobs);
526 #else
527 	if (getloadavg(loadavg, 3) != 3) return(pmake_max_jobs);
528 #endif
529 	adjustment = ((int)loadavg[LOADAVG_1MIN]);
530 	if (adjustment < 2) return(pmake_max_jobs);
531 	if (ncpu > 1) {
532 		adjustment = adjustment / ncpu;
533 	}
534 	adjusted_max_jobs = pmake_max_jobs - adjustment;
535 	if (adjusted_max_jobs < 1) adjusted_max_jobs = 1;
536 	return(adjusted_max_jobs);
537 }
538 
539 /*
540  *  M2 adjust mode data and functions
541  *
542  *  m2_init()		- initializes M2 shared semaphore
543  *  m2_acquire_job()	- decrements M2 semaphore counter
544  *  m2_release_job()	- increments M2 semaphore counter
545  *  m2_fini()		- destroys M2 semaphore and shared memory*
546  *
547  *  Environment variables:
548  *	__DMAKE_M2_FILE__
549  *
550  *  External functions:
551  *	ftok(), shmget(), shmat(), shmdt(), shmctl()
552  *	sem_init(), sem_trywait(), sem_post(), sem_destroy()
553  *	creat(), close(), unlink()
554  *	getenv(), putenv()
555  *
556  *  Static variables:
557  *	m2_file		- tmp file name to create ipc key for shared memory
558  *	m2_shm_id	- shared memory id
559  *	m2_shm_sem	- shared memory semaphore
560  *	m2_toplevel	- true if we are the root process
561  */
562 
563 static char	m2_file[MAXPATHLEN];
564 static int	m2_shm_id = -1;
565 static sem_t*	m2_shm_sem = 0;
566 static int	m2_toplevel = 0;
567 
568 static int
m2_getshm(char * var)569 m2_getshm(char *var)
570 {
571 #ifdef	HAVE_SYS_SHM_H
572 	key_t	key;
573 
574 	/* combine IPC key */
575 	if ((key = ftok(m2_file, 38)) == (key_t) -1) {
576 		return -1;
577 	}
578 
579 	/* create shared memory */
580 	if ((m2_shm_id = shmget(key, sizeof(*m2_shm_sem),
581 			0666 | (var ? 0 : IPC_CREAT|IPC_EXCL))) == -1) {
582 		return -1;
583 	}
584 
585 	/* attach shared memory */
586 	if ((m2_shm_sem = (sem_t*) shmat(m2_shm_id, 0, 0666)) == (sem_t*)-1) {
587 		return -1;
588 	}
589 #else
590 	int	fd = open(m2_file, O_RDWR);
591 	sem_t	shm_sem;
592 
593 	if (fd < 0)
594 		return (-1);
595 
596 	write(fd, &shm_sem, sizeof(shm_sem));
597 
598 	m2_shm_sem = (sem_t *) mmap(0, sizeof(*m2_shm_sem),
599 				PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
600 	close(fd);
601 	if (m2_shm_sem == MAP_FAILED)
602 		return (-1);
603 	m2_shm_id = 0;
604 #endif
605 	return (0);
606 }
607 
608 static int
m2_init()609 m2_init() {
610 	char	*var;
611 
612 	if ((var = getenv(NOCATGETS("__DMAKE_M2_FILE__"))) == 0) {
613 		/* compose tmp file name */
614 		sprintf(m2_file, NOCATGETS("%s/dmake.m2.%d.XXXXXX"), tmpdir, getpid());
615 
616 		/* create tmp file */
617 		int fd = mkstemp(m2_file);
618 		if (fd < 0) {
619 			return -1;
620 		} else {
621 			close(fd);
622 		}
623 		m2_toplevel = 1;
624 	} else {
625 		/* using existing semaphore */
626 		strcpy(m2_file, var);
627 		m2_toplevel = 0;
628 	}
629 
630 	if (m2_getshm(var) < 0)
631 		return (-1);
632 
633 	/* root process */
634 	if (var == 0) {
635 		/* initialize semaphore */
636 		if (sem_init(m2_shm_sem, 1, pmake_max_jobs)) {
637 			return -1;
638 		}
639 
640 		/* alloc memory for env variable */
641 		if ((var = (char*) malloc(MAXPATHLEN)) == 0) {
642 			return -1;
643 		}
644 
645 		/* put key to env */
646 		sprintf(var, NOCATGETS("__DMAKE_M2_FILE__=%s"), m2_file);
647 		if (putenv(var)) {
648 			return -1;
649 		}
650 	}
651 	return 0;
652 }
653 
654 static void
m2_fini()655 m2_fini() {
656 	if (m2_shm_id >= 0) {
657 #ifdef	HAVE_SYS_SHM_H
658 		struct shmid_ds stat;
659 
660 		/* determine the number of attached processes */
661 		if (shmctl(m2_shm_id, IPC_STAT, &stat) == 0) {
662 			if (stat.shm_nattch <= 1) {
663 				/* destroy semaphore */
664 				if (m2_shm_sem != 0) {
665 					(void) sem_destroy(m2_shm_sem);
666 				}
667 
668 				/* destroy shared memory */
669 				(void) shmctl(m2_shm_id, IPC_RMID, &stat);
670 
671 				/* remove tmp file created for the key */
672 				(void) unlink(m2_file);
673 			} else {
674 				/* detach shared memory */
675 				if (m2_shm_sem != 0) {
676 					(void) shmdt((char*) m2_shm_sem);
677 				}
678 			}
679 		}
680 #else
681 		if (m2_toplevel) {
682 			/* destroy semaphore */
683 			if (m2_shm_sem != 0) {
684 				(void) sem_destroy(m2_shm_sem);
685 			}
686 
687 			/* remove tmp file created for the key */
688 			(void) unlink(m2_file);
689 		}
690 		/* unmap shared memory */
691 		(void) munmap((char *) m2_shm_sem, sizeof(*m2_shm_sem));
692 #endif
693 		m2_shm_id = -1;
694 		m2_shm_sem = 0;
695 	}
696 }
697 
698 static int
m2_acquire_job()699 m2_acquire_job() {
700 	if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) {
701 		if (sem_trywait(m2_shm_sem) == 0) {
702 			return 1;
703 		}
704 		if (errno == EAGAIN) {
705 			return 0;
706 		}
707 	}
708 	return -1;
709 }
710 
711 static int
m2_release_job()712 m2_release_job() {
713 	if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) {
714 		if (sem_post(m2_shm_sem) == 0) {
715 			return 0;
716 		}
717 	}
718 	return -1;
719 }
720 
721 /*
722  *  job adjust mode
723  *
724  *  Possible values:
725  *    ADJUST_M1		- adjustment by system load (default)
726  *    ADJUST_M2		- fixed limit of jobs for the group of nested dmakes
727  *    ADJUST_NONE	- no adjustment - fixed limit of jobs for the current dmake
728  */
729 static enum {
730 	ADJUST_UNKNOWN,
731 	ADJUST_M1,
732 	ADJUST_M2,
733 	ADJUST_NONE
734 } job_adjust_mode = ADJUST_UNKNOWN;
735 
736 /*
737  *  void job_adjust_fini()
738  *
739  *  Description:
740  *	Cleans up job adjust data.
741  *
742  *  Static variables:
743  *	job_adjust_mode	Current job adjust mode
744  */
745 void
job_adjust_fini()746 job_adjust_fini() {
747 	if (job_adjust_mode == ADJUST_M2) {
748 		m2_fini();
749 	}
750 }
751 
752 /*
753  *  void job_adjust_error()
754  *
755  *  Description:
756  *	Prints warning message, cleans up job adjust data, and disables job adjustment
757  *
758  *  Environment:
759  *	DMAKE_ADJUST_MAX_JOBS
760  *
761  *  External functions:
762  *	putenv()
763  *
764  *  Static variables:
765  *	job_adjust_mode	Current job adjust mode
766  */
767 static void
job_adjust_error()768 job_adjust_error() {
769 	if (job_adjust_mode != ADJUST_NONE) {
770 		/* cleanup internals */
771 		job_adjust_fini();
772 
773 		/* warning message for the user */
774 		warning(gettext("Encountered max jobs auto adjustment error - disabling auto adjustment."));
775 
776 		/* switch off job adjustment for the children */
777 		putenv((char *)NOCATGETS("DMAKE_ADJUST_MAX_JOBS=NO"));
778 
779 		/* and for this dmake */
780 		job_adjust_mode = ADJUST_NONE;
781 	}
782 }
783 
784 /*
785  *  void job_adjust_posix()
786  *
787  *  Description:
788  *	Sets "DMAKE_ADJUST_MAX_JOBS=M2" for POSIX compatibility in case that
789  *	job_adjust_mode was not yet set. This only works, if job_adjust_init()
790  *	was not yet called.
791  *
792  *  Environment:
793  *	DMAKE_ADJUST_MAX_JOBS
794  *
795  *  External functions:
796  *	putenv()
797  */
798 void
job_adjust_posix()799 job_adjust_posix() {
800 	if (job_adjust_mode == ADJUST_UNKNOWN) {
801 		/* switch adjustment for the children to POSIX mode */
802 		putenv((char *)NOCATGETS("DMAKE_ADJUST_MAX_JOBS=M2"));
803 	}
804 }
805 
806 /*
807  *  void job_adjust_init()
808  *
809  *  Description:
810  *	Parses DMAKE_ADJUST_MAX_JOBS env variable
811  *	and performs appropriate initializations.
812  *
813  *  Environment:
814  *	DMAKE_ADJUST_MAX_JOBS
815  *	  DMAKE_ADJUST_MAX_JOBS == "NO"	- no adjustment
816  *	  DMAKE_ADJUST_MAX_JOBS == "M1"	- M1 adjust mode
817  *	  DMAKE_ADJUST_MAX_JOBS == "M2"	- M2 adjust mode
818  *	  other				- M1 adjust mode
819  *
820  *  External functions:
821  *	getenv()
822  *
823  *  Static variables:
824  *	job_adjust_mode	Current job adjust mode
825  */
826 static void
job_adjust_init()827 job_adjust_init() {
828 	if (job_adjust_mode == ADJUST_UNKNOWN) {
829 		/* default mode */
830 		job_adjust_mode = ADJUST_M1;
831 
832 		/* determine adjust mode */
833 		if (char *var = getenv(NOCATGETS("DMAKE_ADJUST_MAX_JOBS"))) {
834 			if (strcasecmp(var, NOCATGETS("NO")) == 0) {
835 				job_adjust_mode = ADJUST_NONE;
836 			} else if (strcasecmp(var, NOCATGETS("M1")) == 0) {
837 				job_adjust_mode = ADJUST_M1;
838 			} else if (strcasecmp(var, NOCATGETS("M2")) == 0) {
839 				job_adjust_mode = ADJUST_M2;
840 			}
841 		}
842 
843 		/* M2 specific initialization */
844 		if (job_adjust_mode == ADJUST_M2) {
845 			if (m2_init()) {
846 				job_adjust_error();
847 			}
848 		}
849 	}
850 }
851 
852 #endif /* MAXJOBS_ADJUST_RFE4694000 */
853 #endif /* TEAMWARE_MAKE_CMN */
854 
855 /*
856  *	distribute_process(char **commands, Property line)
857  *
858  *	Parameters:
859  *		commands	argv vector of commands to execute
860  *
861  *	Return value:
862  *				The result of the execution
863  *
864  *	Static variables used:
865  *		process_running	Set to the pid of the process set running
866  * #if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000)
867  *		job_adjust_mode	Current job adjust mode
868  * #endif
869  */
870 static Doname
distribute_process(char ** commands,Property line)871 distribute_process(char **commands, Property line)
872 {
873 	static unsigned	file_number = 0;
874 	wchar_t		string[MAXPATHLEN];
875 	char		mbstring[MAXPATHLEN];
876 	int		filed;
877 	int		res;
878 	int		tmp_index;
879 	char		*tmp_index_str_ptr;
880 
881 #if (!defined(TEAMWARE_MAKE_CMN) && !defined(PMAKE)) || !defined (MAXJOBS_ADJUST_RFE4694000)
882 	while (parallel_process_cnt >= pmake_max_jobs) {
883 		await_parallel(false);
884 		finish_children(true);
885 	}
886 #else /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */
887 	/* initialize adjust mode, if not initialized */
888 	if (job_adjust_mode == ADJUST_UNKNOWN) {
889 		job_adjust_init();
890 	}
891 
892 	/* actions depend on adjust mode */
893 	switch (job_adjust_mode) {
894 	case ADJUST_M1:
895 		while (parallel_process_cnt >= adjust_pmake_max_jobs (pmake_max_jobs)) {
896 			await_parallel(false);
897 			finish_children(true);
898 		}
899 		break;
900 	case ADJUST_M2:
901 		if ((res = m2_acquire_job()) == 0) {
902 			if (parallel_process_cnt > 0) {
903 				await_parallel(false);
904 				finish_children(true);
905 
906 				if ((res = m2_acquire_job()) == 0) {
907 					return build_serial;
908 				}
909 			} else {
910 				return build_serial;
911 			}
912 		}
913 		if (res < 0) {
914 			/* job adjustment error */
915 			job_adjust_error();
916 
917 			/* no adjustment */
918 			while (parallel_process_cnt >= pmake_max_jobs) {
919 				await_parallel(false);
920 				finish_children(true);
921 			}
922 		}
923 		break;
924 	default:
925 		while (parallel_process_cnt >= pmake_max_jobs) {
926 			await_parallel(false);
927 			finish_children(true);
928 		}
929 	}
930 #endif /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */
931 #ifdef DISTRIBUTED
932 	if (send_mtool_msgs) {
933 		send_job_start_msg(line);
934 	}
935 #endif
936 #ifdef DISTRIBUTED
937 	setvar_envvar((Avo_DoJobMsg *)NULL);
938 #else
939 	setvar_envvar();
940 #endif
941 	/*
942 	 * Tell the user what DMake is doing.
943 	 */
944 	if (!silent && output_mode != txt2_mode) {
945 		/*
946 		 * Print local_host --> x job(s).
947 		 */
948 		(void) fprintf(stdout,
949 		               gettext("%s --> %d %s\n"),
950 		               local_host,
951 		               parallel_process_cnt + 1,
952 		               (parallel_process_cnt == 0) ? gettext("job") : gettext("jobs"));
953 
954 		/* Print command line(s). */
955 		tmp_index = 0;
956 		while (commands[tmp_index] != NULL) {
957 		    /* No @ char. */
958 		    /* XXX - need to add [2] when + prefix is added */
959 		    if ((commands[tmp_index][0] != (int) at_char) &&
960 		        (commands[tmp_index][1] != (int) at_char)) {
961 			tmp_index_str_ptr = commands[tmp_index];
962 			if (*tmp_index_str_ptr == (int) hyphen_char) {
963 				tmp_index_str_ptr++;
964 			}
965                         (void) fprintf(stdout, "%s\n", tmp_index_str_ptr);
966 		    }
967 		    tmp_index++;
968 		}
969 		(void) fflush(stdout);
970 	}
971 
972 	(void) sprintf(mbstring,
973 		        NOCATGETS("%s/dmake.stdout.%d.%d.XXXXXX"),
974 			tmpdir,
975 		        getpid(),
976 	                file_number++);
977 
978 	mktemp(mbstring);
979 
980 	stdout_file = strdup(mbstring);
981 	stderr_file = NULL;
982 #if (defined(TEAMWARE_MAKE_CMN) || defined(PMAKE)) && defined(REDIRECT_ERR)
983 	if (!out_err_same) {
984 		(void) sprintf(mbstring,
985 			        NOCATGETS("%s/dmake.stderr.%d.%d.XXXXXX"),
986 				tmpdir,
987 			        getpid(),
988 		                file_number++);
989 
990 		mktemp(mbstring);
991 
992 		stderr_file = strdup(mbstring);
993 	}
994 #endif
995 
996 #ifdef SGE_SUPPORT
997 	if (grid) {
998 		static char *dir4gridscripts = NULL;
999 		static char *hostName = NULL;
1000 		if (dir4gridscripts == NULL) {
1001 			Name		dmakeOdir_name, dmakeOdir_value;
1002 			Property	prop;
1003 			MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_ODIR"));
1004 			dmakeOdir_name = GETNAME(wcs_buffer, FIND_LENGTH);
1005 			if (((prop = get_prop(dmakeOdir_name->prop, macro_prop)) != NULL) &&
1006 			    ((dmakeOdir_value = prop->body.macro.value) != NULL)) {
1007 				dir4gridscripts = dmakeOdir_value->string_mb;
1008 			}
1009 			dir4gridscripts = Avo_PathNames::pathname_output_directory(dir4gridscripts);
1010 			hostName = Avo_PathNames::pathname_local_host();
1011 		}
1012 		(void) sprintf(script_file,
1013 		        	NOCATGETS("%s/dmake.script.%s.%d.%d.XXXXXX"),
1014 				dir4gridscripts,
1015 				hostName,
1016 			        getpid(),
1017 		                file_number++);
1018 	}
1019 #endif /* SGE_SUPPORT */
1020 	process_running = run_rule_commands(local_host, commands);
1021 
1022 	return build_running;
1023 }
1024 
1025 /*
1026  *	doname_parallel(target, do_get, implicit)
1027  *
1028  *	Processes the given target and finishes up any parallel
1029  *	processes left running.
1030  *
1031  *	Return value:
1032  *				Result of target build
1033  *
1034  *	Parameters:
1035  *		target		Target to build
1036  *		do_get		True if sccs get to be done
1037  *		implicit	True if this is an implicit target
1038  */
1039 Doname
doname_parallel(Name target,Boolean do_get,Boolean implicit)1040 doname_parallel(Name target, Boolean do_get, Boolean implicit)
1041 {
1042 	Doname		result;
1043 
1044 	result = doname_check(target, do_get, implicit, false);
1045 	if (result == build_ok || result == build_failed) {
1046 		return result;
1047 	}
1048 	finish_running();
1049 	return (Doname) target->state;
1050 }
1051 
1052 /*
1053  *	doname_subtree(target, do_get, implicit)
1054  *
1055  *	Completely computes an object and its dependents for a
1056  *	serial subtree build.
1057  *
1058  *	Parameters:
1059  *		target		Target to build
1060  *		do_get		True if sccs get to be done
1061  *		implicit	True if this is an implicit target
1062  *
1063  *	Static variables used:
1064  *		running_tail	Tail of the list of running processes
1065  *
1066  *	Global variables used:
1067  *		running_list	The list of running processes
1068  */
1069 static void
doname_subtree(Name target,Boolean do_get,Boolean implicit)1070 doname_subtree(Name target, Boolean do_get, Boolean implicit)
1071 {
1072 	Running		save_running_list;
1073 	Running		*save_running_tail;
1074 
1075 	save_running_list = running_list;
1076 	save_running_tail = running_tail;
1077 	running_list = NULL;
1078 	running_tail = &running_list;
1079 	target->state = build_subtree;
1080 	target->checking_subtree = true;
1081 	while(doname_check(target, do_get, implicit, false) == build_running) {
1082 		target->checking_subtree = false;
1083 		finish_running();
1084 		target->state = build_subtree;
1085 	}
1086 	target->checking_subtree = false;
1087 	running_list = save_running_list;
1088 	running_tail = save_running_tail;
1089 }
1090 
1091 /*
1092  *	finish_running()
1093  *
1094  *	Keeps processing until the running_list is emptied out.
1095  *
1096  *	Parameters:
1097  *
1098  *	Global variables used:
1099  *		running_list	The list of running processes
1100  */
1101 void
finish_running(void)1102 finish_running(void)
1103 {
1104 	while (running_list != NULL) {
1105 #ifdef DISTRIBUTED
1106 		if (dmake_mode_type == distributed_mode) {
1107 			if ((just_did_subtree) ||
1108 			    (parallel_process_cnt == 0)) {
1109 				just_did_subtree = false;
1110 			} else {
1111 				(void) await_dist(false);
1112 				finish_children(true);
1113 			}
1114 		} else
1115 #endif
1116 		{
1117 			await_parallel(false);
1118 			finish_children(true);
1119 		}
1120 		if (running_list != NULL) {
1121 			process_next();
1122 		}
1123 	}
1124 }
1125 
1126 /*
1127  *	process_next()
1128  *
1129  *	Searches the running list for any targets which can start processing.
1130  *	This can be a pending target, a serial target, or a subtree target.
1131  *
1132  *	Parameters:
1133  *
1134  *	Static variables used:
1135  *		running_tail		The end of the list of running procs
1136  *		subtree_conflict	A target which conflicts with a subtree
1137  *		subtree_conflict2	The other target which conflicts
1138  *
1139  *	Global variables used:
1140  *		commands_done		True if commands executed
1141  *		debug_level		Controls debug output
1142  *		parallel_process_cnt	Number of parallel process running
1143  *		recursion_level		Indentation for debug output
1144  *		running_list		List of running processes
1145  */
1146 static void
process_next(void)1147 process_next(void)
1148 {
1149 	Running		rp;
1150 	Running		*rp_prev;
1151 	Property	line;
1152 	Chain		target_group;
1153 	Dependency	dep;
1154 	Boolean		quiescent = true;
1155 	Running		*subtree_target;
1156 	Boolean		saved_commands_done;
1157 	Property	*conditionals;
1158 
1159 	subtree_target = NULL;
1160 	subtree_conflict = NULL;
1161 	subtree_conflict2 = NULL;
1162 	/*
1163 	 * If nothing currently running, build a serial target, if any.
1164 	 */
1165 start_loop_1:
1166 	for (rp_prev = &running_list, rp = running_list;
1167 	     rp != NULL && parallel_process_cnt == 0;
1168 	     rp = rp->next) {
1169 		if (rp->state == build_serial) {
1170 			*rp_prev = rp->next;
1171 			if (rp->next == NULL) {
1172 				running_tail = rp_prev;
1173 			}
1174 			recursion_level = rp->recursion_level;
1175 			rp->target->state = build_pending;
1176 			(void) doname_check(rp->target,
1177 					    rp->do_get,
1178 					    rp->implicit,
1179 					    false);
1180 			quiescent = false;
1181 			delete_running_struct(rp);
1182 			goto start_loop_1;
1183 		} else {
1184 			rp_prev = &rp->next;
1185 		}
1186 	}
1187 	/*
1188 	 * Find a target to build.  The target must be pending, have all
1189 	 * its dependencies built, and not be in a target group with a target
1190 	 * currently building.
1191 	 */
1192 start_loop_2:
1193 	for (rp_prev = &running_list, rp = running_list;
1194 	     rp != NULL;
1195 	     rp = rp->next) {
1196 		if (!(rp->state == build_pending ||
1197 		      rp->state == build_subtree)) {
1198 			quiescent = false;
1199 			rp_prev = &rp->next;
1200 		} else if (rp->state == build_pending) {
1201 			line = get_prop(rp->target->prop, line_prop);
1202 			for (dep = line->body.line.dependencies;
1203 			     dep != NULL;
1204 			     dep = dep->next) {
1205 				if (dep->name->state == build_running ||
1206 				    dep->name->state == build_pending ||
1207 				    dep->name->state == build_serial) {
1208 					break;
1209 				}
1210 			}
1211 			if (dep == NULL) {
1212 				for (target_group = line->body.line.target_group;
1213 				     target_group != NULL;
1214 				     target_group = target_group->next) {
1215 					if (is_running(target_group->name)) {
1216 						break;
1217 					}
1218 				}
1219 				if (target_group == NULL) {
1220 					*rp_prev = rp->next;
1221 					if (rp->next == NULL) {
1222 						running_tail = rp_prev;
1223 					}
1224 					recursion_level = rp->recursion_level;
1225 					rp->target->state = rp->redo ?
1226 					  build_dont_know : build_pending;
1227 					saved_commands_done = commands_done;
1228 					conditionals =
1229 						set_conditionals
1230 						    (rp->conditional_cnt,
1231 						     rp->conditional_targets);
1232 					rp->target->dont_activate_cond_values = true;
1233 					if ((doname_check(rp->target,
1234 							  rp->do_get,
1235 							  rp->implicit,
1236 							  rp->target->has_target_prop ? true : false) !=
1237 					     build_running) &&
1238 					    !commands_done) {
1239 						commands_done =
1240 						  saved_commands_done;
1241 					}
1242 					rp->target->dont_activate_cond_values = false;
1243 					reset_conditionals
1244 						(rp->conditional_cnt,
1245 						 rp->conditional_targets,
1246 						 conditionals);
1247 					quiescent = false;
1248 					delete_running_struct(rp);
1249 					goto start_loop_2;
1250 				} else {
1251 					rp_prev = &rp->next;
1252 				}
1253 			} else {
1254 				rp_prev = &rp->next;
1255 			}
1256 		} else {
1257 			rp_prev = &rp->next;
1258 		}
1259 	}
1260 	/*
1261 	 * If nothing has been found to build and there exists a subtree
1262 	 * target with no dependency conflicts, build it.
1263 	 */
1264 	if (quiescent) {
1265 start_loop_3:
1266 		for (rp_prev = &running_list, rp = running_list;
1267 		     rp != NULL;
1268 		     rp = rp->next) {
1269 			if (rp->state == build_subtree) {
1270 				if (!dependency_conflict(rp->target)) {
1271 					*rp_prev = rp->next;
1272 					if (rp->next == NULL) {
1273 						running_tail = rp_prev;
1274 					}
1275 					recursion_level = rp->recursion_level;
1276 					doname_subtree(rp->target,
1277 						       rp->do_get,
1278 						       rp->implicit);
1279 #ifdef DISTRIBUTED
1280 					just_did_subtree = true;
1281 #endif
1282 					quiescent = false;
1283 					delete_running_struct(rp);
1284 					goto start_loop_3;
1285 				} else {
1286 					subtree_target = rp_prev;
1287 					rp_prev = &rp->next;
1288 				}
1289 			} else {
1290 				rp_prev = &rp->next;
1291 			}
1292 		}
1293 	}
1294 	/*
1295 	 * If still nothing found to build, we either have a deadlock
1296 	 * or a subtree with a dependency conflict with something waiting
1297 	 * to build.
1298 	 */
1299 	if (quiescent) {
1300 		if (subtree_target == NULL) {
1301 			fatal(gettext("Internal error: deadlock detected in process_next"));
1302 		} else {
1303 			rp = *subtree_target;
1304 			if (debug_level > 0) {
1305 				warning(gettext("Conditional macro conflict encountered for %s between %s and %s"),
1306 					subtree_conflict2->string_mb,
1307 					rp->target->string_mb,
1308 					subtree_conflict->string_mb);
1309 			}
1310 			*subtree_target = (*subtree_target)->next;
1311 			if (rp->next == NULL) {
1312 				running_tail = subtree_target;
1313 			}
1314 			recursion_level = rp->recursion_level;
1315 			doname_subtree(rp->target, rp->do_get, rp->implicit);
1316 #ifdef DISTRIBUTED
1317 			just_did_subtree = true;
1318 #endif
1319 			delete_running_struct(rp);
1320 		}
1321 	}
1322 }
1323 
1324 /*
1325  *	set_conditionals(cnt, targets)
1326  *
1327  *	Sets the conditional macros for the targets given in the array of
1328  *	targets.  The old macro values are returned in an array of
1329  *	Properties for later resetting.
1330  *
1331  *	Return value:
1332  *					Array of conditional macro settings
1333  *
1334  *	Parameters:
1335  *		cnt			Number of targets
1336  *		targets			Array of targets
1337  */
1338 static Property *
set_conditionals(int cnt,Name * targets)1339 set_conditionals(int cnt, Name *targets)
1340 {
1341 	Property	*locals, *lp;
1342 	Name		*tp;
1343 
1344 	locals = (Property *) getmem(cnt * sizeof(Property));
1345 	for (lp = locals, tp = targets;
1346 	     cnt > 0;
1347 	     cnt--, lp++, tp++) {
1348 		*lp = (Property) getmem((*tp)->conditional_cnt *
1349 					sizeof(struct _Property));
1350 		set_locals(*tp, *lp);
1351 	}
1352 	return locals;
1353 }
1354 
1355 /*
1356  *	reset_conditionals(cnt, targets, locals)
1357  *
1358  *	Resets the conditional macros as saved in the given array of
1359  *	Properties.  The resets are done in reverse order.  Afterwards the
1360  *	data structures are freed.
1361  *
1362  *	Parameters:
1363  *		cnt			Number of targets
1364  *		targets			Array of targets
1365  *		locals			Array of dependency macro settings
1366  */
1367 static void
reset_conditionals(int cnt,Name * targets,Property * locals)1368 reset_conditionals(int cnt, Name *targets, Property *locals)
1369 {
1370 	Name		*tp;
1371 	Property	*lp;
1372 
1373 	for (tp = targets + (cnt - 1), lp = locals + (cnt - 1);
1374 	     cnt > 0;
1375 	     cnt--, tp--, lp--) {
1376 		reset_locals(*tp,
1377 			     *lp,
1378 			     get_prop((*tp)->prop, conditional_prop),
1379 			     0);
1380 		retmem_mb((caddr_t) *lp);
1381 	}
1382 	retmem_mb((caddr_t) locals);
1383 }
1384 
1385 /*
1386  *	dependency_conflict(target)
1387  *
1388  *	Returns true if there is an intersection between
1389  *	the subtree of the target and any dependents of the pending targets.
1390  *
1391  *	Return value:
1392  *					True if conflict found
1393  *
1394  *	Parameters:
1395  *		target			Subtree target to check
1396  *
1397  *	Static variables used:
1398  *		subtree_conflict	Target conflict found
1399  *		subtree_conflict2	Second conflict found
1400  *
1401  *	Global variables used:
1402  *		running_list		List of running processes
1403  *		wait_name		.WAIT, not a real dependency
1404  */
1405 static Boolean
dependency_conflict(Name target)1406 dependency_conflict(Name target)
1407 {
1408 	Property	line;
1409 	Property	pending_line;
1410 	Dependency	dp;
1411 	Dependency	pending_dp;
1412 	Running		rp;
1413 
1414 	/* Return if we are already checking this target */
1415 	if (target->checking_subtree) {
1416 		return false;
1417 	}
1418 	target->checking_subtree = true;
1419 	line = get_prop(target->prop, line_prop);
1420 	if (line == NULL) {
1421 		target->checking_subtree = false;
1422 		return false;
1423 	}
1424 	/* Check each dependency of the target for conflicts */
1425 	for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) {
1426 		/* Ignore .WAIT dependency */
1427 		if (dp->name == wait_name) {
1428 			continue;
1429 		}
1430 		/*
1431 		 * For each pending target, look for a dependency which
1432 		 * is the same as a dependency of the subtree target.  Since
1433 		 * we can't build the subtree until all pending targets have
1434 		 * finished which depend on the same dependency, this is
1435 		 * a conflict.
1436 		 */
1437 		for (rp = running_list; rp != NULL; rp = rp->next) {
1438 			if (rp->state == build_pending) {
1439 				pending_line = get_prop(rp->target->prop,
1440 							line_prop);
1441 				if (pending_line == NULL) {
1442 					continue;
1443 				}
1444 				for(pending_dp = pending_line->
1445 				    			body.line.dependencies;
1446 				    pending_dp != NULL;
1447 				    pending_dp = pending_dp->next) {
1448 					if (dp->name == pending_dp->name) {
1449 						target->checking_subtree
1450 						  		= false;
1451 						subtree_conflict = rp->target;
1452 						subtree_conflict2 = dp->name;
1453 						return true;
1454 					}
1455 				}
1456 			}
1457 		}
1458 		if (dependency_conflict(dp->name)) {
1459 			target->checking_subtree = false;
1460 			return true;
1461 		}
1462 	}
1463 	target->checking_subtree = false;
1464 	return false;
1465 }
1466 
1467 /*
1468  *	await_parallel(waitflg)
1469  *
1470  *	Waits for parallel children to exit and finishes their processing.
1471  *	If waitflg is false, the function returns after update_delay.
1472  *
1473  *	Parameters:
1474  *		waitflg		dwight
1475  */
1476 void
await_parallel(Boolean waitflg)1477 await_parallel(Boolean waitflg)
1478 {
1479 #ifdef _CHECK_UPDATE_H
1480 	static int number_of_unknown_children = 0;
1481 #endif /* _CHECK_UPDATE_H */
1482 	Boolean		nohang;
1483 	pid_t		pid;
1484 	int		status;
1485 	Running		rp;
1486 	int		waiterr;
1487 
1488 	nohang = false;
1489 	for ( ; ; ) {
1490 		if (!nohang) {
1491 			(void) alarm((int) update_delay);
1492 		}
1493 		pid = waitpid((pid_t)-1,
1494 #ifdef	ultrix
1495 			      (WAIT_T *)&status,
1496 #else
1497 			      &status,
1498 #endif
1499 			      nohang ? WNOHANG : 0);
1500 		waiterr = errno;
1501 		if (!nohang) {
1502 			(void) alarm(0);
1503 		}
1504 		if (pid <= 0) {
1505 			if (waiterr == EINTR) {
1506 				if (waitflg) {
1507 					continue;
1508 				} else {
1509 					return;
1510 				}
1511 			} else {
1512 				return;
1513 			}
1514 		}
1515 		for (rp = running_list;
1516 		     (rp != NULL) && (rp->pid != pid);
1517 		     rp = rp->next) {
1518 			;
1519 		}
1520 		if (rp == NULL) {
1521 #ifdef _CHECK_UPDATE_H
1522 			/* Ignore first child - it is check_update */
1523 			if (number_of_unknown_children <= 0) {
1524 				number_of_unknown_children = 1;
1525 				return;
1526 			}
1527 #endif /* _CHECK_UPDATE_H */
1528 			if (send_mtool_msgs) {
1529 				continue;
1530 			} else {
1531 				fatal(gettext("Internal error: returned child pid not in running_list"));
1532 			}
1533 		} else {
1534 			rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed;
1535 		}
1536 		nohang = true;
1537 		parallel_process_cnt--;
1538 
1539 #if (defined(TEAMWARE_MAKE_CMN) || defined(PMAKE)) && \
1540     defined(MAXJOBS_ADJUST_RFE4694000)
1541 		if (job_adjust_mode == ADJUST_M2) {
1542 			if (m2_release_job()) {
1543 				job_adjust_error();
1544 			}
1545 		}
1546 #endif
1547 	}
1548 }
1549 
1550 /*
1551  *	finish_children(docheck)
1552  *
1553  *	Finishes the processing for all targets which were running
1554  *	and have now completed.
1555  *
1556  *	Parameters:
1557  *		docheck		Completely check the finished target
1558  *
1559  *	Static variables used:
1560  *		running_tail	The tail of the running list
1561  *
1562  *	Global variables used:
1563  *		continue_after_error  -k flag
1564  *		fatal_in_progress  True if we are finishing up after fatal err
1565  *		running_list	List of running processes
1566  */
1567 void
finish_children(Boolean docheck)1568 finish_children(Boolean docheck)
1569 {
1570 	int		cmds_length;
1571 	Property	line;
1572 	Property	line2;
1573 	struct stat	out_buf;
1574 	Running		rp;
1575 	Running		*rp_prev;
1576 	Cmd_line	rule;
1577 	Boolean		silent_flag;
1578 
1579 	for (rp_prev = &running_list, rp = running_list;
1580 	     rp != NULL;
1581 	     rp = rp->next) {
1582 bypass_for_loop_inc_4:
1583 		/*
1584 		 * If the state is ok or failed, then this target has
1585 		 * finished building.
1586 		 * In parallel_mode, output the accumulated stdout/stderr.
1587 		 * Read the auto dependency stuff, handle a failed build,
1588 		 * update the target, then finish the doname process for
1589 		 * that target.
1590 		 */
1591 		if (rp->state == build_ok || rp->state == build_failed) {
1592 			*rp_prev = rp->next;
1593 			if (rp->next == NULL) {
1594 				running_tail = rp_prev;
1595 			}
1596 			if ((line2 = rp->command) == NULL) {
1597 				line2 = get_prop(rp->target->prop, line_prop);
1598 			}
1599 			if (dmake_mode_type == distributed_mode) {
1600 				if (rp->make_refd) {
1601 					maybe_reread_make_state();
1602 				}
1603 			} else {
1604 				/*
1605 				 * Send an Avo_MToolJobResultMsg to maketool.
1606 				 */
1607 #ifdef DISTRIBUTED
1608 				if (send_mtool_msgs) {
1609 					send_job_result_msg(rp);
1610 				}
1611 #endif
1612 				/*
1613 				 * Check if there were any job output
1614 				 * from the parallel build.
1615 				 */
1616 				if (rp->stdout_file != NULL) {
1617 					if (stat(rp->stdout_file, &out_buf) < 0) {
1618 						fatal(gettext("stat of %s failed: %s"),
1619 						      rp->stdout_file,
1620 						      errmsg(errno));
1621 					}
1622 					if ((line2 != NULL) &&
1623 					    (out_buf.st_size > 0)) {
1624 						cmds_length = 0;
1625 						for (rule = line2->body.line.command_used,
1626 						     silent_flag = silent;
1627 						     rule != NULL;
1628 						     rule = rule->next) {
1629 							cmds_length += rule->command_line->hash.length + 1;
1630 							silent_flag = BOOLEAN(silent_flag || rule->silent);
1631 						}
1632 						if (out_buf.st_size != cmds_length || silent_flag ||
1633 						    output_mode == txt2_mode) {
1634 							dump_out_file(rp->stdout_file, false);
1635 						}
1636 					}
1637 					(void) unlink(rp->stdout_file);
1638 					retmem_mb(rp->stdout_file);
1639 					rp->stdout_file = NULL;
1640 				}
1641 #if defined(REDIRECT_ERR)
1642 				if (!out_err_same && (rp->stderr_file != NULL)) {
1643 					if (stat(rp->stderr_file, &out_buf) < 0) {
1644 						fatal(gettext("stat of %s failed: %s"),
1645 						      rp->stderr_file,
1646 						      errmsg(errno));
1647 					}
1648 					if ((line2 != NULL) &&
1649 					    (out_buf.st_size > 0)) {
1650 						dump_out_file(rp->stderr_file, true);
1651 					}
1652 					(void) unlink(rp->stderr_file);
1653 					retmem_mb(rp->stderr_file);
1654 					rp->stderr_file = NULL;
1655 				}
1656 #endif
1657 			}
1658 			check_state(rp->temp_file);
1659 			if (rp->temp_file != NULL) {
1660 				free_name(rp->temp_file);
1661 			}
1662 			rp->temp_file = NULL;
1663 			if (rp->state == build_failed) {
1664 				line = get_prop(rp->target->prop, line_prop);
1665 				if (line != NULL) {
1666 					line->body.line.command_used = NULL;
1667 				}
1668 				if (continue_after_error ||
1669 				    fatal_in_progress ||
1670 				    !docheck) {
1671 					warning(gettext("Command failed for target `%s'"),
1672 						rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1673 					build_failed_seen = true;
1674 				} else {
1675 					/*
1676 					 * XXX??? - DMake needs to exit(),
1677 					 * but shouldn't call fatal().
1678 					 */
1679 #ifdef PRINT_EXIT_STATUS
1680 					warning(NOCATGETS("I'm in finish_children. rp->state == build_failed."));
1681 #endif
1682 
1683 					fatal(gettext("Command failed for target `%s'"),
1684 						rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1685 				}
1686 			}
1687 			if (!docheck) {
1688 				delete_running_struct(rp);
1689 				rp = *rp_prev;
1690 				if (rp == NULL) {
1691 					break;
1692 				} else {
1693 					goto bypass_for_loop_inc_4;
1694 				}
1695 			}
1696 			update_target(get_prop(rp->target->prop, line_prop),
1697 				      rp->state);
1698 			finish_doname(rp);
1699 			delete_running_struct(rp);
1700 			rp = *rp_prev;
1701 			if (rp == NULL) {
1702 				break;
1703 			} else {
1704 				goto bypass_for_loop_inc_4;
1705 			}
1706 		} else {
1707 			rp_prev = &rp->next;
1708 		}
1709 	}
1710 }
1711 
1712 /*
1713  *	dump_out_file(filename, err)
1714  *
1715  *	Write the contents of the file to stdout, then unlink the file.
1716  *
1717  *	Parameters:
1718  *		filename	Name of temp file containing output
1719  *
1720  *	Global variables used:
1721  */
1722 static void
dump_out_file(char * filename,Boolean err)1723 dump_out_file(char *filename, Boolean err)
1724 {
1725 	int		chars_read;
1726 	char		copybuf[BUFSIZ];
1727 	int		fd;
1728 	int		out_fd = (err ? 2 : 1);
1729 
1730 	if ((fd = open(filename, O_RDONLY)) < 0) {
1731 		fatal(gettext("open failed for output file %s: %s"),
1732 		      filename,
1733 		      errmsg(errno));
1734 	}
1735 	if (!silent && output_mode != txt2_mode) {
1736 		(void) fprintf(err ? stderr : stdout,
1737 		               err ?
1738 				gettext("%s --> Job errors\n") :
1739 				gettext("%s --> Job output\n"),
1740 		               local_host);
1741 		(void) fflush(err ? stderr : stdout);
1742 	}
1743 	for (chars_read = read(fd, copybuf, BUFSIZ);
1744 	     chars_read > 0;
1745 	     chars_read = read(fd, copybuf, BUFSIZ)) {
1746 		/*
1747 		 * Read buffers from the source file until end or error.
1748 		 */
1749 		if (write(out_fd, copybuf, chars_read) < 0) {
1750 			fatal(gettext("write failed for output file %s: %s"),
1751 			      filename,
1752 			      errmsg(errno));
1753 		}
1754 	}
1755 	(void) close(fd);
1756 	(void) unlink(filename);
1757 }
1758 
1759 /*
1760  *	finish_doname(rp)
1761  *
1762  *	Completes the processing for a target which was left running.
1763  *
1764  *	Parameters:
1765  *		rp		Running list entry for target
1766  *
1767  *	Global variables used:
1768  *		debug_level	Debug flag
1769  *		recursion_level	Indentation for debug output
1770  */
1771 static void
finish_doname(Running rp)1772 finish_doname(Running rp)
1773 {
1774 	int		auto_count = rp->auto_count;
1775 	Name		*automatics = rp->automatics;
1776 	Doname		result = rp->state;
1777 	Name		target = rp->target;
1778 	Name		true_target = rp->true_target;
1779 	Property	*conditionals;
1780 
1781 	recursion_level = rp->recursion_level;
1782 	if (result == build_ok) {
1783 		if (true_target == NULL) {
1784 			(void) printf(NOCATGETS("Target = %s\n"), target->string_mb);
1785 			(void) printf(NOCATGETS(" State = %d\n"), result);
1786 			fatal(NOCATGETS("Internal error: NULL true_target in finish_doname"));
1787 		}
1788 		/* If all went OK, set a nice timestamp */
1789 		if (true_target->stat.time == file_doesnt_exist) {
1790 			true_target->stat.time = file_max_time;
1791 		}
1792 	}
1793 	target->state = result;
1794 	if (target->is_member) {
1795 		Property member;
1796 
1797 		/* Propagate the timestamp from the member file to the member */
1798 		if ((target->stat.time != file_max_time) &&
1799 		    ((member = get_prop(target->prop, member_prop)) != NULL) &&
1800 		    (exists(member->body.member.member) > file_doesnt_exist)) {
1801 			target->stat.time =
1802 /*
1803 			  exists(member->body.member.member);
1804  */
1805 			  member->body.member.member->stat.time;
1806 		}
1807 	}
1808 	/*
1809 	 * Check if we found any new auto dependencies when we
1810 	 * built the target.
1811 	 */
1812 	if ((result == build_ok) && check_auto_dependencies(target,
1813 							    auto_count,
1814 							    automatics)) {
1815 		if (debug_level > 0) {
1816 			(void) printf(gettext("%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"),
1817 				      recursion_level,
1818 				      "",
1819 				      true_target->string_mb);
1820 		}
1821 		target->rechecking_target = true;
1822 		target->state = build_running;
1823 
1824 		/* [tolik, Tue Mar 25 1997]
1825 		 * Fix for bug 4038824:
1826 		 *       command line options set by conditional macros get dropped
1827 		 * rp->conditional_cnt and rp->conditional_targets must be copied
1828 		 * to new 'rp' during add_pending(). Set_conditionals() stores
1829 		 * rp->conditional_targets to the global variable 'conditional_targets'
1830 		 * Add_pending() will use this variable to set up 'rp'.
1831 		 */
1832 		conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets);
1833 		add_pending(target,
1834 			    recursion_level,
1835 			    rp->do_get,
1836 			    rp->implicit,
1837 			    true);
1838 		reset_conditionals(rp->conditional_cnt, rp->conditional_targets, conditionals);
1839 	}
1840 }
1841 
1842 /*
1843  *	new_running_struct()
1844  *
1845  *	Constructor for Running struct. Creates a structure and initializes
1846  *      its fields.
1847  *
1848  */
new_running_struct()1849 static Running new_running_struct()
1850 {
1851 	Running		rp;
1852 
1853 	rp = ALLOC(Running);
1854 	rp->target = NULL;
1855 	rp->true_target = NULL;
1856 	rp->command = NULL;
1857 	rp->sprodep_value = NULL;
1858 	rp->sprodep_env = NULL;
1859 	rp->auto_count = 0;
1860 	rp->automatics = NULL;
1861 	rp->pid = -1;
1862 	rp->job_msg_id = -1;
1863 	rp->stdout_file = NULL;
1864 	rp->stderr_file = NULL;
1865 	rp->temp_file = NULL;
1866 	rp->next = NULL;
1867 	return rp;
1868 }
1869 
1870 /*
1871  *	add_running(target, true_target, command, recursion_level, auto_count,
1872  *					automatics, do_get, implicit)
1873  *
1874  *	Adds a record on the running list for this target, which
1875  *	was just spawned and is running.
1876  *
1877  *	Parameters:
1878  *		target		Target being built
1879  *		true_target	True target for target
1880  *		command		Running command.
1881  *		recursion_level	Debug indentation level
1882  *		auto_count	Count of automatic dependencies
1883  *		automatics	List of automatic dependencies
1884  *		do_get		Sccs get flag
1885  *		implicit	Implicit flag
1886  *
1887  *	Static variables used:
1888  *		running_tail	Tail of running list
1889  *		process_running	PID of process
1890  *
1891  *	Global variables used:
1892  *		current_line	Current line for target
1893  *		current_target	Current target being built
1894  *		stderr_file	Temporary file for stdout
1895  *		stdout_file	Temporary file for stdout
1896  *		temp_file_name	Temporary file for auto dependencies
1897  */
1898 void
add_running(Name target,Name true_target,Property command,int recursion_level,int auto_count,Name * automatics,Boolean do_get,Boolean implicit)1899 add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit)
1900 {
1901 	Running		rp;
1902 	Name		*p;
1903 
1904 	rp = new_running_struct();
1905 	rp->state = build_running;
1906 	rp->target = target;
1907 	rp->true_target = true_target;
1908 	rp->command = command;
1909 #ifdef	__needed__			/* never used in parallel mode */
1910 	Property spro_val = get_prop(sunpro_dependencies->prop, macro_prop);
1911 	if(spro_val) {
1912 		rp->sprodep_value = spro_val->body.macro.value;
1913 		spro_val->body.macro.value = NULL;
1914 		spro_val = get_prop(sunpro_dependencies->prop, env_mem_prop);
1915 		if(spro_val) {
1916 			rp->sprodep_env = spro_val->body.env_mem.value;
1917 			spro_val->body.env_mem.value = NULL;
1918 		}
1919 	}
1920 #endif
1921 	rp->recursion_level = recursion_level;
1922 	rp->do_get = do_get;
1923 	rp->implicit = implicit;
1924 	rp->auto_count = auto_count;
1925 	if (auto_count > 0) {
1926 		rp->automatics = (Name *) getmem(auto_count * sizeof (Name));
1927 		for (p = rp->automatics; auto_count > 0; auto_count--) {
1928 			*p++ = *automatics++;
1929 		}
1930 	} else {
1931 		rp->automatics = NULL;
1932 	}
1933 #ifdef DISTRIBUTED
1934 	if (dmake_mode_type == distributed_mode) {
1935 		rp->make_refd = called_make;
1936 		called_make = false;
1937 	} else
1938 #endif
1939 	{
1940 		rp->pid = process_running;
1941 		process_running = -1;
1942 		childPid = -1;
1943 	}
1944 	rp->job_msg_id = job_msg_id;
1945 	rp->stdout_file = stdout_file;
1946 	rp->stderr_file = stderr_file;
1947 	rp->temp_file = temp_file_name;
1948 	rp->redo = false;
1949 	rp->next = NULL;
1950 	store_conditionals(rp);
1951 	stdout_file = NULL;
1952 	stderr_file = NULL;
1953 	temp_file_name = NULL;
1954 	current_target = NULL;
1955 	current_line = NULL;
1956 	*running_tail = rp;
1957 	running_tail = &rp->next;
1958 }
1959 
1960 /*
1961  *	add_pending(target, recursion_level, do_get, implicit, redo)
1962  *
1963  *	Adds a record on the running list for a pending target
1964  *	(waiting for its dependents to finish running).
1965  *
1966  *	Parameters:
1967  *		target		Target being built
1968  *		recursion_level	Debug indentation level
1969  *		do_get		Sccs get flag
1970  *		implicit	Implicit flag
1971  *		redo		True if this target is being redone
1972  *
1973  *	Static variables used:
1974  *		running_tail	Tail of running list
1975  */
1976 void
add_pending(Name target,int recursion_level,Boolean do_get,Boolean implicit,Boolean redo)1977 add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo)
1978 {
1979 	Running		rp;
1980 	rp = new_running_struct();
1981 	rp->state = build_pending;
1982 	rp->target = target;
1983 	rp->recursion_level = recursion_level;
1984 	rp->do_get = do_get;
1985 	rp->implicit = implicit;
1986 	rp->redo = redo;
1987 	store_conditionals(rp);
1988 	*running_tail = rp;
1989 	running_tail = &rp->next;
1990 }
1991 
1992 /*
1993  *	add_serial(target, recursion_level, do_get, implicit)
1994  *
1995  *	Adds a record on the running list for a target which must be
1996  *	executed in serial after others have finished.
1997  *
1998  *	Parameters:
1999  *		target		Target being built
2000  *		recursion_level	Debug indentation level
2001  *		do_get		Sccs get flag
2002  *		implicit	Implicit flag
2003  *
2004  *	Static variables used:
2005  *		running_tail	Tail of running list
2006  */
2007 void
add_serial(Name target,int recursion_level,Boolean do_get,Boolean implicit)2008 add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit)
2009 {
2010 	Running		rp;
2011 
2012 	rp = new_running_struct();
2013 	rp->target = target;
2014 	rp->recursion_level = recursion_level;
2015 	rp->do_get = do_get;
2016 	rp->implicit = implicit;
2017 	rp->state = build_serial;
2018 	rp->redo = false;
2019 	store_conditionals(rp);
2020 	*running_tail = rp;
2021 	running_tail = &rp->next;
2022 }
2023 
2024 /*
2025  *	add_subtree(target, recursion_level, do_get, implicit)
2026  *
2027  *	Adds a record on the running list for a target which must be
2028  *	executed in isolation after others have finished.
2029  *
2030  *	Parameters:
2031  *		target		Target being built
2032  *		recursion_level	Debug indentation level
2033  *		do_get		Sccs get flag
2034  *		implicit	Implicit flag
2035  *
2036  *	Static variables used:
2037  *		running_tail	Tail of running list
2038  */
2039 void
add_subtree(Name target,int recursion_level,Boolean do_get,Boolean implicit)2040 add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit)
2041 {
2042 	Running		rp;
2043 
2044 	rp = new_running_struct();
2045 	rp->target = target;
2046 	rp->recursion_level = recursion_level;
2047 	rp->do_get = do_get;
2048 	rp->implicit = implicit;
2049 	rp->state = build_subtree;
2050 	rp->redo = false;
2051 	store_conditionals(rp);
2052 	*running_tail = rp;
2053 	running_tail = &rp->next;
2054 }
2055 
2056 /*
2057  *	store_conditionals(rp)
2058  *
2059  *	Creates an array of the currently active targets with conditional
2060  *	macros (found in the chain conditional_targets) and puts that
2061  *	array in the Running struct.
2062  *
2063  *	Parameters:
2064  *		rp		Running struct for storing chain
2065  *
2066  *	Global variables used:
2067  *		conditional_targets  Chain of current dynamic conditionals
2068  */
2069 static void
store_conditionals(Running rp)2070 store_conditionals(Running rp)
2071 {
2072 	int		cnt;
2073 	Chain		cond_name;
2074 
2075 	if (conditional_targets == NULL) {
2076 		rp->conditional_cnt = 0;
2077 		rp->conditional_targets = NULL;
2078 		return;
2079 	}
2080 	cnt = 0;
2081 	for (cond_name = conditional_targets;
2082 	     cond_name != NULL;
2083 	     cond_name = cond_name->next) {
2084 		cnt++;
2085 	}
2086 	rp->conditional_cnt = cnt;
2087 	rp->conditional_targets = (Name *) getmem(cnt * sizeof(Name));
2088 	for (cond_name = conditional_targets;
2089 	     cond_name != NULL;
2090 	     cond_name = cond_name->next) {
2091 		rp->conditional_targets[--cnt] = cond_name->name;
2092 	}
2093 }
2094 
2095 /*
2096  *	parallel_ok(target, line_prop_must_exists)
2097  *
2098  *	Returns true if the target can be run in parallel
2099  *
2100  *	Return value:
2101  *				True if can run in parallel
2102  *
2103  *	Parameters:
2104  *		target		Target being tested
2105  *
2106  *	Global variables used:
2107  *		all_parallel	True if all targets default to parallel
2108  *		only_parallel	True if no targets default to parallel
2109  */
2110 Boolean
parallel_ok(Name target,Boolean line_prop_must_exists)2111 parallel_ok(Name target, Boolean line_prop_must_exists)
2112 {
2113 	Boolean		assign;
2114 	Boolean		make_refd;
2115 	Property	line;
2116 	Cmd_line	rule;
2117 
2118 	assign = make_refd = false;
2119 	if (((line = get_prop(target->prop, line_prop)) == NULL) &&
2120 	    line_prop_must_exists) {
2121 		return false;
2122 	}
2123 	if (line != NULL) {
2124 		for (rule = line->body.line.command_used;
2125 		     rule != NULL;
2126 		     rule = rule->next) {
2127 			if (rule->assign) {
2128 				assign = true;
2129 			} else if (rule->make_refd) {
2130 				make_refd = true;
2131 			}
2132 		}
2133 	}
2134 	if (assign) {
2135 		return false;
2136 	} else if (target->parallel) {
2137 		return true;
2138 	} else if (target->no_parallel) {
2139 		return false;
2140 	} else if (all_parallel) {
2141 		return true;
2142 	} else if (only_parallel) {
2143 		return false;
2144 	} else if (make_refd) {
2145 		return false;
2146 	} else {
2147 		return true;
2148 	}
2149 }
2150 
2151 /*
2152  *	is_running(target)
2153  *
2154  *	Returns true if the target is running.
2155  *
2156  *	Return value:
2157  *				True if target is running
2158  *
2159  *	Parameters:
2160  *		target		Target to check
2161  *
2162  *	Global variables used:
2163  *		running_list	List of running processes
2164  */
2165 Boolean
is_running(Name target)2166 is_running(Name target)
2167 {
2168 	Running		rp;
2169 
2170 	if (target->state != build_running) {
2171 		return false;
2172 	}
2173 	for (rp = running_list;
2174 	     rp != NULL && target != rp->target;
2175 	     rp = rp->next);
2176 	if (rp == NULL) {
2177 		return false;
2178 	} else {
2179 		return (rp->state == build_running) ? true : false;
2180 	}
2181 }
2182 
2183 /*
2184  * This function replaces the makesh binary.
2185  */
2186 
2187 #ifdef SGE_SUPPORT
2188 #define DO_CHECK(f)	if (f <= 0) { \
2189 				fprintf(stderr, \
2190 					gettext("Could not write to file: %s: %s\n"), \
2191 						script_file, errmsg(errno)); \
2192 				_exit(1); \
2193 			}
2194 #endif /* SGE_SUPPORT */
2195 
2196 static pid_t
run_rule_commands(char * host,char ** commands)2197 run_rule_commands(char *host, char **commands)
2198 {
2199 	Boolean		always_exec;
2200 	Name		command;
2201 	Boolean		ignore;
2202 	int		length;
2203 	Doname		result;
2204 	Boolean		silent_flag;
2205 #ifdef SGE_SUPPORT
2206 	wchar_t		*wcmd, *tmp_wcs_buffer = NULL;
2207 	char		*cmd, *tmp_mbs_buffer = NULL;
2208 	FILE		*scrfp;
2209 	Name		shell = getvar(shell_name);
2210 #else
2211 	wchar_t		*tmp_wcs_buffer;
2212 #endif /* SGE_SUPPORT */
2213 
2214 	childPid = fork();
2215 	switch (childPid) {
2216 	case -1:	/* Error */
2217 		fatal(gettext("Could not fork child process for dmake job: %s"),
2218 		      errmsg(errno));
2219 		break;
2220 	case 0:		/* Child */
2221 		/* To control the processed targets list is not the child's business */
2222 		running_list = NULL;
2223 #if defined(REDIRECT_ERR)
2224 		if(out_err_same) {
2225 			redirect_io(stdout_file, (char*)NULL);
2226 		} else {
2227 			redirect_io(stdout_file, stderr_file);
2228 		}
2229 #else
2230 		redirect_io(stdout_file, (char*)NULL);
2231 #endif
2232 #ifdef SGE_SUPPORT
2233 		if (grid) {
2234 			int fdes = mkstemp(script_file);
2235 			if ((fdes < 0) || (scrfp = fdopen(fdes, "w")) == NULL) {
2236 				fprintf(stderr,
2237 					gettext("Could not create file: %s: %s\n"),
2238 					script_file, errmsg(errno));
2239 				_exit(1);
2240 			}
2241 			if (IS_EQUAL(shell->string_mb, "")) {
2242 				shell = shell_name;
2243 			}
2244 		}
2245 #endif /* SGE_SUPPORT */
2246 		for (commands = commands;
2247 		     (*commands != (char *)NULL);
2248 		     commands++) {
2249 			silent_flag = silent;
2250 			ignore = false;
2251 			always_exec = false;
2252 			while ((**commands == (int) at_char) ||
2253 			       (**commands == (int) hyphen_char) ||
2254 			       (**commands == (int) plus_char)) {
2255 				if (**commands == (int) at_char) {
2256 					silent_flag = true;
2257 				}
2258 				if (**commands == (int) hyphen_char) {
2259 					ignore = true;
2260 				}
2261 				if (**commands == (int) plus_char) {
2262 					always_exec = true;
2263 				}
2264 				(*commands)++;
2265 			}
2266 #ifdef SGE_SUPPORT
2267 			if (grid) {
2268 				if ((length = strlen(*commands)) >= MAXPATHLEN / 2) {
2269 					wcmd = tmp_wcs_buffer = ALLOC_WC(length * 2 + 1);
2270 					(void) mbstowcs(tmp_wcs_buffer, *commands, length * 2 + 1);
2271 				} else {
2272 					MBSTOWCS(wcs_buffer, *commands);
2273 					wcmd = wcs_buffer;
2274 					cmd = mbs_buffer;
2275 				}
2276 				wchar_t	*from = wcmd + wcslen(wcmd);
2277 				wchar_t	*to = from + (from - wcmd);
2278 				*to = (int) nul_char;
2279 				while (from > wcmd) {
2280 					*--to = *--from;
2281 					if (*from == (int) newline_char) { // newline symbols are already quoted
2282 						*--to = *--from;
2283 					} else if (wcschr(char_semantics_char, *from)) {
2284 						*--to = (int) backslash_char;
2285 					}
2286 				}
2287 				if (length >= MAXPATHLEN*MB_LEN_MAX/2) { // sizeof(mbs_buffer) / 2
2288 					cmd = tmp_mbs_buffer = getmem((length * MB_LEN_MAX * 2) + 1);
2289 					(void) wcstombs(tmp_mbs_buffer, to, (length * MB_LEN_MAX * 2) + 1);
2290 				} else {
2291 					WCSTOMBS(mbs_buffer, to);
2292 					cmd = mbs_buffer;
2293 				}
2294 				char *mbst, *mbend;
2295 				if ((length > 0) &&
2296 				    !silent_flag) {
2297 				    	for (mbst = cmd; (mbend = strstr(mbst, "\\\n")) != NULL; mbst = mbend + 2) {
2298 						*mbend = '\0';
2299 						DO_CHECK(fprintf(scrfp, NOCATGETS("/usr/bin/printf '%%s\\n' %s\\\\\n"), mbst));
2300 						*mbend = '\\';
2301 					}
2302 					DO_CHECK(fprintf(scrfp, NOCATGETS("/usr/bin/printf '%%s\\n' %s\n"), mbst));
2303 				}
2304 				if (!do_not_exec_rule ||
2305 				    !working_on_targets ||
2306 				    always_exec) {
2307 #if defined(linux)
2308 					if (0 != strcmp(shell->string_mb, (char*)NOCATGETS("/bin/sh"))) {
2309 						DO_CHECK(fprintf(scrfp, NOCATGETS("%s -c %s\n"), shell->string_mb, cmd));
2310 					} else
2311 #endif
2312 					DO_CHECK(fprintf(scrfp, NOCATGETS("%s -ce %s\n"), shell->string_mb, cmd));
2313 					DO_CHECK(fputs(NOCATGETS("__DMAKECMDEXITSTAT=$?\nif [ ${__DMAKECMDEXITSTAT} -ne 0 ]; then\n"), scrfp));
2314 					if (ignore) {
2315 						DO_CHECK(fprintf(scrfp, NOCATGETS("\techo %s ${__DMAKECMDEXITSTAT} %s\n"),
2316 							gettext("\"*** Error code"),
2317 							gettext("(ignored)\"")));
2318 					} else {
2319 						DO_CHECK(fprintf(scrfp, NOCATGETS("\techo %s ${__DMAKECMDEXITSTAT}\n"),
2320 							gettext("\"*** Error code\"")));
2321 					}
2322 					if (silent_flag) {
2323 						DO_CHECK(fprintf(scrfp, NOCATGETS("\techo %s\n"),
2324 							gettext("The following command caused the error:")));
2325 				 	   	for (mbst = cmd; (mbend = strstr(mbst, "\\\n")) != NULL; mbst = mbend + 2) {
2326 							*mbend = '\0';
2327 							DO_CHECK(fprintf(scrfp, NOCATGETS("\t/usr/bin/printf '%%s\\n' %s\\\\\n"), mbst));
2328 							*mbend = '\\';
2329 						}
2330 						DO_CHECK(fprintf(scrfp, NOCATGETS("\t/usr/bin/printf '%%s\\n' %s\n"), mbst));
2331 					}
2332 					if (!ignore) {
2333 						DO_CHECK(fputs(NOCATGETS("\texit ${__DMAKECMDEXITSTAT}\n"), scrfp));
2334 					}
2335 					DO_CHECK(fputs(NOCATGETS("fi\n"), scrfp));
2336 				}
2337 				if (tmp_wcs_buffer) {
2338 					retmem_mb(tmp_mbs_buffer);
2339 					tmp_mbs_buffer = NULL;
2340 				}
2341 				if (tmp_wcs_buffer) {
2342 					retmem(tmp_wcs_buffer);
2343 					tmp_wcs_buffer = NULL;
2344 				}
2345 				continue;
2346 			}
2347 #endif /* SGE_SUPPORT */
2348 			if ((length = strlen(*commands)) >= MAXPATHLEN) {
2349 				tmp_wcs_buffer = ALLOC_WC(length + 1);
2350 				(void) mbstowcs(tmp_wcs_buffer, *commands, length + 1);
2351 				command = GETNAME(tmp_wcs_buffer, FIND_LENGTH);
2352 				retmem(tmp_wcs_buffer);
2353 			} else {
2354 				MBSTOWCS(wcs_buffer, *commands);
2355 				command = GETNAME(wcs_buffer, FIND_LENGTH);
2356 			}
2357 			if ((command->hash.length > 0) &&
2358 			    !silent_flag) {
2359 				(void) printf("%s\n", command->string_mb);
2360 			}
2361 			result = dosys(command,
2362 			               ignore,
2363 			               false,
2364 			               false, /* bugs #4085164 & #4990057 */
2365 			               /* BOOLEAN(silent_flag && ignore), */
2366 			               always_exec,
2367 			               (Name) NULL,
2368 			               false);
2369 			if (result == build_failed) {
2370 				if (silent_flag) {
2371 					(void) printf(gettext("The following command caused the error:\n%s\n"), command->string_mb);
2372 				}
2373 				if (!ignore) {
2374 					_exit(1);
2375 				}
2376 			}
2377 		}
2378 #ifndef SGE_SUPPORT
2379 		_exit(0);
2380 #else
2381 		if (!grid) {
2382 			_exit(0);
2383 		}
2384 		DO_CHECK(fputs(NOCATGETS("exit 0\n"), scrfp));
2385 		if (fclose(scrfp) != 0) {
2386 			fprintf(stderr,
2387 				gettext("Could not close file: %s: %s\n"),
2388 				script_file, errmsg(errno));
2389 			_exit(1);
2390 		}
2391 		{
2392 
2393 #define DEFAULT_QRSH_TRIES_NUMBER	1
2394 #define DEFAULT_QRSH_TIMEOUT		0
2395 
2396 		static char	*sge_env_var = NULL;
2397 		static int	qrsh_tries_number = DEFAULT_QRSH_TRIES_NUMBER;
2398 		static int	qrsh_timeout = DEFAULT_QRSH_TIMEOUT;
2399 #define SGE_DEBUG
2400 #ifdef SGE_DEBUG
2401 		static	Boolean do_not_remove = false;
2402 #endif /* SGE_DEBUG */
2403 		if (sge_env_var == NULL) {
2404 			sge_env_var = getenv(NOCATGETS("__SPRO_DMAKE_SGE_TRIES"));
2405 			if (sge_env_var != NULL) {
2406 				qrsh_tries_number = atoi(sge_env_var);
2407 				if (qrsh_tries_number < 1 || qrsh_tries_number > 9) {
2408 					qrsh_tries_number = DEFAULT_QRSH_TRIES_NUMBER;
2409 				}
2410 			}
2411 			sge_env_var = getenv(NOCATGETS("__SPRO_DMAKE_SGE_TIMEOUT"));
2412 			if (sge_env_var != NULL) {
2413 				qrsh_timeout = atoi(sge_env_var);
2414 				if (qrsh_timeout <= 0) {
2415 					qrsh_timeout = DEFAULT_QRSH_TIMEOUT;
2416 				}
2417 			} else {
2418 				sge_env_var = "";
2419 			}
2420 #ifdef SGE_DEBUG
2421 			sge_env_var = getenv(NOCATGETS("__SPRO_DMAKE_SGE_DEBUG"));
2422 			if (sge_env_var == NULL) {
2423 				sge_env_var = "";
2424 			}
2425 			if (strstr(sge_env_var, NOCATGETS("noqrsh")) != NULL)
2426 				qrsh_tries_number = 0;
2427 			if (strstr(sge_env_var, NOCATGETS("donotremove")) != NULL)
2428 				do_not_remove = true;
2429 #endif /* SGE_DEBUG */
2430 		}
2431 		for (int i = qrsh_tries_number; ; i--)
2432 		if ((childPid = fork()) < 0) {
2433 			fatal(gettext("Could not fork child process for qrsh job: %s"),
2434 		      		errmsg(errno));
2435 			_exit(1);
2436 		} else if (childPid == 0) {
2437 			enable_interrupt((void (*) (int))SIG_DFL);
2438 			if (i > 0) {
2439 				static char qrsh_cmd[50+MAXPATHLEN] = NOCATGETS("qrsh -cwd -V -noshell -nostdin /bin/sh ");
2440 				static char *fname_ptr = NULL;
2441 				static char *argv[] = { NOCATGETS("sh"),
2442 							NOCATGETS("-fce"),
2443 							qrsh_cmd,
2444 							NULL};
2445 				if (fname_ptr == NULL) {
2446 					fname_ptr = qrsh_cmd + strlen(qrsh_cmd);
2447 				}
2448 				strcpy(fname_ptr, script_file);
2449 				(void) execve(NOCATGETS("/bin/sh"), argv, environ);
2450 			} else {
2451 				static char *argv[] = { NOCATGETS("sh"),
2452 							script_file,
2453 							NULL};
2454 				(void) execve(NOCATGETS("/bin/sh"), argv, environ);
2455 			}
2456 			fprintf(stderr,
2457 				gettext("Could not load `qrsh': %s\n"),
2458 				errmsg(errno));
2459 			_exit(1);
2460 		} else {
2461 			WAIT_T	status;
2462 			pid_t	pid;
2463 
2464 			while ((pid = wait(&status)) != childPid) {
2465 				if (pid == -1) {
2466 					fprintf(stderr,
2467 						gettext("wait() failed: %s\n"),
2468 						errmsg(errno));
2469 					_exit(1);
2470 				}
2471 			}
2472 			if (status != 0 && i > 0) {
2473 				if (i > 1) {
2474 					sleep(qrsh_timeout);
2475 				}
2476 				continue;
2477 			}
2478 #ifdef SGE_DEBUG
2479 			if (do_not_remove) {
2480 				if (status) {
2481 					fprintf(stderr,
2482 						NOCATGETS("SGE script failed: %s\n"),
2483 						script_file);
2484 				}
2485 				_exit(status ? 1 : 0);
2486 			}
2487 #endif /* SGE_DEBUG */
2488 			(void) unlink(script_file);
2489 			_exit(status ? 1 : 0);
2490 		}
2491 		}
2492 #endif /* SGE_SUPPORT */
2493 		break;
2494 	default:
2495 		break;
2496 	}
2497 	return childPid;
2498 }
2499 
2500 static void
maybe_reread_make_state(void)2501 maybe_reread_make_state(void)
2502 {
2503 	/* Copying dosys()... */
2504 	if (report_dependencies_level == 0) {
2505 		make_state->stat.time = file_no_time;
2506 		(void) exists(make_state);
2507 		if (make_state_before == make_state->stat.time) {
2508 			return;
2509 		}
2510 		makefile_type = reading_statefile;
2511 		if (read_trace_level > 1) {
2512 			trace_reader = true;
2513 		}
2514 		temp_file_number++;
2515 		(void) read_simple_file(make_state,
2516 					false,
2517 					false,
2518 					false,
2519 					false,
2520 					false,
2521 					true);
2522 		trace_reader = false;
2523 	}
2524 }
2525 
2526 #ifdef DISTRIBUTED
2527 /*
2528  * Create and send an Avo_MToolJobResultMsg.
2529  */
2530 static void
send_job_result_msg(Running rp)2531 send_job_result_msg(Running rp)
2532 {
2533 	Avo_MToolJobResultMsg	*msg;
2534 	RWCollectable		*xdr_msg;
2535 
2536 	msg = new Avo_MToolJobResultMsg();
2537 	msg->setResult(rp->job_msg_id,
2538 	               (rp->state == build_ok) ? 0 : 1,
2539 	               DONE);
2540 	append_job_result_msg(msg,
2541 				rp->stdout_file,
2542 				rp->stderr_file);
2543 
2544 	xdr_msg = (RWCollectable *)msg;
2545 	xdr(get_xdrs_ptr(), xdr_msg);
2546 	(void) fflush(get_mtool_msgs_fp());
2547 
2548 	delete msg;
2549 }
2550 
2551 /*
2552  * Append the stdout/err to Avo_MToolJobResultMsg.
2553  */
2554 static void
append_job_result_msg(Avo_MToolJobResultMsg * msg,char * outFn,char * errFn)2555 append_job_result_msg(Avo_MToolJobResultMsg *msg, char *outFn, char *errFn)
2556 {
2557 	FILE		*fp;
2558 	char		line[MAXPATHLEN];
2559 
2560 	fp = fopen(outFn, "r");
2561 	if (fp == NULL) {
2562 		/* Hmmm... what should we do here? */
2563 		return;
2564 	}
2565 	while (fgets(line, MAXPATHLEN, fp) != NULL) {
2566 		if (line[strlen(line) - 1] == '\n') {
2567 			line[strlen(line) - 1] = '\0';
2568 		}
2569 		msg->appendOutput(AVO_STRDUP(line));
2570 	}
2571 	(void) fclose(fp);
2572 }
2573 #endif
2574 
2575 static void
delete_running_struct(Running rp)2576 delete_running_struct(Running rp)
2577 {
2578 	if ((rp->conditional_cnt > 0) &&
2579 	    (rp->conditional_targets != NULL)) {
2580 		retmem_mb((char *) rp->conditional_targets);
2581 		rp->conditional_targets = NULL;
2582 	}
2583 /**/
2584 	if ((rp->auto_count > 0) &&
2585 	    (rp->automatics != NULL)) {
2586 		retmem_mb((char *) rp->automatics);
2587 		rp->automatics = NULL;
2588 	}
2589 /**/
2590 	if(rp->sprodep_value) {
2591 		free_name(rp->sprodep_value);
2592 		rp->sprodep_value = NULL;
2593 	}
2594 	if(rp->sprodep_env) {
2595 		retmem_mb(rp->sprodep_env);
2596 		rp->sprodep_env = NULL;
2597 	}
2598 	retmem_mb((char *) rp);
2599 
2600 }
2601 
2602 #endif
2603