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  * @(#)dosys.cc 1.38 06/12/12
29  */
30 
31 #pragma	ident	"@(#)dosys.cc	1.38	06/12/12"
32 
33 /*
34  * Copyright 2017-2021 J. Schilling
35  *
36  * @(#)dosys.cc	1.19 21/08/15 2017-2021 J. Schilling
37  */
38 #include <schily/mconfig.h>
39 #ifndef lint
40 static	UConst char sccsid[] =
41 	"@(#)dosys.cc	1.19 21/08/15 2017-2021 J. Schilling";
42 #endif
43 
44 /*
45  *	dosys.cc
46  *
47  *	Execute one commandline
48  */
49 
50 /*
51  * Included files
52  */
53 #include <avo/avo_alloca.h>		/* alloca() */
54 
55 #if defined(TEAMWARE_MAKE_CMN) || defined(MAKETOOL) /* tolik */
56 #	include <avo/strings.h>	/* AVO_STRDUP() */
57 #if defined(DISTRIBUTED)
58 #	include <dm/Avo_CmdOutput.h>
59 #	include <rw/xdrstrea.h>
60 #endif
61 #endif
62 
63 #include <mksh/defs.h>
64 #include <mksh/dosys.h>
65 #include <mksh/macro.h>		/* getvar() */
66 #include <mksh/misc.h>		/* getmem(), fatal_mksh(), errmsg() */
67 #if defined(SCHILY_BUILD) || defined(SCHILY_INCLUDES)
68 #include <schily/wait.h>	/* wait(), WIFEXITED(status) */
69 #else
70 #include <sys/wait.h>		/* wait(), WIFEXITED(status) */
71 #define	WAIT_T	int
72 #endif
73 #if defined(sun) && !defined(HAVE_ULIMIT)
74 #define	HAVE_ULIMIT
75 #endif
76 #ifdef	HAVE_ULIMIT
77 #include <ulimit.h>		/* ulimit() */
78 #endif
79 
80 /*
81  * Defined macros
82  */
83 #if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */
84 #define SEND_MTOOL_MSG(cmds) \
85 	if (send_mtool_msgs) { \
86 		cmds \
87 	}
88 #else
89 #define SEND_MTOOL_MSG(cmds)
90 #endif
91 
92 /*
93  * typedefs & structs
94  */
95 
96 /*
97  * Static variables
98  */
99 
100 /*
101  * File table of contents
102  */
103 static Boolean	exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path);
104 
105 /*
106  * Workaround for NFS bug. Sometimes, when running 'open' on a remote
107  * dmake server, it fails with "Stale NFS file handle" error.
108  * The second attempt seems to work.
109  */
110 int
my_open(const char * path,int oflag,mode_t mode)111 my_open(const char *path, int oflag, mode_t mode) {
112 	int res = open(path, oflag, mode);
113 #ifdef linux
114 // Workaround for NFS problem: even when all directories in 'path'
115 // exist, 'open' (file creation) fails with ENOENT.
116 	int nattempt = 0;
117 	while (res < 0 && (errno == ESTALE || errno == EAGAIN || errno == ENOENT)) {
118 		nattempt++;
119 		if(nattempt > 30) {
120 			break;
121 		}
122 		sleep(1);
123 #else
124 #ifdef	ESTALE
125 	if (res < 0 && (errno == ESTALE || errno == EAGAIN)) {
126 #else
127 	if (res < 0 && (errno == EAGAIN)) {
128 #endif
129 #endif
130 		/* Stale NFS file handle. Try again */
131 		res = open(path, oflag, mode);
132 	}
133 	return res;
134 }
135 
136 /*
137  *	void
138  *	redirect_io(char *stdout_file, char *stderr_file)
139  *
140  *	Redirects stdout and stderr for a child mksh process.
141  */
142 void
143 redirect_io(char *stdout_file, char *stderr_file)
144 {
145 	int		i;
146 
147 #ifdef	HAVE_CLOSEFROM
148 	closefrom(3);
149 #else
150 	long		descriptor_limit;
151 
152 #if !defined(UL_GDESLIM)
153 	/*
154 	 * XXX we should try to use getrlimit() in this case.
155 	 */
156         /*
157          *  HP-UX does not support the UL_GDESLIM command for ulimit().
158 	 *  NOFILE == max num open files per process (from <sys/param.h>)
159          */
160 #ifndef	NOFILE
161 #define	NOFILE	20	/* Make a plausible guess */
162 #endif
163 	descriptor_limit = NOFILE;
164 #else
165 	if ((descriptor_limit = ulimit(UL_GDESLIM)) < 0) {
166 		fatal_mksh(gettext("ulimit() failed: %s"), errmsg(errno));
167 	}
168 #endif
169 	for (i = 3; i < descriptor_limit; i++) {
170 		(void) close(i);
171 	}
172 #endif	/* HAVE_CLOSEFROM */
173 #ifndef	O_DSYNC
174 #ifdef	O_SYNC
175 #define	O_DSYNC	O_SYNC
176 #else
177 #ifdef	O_FSYNC
178 #define	O_DSYNC	O_FSYNC
179 #else
180 #define	O_DSYNC	0
181 #endif	/* O_FSYNC */
182 #endif	/* O_SYNC */
183 #endif	/* O_DSYNC */
184 #ifndef	DO_DIRECTIO
185 	/*
186 	 * Using O_DSYNC was introduced by Sun Microsystems. But since this
187 	 * would just enforce a consistent view to the filesystem background
188 	 * storage and we do not need to survive a reboot, there does not seem
189 	 * to be a need to use O_DSYNC.
190 	 *
191 	 * This is imprtant since using O_DSYNC would cause a massive slow down
192 	 * on platforms with bad filesystem performance like e.g. Linux or on
193 	 * COW filesystems like ZFS in general.
194 	 *
195 	 * So we decided to turn this off by defult.
196 	 */
197 #undef	O_DSYNC
198 #define	O_DSYNC	0
199 #endif
200 	if ((i = my_open(stdout_file,
201 	         O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC,
202 	         S_IREAD | S_IWRITE)) < 0) {
203 		fatal_mksh(gettext("Couldn't open standard out temp file `%s': %s"),
204 		      stdout_file,
205 		      errmsg(errno));
206 	} else {
207 		if (dup2(i, 1) == -1) {
208 			fatal_mksh(NOCATGETS("*** Error: dup2(3, 1) failed: %s"),
209 				errmsg(errno));
210 		}
211 		close(i);
212 	}
213 	if (stderr_file == NULL) {
214 		if (dup2(1, 2) == -1) {
215 			fatal_mksh(NOCATGETS("*** Error: dup2(1, 2) failed: %s"),
216 				errmsg(errno));
217 		}
218 	} else if ((i = my_open(stderr_file,
219 	                O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC,
220 	                S_IREAD | S_IWRITE)) < 0) {
221 		fatal_mksh(gettext("Couldn't open standard error temp file `%s': %s"),
222 		      stderr_file,
223 		      errmsg(errno));
224 	} else {
225 		if (dup2(i, 2) == -1) {
226 			fatal_mksh(NOCATGETS("*** Error: dup2(3, 2) failed: %s"),
227 				errmsg(errno));
228 		}
229 		close(i);
230 	}
231 }
232 
233 /*
234  *	dosys_mksh(command, ignore_error, call_make, silent_error, target)
235  *
236  *	Check if command string contains meta chars and dispatch to
237  *	the proper routine for executing one command line.
238  *
239  *	Return value:
240  *				Indicates if the command execution failed
241  *
242  *	Parameters:
243  *		command		The command to run
244  *		ignore_error	Should we abort when an error is seen?
245  *		call_make	Did command reference $(MAKE) ?
246  *		silent_error	Should error messages be suppressed for dmake?
247  *		target		Target we are building
248  *
249  *	Global variables used:
250  *		do_not_exec_rule Is -n on?
251  *		working_on_targets We started processing real targets
252  */
253 Doname
254 dosys_mksh(register Name command, register Boolean ignore_error, register Boolean call_make, Boolean silent_error, Boolean always_exec, Name target, Boolean redirect_out_err, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio)
255 {
256 	register int		length = command->hash.length;
257 	register wchar_t	*p;
258 	register wchar_t	*q;
259 	register wchar_t	*cmd_string;
260 	struct stat		before;
261 	Doname			result;
262 	Boolean			working_on_targets_mksh = true;
263 	Wstring wcb(command);
264 	p = wcb.get_string();
265 	cmd_string = p;
266 
267 	/* Strip spaces from head of command string */
268 	while (iswspace(*p)) {
269 		p++, length--;
270 	}
271 	if (*p == (int) nul_char) {
272 		return build_failed;
273 	}
274 	/* If we are faking it we just return */
275 	if (do_not_exec_rule &&
276 	    working_on_targets_mksh &&
277 	    !call_make &&
278 	    !always_exec) {
279 		return build_ok;
280 	}
281 
282 	/* Copy string to make it OK to write it. */
283 	q = ALLOC_WC(length + 1);
284 	(void) wcscpy(q, p);
285 	/* Write the state file iff this command uses make. */
286 /* XXX - currently does not support recursive make's, $(MAKE)'s
287 	if (call_make && command_changed) {
288 		write_state_file(0, false);
289 	}
290 	(void) stat(make_state->string_mb, &before);
291  */
292 	/*
293 	 * Run command directly if it contains no shell meta chars,
294 	 * else run it using the shell.
295 	 */
296 	/* XXX - command->meta *may* not be set correctly */
297 	if (await(ignore_error,
298 		  silent_error,
299 		  target,
300 		  cmd_string,
301                   command->meta ?
302 	            doshell(q, ignore_error, redirect_out_err, stdout_file, stderr_file, nice_prio) :
303 	            doexec(q, ignore_error, redirect_out_err, stdout_file, stderr_file, vroot_path, nice_prio),
304 	          false,
305 	          NULL,
306 	          -1)) {
307 
308 #ifdef PRINT_EXIT_STATUS
309 		warning_mksh(NOCATGETS("I'm in dosys_mksh(), and await() returned result of build_ok."));
310 #endif
311 
312 		result = build_ok;
313 	} else {
314 
315 #ifdef PRINT_EXIT_STATUS
316 		warning_mksh(NOCATGETS("I'm in dosys_mksh(), and await() returned result of build_failed."));
317 #endif
318 
319 		result = build_failed;
320 	}
321 	retmem(q);
322 
323 /* XXX - currently does not support recursive make's, $(MAKE)'s
324 	if ((report_dependencies_level == 0) &&
325 	    call_make) {
326 		make_state->stat.time = (time_t)file_no_time;
327 		(void)exists(make_state);
328 		if (before.st_mtime == make_state->stat.time) {
329 			return result;
330 		}
331 		makefile_type = reading_statefile;
332 		if (read_trace_level > 1) {
333 			trace_reader = true;
334 		}
335 		(void) read_simple_file(make_state,
336 					false,
337 					false,
338 					false,
339 					false,
340 					false,
341 					true);
342 		trace_reader = false;
343 	}
344  */
345 	return result;
346 }
347 
348 /*
349  *	doshell(command, ignore_error)
350  *
351  *	Used to run command lines that include shell meta-characters.
352  *	The make macro SHELL is supposed to contain a path to the shell.
353  *
354  *	Return value:
355  *				The pid of the process we started
356  *
357  *	Parameters:
358  *		command		The command to run
359  *		ignore_error	Should we abort on error?
360  *
361  *	Global variables used:
362  *		filter_stderr	If -X is on we redirect stderr
363  *		shell_name	The Name "SHELL", used to get the path to shell
364  */
365 int
366 doshell(wchar_t *command, register Boolean ignore_error, Boolean redirect_out_err, char *stdout_file, char *stderr_file, int nice_prio)
367 {
368 	char			*argv[6];
369 	int			argv_index = 0;
370 	int			cmd_argv_index;
371 	int			length;
372 	char			nice_prio_buf[MAXPATHLEN];
373 	register Name		shell = getvar(shell_name);
374 	register char		*shellname;
375 	char			*tmp_mbs_buffer;
376 
377 
378 	if (IS_EQUAL(shell->string_mb, "")) {
379 		shell = shell_name;
380 	}
381 	if ((shellname = strrchr(shell->string_mb, (int) slash_char)) == NULL) {
382 		shellname = shell->string_mb;
383 	} else {
384 		shellname++;
385 	}
386 
387 	/*
388 	 * Only prepend the /usr/bin/nice command to the original command
389 	 * if the nice priority, nice_prio, is NOT zero (0).
390 	 * Nice priorities can be a positive or a negative number.
391 	 */
392 	if (nice_prio != 0) {
393 		argv[argv_index++] = (char *)NOCATGETS("nice");
394 		(void) sprintf(nice_prio_buf, NOCATGETS("-%d"), nice_prio);
395 		argv[argv_index++] = strdup(nice_prio_buf);
396 	}
397 	argv[argv_index++] = shellname;
398 #if defined(linux)
399 	if(0 == strcmp(shell->string_mb, (char*)NOCATGETS("/bin/sh"))) {
400 		argv[argv_index++] = (char*)(ignore_error ? NOCATGETS("-c") : NOCATGETS("-ce"));
401 	} else {
402 		argv[argv_index++] = (char*)NOCATGETS("-c");
403 	}
404 #else
405 	argv[argv_index++] = (char*)(ignore_error ? NOCATGETS("-c") : NOCATGETS("-ce"));
406 #endif
407 	if ((length = wcslen(command)) >= MAXPATHLEN) {
408 		tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1);
409                 (void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1);
410 		cmd_argv_index = argv_index;
411                 argv[argv_index++] = strdup(tmp_mbs_buffer);
412                 retmem_mb(tmp_mbs_buffer);
413 	} else {
414 		WCSTOMBS(mbs_buffer, command);
415 		cmd_argv_index = argv_index;
416 #if defined(linux)
417 		int mbl = strlen(mbs_buffer);
418 		if(mbl > 2) {
419 			if(mbs_buffer[mbl-1] == '\n' && mbs_buffer[mbl-2] == '\\') {
420 				mbs_buffer[mbl] = '\n';
421 				mbs_buffer[mbl+1] = 0;
422 			}
423 		}
424 #endif
425 		argv[argv_index++] = strdup(mbs_buffer);
426 	}
427 	argv[argv_index] = NULL;
428 	(void) fflush(stdout);
429 	if ((childPid = fork()) == 0) {
430 		enable_interrupt((void (*) (int)) SIG_DFL);
431 		if (redirect_out_err) {
432 			redirect_io(stdout_file, stderr_file);
433 		}
434 #if 0
435 		if (filter_stderr) {
436 			redirect_stderr();
437 		}
438 #endif
439 		if (nice_prio != 0) {
440 			(void) execve(NOCATGETS("/usr/bin/nice"), argv, environ);
441 			fatal_mksh(gettext("Could not load `/usr/bin/nice': %s"),
442 			      errmsg(errno));
443 		} else {
444 			(void) execve(shell->string_mb, argv, environ);
445 			fatal_mksh(gettext("Could not load Shell from `%s': %s"),
446 			      shell->string_mb,
447 			      errmsg(errno));
448 		}
449 	}
450 	if (childPid  == -1) {
451 		fatal_mksh(gettext("fork failed: %s"),
452 		      errmsg(errno));
453 	}
454 	retmem_mb(argv[cmd_argv_index]);
455 	return childPid;
456 }
457 
458 /*
459  *	exec_vp(name, argv, envp, ignore_error)
460  *
461  *	Like execve, but does path search.
462  *	This starts command when make invokes it directly (without a shell).
463  *
464  *	Return value:
465  *				Returns false if the exec failed
466  *
467  *	Parameters:
468  *		name		The name of the command to run
469  *		argv		Arguments for the command
470  *		envp		The environment for it
471  *		ignore_error	Should we abort on error?
472  *
473  *	Global variables used:
474  *		shell_name	The Name "SHELL", used to get the path to shell
475  *		vroot_path	The path used by the vroot package
476  */
477 static Boolean
478 exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path)
479 {
480 	register Name		shell = getvar(shell_name);
481 	register char		*shellname;
482 	char			*shargv[4];
483 	Name			tmp_shell;
484 
485 	if (IS_EQUAL(shell->string_mb, "")) {
486 		shell = shell_name;
487 	}
488 
489 	for (int i = 0; i < 5; i++) {
490 		(void) execve_vroot(name,
491 				    argv + 1,
492 				    envp,
493 				    vroot_path,
494 				    VROOT_DEFAULT);
495 		switch (errno) {
496 		case ENOEXEC:
497 		case ENOENT:
498 			/* That failed. Let the shell handle it */
499 			shellname = strrchr(shell->string_mb, (int) slash_char);
500 			if (shellname == NULL) {
501 				shellname = shell->string_mb;
502 			} else {
503 				shellname++;
504 			}
505 			shargv[0] = shellname;
506 			shargv[1] = (char*)(ignore_error ? NOCATGETS("-c") : NOCATGETS("-ce"));
507 			shargv[2] = argv[0];
508 			shargv[3] = NULL;
509 			tmp_shell = getvar(shell_name);
510 			if (IS_EQUAL(tmp_shell->string_mb, "")) {
511 				tmp_shell = shell_name;
512 			}
513 			(void) execve_vroot(tmp_shell->string_mb,
514 					    shargv,
515 					    envp,
516 					    vroot_path,
517 					    VROOT_DEFAULT);
518 			return failed;
519 #ifdef	ETXTBSY
520 		case ETXTBSY:
521 			/*
522 			 * The program is busy (debugged?).
523 			 * Wait and then try again.
524 			 */
525 			(void) sleep((unsigned) i);
526 #endif
527 		case EAGAIN:
528 			break;
529 		default:
530 			return failed;
531 		}
532 	}
533 	return failed;
534 }
535 
536 /*
537  *	doexec(command, ignore_error)
538  *
539  *	Will scan an argument string and split it into words
540  *	thus building an argument list that can be passed to exec_ve()
541  *
542  *	Return value:
543  *				The pid of the process started here
544  *
545  *	Parameters:
546  *		command		The command to run
547  *		ignore_error	Should we abort on error?
548  *
549  *	Global variables used:
550  *		filter_stderr	If -X is on we redirect stderr
551  */
552 int
553 doexec(register wchar_t *command, register Boolean ignore_error, Boolean redirect_out_err, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio)
554 {
555 	int			arg_count = 5;
556 	char			**argv;
557 	int			length;
558 	char			nice_prio_buf[MAXPATHLEN];
559 	register char		**p;
560 	wchar_t			*q;
561 	register wchar_t	*t;
562 	char			*tmp_mbs_buffer;
563 
564 	/*
565 	 * Only prepend the /usr/bin/nice command to the original command
566 	 * if the nice priority, nice_prio, is NOT zero (0).
567 	 * Nice priorities can be a positive or a negative number.
568 	 */
569 	if (nice_prio != 0) {
570 		arg_count += 2;
571 	}
572 	for (t = command; *t != (int) nul_char; t++) {
573 		if (iswspace(*t)) {
574 			arg_count++;
575 		}
576 	}
577 	argv = (char **)alloca(arg_count * (sizeof(char *)));
578 	/*
579 	 * Reserve argv[0] for sh in case of exec_vp failure.
580 	 * Don't worry about prepending /usr/bin/nice command to argv[0].
581 	 * In fact, doing it may cause the sh command to fail!
582 	 */
583 	p = &argv[1];
584 	if ((length = wcslen(command)) >= MAXPATHLEN) {
585 		tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1);
586 		(void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1);
587 		argv[0] = strdup(tmp_mbs_buffer);
588 		retmem_mb(tmp_mbs_buffer);
589         } else {
590 		WCSTOMBS(mbs_buffer, command);
591 		argv[0] = strdup(mbs_buffer);
592 	}
593 
594 	if (nice_prio != 0) {
595 		*p++ = strdup(NOCATGETS("/usr/bin/nice"));
596 		(void) sprintf(nice_prio_buf, NOCATGETS("-%d"), nice_prio);
597 		*p++ = strdup(nice_prio_buf);
598 	}
599 	/* Build list of argument words. */
600 	for (t = command; *t;) {
601 		if (p >= &argv[arg_count]) {
602 			/* This should never happen, right? */
603 			WCSTOMBS(mbs_buffer, command);
604 			fatal_mksh(gettext("Command `%s' has more than %d arguments"),
605 			      mbs_buffer,
606 			      arg_count);
607 		}
608 		q = t;
609 		while (!iswspace(*t) && (*t != (int) nul_char)) {
610 			t++;
611 		}
612 		if (*t) {
613 			for (*t++ = (int) nul_char; iswspace(*t); t++);
614 		}
615 		if ((length = wcslen(q)) >= MAXPATHLEN) {
616 			tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1);
617 			(void) wcstombs(tmp_mbs_buffer, q, (length * MB_LEN_MAX) + 1);
618 			*p++ = strdup(tmp_mbs_buffer);
619 			retmem_mb(tmp_mbs_buffer);
620 		} else {
621 			WCSTOMBS(mbs_buffer, q);
622 			*p++ = strdup(mbs_buffer);
623 		}
624 	}
625 	*p = NULL;
626 
627 	/* Then exec the command with that argument list. */
628 	(void) fflush(stdout);
629 	if ((childPid = fork()) == 0) {
630 		enable_interrupt((void (*) (int)) SIG_DFL);
631 		if (redirect_out_err) {
632 			redirect_io(stdout_file, stderr_file);
633 		}
634 #if 0
635 		if (filter_stderr) {
636 			redirect_stderr();
637 		}
638 #endif
639 		(void) exec_vp(argv[1], argv, environ, ignore_error, vroot_path);
640 		fatal_mksh(gettext("Cannot load command `%s': %s"), argv[1], errmsg(errno));
641 	}
642 	if (childPid  == -1) {
643 		fatal_mksh(gettext("fork failed: %s"),
644 		      errmsg(errno));
645 	}
646 	for (int i = 0; argv[i] != NULL; i++) {
647 		retmem_mb(argv[i]);
648 	}
649 	return childPid;
650 }
651 
652 /*
653  *	await(ignore_error, silent_error, target, command, running_pid)
654  *
655  *	Wait for one child process and analyzes
656  *	the returned status when the child process terminates.
657  *
658  *	Return value:
659  *				Returns true if commands ran OK
660  *
661  *	Parameters:
662  *		ignore_error	Should we abort on error?
663  *		silent_error	Should error messages be suppressed for dmake?
664  *		target		The target we are building, for error msgs
665  *		command		The command we ran, for error msgs
666  *		running_pid	The pid of the process we are waiting for
667  *
668  *	Static variables used:
669  *		filter_file	The fd for the filter file
670  *		filter_file_name The name of the filter file
671  *
672  *	Global variables used:
673  *		filter_stderr	Set if -X is on
674  */
675 #if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */
676 Boolean
677 await(register Boolean ignore_error, register Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, Boolean send_mtool_msgs, XDR *xdrs_p, int job_msg_id)
678 #else
679 Boolean
680 await(register Boolean ignore_error, register Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, Boolean send_mtool_msgs, void *xdrs_p, int job_msg_id)
681 #endif
682 {
683 	WAIT_T			status;
684 	char			*buffer;
685 	int			core_dumped;
686 	int			exit_status;
687 #if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */
688 	Avo_CmdOutput		*make_output_msg;
689 #endif
690 	FILE			*outfp;
691 	register pid_t		pid;
692 	struct stat		stat_buff;
693 	int			termination_signal;
694 	char			tmp_buf[MAXPATHLEN];
695 #if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */
696 	RWCollectable		*xdr_msg;
697 #endif
698 
699 	while ((pid = wait(&status)) != running_pid) {
700 		if (pid == -1) {
701 			fatal_mksh(gettext("wait() failed: %s"), errmsg(errno));
702 		}
703 	}
704 	(void) fflush(stdout);
705 	(void) fflush(stderr);
706 
707         if (*(int *)&status == 0) {
708 
709 #ifdef PRINT_EXIT_STATUS
710 		warning_mksh(NOCATGETS("I'm in await(), and status is 0."));
711 #endif
712 
713                 return succeeded;
714 	}
715 
716 #ifdef PRINT_EXIT_STATUS
717 	warning_mksh(NOCATGETS("I'm in await(), and status is *NOT* 0."));
718 #endif
719 
720         exit_status = WEXITSTATUS(status);
721 
722 #ifdef PRINT_EXIT_STATUS
723 	warning_mksh(NOCATGETS("I'm in await(), and exit_status is %d."), exit_status);
724 #endif
725 
726         termination_signal = WTERMSIG(status);
727         core_dumped = WCOREDUMP(status);
728 
729 	/*
730 	 * If the child returned an error, we now try to print a
731 	 * nice message about it.
732 	 */
733 	SEND_MTOOL_MSG(
734 		make_output_msg = new Avo_CmdOutput();
735 		(void) sprintf(tmp_buf, "%d", job_msg_id);
736 		make_output_msg->appendOutput(AVO_STRDUP(tmp_buf));
737 	);
738 
739 	tmp_buf[0] = (int) nul_char;
740 	if (!silent_error) {
741 		if (exit_status != 0) {
742 			(void) fprintf(stdout,
743 				       gettext("*** Error code %d"),
744 				       exit_status);
745 			SEND_MTOOL_MSG(
746 				(void) sprintf(&tmp_buf[strlen(tmp_buf)],
747 					       gettext("*** Error code %d"),
748 					       exit_status);
749 			);
750 		} else {
751 #if ! defined(SUN5_0) && ! defined(HP_UX) && ! defined(linux)
752 			if (termination_signal > NSIG) {
753 #endif
754 				(void) fprintf(stdout,
755 					       gettext("*** Signal %d"),
756 					       termination_signal);
757 				SEND_MTOOL_MSG(
758 					(void) sprintf(&tmp_buf[strlen(tmp_buf)],
759 						       gettext("*** Signal %d"),
760 						       termination_signal);
761 				);
762 #if ! defined(SUN5_0) && ! defined(HP_UX) && ! defined(linux)
763 			} else {
764 				(void) fprintf(stdout,
765 					       "*** %s",
766 					       sys_siglist[termination_signal]);
767 				SEND_MTOOL_MSG(
768 					(void) sprintf(&tmp_buf[strlen(tmp_buf)],
769 						       "*** %s",
770 						       sys_siglist[termination_signal]);
771 				);
772 			}
773 #endif
774 			if (core_dumped) {
775 				(void) fprintf(stdout,
776 					       gettext(" - core dumped"));
777 				SEND_MTOOL_MSG(
778 					(void) sprintf(&tmp_buf[strlen(tmp_buf)],
779 						       gettext(" - core dumped"));
780 				);
781 			}
782 		}
783 		if (ignore_error) {
784 			(void) fprintf(stdout,
785 				       gettext(" (ignored)"));
786 			SEND_MTOOL_MSG(
787 				(void) sprintf(&tmp_buf[strlen(tmp_buf)],
788 					       gettext(" (ignored)"));
789 			);
790 		}
791 		(void) fprintf(stdout, "\n");
792 		(void) fflush(stdout);
793 		SEND_MTOOL_MSG(
794 			make_output_msg->appendOutput(AVO_STRDUP(tmp_buf));
795 		);
796 	}
797 	SEND_MTOOL_MSG(
798 		xdr_msg = (RWCollectable*) make_output_msg;
799 		xdr(xdrs_p, xdr_msg);
800 		delete make_output_msg;
801 	);
802 
803 #ifdef PRINT_EXIT_STATUS
804 	warning_mksh(NOCATGETS("I'm in await(), returning failed."));
805 #endif
806 
807 	return failed;
808 }
809 
810 /*
811  *	sh_command2string(command, destination)
812  *
813  *	Run one sh command and capture the output from it.
814  *
815  *	Return value:
816  *
817  *	Parameters:
818  *		command		The command to run
819  *		destination	Where to deposit the output from the command
820  *
821  *	Static variables used:
822  *
823  *	Global variables used:
824  */
825 void
826 sh_command2string(register String command, register String destination)
827 {
828 	register FILE		*fd;
829 	register int		chr;
830 	int			status;
831 	Boolean			command_generated_output = false;
832 
833 #ifdef	__needed__
834 	command->text.p = (int) nul_char;
835 	/*
836 	 * The correct (if ever) statement would be most likely:
837 	 *	*command->text.p = nul_char;
838 	 * but currently, it seems that there is no case with a
839 	 * forgotten nul char at the end of the string.
840 	 */
841 #endif
842 	WCSTOMBS(mbs_buffer, command->buffer.start);
843 	if ((fd = popen(mbs_buffer, "r")) == NULL) {
844 		WCSTOMBS(mbs_buffer, command->buffer.start);
845 		fatal_mksh(gettext("Could not run command `%s' for :sh transformation"),
846 		      mbs_buffer);
847 	}
848 	while ((chr = getc(fd)) != EOF) {
849 		if (chr == (int) newline_char) {
850 			chr = (int) space_char;
851 		}
852 		command_generated_output = true;
853 		append_char(chr, destination);
854 	}
855 
856 	/*
857 	 * We don't want to keep the last LINE_FEED since usually
858 	 * the output of the 'sh:' command is used to evaluate
859 	 * some MACRO. ( /bin/sh and other shell add a line feed
860 	 * to the output so that the prompt appear in the right place.
861 	 * We don't need that
862 	 */
863 	if (command_generated_output){
864 		if ( *(destination->text.p-1) == (int) space_char) {
865 			* (-- destination->text.p) = '\0';
866 		}
867 	} else {
868 		/*
869 		 * If the command didn't generate any output,
870 		 * set the buffer to a null string.
871 		 */
872 		*(destination->text.p) = '\0';
873 	}
874 
875 	status = pclose(fd);
876 	if (status != 0) {
877 		WCSTOMBS(mbs_buffer, command->buffer.start);
878 		fatal_mksh(gettext("The command `%s' returned status `%d'"),
879 		      mbs_buffer,
880 		      WEXITSTATUS(status));
881 	}
882 }
883 
884 
885