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 #ifdef TEAMWARE_MAKE_CMN
  27 
  28 /*
  29  *      parallel.cc
  30  *
  31  *      Deal with the parallel processing
  32  */
  33 
  34 /*
  35  * Included files
  36  */
  37 #ifdef DISTRIBUTED
  38 #include <avo/strings.h>  /* AVO_STRDUP() */
  39 #include <dm/Avo_DoJobMsg.h>
  40 #include <dm/Avo_MToolJobResultMsg.h>
  41 #endif
  42 #include <errno.h>                /* errno */
  43 #include <fcntl.h>
  44 #include <avo/util.h>             /* avo_get_user(), avo_hostname() */
  45 #include <mk/defs.h>
  46 #include <mksh/dosys.h>           /* redirect_io() */
  47 #include <mksh/macro.h>           /* expand_value() */
  48 #include <mksh/misc.h>            /* getmem() */
  49 #include <sys/signal.h>
  50 #include <sys/stat.h>
  51 #include <sys/types.h>
  52 #include <sys/utsname.h>
  53 #include <sys/wait.h>
  54 #include <unistd.h>
  55 
  56 
  57 
  58 /*
  59  * Defined macros
  60  */
  61 #define MAXRULES                100
  62 
  63 /*
  64  * This const should be in avo_dms/include/AvoDmakeCommand.h
  65  */
  66 const int local_host_mask = 0x20;
  67 
  68 
  69 /*
  70  * typedefs & structs
  71  */
  72 
  73 
  74 /*
  75  * Static variables
  76  */
  77 #ifdef TEAMWARE_MAKE_CMN
  78 static  Boolean         just_did_subtree = false;
  79 static  char            local_host[MAXNAMELEN] = "";
  80 static  char            user_name[MAXNAMELEN] = "";
  81 #endif
  82 static  int             pmake_max_jobs = 0;
  83 static  pid_t           process_running = -1;
  84 static  Running         *running_tail = &running_list;
  85 static  Name            subtree_conflict;
  86 static  Name            subtree_conflict2;
  87 
  88 
  89 /*
  90  * File table of contents
  91  */
  92 #ifdef DISTRIBUTED
  93 static  void            append_dmake_cmd(Avo_DoJobMsg *dmake_job_msg, char *orig_cmd_line, int cmd_options);
  94 static  void            append_job_result_msg(Avo_MToolJobResultMsg *msg, char *outFn, char *errFn);
  95 static  void            send_job_result_msg(Running rp);
  96 #endif
  97 static  void            delete_running_struct(Running rp);
  98 static  Boolean         dependency_conflict(Name target);
  99 static  Doname          distribute_process(char **commands, Property line);
 100 static  void            doname_subtree(Name target, Boolean do_get, Boolean implicit);
 101 static  void            dump_out_file(char *filename, Boolean err);
 102 static  void            finish_doname(Running rp);
 103 static  void            maybe_reread_make_state(void);
 104 static  void            process_next(void);
 105 static  void            reset_conditionals(int cnt, Name *targets, Property *locals);
 106 static  pid_t           run_rule_commands(char *host, char **commands);
 107 static  Property        *set_conditionals(int cnt, Name *targets);
 108 static  void            store_conditionals(Running rp);
 109 
 110 
 111 /*
 112  *      execute_parallel(line, waitflg)
 113  *
 114  *      DMake 2.x:
 115  *      parallel mode: spawns a parallel process to execute the command group.
 116  *      distributed mode: sends the command group down the pipe to rxm.
 117  *
 118  *      Return value:
 119  *                              The result of the execution
 120  *
 121  *      Parameters:
 122  *              line            The command group to execute
 123  */
 124 Doname
 125 execute_parallel(Property line, Boolean waitflg, Boolean local)
 126 {
 127         int                     argcnt;
 128         int                     cmd_options = 0;
 129         char                    *commands[MAXRULES + 5];
 130         char                    *cp;
 131 #ifdef DISTRIBUTED
 132         Avo_DoJobMsg            *dmake_job_msg = NULL;
 133 #endif
 134         Name                    dmake_name;
 135         Name                    dmake_value;
 136         int                     ignore;
 137         Name                    make_machines_name;
 138         char                    **p;
 139         Property                prop;
 140         Doname                  result = build_ok;
 141         Cmd_line                rule;
 142         Boolean                 silent_flag;
 143         Name                    target = line->body.line.target;
 144         Boolean                 wrote_state_file = false;
 145 
 146         if ((pmake_max_jobs == 0) &&
 147             (dmake_mode_type == parallel_mode)) {
 148                 if (user_name[0] == '\0') {
 149                         avo_get_user(user_name, NULL);
 150                 }
 151                 if (local_host[0] == '\0') {
 152                         strcpy(local_host, avo_hostname());
 153                 }
 154                 MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MAX_JOBS"));
 155                 dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
 156                 if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
 157                     ((dmake_value = prop->body.macro.value) != NULL)) {
 158                         pmake_max_jobs = atoi(dmake_value->string_mb);
 159                         if (pmake_max_jobs <= 0) {
 160                                 warning(catgets(catd, 1, 308, "DMAKE_MAX_JOBS cannot be less than or equal to zero."));
 161                                 warning(catgets(catd, 1, 309, "setting DMAKE_MAX_JOBS to %d."), PMAKE_DEF_MAX_JOBS);
 162                                 pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
 163                         }
 164                 } else {
 165                         /*
 166                          * For backwards compatibility w/ PMake 1.x, when
 167                          * DMake 2.x is being run in parallel mode, DMake
 168                          * should parse the PMake startup file
 169                          * $(HOME)/.make.machines to get the pmake_max_jobs.
 170                          */
 171                         MBSTOWCS(wcs_buffer, NOCATGETS("PMAKE_MACHINESFILE"));
 172                         dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
 173                         if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
 174                             ((dmake_value = prop->body.macro.value) != NULL)) {
 175                                 make_machines_name = dmake_value;
 176                         } else {
 177                                 make_machines_name = NULL;
 178                         }
 179                         if ((pmake_max_jobs = read_make_machines(make_machines_name)) <= 0) {
 180                                 pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
 181                         }
 182                 }
 183 #ifdef DISTRIBUTED
 184                 if (send_mtool_msgs) {
 185                         send_rsrc_info_msg(pmake_max_jobs, local_host, user_name);
 186                 }
 187 #endif
 188         }
 189 
 190         if ((dmake_mode_type == serial_mode) ||
 191             ((dmake_mode_type == parallel_mode) && (waitflg))) {
 192                 return (execute_serial(line));
 193         }
 194 
 195 #ifdef DISTRIBUTED
 196         if (dmake_mode_type == distributed_mode) {
 197                 if(local) {
 198 //                      return (execute_serial(line));
 199                         waitflg = true;
 200                 }
 201                 dmake_job_msg = new Avo_DoJobMsg();
 202                 dmake_job_msg->setJobId(++job_msg_id);
 203                 dmake_job_msg->setTarget(target->string_mb);
 204                 dmake_job_msg->setImmediateOutput(0);
 205                 called_make = false;
 206         } else
 207 #endif
 208         {
 209                 p = commands;
 210         }
 211 
 212         argcnt = 0;
 213         for (rule = line->body.line.command_used;
 214              rule != NULL;
 215              rule = rule->next) {
 216                 if (posix && (touch || quest) && !rule->always_exec) {
 217                         continue;
 218                 }
 219                 if (vpath_defined) {
 220                         rule->command_line =
 221                           vpath_translation(rule->command_line);
 222                 }
 223                 if (dmake_mode_type == distributed_mode) {
 224                         cmd_options = 0;
 225                         if(local) {
 226                                 cmd_options |= local_host_mask;
 227                         }
 228                 } else {
 229                         silent_flag = false;
 230                         ignore = 0;
 231                 }
 232                 if (rule->command_line->hash.length > 0) {
 233                         if (++argcnt == MAXRULES) {
 234                                 if (dmake_mode_type == distributed_mode) {
 235                                         /* XXX - tell rxm to execute on local host. */
 236                                         /* I WAS HERE!!! */
 237                                 } else {
 238                                         /* Too many rules, run serially instead. */
 239                                         return build_serial;
 240                                 }
 241                         }
 242 #ifdef DISTRIBUTED
 243                         if (dmake_mode_type == distributed_mode) {
 244                                 /*
 245                                  * XXX - set assign_mask to tell rxm
 246                                  * to do the following.
 247                                  */
 248 /* From execute_serial():
 249                                 if (rule->assign) {
 250                                         result = build_ok;
 251                                         do_assign(rule->command_line, target);
 252  */
 253                                 if (0) {
 254                                 } else if (report_dependencies_level == 0) {
 255                                         if (rule->ignore_error) {
 256                                                 cmd_options |= ignore_mask;
 257                                         }
 258                                         if (rule->silent) {
 259                                                 cmd_options |= silent_mask;
 260                                         }
 261                                         if (rule->command_line->meta) {
 262                                                 cmd_options |= meta_mask;
 263                                         }
 264                                         if (rule->make_refd) {
 265                                                 cmd_options |= make_refd_mask;
 266                                         }
 267                                         if (do_not_exec_rule) {
 268                                                 cmd_options |= do_not_exec_mask;
 269                                         }
 270                                         append_dmake_cmd(dmake_job_msg,
 271                                                          rule->command_line->string_mb,
 272                                                          cmd_options);
 273                                         /* Copying dosys()... */
 274                                         if (rule->make_refd) {
 275                                                 if (waitflg) {
 276                                                         dmake_job_msg->setImmediateOutput(1);
 277                                                 }
 278                                                 called_make = true;
 279                                                 if (command_changed &&
 280                                                     !wrote_state_file) {
 281                                                         write_state_file(0, false);
 282                                                         wrote_state_file = true;
 283                                                 }
 284                                         }
 285                                 }
 286                         } else
 287 #endif
 288                         {
 289                                 if (rule->silent && !silent) {
 290                                         silent_flag = true;
 291                                 }
 292                                 if (rule->ignore_error) {
 293                                         ignore++;
 294                                 }
 295                                 /* XXX - need to add support for + prefix */
 296                                 if (silent_flag || ignore) {
 297                                         *p = getmem((silent_flag ? 1 : 0) +
 298                                                     ignore +
 299                                                     (strlen(rule->
 300                                                            command_line->
 301                                                            string_mb)) +
 302                                                     1);
 303                                         cp = *p++;
 304                                         if (silent_flag) {
 305                                                 *cp++ = (int) at_char;
 306                                         }
 307                                         if (ignore) {
 308                                                 *cp++ = (int) hyphen_char;
 309                                         }
 310                                         (void) strcpy(cp, rule->command_line->string_mb);
 311                                 } else {
 312                                         *p++ = rule->command_line->string_mb;
 313                                 }
 314                         }
 315                 }
 316         }
 317         if ((argcnt == 0) ||
 318             (report_dependencies_level > 0)) {
 319 #ifdef DISTRIBUTED
 320                 if (dmake_job_msg) {
 321                         delete dmake_job_msg;
 322                 }
 323 #endif
 324                 return build_ok;
 325         }
 326 #ifdef DISTRIBUTED
 327         if (dmake_mode_type == distributed_mode) {
 328                 // Send  a DoJob message to the rxm process.
 329                 distribute_rxm(dmake_job_msg);
 330 
 331                 // Wait for an acknowledgement.
 332                 Avo_AcknowledgeMsg *ackMsg = getAcknowledgeMsg();
 333                 if (ackMsg) {
 334                         delete ackMsg;
 335                 }
 336 
 337                 if (waitflg) {
 338                         // Wait for, and process a job result.
 339                         result = await_dist(waitflg);
 340                         if (called_make) {
 341                                 maybe_reread_make_state();
 342                         }
 343                         check_state(temp_file_name);
 344                         if (result == build_failed) {
 345                                 if (!continue_after_error) {
 346 
 347 #ifdef PRINT_EXIT_STATUS
 348                                         warning(NOCATGETS("I'm in execute_parallel. await_dist() returned build_failed"));
 349 #endif
 350 
 351                                         fatal(catgets(catd, 1, 252, "Command failed for target `%s'"),
 352                                               target->string_mb);
 353                                 }
 354                                 /*
 355                                  * Make sure a failing command is not
 356                                  * saved in .make.state.
 357                                  */
 358                                 line->body.line.command_used = NULL;
 359                         }
 360                         if (temp_file_name != NULL) {
 361                                 free_name(temp_file_name);
 362                         }
 363                         temp_file_name = NULL;
 364                         Property spro = get_prop(sunpro_dependencies->prop, macro_prop);
 365                         if(spro != NULL) {
 366                                 Name val = spro->body.macro.value;
 367                                 if(val != NULL) {
 368                                         free_name(val);
 369                                         spro->body.macro.value = NULL;
 370                                 }
 371                         }
 372                         spro = get_prop(sunpro_dependencies->prop, env_mem_prop);
 373                         if(spro) {
 374                                 char *val = spro->body.env_mem.value;
 375                                 if(val != NULL) {
 376                                         retmem_mb(val);
 377                                         spro->body.env_mem.value = NULL;
 378                                 }
 379                         }
 380                         return result;
 381                 } else {
 382                         parallel_process_cnt++;
 383                         return build_running;
 384                 }
 385         } else
 386 #endif
 387         {
 388                 *p = NULL;
 389 
 390                 Doname res = distribute_process(commands, line);
 391                 if (res == build_running) {
 392                         parallel_process_cnt++;
 393                 }
 394 
 395                 /*
 396                  * Return only those memory that were specially allocated
 397                  * for part of commands.
 398                  */
 399                 for (int i = 0; commands[i] != NULL; i++) {
 400                         if ((commands[i][0] == (int) at_char) ||
 401                             (commands[i][0] == (int) hyphen_char)) {
 402                                 retmem_mb(commands[i]);
 403                         }
 404                 }
 405                 return res;
 406         }
 407 }
 408 
 409 #ifdef DISTRIBUTED
 410 /*
 411  *      append_dmake_cmd()
 412  *
 413  *      Replaces all escaped newline's (\<cr>)
 414  *        in the original command line with space's,
 415  *        then append the new command line to the DoJobMsg object.
 416  */
 417 static void
 418 append_dmake_cmd(Avo_DoJobMsg *dmake_job_msg,
 419                  char *orig_cmd_line,
 420                  int cmd_options)
 421 {
 422 /*
 423         Avo_DmakeCommand        *tmp_dmake_command;
 424 
 425         tmp_dmake_command = new Avo_DmakeCommand(orig_cmd_line, cmd_options);
 426         dmake_job_msg->appendCmd(tmp_dmake_command);
 427         delete tmp_dmake_command;
 428  */
 429         dmake_job_msg->appendCmd(new Avo_DmakeCommand(orig_cmd_line, cmd_options));
 430 }
 431 #endif
 432 
 433 #ifdef TEAMWARE_MAKE_CMN
 434 #define MAXJOBS_ADJUST_RFE4694000
 435 
 436 #ifdef MAXJOBS_ADJUST_RFE4694000
 437 
 438 #include <unistd.h>       /* sysconf(_SC_NPROCESSORS_ONLN) */
 439 #include <sys/ipc.h>              /* ftok() */
 440 #include <sys/shm.h>              /* shmget(), shmat(), shmdt(), shmctl() */
 441 #include <semaphore.h>            /* sem_init(), sem_trywait(), sem_post(), sem_destroy() */
 442 #include <sys/loadavg.h>  /* getloadavg() */
 443 
 444 /*
 445  *      adjust_pmake_max_jobs (int pmake_max_jobs)
 446  *
 447  *      Parameters:
 448  *              pmake_max_jobs  - max jobs limit set by user
 449  *
 450  *      External functions used:
 451  *              sysconf()
 452  *              getloadavg()
 453  */
 454 static int
 455 adjust_pmake_max_jobs (int pmake_max_jobs)
 456 {
 457         static int      ncpu = 0;
 458         double          loadavg[3];
 459         int             adjustment;
 460         int             adjusted_max_jobs;
 461 
 462         if (ncpu <= 0) {
 463                 if ((ncpu = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) {
 464                         ncpu = 1;
 465                 }
 466         }
 467         if (getloadavg(loadavg, 3) != 3) return(pmake_max_jobs);
 468         adjustment = ((int)loadavg[LOADAVG_1MIN]);
 469         if (adjustment < 2) return(pmake_max_jobs);
 470         if (ncpu > 1) {
 471                 adjustment = adjustment / ncpu;
 472         }
 473         adjusted_max_jobs = pmake_max_jobs - adjustment;
 474         if (adjusted_max_jobs < 1) adjusted_max_jobs = 1;
 475         return(adjusted_max_jobs);
 476 }
 477 
 478 /*
 479  *  M2 adjust mode data and functions
 480  *
 481  *  m2_init()           - initializes M2 shared semaphore
 482  *  m2_acquire_job()    - decrements M2 semaphore counter
 483  *  m2_release_job()    - increments M2 semaphore counter
 484  *  m2_fini()           - destroys M2 semaphore and shared memory*
 485  *
 486  *  Environment variables:
 487  *      __DMAKE_M2_FILE__
 488  *
 489  *  External functions:
 490  *      ftok(), shmget(), shmat(), shmdt(), shmctl()
 491  *      sem_init(), sem_trywait(), sem_post(), sem_destroy()
 492  *      creat(), close(), unlink()
 493  *      getenv(), putenv()
 494  *
 495  *  Static variables:
 496  *      m2_file         - tmp file name to create ipc key for shared memory
 497  *      m2_shm_id       - shared memory id
 498  *      m2_shm_sem      - shared memory semaphore
 499  */
 500 
 501 static char     m2_file[MAXPATHLEN];
 502 static int      m2_shm_id = -1;
 503 static sem_t*   m2_shm_sem = 0;
 504 
 505 static int
 506 m2_init() {
 507         char    *var;
 508         key_t   key;
 509 
 510         if ((var = getenv(NOCATGETS("__DMAKE_M2_FILE__"))) == 0) {
 511                 /* compose tmp file name */
 512                 sprintf(m2_file, NOCATGETS("%s/dmake.m2.%d.XXXXXX"), tmpdir, getpid());
 513 
 514                 /* create tmp file */
 515                 int fd = mkstemp(m2_file);
 516                 if (fd < 0) {
 517                         return -1;
 518                 } else {
 519                         close(fd);
 520                 }
 521         } else {
 522                 /* using existing semaphore */
 523                 strcpy(m2_file, var);
 524         }
 525 
 526         /* combine IPC key */
 527         if ((key = ftok(m2_file, 38)) == (key_t) -1) {
 528                 return -1;
 529         }
 530 
 531         /* create shared memory */
 532         if ((m2_shm_id = shmget(key, sizeof(*m2_shm_sem), 0666 | (var ? 0 : IPC_CREAT|IPC_EXCL))) == -1) {
 533                 return -1;
 534         }
 535 
 536         /* attach shared memory */
 537         if ((m2_shm_sem = (sem_t*) shmat(m2_shm_id, 0, 0666)) == (sem_t*)-1) {
 538                 return -1;
 539         }
 540 
 541         /* root process */
 542         if (var == 0) {
 543                 /* initialize semaphore */
 544                 if (sem_init(m2_shm_sem, 1, pmake_max_jobs)) {
 545                         return -1;
 546                 }
 547 
 548                 /* alloc memory for env variable */
 549                 if ((var = (char*) malloc(MAXPATHLEN)) == 0) {
 550                         return -1;
 551                 }
 552 
 553                 /* put key to env */
 554                 sprintf(var, NOCATGETS("__DMAKE_M2_FILE__=%s"), m2_file);
 555                 if (putenv(var)) {
 556                         return -1;
 557                 }
 558         }
 559         return 0;
 560 }
 561 
 562 static void
 563 m2_fini() {
 564         if (m2_shm_id >= 0) {
 565                 struct shmid_ds stat;
 566 
 567                 /* determine the number of attached processes */
 568                 if (shmctl(m2_shm_id, IPC_STAT, &stat) == 0) {
 569                         if (stat.shm_nattch <= 1) {
 570                                 /* destroy semaphore */
 571                                 if (m2_shm_sem != 0) {
 572                                         (void) sem_destroy(m2_shm_sem);
 573                                 }
 574 
 575                                 /* destroy shared memory */
 576                                 (void) shmctl(m2_shm_id, IPC_RMID, &stat);
 577 
 578                                 /* remove tmp file created for the key */
 579                                 (void) unlink(m2_file);
 580                         } else {
 581                                 /* detach shared memory */
 582                                 if (m2_shm_sem != 0) {
 583                                         (void) shmdt((char*) m2_shm_sem);
 584                                 }
 585                         }
 586                 }
 587 
 588                 m2_shm_id = -1;
 589                 m2_shm_sem = 0;
 590         }
 591 }
 592 
 593 static int
 594 m2_acquire_job() {
 595         if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) {
 596                 if (sem_trywait(m2_shm_sem) == 0) {
 597                         return 1;
 598                 }
 599                 if (errno == EAGAIN) {
 600                         return 0;
 601                 }
 602         }
 603         return -1;
 604 }
 605 
 606 static int
 607 m2_release_job() {
 608         if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) {
 609                 if (sem_post(m2_shm_sem) == 0) {
 610                         return 0;
 611                 }
 612         }
 613         return -1;
 614 }
 615 
 616 /*
 617  *  job adjust mode
 618  *
 619  *  Possible values:
 620  *    ADJUST_M1         - adjustment by system load (default)
 621  *    ADJUST_M2         - fixed limit of jobs for the group of nested dmakes
 622  *    ADJUST_NONE       - no adjustment - fixed limit of jobs for the current dmake
 623  */
 624 static enum {
 625         ADJUST_UNKNOWN,
 626         ADJUST_M1,
 627         ADJUST_M2,
 628         ADJUST_NONE
 629 } job_adjust_mode = ADJUST_UNKNOWN;
 630 
 631 /*
 632  *  void job_adjust_fini()
 633  *
 634  *  Description:
 635  *      Cleans up job adjust data.
 636  *
 637  *  Static variables:
 638  *      job_adjust_mode Current job adjust mode
 639  */
 640 void
 641 job_adjust_fini() {
 642         if (job_adjust_mode == ADJUST_M2) {
 643                 m2_fini();
 644         }
 645 }
 646 
 647 /*
 648  *  void job_adjust_error()
 649  *
 650  *  Description:
 651  *      Prints warning message, cleans up job adjust data, and disables job adjustment
 652  *
 653  *  Environment:
 654  *      DMAKE_ADJUST_MAX_JOBS
 655  *
 656  *  External functions:
 657  *      putenv()
 658  *
 659  *  Static variables:
 660  *      job_adjust_mode Current job adjust mode
 661  */
 662 static void
 663 job_adjust_error() {
 664         if (job_adjust_mode != ADJUST_NONE) {
 665                 /* cleanup internals */
 666                 job_adjust_fini();
 667 
 668                 /* warning message for the user */
 669                 warning(catgets(catd, 1, 339, "Encountered max jobs auto adjustment error - disabling auto adjustment."));
 670 
 671                 /* switch off job adjustment for the children */
 672                 putenv(NOCATGETS("DMAKE_ADJUST_MAX_JOBS=NO"));
 673 
 674                 /* and for this dmake */
 675                 job_adjust_mode = ADJUST_NONE;
 676         }
 677 }
 678 
 679 /*
 680  *  void job_adjust_init()
 681  *
 682  *  Description:
 683  *      Parses DMAKE_ADJUST_MAX_JOBS env variable
 684  *      and performs appropriate initializations.
 685  *
 686  *  Environment:
 687  *      DMAKE_ADJUST_MAX_JOBS
 688  *        DMAKE_ADJUST_MAX_JOBS == "NO" - no adjustment
 689  *        DMAKE_ADJUST_MAX_JOBS == "M2" - M2 adjust mode
 690  *        other                         - M1 adjust mode
 691  *
 692  *  External functions:
 693  *      getenv()
 694  *
 695  *  Static variables:
 696  *      job_adjust_mode Current job adjust mode
 697  */
 698 static void
 699 job_adjust_init() {
 700         if (job_adjust_mode == ADJUST_UNKNOWN) {
 701                 /* default mode */
 702                 job_adjust_mode = ADJUST_M1;
 703 
 704                 /* determine adjust mode */
 705                 if (char *var = getenv(NOCATGETS("DMAKE_ADJUST_MAX_JOBS"))) {
 706                         if (strcasecmp(var, NOCATGETS("NO")) == 0) {
 707                                 job_adjust_mode = ADJUST_NONE;
 708                         } else if (strcasecmp(var, NOCATGETS("M2")) == 0) {
 709                                 job_adjust_mode = ADJUST_M2;
 710                         }
 711                 }
 712 
 713                 /* M2 specific initialization */
 714                 if (job_adjust_mode == ADJUST_M2) {
 715                         if (m2_init()) {
 716                                 job_adjust_error();
 717                         }
 718                 }
 719         }
 720 }
 721 
 722 #endif /* MAXJOBS_ADJUST_RFE4694000 */
 723 #endif /* TEAMWARE_MAKE_CMN */
 724 
 725 /*
 726  *      distribute_process(char **commands, Property line)
 727  *
 728  *      Parameters:
 729  *              commands        argv vector of commands to execute
 730  *
 731  *      Return value:
 732  *                              The result of the execution
 733  *
 734  *      Static variables used:
 735  *              process_running Set to the pid of the process set running
 736  * #if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000)
 737  *              job_adjust_mode Current job adjust mode
 738  * #endif
 739  */
 740 static Doname
 741 distribute_process(char **commands, Property line)
 742 {
 743         static unsigned file_number = 0;
 744         wchar_t         string[MAXPATHLEN];
 745         char            mbstring[MAXPATHLEN];
 746         int             filed;
 747         int             res;
 748         int             tmp_index;
 749         char            *tmp_index_str_ptr;
 750 
 751 #if !defined (TEAMWARE_MAKE_CMN) || !defined (MAXJOBS_ADJUST_RFE4694000)
 752         while (parallel_process_cnt >= pmake_max_jobs) {
 753                 await_parallel(false);
 754                 finish_children(true);
 755         }
 756 #else /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */
 757         /* initialize adjust mode, if not initialized */
 758         if (job_adjust_mode == ADJUST_UNKNOWN) {
 759                 job_adjust_init();
 760         }
 761 
 762         /* actions depend on adjust mode */
 763         switch (job_adjust_mode) {
 764         case ADJUST_M1:
 765                 while (parallel_process_cnt >= adjust_pmake_max_jobs (pmake_max_jobs)) {
 766                         await_parallel(false);
 767                         finish_children(true);
 768                 }
 769                 break;
 770         case ADJUST_M2:
 771                 if ((res = m2_acquire_job()) == 0) {
 772                         if (parallel_process_cnt > 0) {
 773                                 await_parallel(false);
 774                                 finish_children(true);
 775 
 776                                 if ((res = m2_acquire_job()) == 0) {
 777                                         return build_serial;
 778                                 }
 779                         } else {
 780                                 return build_serial;
 781                         }
 782                 }
 783                 if (res < 0) {
 784                         /* job adjustment error */
 785                         job_adjust_error();
 786 
 787                         /* no adjustment */
 788                         while (parallel_process_cnt >= pmake_max_jobs) {
 789                                 await_parallel(false);
 790                                 finish_children(true);
 791                         }
 792                 }
 793                 break;
 794         default:
 795                 while (parallel_process_cnt >= pmake_max_jobs) {
 796                         await_parallel(false);
 797                         finish_children(true);
 798                 }
 799         }
 800 #endif /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */
 801 #ifdef DISTRIBUTED
 802         if (send_mtool_msgs) {
 803                 send_job_start_msg(line);
 804         }
 805 #endif
 806 #ifdef DISTRIBUTED
 807         setvar_envvar((Avo_DoJobMsg *)NULL);
 808 #else
 809         setvar_envvar();
 810 #endif
 811         /*
 812          * Tell the user what DMake is doing.
 813          */
 814         if (!silent && output_mode != txt2_mode) {
 815                 /*
 816                  * Print local_host --> x job(s).
 817                  */
 818                 (void) fprintf(stdout,
 819                                catgets(catd, 1, 325, "%s --> %d %s\n"),
 820                                local_host,
 821                                parallel_process_cnt + 1,
 822                                (parallel_process_cnt == 0) ? catgets(catd, 1, 124, "job") : catgets(catd, 1, 125, "jobs"));
 823 
 824                 /* Print command line(s). */
 825                 tmp_index = 0;
 826                 while (commands[tmp_index] != NULL) {
 827                     /* No @ char. */
 828                     /* XXX - need to add [2] when + prefix is added */
 829                     if ((commands[tmp_index][0] != (int) at_char) &&
 830                         (commands[tmp_index][1] != (int) at_char)) {
 831                         tmp_index_str_ptr = commands[tmp_index];
 832                         if (*tmp_index_str_ptr == (int) hyphen_char) {
 833                                 tmp_index_str_ptr++;
 834                         }
 835                         (void) fprintf(stdout, "%s\n", tmp_index_str_ptr);
 836                     }
 837                     tmp_index++;
 838                 }
 839                 (void) fflush(stdout);
 840         }
 841 
 842         (void) sprintf(mbstring,
 843                         NOCATGETS("%s/dmake.stdout.%d.%d.XXXXXX"),
 844                         tmpdir,
 845                         getpid(),
 846                         file_number++);
 847 
 848         mktemp(mbstring);
 849 
 850         stdout_file = strdup(mbstring);
 851         stderr_file = NULL;
 852 #if defined (TEAMWARE_MAKE_CMN) && defined(REDIRECT_ERR)
 853         if (!out_err_same) {
 854                 (void) sprintf(mbstring,
 855                                 NOCATGETS("%s/dmake.stderr.%d.%d.XXXXXX"),
 856                                 tmpdir,
 857                                 getpid(),
 858                                 file_number++);
 859 
 860                 mktemp(mbstring);
 861 
 862                 stderr_file = strdup(mbstring);
 863         }
 864 #endif
 865 
 866         process_running = run_rule_commands(local_host, commands);
 867 
 868         return build_running;
 869 }
 870 
 871 /*
 872  *      doname_parallel(target, do_get, implicit)
 873  *
 874  *      Processes the given target and finishes up any parallel
 875  *      processes left running.
 876  *
 877  *      Return value:
 878  *                              Result of target build
 879  *
 880  *      Parameters:
 881  *              target          Target to build
 882  *              do_get          True if sccs get to be done
 883  *              implicit        True if this is an implicit target
 884  */
 885 Doname
 886 doname_parallel(Name target, Boolean do_get, Boolean implicit)
 887 {
 888         Doname          result;
 889 
 890         result = doname_check(target, do_get, implicit, false);
 891         if (result == build_ok || result == build_failed) {
 892                 return result;
 893         }
 894         finish_running();
 895         return (Doname) target->state;
 896 }
 897 
 898 /*
 899  *      doname_subtree(target, do_get, implicit)
 900  *
 901  *      Completely computes an object and its dependents for a
 902  *      serial subtree build.
 903  *
 904  *      Parameters:
 905  *              target          Target to build
 906  *              do_get          True if sccs get to be done
 907  *              implicit        True if this is an implicit target
 908  *
 909  *      Static variables used:
 910  *              running_tail    Tail of the list of running processes
 911  *
 912  *      Global variables used:
 913  *              running_list    The list of running processes
 914  */
 915 static void
 916 doname_subtree(Name target, Boolean do_get, Boolean implicit)
 917 {
 918         Running         save_running_list;
 919         Running         *save_running_tail;
 920 
 921         save_running_list = running_list;
 922         save_running_tail = running_tail;
 923         running_list = NULL;
 924         running_tail = &running_list;
 925         target->state = build_subtree;
 926         target->checking_subtree = true;
 927         while(doname_check(target, do_get, implicit, false) == build_running) {
 928                 target->checking_subtree = false;
 929                 finish_running();
 930                 target->state = build_subtree;
 931         }
 932         target->checking_subtree = false;
 933         running_list = save_running_list;
 934         running_tail = save_running_tail;
 935 }
 936 
 937 /*
 938  *      finish_running()
 939  *
 940  *      Keeps processing until the running_list is emptied out.
 941  *
 942  *      Parameters:
 943  *
 944  *      Global variables used:
 945  *              running_list    The list of running processes
 946  */
 947 void
 948 finish_running(void)
 949 {
 950         while (running_list != NULL) {
 951 #ifdef DISTRIBUTED
 952                 if (dmake_mode_type == distributed_mode) {
 953                         if ((just_did_subtree) ||
 954                             (parallel_process_cnt == 0)) {
 955                                 just_did_subtree = false;
 956                         } else {
 957                                 (void) await_dist(false);
 958                                 finish_children(true);
 959                         }
 960                 } else
 961 #endif
 962                 {
 963                         await_parallel(false);
 964                         finish_children(true);
 965                 }
 966                 if (running_list != NULL) {
 967                         process_next();
 968                 }
 969         }
 970 }
 971 
 972 /*
 973  *      process_next()
 974  *
 975  *      Searches the running list for any targets which can start processing.
 976  *      This can be a pending target, a serial target, or a subtree target.
 977  *
 978  *      Parameters:
 979  *
 980  *      Static variables used:
 981  *              running_tail            The end of the list of running procs
 982  *              subtree_conflict        A target which conflicts with a subtree
 983  *              subtree_conflict2       The other target which conflicts
 984  *
 985  *      Global variables used:
 986  *              commands_done           True if commands executed
 987  *              debug_level             Controls debug output
 988  *              parallel_process_cnt    Number of parallel process running
 989  *              recursion_level         Indentation for debug output
 990  *              running_list            List of running processes
 991  */
 992 static void
 993 process_next(void)
 994 {
 995         Running         rp;
 996         Running         *rp_prev;
 997         Property        line;
 998         Chain           target_group;
 999         Dependency      dep;
1000         Boolean         quiescent = true;
1001         Running         *subtree_target;
1002         Boolean         saved_commands_done;
1003         Property        *conditionals;
1004 
1005         subtree_target = NULL;
1006         subtree_conflict = NULL;
1007         subtree_conflict2 = NULL;
1008         /*
1009          * If nothing currently running, build a serial target, if any.
1010          */
1011 start_loop_1:
1012         for (rp_prev = &running_list, rp = running_list;
1013              rp != NULL && parallel_process_cnt == 0;
1014              rp = rp->next) {
1015                 if (rp->state == build_serial) {
1016                         *rp_prev = rp->next;
1017                         if (rp->next == NULL) {
1018                                 running_tail = rp_prev;
1019                         }
1020                         recursion_level = rp->recursion_level;
1021                         rp->target->state = build_pending;
1022                         (void) doname_check(rp->target,
1023                                             rp->do_get,
1024                                             rp->implicit,
1025                                             false);
1026                         quiescent = false;
1027                         delete_running_struct(rp);
1028                         goto start_loop_1;
1029                 } else {
1030                         rp_prev = &rp->next;
1031                 }
1032         }
1033         /*
1034          * Find a target to build.  The target must be pending, have all
1035          * its dependencies built, and not be in a target group with a target
1036          * currently building.
1037          */
1038 start_loop_2:
1039         for (rp_prev = &running_list, rp = running_list;
1040              rp != NULL;
1041              rp = rp->next) {
1042                 if (!(rp->state == build_pending ||
1043                       rp->state == build_subtree)) {
1044                         quiescent = false;
1045                         rp_prev = &rp->next;
1046                 } else if (rp->state == build_pending) {
1047                         line = get_prop(rp->target->prop, line_prop);
1048                         for (dep = line->body.line.dependencies;
1049                              dep != NULL;
1050                              dep = dep->next) {
1051                                 if (dep->name->state == build_running ||
1052                                     dep->name->state == build_pending ||
1053                                     dep->name->state == build_serial) {
1054                                         break;
1055                                 }
1056                         }
1057                         if (dep == NULL) {
1058                                 for (target_group = line->body.line.target_group;
1059                                      target_group != NULL;
1060                                      target_group = target_group->next) {
1061                                         if (is_running(target_group->name)) {
1062                                                 break;
1063                                         }
1064                                 }
1065                                 if (target_group == NULL) {
1066                                         *rp_prev = rp->next;
1067                                         if (rp->next == NULL) {
1068                                                 running_tail = rp_prev;
1069                                         }
1070                                         recursion_level = rp->recursion_level;
1071                                         rp->target->state = rp->redo ?
1072                                           build_dont_know : build_pending;
1073                                         saved_commands_done = commands_done;
1074                                         conditionals =
1075                                                 set_conditionals
1076                                                     (rp->conditional_cnt,
1077                                                      rp->conditional_targets);
1078                                         rp->target->dont_activate_cond_values = true;
1079                                         if ((doname_check(rp->target,
1080                                                           rp->do_get,
1081                                                           rp->implicit,
1082                                                           rp->target->has_target_prop ? true : false) !=
1083                                              build_running) &&
1084                                             !commands_done) {
1085                                                 commands_done =
1086                                                   saved_commands_done;
1087                                         }
1088                                         rp->target->dont_activate_cond_values = false;
1089                                         reset_conditionals
1090                                                 (rp->conditional_cnt,
1091                                                  rp->conditional_targets,
1092                                                  conditionals);
1093                                         quiescent = false;
1094                                         delete_running_struct(rp);
1095                                         goto start_loop_2;
1096                                 } else {
1097                                         rp_prev = &rp->next;
1098                                 }
1099                         } else {
1100                                 rp_prev = &rp->next;
1101                         }
1102                 } else {
1103                         rp_prev = &rp->next;
1104                 }
1105         }
1106         /*
1107          * If nothing has been found to build and there exists a subtree
1108          * target with no dependency conflicts, build it.
1109          */
1110         if (quiescent) {
1111 start_loop_3:
1112                 for (rp_prev = &running_list, rp = running_list;
1113                      rp != NULL;
1114                      rp = rp->next) {
1115                         if (rp->state == build_subtree) {
1116                                 if (!dependency_conflict(rp->target)) {
1117                                         *rp_prev = rp->next;
1118                                         if (rp->next == NULL) {
1119                                                 running_tail = rp_prev;
1120                                         }
1121                                         recursion_level = rp->recursion_level;
1122                                         doname_subtree(rp->target,
1123                                                        rp->do_get,
1124                                                        rp->implicit);
1125 #ifdef DISTRIBUTED
1126                                         just_did_subtree = true;
1127 #endif
1128                                         quiescent = false;
1129                                         delete_running_struct(rp);
1130                                         goto start_loop_3;
1131                                 } else {
1132                                         subtree_target = rp_prev;
1133                                         rp_prev = &rp->next;
1134                                 }
1135                         } else {
1136                                 rp_prev = &rp->next;
1137                         }
1138                 }
1139         }
1140         /*
1141          * If still nothing found to build, we either have a deadlock
1142          * or a subtree with a dependency conflict with something waiting
1143          * to build.
1144          */
1145         if (quiescent) {
1146                 if (subtree_target == NULL) {
1147                         fatal(catgets(catd, 1, 126, "Internal error: deadlock detected in process_next"));
1148                 } else {
1149                         rp = *subtree_target;
1150                         if (debug_level > 0) {
1151                                 warning(catgets(catd, 1, 127, "Conditional macro conflict encountered for %s between %s and %s"),
1152                                         subtree_conflict2->string_mb,
1153                                         rp->target->string_mb,
1154                                         subtree_conflict->string_mb);
1155                         }
1156                         *subtree_target = (*subtree_target)->next;
1157                         if (rp->next == NULL) {
1158                                 running_tail = subtree_target;
1159                         }
1160                         recursion_level = rp->recursion_level;
1161                         doname_subtree(rp->target, rp->do_get, rp->implicit);
1162 #ifdef DISTRIBUTED
1163                         just_did_subtree = true;
1164 #endif
1165                         delete_running_struct(rp);
1166                 }
1167         }
1168 }
1169 
1170 /*
1171  *      set_conditionals(cnt, targets)
1172  *
1173  *      Sets the conditional macros for the targets given in the array of
1174  *      targets.  The old macro values are returned in an array of
1175  *      Properties for later resetting.
1176  *
1177  *      Return value:
1178  *                                      Array of conditional macro settings
1179  *
1180  *      Parameters:
1181  *              cnt                     Number of targets
1182  *              targets                 Array of targets
1183  */
1184 static Property *
1185 set_conditionals(int cnt, Name *targets)
1186 {
1187         Property        *locals, *lp;
1188         Name            *tp;
1189 
1190         locals = (Property *) getmem(cnt * sizeof(Property));
1191         for (lp = locals, tp = targets;
1192              cnt > 0;
1193              cnt--, lp++, tp++) {
1194                 *lp = (Property) getmem((*tp)->conditional_cnt *
1195                                         sizeof(struct _Property));
1196                 set_locals(*tp, *lp);
1197         }
1198         return locals;
1199 }
1200 
1201 /*
1202  *      reset_conditionals(cnt, targets, locals)
1203  *
1204  *      Resets the conditional macros as saved in the given array of
1205  *      Properties.  The resets are done in reverse order.  Afterwards the
1206  *      data structures are freed.
1207  *
1208  *      Parameters:
1209  *              cnt                     Number of targets
1210  *              targets                 Array of targets
1211  *              locals                  Array of dependency macro settings
1212  */
1213 static void
1214 reset_conditionals(int cnt, Name *targets, Property *locals)
1215 {
1216         Name            *tp;
1217         Property        *lp;
1218 
1219         for (tp = targets + (cnt - 1), lp = locals + (cnt - 1);
1220              cnt > 0;
1221              cnt--, tp--, lp--) {
1222                 reset_locals(*tp,
1223                              *lp,
1224                              get_prop((*tp)->prop, conditional_prop),
1225                              0);
1226                 retmem_mb((caddr_t) *lp);
1227         }
1228         retmem_mb((caddr_t) locals);
1229 }
1230 
1231 /*
1232  *      dependency_conflict(target)
1233  *
1234  *      Returns true if there is an intersection between
1235  *      the subtree of the target and any dependents of the pending targets.
1236  *
1237  *      Return value:
1238  *                                      True if conflict found
1239  *
1240  *      Parameters:
1241  *              target                  Subtree target to check
1242  *
1243  *      Static variables used:
1244  *              subtree_conflict        Target conflict found
1245  *              subtree_conflict2       Second conflict found
1246  *
1247  *      Global variables used:
1248  *              running_list            List of running processes
1249  *              wait_name               .WAIT, not a real dependency
1250  */
1251 static Boolean
1252 dependency_conflict(Name target)
1253 {
1254         Property        line;
1255         Property        pending_line;
1256         Dependency      dp;
1257         Dependency      pending_dp;
1258         Running         rp;
1259 
1260         /* Return if we are already checking this target */
1261         if (target->checking_subtree) {
1262                 return false;
1263         }
1264         target->checking_subtree = true;
1265         line = get_prop(target->prop, line_prop);
1266         if (line == NULL) {
1267                 target->checking_subtree = false;
1268                 return false;
1269         }
1270         /* Check each dependency of the target for conflicts */
1271         for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) {
1272                 /* Ignore .WAIT dependency */
1273                 if (dp->name == wait_name) {
1274                         continue;
1275                 }
1276                 /*
1277                  * For each pending target, look for a dependency which
1278                  * is the same as a dependency of the subtree target.  Since
1279                  * we can't build the subtree until all pending targets have
1280                  * finished which depend on the same dependency, this is
1281                  * a conflict.
1282                  */
1283                 for (rp = running_list; rp != NULL; rp = rp->next) {
1284                         if (rp->state == build_pending) {
1285                                 pending_line = get_prop(rp->target->prop,
1286                                                         line_prop);
1287                                 if (pending_line == NULL) {
1288                                         continue;
1289                                 }
1290                                 for(pending_dp = pending_line->
1291                                                         body.line.dependencies;
1292                                     pending_dp != NULL;
1293                                     pending_dp = pending_dp->next) {
1294                                         if (dp->name == pending_dp->name) {
1295                                                 target->checking_subtree
1296                                                                 = false;
1297                                                 subtree_conflict = rp->target;
1298                                                 subtree_conflict2 = dp->name;
1299                                                 return true;
1300                                         }
1301                                 }
1302                         }
1303                 }
1304                 if (dependency_conflict(dp->name)) {
1305                         target->checking_subtree = false;
1306                         return true;
1307                 }
1308         }
1309         target->checking_subtree = false;
1310         return false;
1311 }
1312 
1313 /*
1314  *      await_parallel(waitflg)
1315  *
1316  *      Waits for parallel children to exit and finishes their processing.
1317  *      If waitflg is false, the function returns after update_delay.
1318  *
1319  *      Parameters:
1320  *              waitflg         dwight
1321  */
1322 void
1323 await_parallel(Boolean waitflg)
1324 {
1325 #ifdef _CHECK_UPDATE_H
1326         static int number_of_unknown_children = 0;
1327 #endif /* _CHECK_UPDATE_H */
1328         Boolean         nohang;
1329         pid_t           pid;
1330         int             status;
1331         Running         rp;
1332         int             waiterr;
1333 
1334         nohang = false;
1335         for ( ; ; ) {
1336                 if (!nohang) {
1337                         (void) alarm((int) update_delay);
1338                 }
1339                 pid = waitpid((pid_t)-1,
1340                               &status,
1341                               nohang ? WNOHANG : 0);
1342                 waiterr = errno;
1343                 if (!nohang) {
1344                         (void) alarm(0);
1345                 }
1346                 if (pid <= 0) {
1347                         if (waiterr == EINTR) {
1348                                 if (waitflg) {
1349                                         continue;
1350                                 } else {
1351                                         return;
1352                                 }
1353                         } else {
1354                                 return;
1355                         }
1356                 }
1357                 for (rp = running_list;
1358                      (rp != NULL) && (rp->pid != pid);
1359                      rp = rp->next) {
1360                         ;
1361                 }
1362                 if (rp == NULL) {
1363 #ifdef _CHECK_UPDATE_H
1364                         /* Ignore first child - it is check_update */
1365                         if (number_of_unknown_children <= 0) {
1366                                 number_of_unknown_children = 1;
1367                                 return;
1368                         }
1369 #endif /* _CHECK_UPDATE_H */
1370                         if (send_mtool_msgs) {
1371                                 continue;
1372                         } else {
1373                                 fatal(catgets(catd, 1, 128, "Internal error: returned child pid not in running_list"));
1374                         }
1375                 } else {
1376                         rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed;
1377                 }
1378                 nohang = true;
1379                 parallel_process_cnt--;
1380 
1381 #if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000)
1382                 if (job_adjust_mode == ADJUST_M2) {
1383                         if (m2_release_job()) {
1384                                 job_adjust_error();
1385                         }
1386                 }
1387 #endif
1388         }
1389 }
1390 
1391 /*
1392  *      finish_children(docheck)
1393  *
1394  *      Finishes the processing for all targets which were running
1395  *      and have now completed.
1396  *
1397  *      Parameters:
1398  *              docheck         Completely check the finished target
1399  *
1400  *      Static variables used:
1401  *              running_tail    The tail of the running list
1402  *
1403  *      Global variables used:
1404  *              continue_after_error  -k flag
1405  *              fatal_in_progress  True if we are finishing up after fatal err
1406  *              running_list    List of running processes
1407  */
1408 void
1409 finish_children(Boolean docheck)
1410 {
1411         int             cmds_length;
1412         Property        line;
1413         Property        line2;
1414         struct stat     out_buf;
1415         Running         rp;
1416         Running         *rp_prev;
1417         Cmd_line        rule;
1418         Boolean         silent_flag;
1419 
1420         for (rp_prev = &running_list, rp = running_list;
1421              rp != NULL;
1422              rp = rp->next) {
1423 bypass_for_loop_inc_4:
1424                 /*
1425                  * If the state is ok or failed, then this target has
1426                  * finished building.
1427                  * In parallel_mode, output the accumulated stdout/stderr.
1428                  * Read the auto dependency stuff, handle a failed build,
1429                  * update the target, then finish the doname process for
1430                  * that target.
1431                  */
1432                 if (rp->state == build_ok || rp->state == build_failed) {
1433                         *rp_prev = rp->next;
1434                         if (rp->next == NULL) {
1435                                 running_tail = rp_prev;
1436                         }
1437                         if ((line2 = rp->command) == NULL) {
1438                                 line2 = get_prop(rp->target->prop, line_prop);
1439                         }
1440                         if (dmake_mode_type == distributed_mode) {
1441                                 if (rp->make_refd) {
1442                                         maybe_reread_make_state();
1443                                 }
1444                         } else {
1445                                 /*
1446                                  * Send an Avo_MToolJobResultMsg to maketool.
1447                                  */
1448 #ifdef DISTRIBUTED
1449                                 if (send_mtool_msgs) {
1450                                         send_job_result_msg(rp);
1451                                 }
1452 #endif
1453                                 /*
1454                                  * Check if there were any job output
1455                                  * from the parallel build.
1456                                  */
1457                                 if (rp->stdout_file != NULL) {
1458                                         if (stat(rp->stdout_file, &out_buf) < 0) {
1459                                                 fatal(catgets(catd, 1, 130, "stat of %s failed: %s"),
1460                                                       rp->stdout_file,
1461                                                       errmsg(errno));
1462                                         }
1463                                         if ((line2 != NULL) &&
1464                                             (out_buf.st_size > 0)) {
1465                                                 cmds_length = 0;
1466                                                 for (rule = line2->body.line.command_used,
1467                                                      silent_flag = silent;
1468                                                      rule != NULL;
1469                                                      rule = rule->next) {
1470                                                         cmds_length += rule->command_line->hash.length + 1;
1471                                                         silent_flag = BOOLEAN(silent_flag || rule->silent);
1472                                                 }
1473                                                 if (out_buf.st_size != cmds_length || silent_flag ||
1474                                                     output_mode == txt2_mode) {
1475                                                         dump_out_file(rp->stdout_file, false);
1476                                                 }
1477                                         }
1478                                         (void) unlink(rp->stdout_file);
1479                                         retmem_mb(rp->stdout_file);
1480                                         rp->stdout_file = NULL;
1481                                 }
1482 #if defined(REDIRECT_ERR)
1483                                 if (!out_err_same && (rp->stderr_file != NULL)) {
1484                                         if (stat(rp->stderr_file, &out_buf) < 0) {
1485                                                 fatal(catgets(catd, 1, 130, "stat of %s failed: %s"),
1486                                                       rp->stderr_file,
1487                                                       errmsg(errno));
1488                                         }
1489                                         if ((line2 != NULL) &&
1490                                             (out_buf.st_size > 0)) {
1491                                                 dump_out_file(rp->stderr_file, true);
1492                                         }
1493                                         (void) unlink(rp->stderr_file);
1494                                         retmem_mb(rp->stderr_file);
1495                                         rp->stderr_file = NULL;
1496                                 }
1497 #endif
1498                         }
1499                         check_state(rp->temp_file);
1500                         if (rp->temp_file != NULL) {
1501                                 free_name(rp->temp_file);
1502                         }
1503                         rp->temp_file = NULL;
1504                         if (rp->state == build_failed) {
1505                                 line = get_prop(rp->target->prop, line_prop);
1506                                 if (line != NULL) {
1507                                         line->body.line.command_used = NULL;
1508                                 }
1509                                 if (continue_after_error ||
1510                                     fatal_in_progress ||
1511                                     !docheck) {
1512                                         warning(catgets(catd, 1, 256, "Command failed for target `%s'"),
1513                                                 rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1514                                         build_failed_seen = true;
1515                                 } else {
1516                                         /*
1517                                          * XXX??? - DMake needs to exit(),
1518                                          * but shouldn't call fatal().
1519                                          */
1520 #ifdef PRINT_EXIT_STATUS
1521                                         warning(NOCATGETS("I'm in finish_children. rp->state == build_failed."));
1522 #endif
1523 
1524                                         fatal(catgets(catd, 1, 258, "Command failed for target `%s'"),
1525                                                 rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1526                                 }
1527                         }
1528                         if (!docheck) {
1529                                 delete_running_struct(rp);
1530                                 rp = *rp_prev;
1531                                 if (rp == NULL) {
1532                                         break;
1533                                 } else {
1534                                         goto bypass_for_loop_inc_4;
1535                                 }
1536                         }
1537                         update_target(get_prop(rp->target->prop, line_prop),
1538                                       rp->state);
1539                         finish_doname(rp);
1540                         delete_running_struct(rp);
1541                         rp = *rp_prev;
1542                         if (rp == NULL) {
1543                                 break;
1544                         } else {
1545                                 goto bypass_for_loop_inc_4;
1546                         }
1547                 } else {
1548                         rp_prev = &rp->next;
1549                 }
1550         }
1551 }
1552 
1553 /*
1554  *      dump_out_file(filename, err)
1555  *
1556  *      Write the contents of the file to stdout, then unlink the file.
1557  *
1558  *      Parameters:
1559  *              filename        Name of temp file containing output
1560  *
1561  *      Global variables used:
1562  */
1563 static void
1564 dump_out_file(char *filename, Boolean err)
1565 {
1566         int             chars_read;
1567         char            copybuf[BUFSIZ];
1568         int             fd;
1569         int             out_fd = (err ? 2 : 1);
1570 
1571         if ((fd = open(filename, O_RDONLY)) < 0) {
1572                 fatal(catgets(catd, 1, 141, "open failed for output file %s: %s"),
1573                       filename,
1574                       errmsg(errno));
1575         }
1576         if (!silent && output_mode != txt2_mode) {
1577                 (void) fprintf(err ? stderr : stdout,
1578                                err ?
1579                                 catgets(catd, 1, 338, "%s --> Job errors\n") :
1580                                 catgets(catd, 1, 259, "%s --> Job output\n"),
1581                                local_host);
1582                 (void) fflush(err ? stderr : stdout);
1583         }
1584         for (chars_read = read(fd, copybuf, BUFSIZ);
1585              chars_read > 0;
1586              chars_read = read(fd, copybuf, BUFSIZ)) {
1587                 /*
1588                  * Read buffers from the source file until end or error.
1589                  */
1590                 if (write(out_fd, copybuf, chars_read) < 0) {
1591                         fatal(catgets(catd, 1, 260, "write failed for output file %s: %s"),
1592                               filename,
1593                               errmsg(errno));
1594                 }
1595         }
1596         (void) close(fd);
1597         (void) unlink(filename);
1598 }
1599 
1600 /*
1601  *      finish_doname(rp)
1602  *
1603  *      Completes the processing for a target which was left running.
1604  *
1605  *      Parameters:
1606  *              rp              Running list entry for target
1607  *
1608  *      Global variables used:
1609  *              debug_level     Debug flag
1610  *              recursion_level Indentation for debug output
1611  */
1612 static void
1613 finish_doname(Running rp)
1614 {
1615         int             auto_count = rp->auto_count;
1616         Name            *automatics = rp->automatics;
1617         Doname          result = rp->state;
1618         Name            target = rp->target;
1619         Name            true_target = rp->true_target;
1620         Property        *conditionals;
1621 
1622         recursion_level = rp->recursion_level;
1623         if (result == build_ok) {
1624                 if (true_target == NULL) {
1625                         (void) printf(NOCATGETS("Target = %s\n"), target->string_mb);
1626                         (void) printf(NOCATGETS(" State = %d\n"), result);
1627                         fatal(NOCATGETS("Internal error: NULL true_target in finish_doname"));
1628                 }
1629                 /* If all went OK, set a nice timestamp */
1630                 if (true_target->stat.time == file_doesnt_exist) {
1631                         true_target->stat.time = file_max_time;
1632                 }
1633         }
1634         target->state = result;
1635         if (target->is_member) {
1636                 Property member;
1637 
1638                 /* Propagate the timestamp from the member file to the member */
1639                 if ((target->stat.time != file_max_time) &&
1640                     ((member = get_prop(target->prop, member_prop)) != NULL) &&
1641                     (exists(member->body.member.member) > file_doesnt_exist)) {
1642                         target->stat.time =
1643 /*
1644                           exists(member->body.member.member);
1645  */
1646                           member->body.member.member->stat.time;
1647                 }
1648         }
1649         /*
1650          * Check if we found any new auto dependencies when we
1651          * built the target.
1652          */
1653         if ((result == build_ok) && check_auto_dependencies(target,
1654                                                             auto_count,
1655                                                             automatics)) {
1656                 if (debug_level > 0) {
1657                         (void) printf(catgets(catd, 1, 261, "%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"),
1658                                       recursion_level,
1659                                       "",
1660                                       true_target->string_mb);
1661                 }
1662                 target->rechecking_target = true;
1663                 target->state = build_running;
1664 
1665                 /* [tolik, Tue Mar 25 1997]
1666                  * Fix for bug 4038824:
1667                  *       command line options set by conditional macros get dropped
1668                  * rp->conditional_cnt and rp->conditional_targets must be copied
1669                  * to new 'rp' during add_pending(). Set_conditionals() stores
1670                  * rp->conditional_targets to the global variable 'conditional_targets'
1671                  * Add_pending() will use this variable to set up 'rp'.
1672                  */
1673                 conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets);
1674                 add_pending(target,
1675                             recursion_level,
1676                             rp->do_get,
1677                             rp->implicit,
1678                             true);
1679                 reset_conditionals(rp->conditional_cnt, rp->conditional_targets, conditionals);
1680         }
1681 }
1682 
1683 /*
1684  *      new_running_struct()
1685  *
1686  *      Constructor for Running struct. Creates a structure and initializes
1687  *      its fields.
1688  *
1689  */
1690 static Running new_running_struct()
1691 {
1692         Running         rp;
1693 
1694         rp = ALLOC(Running);
1695         rp->target = NULL;
1696         rp->true_target = NULL;
1697         rp->command = NULL;
1698         rp->sprodep_value = NULL;
1699         rp->sprodep_env = NULL;
1700         rp->auto_count = 0;
1701         rp->automatics = NULL;
1702         rp->pid = -1;
1703         rp->job_msg_id = -1;
1704         rp->stdout_file = NULL;
1705         rp->stderr_file = NULL;
1706         rp->temp_file = NULL;
1707         rp->next = NULL;
1708         return rp;
1709 }
1710 
1711 /*
1712  *      add_running(target, true_target, command, recursion_level, auto_count,
1713  *                                      automatics, do_get, implicit)
1714  *
1715  *      Adds a record on the running list for this target, which
1716  *      was just spawned and is running.
1717  *
1718  *      Parameters:
1719  *              target          Target being built
1720  *              true_target     True target for target
1721  *              command         Running command.
1722  *              recursion_level Debug indentation level
1723  *              auto_count      Count of automatic dependencies
1724  *              automatics      List of automatic dependencies
1725  *              do_get          Sccs get flag
1726  *              implicit        Implicit flag
1727  *
1728  *      Static variables used:
1729  *              running_tail    Tail of running list
1730  *              process_running PID of process
1731  *
1732  *      Global variables used:
1733  *              current_line    Current line for target
1734  *              current_target  Current target being built
1735  *              stderr_file     Temporary file for stdout
1736  *              stdout_file     Temporary file for stdout
1737  *              temp_file_name  Temporary file for auto dependencies
1738  */
1739 void
1740 add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit)
1741 {
1742         Running         rp;
1743         Name            *p;
1744 
1745         rp = new_running_struct();
1746         rp->state = build_running;
1747         rp->target = target;
1748         rp->true_target = true_target;
1749         rp->command = command;
1750         Property spro_val = get_prop(sunpro_dependencies->prop, macro_prop);
1751         if(spro_val) {
1752                 rp->sprodep_value = spro_val->body.macro.value;
1753                 spro_val->body.macro.value = NULL;
1754                 spro_val = get_prop(sunpro_dependencies->prop, env_mem_prop);
1755                 if(spro_val) {
1756                         rp->sprodep_env = spro_val->body.env_mem.value;
1757                         spro_val->body.env_mem.value = NULL;
1758                 }
1759         }
1760         rp->recursion_level = recursion_level;
1761         rp->do_get = do_get;
1762         rp->implicit = implicit;
1763         rp->auto_count = auto_count;
1764         if (auto_count > 0) {
1765                 rp->automatics = (Name *) getmem(auto_count * sizeof (Name));
1766                 for (p = rp->automatics; auto_count > 0; auto_count--) {
1767                         *p++ = *automatics++;
1768                 }
1769         } else {
1770                 rp->automatics = NULL;
1771         }
1772 #ifdef DISTRIBUTED
1773         if (dmake_mode_type == distributed_mode) {
1774                 rp->make_refd = called_make;
1775                 called_make = false;
1776         } else
1777 #endif
1778         {
1779                 rp->pid = process_running;
1780                 process_running = -1;
1781                 childPid = -1;
1782         }
1783         rp->job_msg_id = job_msg_id;
1784         rp->stdout_file = stdout_file;
1785         rp->stderr_file = stderr_file;
1786         rp->temp_file = temp_file_name;
1787         rp->redo = false;
1788         rp->next = NULL;
1789         store_conditionals(rp);
1790         stdout_file = NULL;
1791         stderr_file = NULL;
1792         temp_file_name = NULL;
1793         current_target = NULL;
1794         current_line = NULL;
1795         *running_tail = rp;
1796         running_tail = &rp->next;
1797 }
1798 
1799 /*
1800  *      add_pending(target, recursion_level, do_get, implicit, redo)
1801  *
1802  *      Adds a record on the running list for a pending target
1803  *      (waiting for its dependents to finish running).
1804  *
1805  *      Parameters:
1806  *              target          Target being built
1807  *              recursion_level Debug indentation level
1808  *              do_get          Sccs get flag
1809  *              implicit        Implicit flag
1810  *              redo            True if this target is being redone
1811  *
1812  *      Static variables used:
1813  *              running_tail    Tail of running list
1814  */
1815 void
1816 add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo)
1817 {
1818         Running         rp;
1819         rp = new_running_struct();
1820         rp->state = build_pending;
1821         rp->target = target;
1822         rp->recursion_level = recursion_level;
1823         rp->do_get = do_get;
1824         rp->implicit = implicit;
1825         rp->redo = redo;
1826         store_conditionals(rp);
1827         *running_tail = rp;
1828         running_tail = &rp->next;
1829 }
1830 
1831 /*
1832  *      add_serial(target, recursion_level, do_get, implicit)
1833  *
1834  *      Adds a record on the running list for a target which must be
1835  *      executed in serial after others have finished.
1836  *
1837  *      Parameters:
1838  *              target          Target being built
1839  *              recursion_level Debug indentation level
1840  *              do_get          Sccs get flag
1841  *              implicit        Implicit flag
1842  *
1843  *      Static variables used:
1844  *              running_tail    Tail of running list
1845  */
1846 void
1847 add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit)
1848 {
1849         Running         rp;
1850 
1851         rp = new_running_struct();
1852         rp->target = target;
1853         rp->recursion_level = recursion_level;
1854         rp->do_get = do_get;
1855         rp->implicit = implicit;
1856         rp->state = build_serial;
1857         rp->redo = false;
1858         store_conditionals(rp);
1859         *running_tail = rp;
1860         running_tail = &rp->next;
1861 }
1862 
1863 /*
1864  *      add_subtree(target, recursion_level, do_get, implicit)
1865  *
1866  *      Adds a record on the running list for a target which must be
1867  *      executed in isolation after others have finished.
1868  *
1869  *      Parameters:
1870  *              target          Target being built
1871  *              recursion_level Debug indentation level
1872  *              do_get          Sccs get flag
1873  *              implicit        Implicit flag
1874  *
1875  *      Static variables used:
1876  *              running_tail    Tail of running list
1877  */
1878 void
1879 add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit)
1880 {
1881         Running         rp;
1882 
1883         rp = new_running_struct();
1884         rp->target = target;
1885         rp->recursion_level = recursion_level;
1886         rp->do_get = do_get;
1887         rp->implicit = implicit;
1888         rp->state = build_subtree;
1889         rp->redo = false;
1890         store_conditionals(rp);
1891         *running_tail = rp;
1892         running_tail = &rp->next;
1893 }
1894 
1895 /*
1896  *      store_conditionals(rp)
1897  *
1898  *      Creates an array of the currently active targets with conditional
1899  *      macros (found in the chain conditional_targets) and puts that
1900  *      array in the Running struct.
1901  *
1902  *      Parameters:
1903  *              rp              Running struct for storing chain
1904  *
1905  *      Global variables used:
1906  *              conditional_targets  Chain of current dynamic conditionals
1907  */
1908 static void
1909 store_conditionals(Running rp)
1910 {
1911         int             cnt;
1912         Chain           cond_name;
1913 
1914         if (conditional_targets == NULL) {
1915                 rp->conditional_cnt = 0;
1916                 rp->conditional_targets = NULL;
1917                 return;
1918         }
1919         cnt = 0;
1920         for (cond_name = conditional_targets;
1921              cond_name != NULL;
1922              cond_name = cond_name->next) {
1923                 cnt++;
1924         }
1925         rp->conditional_cnt = cnt;
1926         rp->conditional_targets = (Name *) getmem(cnt * sizeof(Name));
1927         for (cond_name = conditional_targets;
1928              cond_name != NULL;
1929              cond_name = cond_name->next) {
1930                 rp->conditional_targets[--cnt] = cond_name->name;
1931         }
1932 }
1933 
1934 /*
1935  *      parallel_ok(target, line_prop_must_exists)
1936  *
1937  *      Returns true if the target can be run in parallel
1938  *
1939  *      Return value:
1940  *                              True if can run in parallel
1941  *
1942  *      Parameters:
1943  *              target          Target being tested
1944  *
1945  *      Global variables used:
1946  *              all_parallel    True if all targets default to parallel
1947  *              only_parallel   True if no targets default to parallel
1948  */
1949 Boolean
1950 parallel_ok(Name target, Boolean line_prop_must_exists)
1951 {
1952         Boolean         assign;
1953         Boolean         make_refd;
1954         Property        line;
1955         Cmd_line        rule;
1956 
1957         assign = make_refd = false;
1958         if (((line = get_prop(target->prop, line_prop)) == NULL) &&
1959             line_prop_must_exists) {
1960                 return false;
1961         }
1962         if (line != NULL) {
1963                 for (rule = line->body.line.command_used;
1964                      rule != NULL;
1965                      rule = rule->next) {
1966                         if (rule->assign) {
1967                                 assign = true;
1968                         } else if (rule->make_refd) {
1969                                 make_refd = true;
1970                         }
1971                 }
1972         }
1973         if (assign) {
1974                 return false;
1975         } else if (target->parallel) {
1976                 return true;
1977         } else if (target->no_parallel) {
1978                 return false;
1979         } else if (all_parallel) {
1980                 return true;
1981         } else if (only_parallel) {
1982                 return false;
1983         } else if (make_refd) {
1984                 return false;
1985         } else {
1986                 return true;
1987         }
1988 }
1989 
1990 /*
1991  *      is_running(target)
1992  *
1993  *      Returns true if the target is running.
1994  *
1995  *      Return value:
1996  *                              True if target is running
1997  *
1998  *      Parameters:
1999  *              target          Target to check
2000  *
2001  *      Global variables used:
2002  *              running_list    List of running processes
2003  */
2004 Boolean
2005 is_running(Name target)
2006 {
2007         Running         rp;
2008 
2009         if (target->state != build_running) {
2010                 return false;
2011         }
2012         for (rp = running_list;
2013              rp != NULL && target != rp->target;
2014              rp = rp->next);
2015         if (rp == NULL) {
2016                 return false;
2017         } else {
2018                 return (rp->state == build_running) ? true : false;
2019         }
2020 }
2021 
2022 /*
2023  * This function replaces the makesh binary.
2024  */
2025  
2026 
2027 static pid_t
2028 run_rule_commands(char *host, char **commands)
2029 {
2030         Boolean         always_exec;
2031         Name            command;
2032         Boolean         ignore;
2033         int             length;
2034         Doname          result;
2035         Boolean         silent_flag;
2036         wchar_t         *tmp_wcs_buffer;
2037 
2038         childPid = fork();
2039         switch (childPid) {
2040         case -1:        /* Error */
2041                 fatal(catgets(catd, 1, 337, "Could not fork child process for dmake job: %s"),
2042                       errmsg(errno));
2043                 break;
2044         case 0:         /* Child */
2045                 /* To control the processed targets list is not the child's business */
2046                 running_list = NULL;
2047 #if defined(REDIRECT_ERR)
2048                 if(out_err_same) {
2049                         redirect_io(stdout_file, (char*)NULL);
2050                 } else {
2051                         redirect_io(stdout_file, stderr_file);
2052                 }
2053 #else
2054                 redirect_io(stdout_file, (char*)NULL);
2055 #endif
2056                 for (commands = commands;
2057                      (*commands != (char *)NULL);
2058                      commands++) {
2059                         silent_flag = silent;
2060                         ignore = false;
2061                         always_exec = false;
2062                         while ((**commands == (int) at_char) ||
2063                                (**commands == (int) hyphen_char) ||
2064                                (**commands == (int) plus_char)) {
2065                                 if (**commands == (int) at_char) {
2066                                         silent_flag = true;
2067                                 }
2068                                 if (**commands == (int) hyphen_char) {
2069                                         ignore = true;
2070                                 }
2071                                 if (**commands == (int) plus_char) {
2072                                         always_exec = true;
2073                                 }
2074                                 (*commands)++;
2075                         }
2076                         if ((length = strlen(*commands)) >= MAXPATHLEN) {
2077                                 tmp_wcs_buffer = ALLOC_WC(length + 1);
2078                                 (void) mbstowcs(tmp_wcs_buffer, *commands, length + 1);
2079                                 command = GETNAME(tmp_wcs_buffer, FIND_LENGTH);
2080                                 retmem(tmp_wcs_buffer);
2081                         } else {
2082                                 MBSTOWCS(wcs_buffer, *commands);
2083                                 command = GETNAME(wcs_buffer, FIND_LENGTH);
2084                         }
2085                         if ((command->hash.length > 0) &&
2086                             !silent_flag) {
2087                                 (void) printf("%s\n", command->string_mb);
2088                         }
2089                         result = dosys(command,
2090                                        ignore,
2091                                        false,
2092                                        false, /* bugs #4085164 & #4990057 */
2093                                        /* BOOLEAN(silent_flag && ignore), */
2094                                        always_exec, 
2095                                        (Name) NULL,
2096                                        false);
2097                         if (result == build_failed) {
2098                                 if (silent_flag) {
2099                                         (void) printf(catgets(catd, 1, 152, "The following command caused the error:\n%s\n"), command->string_mb);
2100                                 }
2101                                 if (!ignore) {
2102                                         _exit(1);
2103                                 }
2104                         }
2105                 }
2106                 _exit(0);
2107                 break;
2108         default:
2109                 break;
2110         }
2111         return childPid;
2112 }
2113 
2114 static void
2115 maybe_reread_make_state(void)
2116 {
2117         /* Copying dosys()... */
2118         if (report_dependencies_level == 0) {
2119                 make_state->stat.time = file_no_time;
2120                 (void) exists(make_state);
2121                 if (make_state_before == make_state->stat.time) {
2122                         return;
2123                 }
2124                 makefile_type = reading_statefile;
2125                 if (read_trace_level > 1) {
2126                         trace_reader = true;
2127                 }
2128                 temp_file_number++;
2129                 (void) read_simple_file(make_state,
2130                                         false,
2131                                         false,
2132                                         false,
2133                                         false,
2134                                         false,
2135                                         true);
2136                 trace_reader = false;
2137         }
2138 }
2139 
2140 #ifdef DISTRIBUTED
2141 /*
2142  * Create and send an Avo_MToolJobResultMsg.
2143  */
2144 static void
2145 send_job_result_msg(Running rp)
2146 {
2147         Avo_MToolJobResultMsg   *msg;
2148         RWCollectable           *xdr_msg;
2149 
2150         msg = new Avo_MToolJobResultMsg();
2151         msg->setResult(rp->job_msg_id,
2152                        (rp->state == build_ok) ? 0 : 1,
2153                        DONE);
2154         append_job_result_msg(msg,
2155                                 rp->stdout_file,
2156                                 rp->stderr_file);
2157 
2158         xdr_msg = (RWCollectable *)msg;
2159         xdr(get_xdrs_ptr(), xdr_msg);
2160         (void) fflush(get_mtool_msgs_fp());
2161 
2162         delete msg;
2163 }
2164 
2165 /*
2166  * Append the stdout/err to Avo_MToolJobResultMsg.
2167  */
2168 static void
2169 append_job_result_msg(Avo_MToolJobResultMsg *msg, char *outFn, char *errFn)
2170 {
2171         FILE            *fp;
2172         char            line[MAXPATHLEN];
2173 
2174         fp = fopen(outFn, "r");
2175         if (fp == NULL) {
2176                 /* Hmmm... what should we do here? */
2177                 return;
2178         }
2179         while (fgets(line, MAXPATHLEN, fp) != NULL) {
2180                 if (line[strlen(line) - 1] == '\n') {
2181                         line[strlen(line) - 1] = '\0';
2182                 }
2183                 msg->appendOutput(AVO_STRDUP(line));
2184         }
2185         (void) fclose(fp);
2186 }
2187 #endif
2188 
2189 static void
2190 delete_running_struct(Running rp)
2191 {
2192         if ((rp->conditional_cnt > 0) &&
2193             (rp->conditional_targets != NULL)) {
2194                 retmem_mb((char *) rp->conditional_targets);
2195         }
2196 /**/
2197         if ((rp->auto_count > 0) &&
2198             (rp->automatics != NULL)) {
2199                 retmem_mb((char *) rp->automatics);
2200         }
2201 /**/
2202         if(rp->sprodep_value) {
2203                 free_name(rp->sprodep_value);
2204         }
2205         if(rp->sprodep_env) {
2206                 retmem_mb(rp->sprodep_env);
2207         }
2208         retmem_mb((char *) rp);
2209 
2210 }
2211 
2212 #endif
2213