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