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