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