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