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)
 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         Boolean         nohang;
1326         pid_t           pid;
1327         int             status;
1328         Running         rp;
1329         int             waiterr;
1330 
1331         nohang = false;
1332         for ( ; ; ) {
1333                 if (!nohang) {
1334                         (void) alarm((int) update_delay);
1335                 }
1336                 pid = waitpid((pid_t)-1,
1337                               &status,
1338                               nohang ? WNOHANG : 0);
1339                 waiterr = errno;
1340                 if (!nohang) {
1341                         (void) alarm(0);
1342                 }
1343                 if (pid <= 0) {
1344                         if (waiterr == EINTR) {
1345                                 if (waitflg) {
1346                                         continue;
1347                                 } else {
1348                                         return;
1349                                 }
1350                         } else {
1351                                 return;
1352                         }
1353                 }
1354                 for (rp = running_list;
1355                      (rp != NULL) && (rp->pid != pid);
1356                      rp = rp->next) {
1357                         ;
1358                 }
1359                 if (rp == NULL) {
1360                         if (send_mtool_msgs) {
1361                                 continue;
1362                         } else {
1363                                 fatal(catgets(catd, 1, 128, "Internal error: returned child pid not in running_list"));
1364                         }
1365                 } else {
1366                         rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed;
1367                 }
1368                 nohang = true;
1369                 parallel_process_cnt--;
1370 
1371 #if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000)
1372                 if (job_adjust_mode == ADJUST_M2) {
1373                         if (m2_release_job()) {
1374                                 job_adjust_error();
1375                         }
1376                 }
1377 #endif
1378         }
1379 }
1380 
1381 /*
1382  *      finish_children(docheck)
1383  *
1384  *      Finishes the processing for all targets which were running
1385  *      and have now completed.
1386  *
1387  *      Parameters:
1388  *              docheck         Completely check the finished target
1389  *
1390  *      Static variables used:
1391  *              running_tail    The tail of the running list
1392  *
1393  *      Global variables used:
1394  *              continue_after_error  -k flag
1395  *              fatal_in_progress  True if we are finishing up after fatal err
1396  *              running_list    List of running processes
1397  */
1398 void
1399 finish_children(Boolean docheck)
1400 {
1401         int             cmds_length;
1402         Property        line;
1403         Property        line2;
1404         struct stat     out_buf;
1405         Running         rp;
1406         Running         *rp_prev;
1407         Cmd_line        rule;
1408         Boolean         silent_flag;
1409 
1410         for (rp_prev = &running_list, rp = running_list;
1411              rp != NULL;
1412              rp = rp->next) {
1413 bypass_for_loop_inc_4:
1414                 /*
1415                  * If the state is ok or failed, then this target has
1416                  * finished building.
1417                  * In parallel_mode, output the accumulated stdout/stderr.
1418                  * Read the auto dependency stuff, handle a failed build,
1419                  * update the target, then finish the doname process for
1420                  * that target.
1421                  */
1422                 if (rp->state == build_ok || rp->state == build_failed) {
1423                         *rp_prev = rp->next;
1424                         if (rp->next == NULL) {
1425                                 running_tail = rp_prev;
1426                         }
1427                         if ((line2 = rp->command) == NULL) {
1428                                 line2 = get_prop(rp->target->prop, line_prop);
1429                         }
1430                         if (dmake_mode_type == distributed_mode) {
1431                                 if (rp->make_refd) {
1432                                         maybe_reread_make_state();
1433                                 }
1434                         } else {
1435                                 /*
1436                                  * Send an Avo_MToolJobResultMsg to maketool.
1437                                  */
1438 #ifdef DISTRIBUTED
1439                                 if (send_mtool_msgs) {
1440                                         send_job_result_msg(rp);
1441                                 }
1442 #endif
1443                                 /*
1444                                  * Check if there were any job output
1445                                  * from the parallel build.
1446                                  */
1447                                 if (rp->stdout_file != NULL) {
1448                                         if (stat(rp->stdout_file, &out_buf) < 0) {
1449                                                 fatal(catgets(catd, 1, 130, "stat of %s failed: %s"),
1450                                                       rp->stdout_file,
1451                                                       errmsg(errno));
1452                                         }
1453                                         if ((line2 != NULL) &&
1454                                             (out_buf.st_size > 0)) {
1455                                                 cmds_length = 0;
1456                                                 for (rule = line2->body.line.command_used,
1457                                                      silent_flag = silent;
1458                                                      rule != NULL;
1459                                                      rule = rule->next) {
1460                                                         cmds_length += rule->command_line->hash.length + 1;
1461                                                         silent_flag = BOOLEAN(silent_flag || rule->silent);
1462                                                 }
1463                                                 if (out_buf.st_size != cmds_length || silent_flag ||
1464                                                     output_mode == txt2_mode) {
1465                                                         dump_out_file(rp->stdout_file, false);
1466                                                 }
1467                                         }
1468                                         (void) unlink(rp->stdout_file);
1469                                         retmem_mb(rp->stdout_file);
1470                                         rp->stdout_file = NULL;
1471                                 }
1472 
1473                                 if (!out_err_same && (rp->stderr_file != NULL)) {
1474                                         if (stat(rp->stderr_file, &out_buf) < 0) {
1475                                                 fatal(catgets(catd, 1, 130, "stat of %s failed: %s"),
1476                                                       rp->stderr_file,
1477                                                       errmsg(errno));
1478                                         }
1479                                         if ((line2 != NULL) &&
1480                                             (out_buf.st_size > 0)) {
1481                                                 dump_out_file(rp->stderr_file, true);
1482                                         }
1483                                         (void) unlink(rp->stderr_file);
1484                                         retmem_mb(rp->stderr_file);
1485                                         rp->stderr_file = NULL;
1486                                 }
1487                         }
1488                         check_state(rp->temp_file);
1489                         if (rp->temp_file != NULL) {
1490                                 free_name(rp->temp_file);
1491                         }
1492                         rp->temp_file = NULL;
1493                         if (rp->state == build_failed) {
1494                                 line = get_prop(rp->target->prop, line_prop);
1495                                 if (line != NULL) {
1496                                         line->body.line.command_used = NULL;
1497                                 }
1498                                 if (continue_after_error ||
1499                                     fatal_in_progress ||
1500                                     !docheck) {
1501                                         warning(catgets(catd, 1, 256, "Command failed for target `%s'"),
1502                                                 rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1503                                         build_failed_seen = true;
1504                                 } else {
1505                                         /*
1506                                          * XXX??? - DMake needs to exit(),
1507                                          * but shouldn't call fatal().
1508                                          */
1509 #ifdef PRINT_EXIT_STATUS
1510                                         warning(NOCATGETS("I'm in finish_children. rp->state == build_failed."));
1511 #endif
1512 
1513                                         fatal(catgets(catd, 1, 258, "Command failed for target `%s'"),
1514                                                 rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1515                                 }
1516                         }
1517                         if (!docheck) {
1518                                 delete_running_struct(rp);
1519                                 rp = *rp_prev;
1520                                 if (rp == NULL) {
1521                                         break;
1522                                 } else {
1523                                         goto bypass_for_loop_inc_4;
1524                                 }
1525                         }
1526                         update_target(get_prop(rp->target->prop, line_prop),
1527                                       rp->state);
1528                         finish_doname(rp);
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                 } else {
1537                         rp_prev = &rp->next;
1538                 }
1539         }
1540 }
1541 
1542 /*
1543  *      dump_out_file(filename, err)
1544  *
1545  *      Write the contents of the file to stdout, then unlink the file.
1546  *
1547  *      Parameters:
1548  *              filename        Name of temp file containing output
1549  *
1550  *      Global variables used:
1551  */
1552 static void
1553 dump_out_file(char *filename, Boolean err)
1554 {
1555         int             chars_read;
1556         char            copybuf[BUFSIZ];
1557         int             fd;
1558         int             out_fd = (err ? 2 : 1);
1559 
1560         if ((fd = open(filename, O_RDONLY)) < 0) {
1561                 fatal(catgets(catd, 1, 141, "open failed for output file %s: %s"),
1562                       filename,
1563                       errmsg(errno));
1564         }
1565         if (!silent && output_mode != txt2_mode) {
1566                 (void) fprintf(err ? stderr : stdout,
1567                                err ?
1568                                 catgets(catd, 1, 338, "%s --> Job errors\n") :
1569                                 catgets(catd, 1, 259, "%s --> Job output\n"),
1570                                local_host);
1571                 (void) fflush(err ? stderr : stdout);
1572         }
1573         for (chars_read = read(fd, copybuf, BUFSIZ);
1574              chars_read > 0;
1575              chars_read = read(fd, copybuf, BUFSIZ)) {
1576                 /*
1577                  * Read buffers from the source file until end or error.
1578                  */
1579                 if (write(out_fd, copybuf, chars_read) < 0) {
1580                         fatal(catgets(catd, 1, 260, "write failed for output file %s: %s"),
1581                               filename,
1582                               errmsg(errno));
1583                 }
1584         }
1585         (void) close(fd);
1586         (void) unlink(filename);
1587 }
1588 
1589 /*
1590  *      finish_doname(rp)
1591  *
1592  *      Completes the processing for a target which was left running.
1593  *
1594  *      Parameters:
1595  *              rp              Running list entry for target
1596  *
1597  *      Global variables used:
1598  *              debug_level     Debug flag
1599  *              recursion_level Indentation for debug output
1600  */
1601 static void
1602 finish_doname(Running rp)
1603 {
1604         int             auto_count = rp->auto_count;
1605         Name            *automatics = rp->automatics;
1606         Doname          result = rp->state;
1607         Name            target = rp->target;
1608         Name            true_target = rp->true_target;
1609         Property        *conditionals;
1610 
1611         recursion_level = rp->recursion_level;
1612         if (result == build_ok) {
1613                 if (true_target == NULL) {
1614                         (void) printf(NOCATGETS("Target = %s\n"), target->string_mb);
1615                         (void) printf(NOCATGETS(" State = %d\n"), result);
1616                         fatal(NOCATGETS("Internal error: NULL true_target in finish_doname"));
1617                 }
1618                 /* If all went OK, set a nice timestamp */
1619                 if (true_target->stat.time == file_doesnt_exist) {
1620                         true_target->stat.time = file_max_time;
1621                 }
1622         }
1623         target->state = result;
1624         if (target->is_member) {
1625                 Property member;
1626 
1627                 /* Propagate the timestamp from the member file to the member */
1628                 if ((target->stat.time != file_max_time) &&
1629                     ((member = get_prop(target->prop, member_prop)) != NULL) &&
1630                     (exists(member->body.member.member) > file_doesnt_exist)) {
1631                         target->stat.time =
1632 /*
1633                           exists(member->body.member.member);
1634  */
1635                           member->body.member.member->stat.time;
1636                 }
1637         }
1638         /*
1639          * Check if we found any new auto dependencies when we
1640          * built the target.
1641          */
1642         if ((result == build_ok) && check_auto_dependencies(target,
1643                                                             auto_count,
1644                                                             automatics)) {
1645                 if (debug_level > 0) {
1646                         (void) printf(catgets(catd, 1, 261, "%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"),
1647                                       recursion_level,
1648                                       "",
1649                                       true_target->string_mb);
1650                 }
1651                 target->rechecking_target = true;
1652                 target->state = build_running;
1653 
1654                 /* [tolik, Tue Mar 25 1997]
1655                  * Fix for bug 4038824:
1656                  *       command line options set by conditional macros get dropped
1657                  * rp->conditional_cnt and rp->conditional_targets must be copied
1658                  * to new 'rp' during add_pending(). Set_conditionals() stores
1659                  * rp->conditional_targets to the global variable 'conditional_targets'
1660                  * Add_pending() will use this variable to set up 'rp'.
1661                  */
1662                 conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets);
1663                 add_pending(target,
1664                             recursion_level,
1665                             rp->do_get,
1666                             rp->implicit,
1667                             true);
1668                 reset_conditionals(rp->conditional_cnt, rp->conditional_targets, conditionals);
1669         }
1670 }
1671 
1672 /*
1673  *      new_running_struct()
1674  *
1675  *      Constructor for Running struct. Creates a structure and initializes
1676  *      its fields.
1677  *
1678  */
1679 static Running new_running_struct()
1680 {
1681         Running         rp;
1682 
1683         rp = ALLOC(Running);
1684         rp->target = NULL;
1685         rp->true_target = NULL;
1686         rp->command = NULL;
1687         rp->sprodep_value = NULL;
1688         rp->sprodep_env = NULL;
1689         rp->auto_count = 0;
1690         rp->automatics = NULL;
1691         rp->pid = -1;
1692         rp->job_msg_id = -1;
1693         rp->stdout_file = NULL;
1694         rp->stderr_file = NULL;
1695         rp->temp_file = NULL;
1696         rp->next = NULL;
1697         return rp;
1698 }
1699 
1700 /*
1701  *      add_running(target, true_target, command, recursion_level, auto_count,
1702  *                                      automatics, do_get, implicit)
1703  *
1704  *      Adds a record on the running list for this target, which
1705  *      was just spawned and is running.
1706  *
1707  *      Parameters:
1708  *              target          Target being built
1709  *              true_target     True target for target
1710  *              command         Running command.
1711  *              recursion_level Debug indentation level
1712  *              auto_count      Count of automatic dependencies
1713  *              automatics      List of automatic dependencies
1714  *              do_get          Sccs get flag
1715  *              implicit        Implicit flag
1716  *
1717  *      Static variables used:
1718  *              running_tail    Tail of running list
1719  *              process_running PID of process
1720  *
1721  *      Global variables used:
1722  *              current_line    Current line for target
1723  *              current_target  Current target being built
1724  *              stderr_file     Temporary file for stdout
1725  *              stdout_file     Temporary file for stdout
1726  *              temp_file_name  Temporary file for auto dependencies
1727  */
1728 void
1729 add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit)
1730 {
1731         Running         rp;
1732         Name            *p;
1733 
1734         rp = new_running_struct();
1735         rp->state = build_running;
1736         rp->target = target;
1737         rp->true_target = true_target;
1738         rp->command = command;
1739         Property spro_val = get_prop(sunpro_dependencies->prop, macro_prop);
1740         if(spro_val) {
1741                 rp->sprodep_value = spro_val->body.macro.value;
1742                 spro_val->body.macro.value = NULL;
1743                 spro_val = get_prop(sunpro_dependencies->prop, env_mem_prop);
1744                 if(spro_val) {
1745                         rp->sprodep_env = spro_val->body.env_mem.value;
1746                         spro_val->body.env_mem.value = NULL;
1747                 }
1748         }
1749         rp->recursion_level = recursion_level;
1750         rp->do_get = do_get;
1751         rp->implicit = implicit;
1752         rp->auto_count = auto_count;
1753         if (auto_count > 0) {
1754                 rp->automatics = (Name *) getmem(auto_count * sizeof (Name));
1755                 for (p = rp->automatics; auto_count > 0; auto_count--) {
1756                         *p++ = *automatics++;
1757                 }
1758         } else {
1759                 rp->automatics = NULL;
1760         }
1761 #ifdef DISTRIBUTED
1762         if (dmake_mode_type == distributed_mode) {
1763                 rp->make_refd = called_make;
1764                 called_make = false;
1765         } else
1766 #endif
1767         {
1768                 rp->pid = process_running;
1769                 process_running = -1;
1770                 childPid = -1;
1771         }
1772         rp->job_msg_id = job_msg_id;
1773         rp->stdout_file = stdout_file;
1774         rp->stderr_file = stderr_file;
1775         rp->temp_file = temp_file_name;
1776         rp->redo = false;
1777         rp->next = NULL;
1778         store_conditionals(rp);
1779         stdout_file = NULL;
1780         stderr_file = NULL;
1781         temp_file_name = NULL;
1782         current_target = NULL;
1783         current_line = NULL;
1784         *running_tail = rp;
1785         running_tail = &rp->next;
1786 }
1787 
1788 /*
1789  *      add_pending(target, recursion_level, do_get, implicit, redo)
1790  *
1791  *      Adds a record on the running list for a pending target
1792  *      (waiting for its dependents to finish running).
1793  *
1794  *      Parameters:
1795  *              target          Target being built
1796  *              recursion_level Debug indentation level
1797  *              do_get          Sccs get flag
1798  *              implicit        Implicit flag
1799  *              redo            True if this target is being redone
1800  *
1801  *      Static variables used:
1802  *              running_tail    Tail of running list
1803  */
1804 void
1805 add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo)
1806 {
1807         Running         rp;
1808         rp = new_running_struct();
1809         rp->state = build_pending;
1810         rp->target = target;
1811         rp->recursion_level = recursion_level;
1812         rp->do_get = do_get;
1813         rp->implicit = implicit;
1814         rp->redo = redo;
1815         store_conditionals(rp);
1816         *running_tail = rp;
1817         running_tail = &rp->next;
1818 }
1819 
1820 /*
1821  *      add_serial(target, recursion_level, do_get, implicit)
1822  *
1823  *      Adds a record on the running list for a target which must be
1824  *      executed in serial after others have finished.
1825  *
1826  *      Parameters:
1827  *              target          Target being built
1828  *              recursion_level Debug indentation level
1829  *              do_get          Sccs get flag
1830  *              implicit        Implicit flag
1831  *
1832  *      Static variables used:
1833  *              running_tail    Tail of running list
1834  */
1835 void
1836 add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit)
1837 {
1838         Running         rp;
1839 
1840         rp = new_running_struct();
1841         rp->target = target;
1842         rp->recursion_level = recursion_level;
1843         rp->do_get = do_get;
1844         rp->implicit = implicit;
1845         rp->state = build_serial;
1846         rp->redo = false;
1847         store_conditionals(rp);
1848         *running_tail = rp;
1849         running_tail = &rp->next;
1850 }
1851 
1852 /*
1853  *      add_subtree(target, recursion_level, do_get, implicit)
1854  *
1855  *      Adds a record on the running list for a target which must be
1856  *      executed in isolation after others have finished.
1857  *
1858  *      Parameters:
1859  *              target          Target being built
1860  *              recursion_level Debug indentation level
1861  *              do_get          Sccs get flag
1862  *              implicit        Implicit flag
1863  *
1864  *      Static variables used:
1865  *              running_tail    Tail of running list
1866  */
1867 void
1868 add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit)
1869 {
1870         Running         rp;
1871 
1872         rp = new_running_struct();
1873         rp->target = target;
1874         rp->recursion_level = recursion_level;
1875         rp->do_get = do_get;
1876         rp->implicit = implicit;
1877         rp->state = build_subtree;
1878         rp->redo = false;
1879         store_conditionals(rp);
1880         *running_tail = rp;
1881         running_tail = &rp->next;
1882 }
1883 
1884 /*
1885  *      store_conditionals(rp)
1886  *
1887  *      Creates an array of the currently active targets with conditional
1888  *      macros (found in the chain conditional_targets) and puts that
1889  *      array in the Running struct.
1890  *
1891  *      Parameters:
1892  *              rp              Running struct for storing chain
1893  *
1894  *      Global variables used:
1895  *              conditional_targets  Chain of current dynamic conditionals
1896  */
1897 static void
1898 store_conditionals(Running rp)
1899 {
1900         int             cnt;
1901         Chain           cond_name;
1902 
1903         if (conditional_targets == NULL) {
1904                 rp->conditional_cnt = 0;
1905                 rp->conditional_targets = NULL;
1906                 return;
1907         }
1908         cnt = 0;
1909         for (cond_name = conditional_targets;
1910              cond_name != NULL;
1911              cond_name = cond_name->next) {
1912                 cnt++;
1913         }
1914         rp->conditional_cnt = cnt;
1915         rp->conditional_targets = (Name *) getmem(cnt * sizeof(Name));
1916         for (cond_name = conditional_targets;
1917              cond_name != NULL;
1918              cond_name = cond_name->next) {
1919                 rp->conditional_targets[--cnt] = cond_name->name;
1920         }
1921 }
1922 
1923 /*
1924  *      parallel_ok(target, line_prop_must_exists)
1925  *
1926  *      Returns true if the target can be run in parallel
1927  *
1928  *      Return value:
1929  *                              True if can run in parallel
1930  *
1931  *      Parameters:
1932  *              target          Target being tested
1933  *
1934  *      Global variables used:
1935  *              all_parallel    True if all targets default to parallel
1936  *              only_parallel   True if no targets default to parallel
1937  */
1938 Boolean
1939 parallel_ok(Name target, Boolean line_prop_must_exists)
1940 {
1941         Boolean         assign;
1942         Boolean         make_refd;
1943         Property        line;
1944         Cmd_line        rule;
1945 
1946         assign = make_refd = false;
1947         if (((line = get_prop(target->prop, line_prop)) == NULL) &&
1948             line_prop_must_exists) {
1949                 return false;
1950         }
1951         if (line != NULL) {
1952                 for (rule = line->body.line.command_used;
1953                      rule != NULL;
1954                      rule = rule->next) {
1955                         if (rule->assign) {
1956                                 assign = true;
1957                         } else if (rule->make_refd) {
1958                                 make_refd = true;
1959                         }
1960                 }
1961         }
1962         if (assign) {
1963                 return false;
1964         } else if (target->parallel) {
1965                 return true;
1966         } else if (target->no_parallel) {
1967                 return false;
1968         } else if (all_parallel) {
1969                 return true;
1970         } else if (only_parallel) {
1971                 return false;
1972         } else if (make_refd) {
1973                 return false;
1974         } else {
1975                 return true;
1976         }
1977 }
1978 
1979 /*
1980  *      is_running(target)
1981  *
1982  *      Returns true if the target is running.
1983  *
1984  *      Return value:
1985  *                              True if target is running
1986  *
1987  *      Parameters:
1988  *              target          Target to check
1989  *
1990  *      Global variables used:
1991  *              running_list    List of running processes
1992  */
1993 Boolean
1994 is_running(Name target)
1995 {
1996         Running         rp;
1997 
1998         if (target->state != build_running) {
1999                 return false;
2000         }
2001         for (rp = running_list;
2002              rp != NULL && target != rp->target;
2003              rp = rp->next);
2004         if (rp == NULL) {
2005                 return false;
2006         } else {
2007                 return (rp->state == build_running) ? true : false;
2008         }
2009 }
2010 
2011 /*
2012  * This function replaces the makesh binary.
2013  */
2014  
2015 
2016 static pid_t
2017 run_rule_commands(char *host, char **commands)
2018 {
2019         Boolean         always_exec;
2020         Name            command;
2021         Boolean         ignore;
2022         int             length;
2023         Doname          result;
2024         Boolean         silent_flag;
2025         wchar_t         *tmp_wcs_buffer;
2026 
2027         childPid = fork();
2028         switch (childPid) {
2029         case -1:        /* Error */
2030                 fatal(catgets(catd, 1, 337, "Could not fork child process for dmake job: %s"),
2031                       errmsg(errno));
2032                 break;
2033         case 0:         /* Child */
2034                 /* To control the processed targets list is not the child's business */
2035                 running_list = NULL;
2036                 if(out_err_same) {
2037                         redirect_io(stdout_file, (char*)NULL);
2038                 } else {
2039                         redirect_io(stdout_file, stderr_file);
2040                 }
2041                 for (commands = commands;
2042                      (*commands != (char *)NULL);
2043                      commands++) {
2044                         silent_flag = silent;
2045                         ignore = false;
2046                         always_exec = false;
2047                         while ((**commands == (int) at_char) ||
2048                                (**commands == (int) hyphen_char) ||
2049                                (**commands == (int) plus_char)) {
2050                                 if (**commands == (int) at_char) {
2051                                         silent_flag = true;
2052                                 }
2053                                 if (**commands == (int) hyphen_char) {
2054                                         ignore = true;
2055                                 }
2056                                 if (**commands == (int) plus_char) {
2057                                         always_exec = true;
2058                                 }
2059                                 (*commands)++;
2060                         }
2061                         if ((length = strlen(*commands)) >= MAXPATHLEN) {
2062                                 tmp_wcs_buffer = ALLOC_WC(length + 1);
2063                                 (void) mbstowcs(tmp_wcs_buffer, *commands, length + 1);
2064                                 command = GETNAME(tmp_wcs_buffer, FIND_LENGTH);
2065                                 retmem(tmp_wcs_buffer);
2066                         } else {
2067                                 MBSTOWCS(wcs_buffer, *commands);
2068                                 command = GETNAME(wcs_buffer, FIND_LENGTH);
2069                         }
2070                         if ((command->hash.length > 0) &&
2071                             !silent_flag) {
2072                                 (void) printf("%s\n", command->string_mb);
2073                         }
2074                         result = dosys(command,
2075                                        ignore,
2076                                        false,
2077                                        false, /* bugs #4085164 & #4990057 */
2078                                        /* BOOLEAN(silent_flag && ignore), */
2079                                        always_exec, 
2080                                        (Name) NULL,
2081                                        false);
2082                         if (result == build_failed) {
2083                                 if (silent_flag) {
2084                                         (void) printf(catgets(catd, 1, 152, "The following command caused the error:\n%s\n"), command->string_mb);
2085                                 }
2086                                 if (!ignore) {
2087                                         _exit(1);
2088                                 }
2089                         }
2090                 }
2091                 _exit(0);
2092                 break;
2093         default:
2094                 break;
2095         }
2096         return childPid;
2097 }
2098 
2099 static void
2100 maybe_reread_make_state(void)
2101 {
2102         /* Copying dosys()... */
2103         if (report_dependencies_level == 0) {
2104                 make_state->stat.time = file_no_time;
2105                 (void) exists(make_state);
2106                 if (make_state_before == make_state->stat.time) {
2107                         return;
2108                 }
2109                 makefile_type = reading_statefile;
2110                 if (read_trace_level > 1) {
2111                         trace_reader = true;
2112                 }
2113                 temp_file_number++;
2114                 (void) read_simple_file(make_state,
2115                                         false,
2116                                         false,
2117                                         false,
2118                                         false,
2119                                         false,
2120                                         true);
2121                 trace_reader = false;
2122         }
2123 }
2124 
2125 #ifdef DISTRIBUTED
2126 /*
2127  * Create and send an Avo_MToolJobResultMsg.
2128  */
2129 static void
2130 send_job_result_msg(Running rp)
2131 {
2132         Avo_MToolJobResultMsg   *msg;
2133         RWCollectable           *xdr_msg;
2134 
2135         msg = new Avo_MToolJobResultMsg();
2136         msg->setResult(rp->job_msg_id,
2137                        (rp->state == build_ok) ? 0 : 1,
2138                        DONE);
2139         append_job_result_msg(msg,
2140                                 rp->stdout_file,
2141                                 rp->stderr_file);
2142 
2143         xdr_msg = (RWCollectable *)msg;
2144         xdr(get_xdrs_ptr(), xdr_msg);
2145         (void) fflush(get_mtool_msgs_fp());
2146 
2147         delete msg;
2148 }
2149 
2150 /*
2151  * Append the stdout/err to Avo_MToolJobResultMsg.
2152  */
2153 static void
2154 append_job_result_msg(Avo_MToolJobResultMsg *msg, char *outFn, char *errFn)
2155 {
2156         FILE            *fp;
2157         char            line[MAXPATHLEN];
2158 
2159         fp = fopen(outFn, "r");
2160         if (fp == NULL) {
2161                 /* Hmmm... what should we do here? */
2162                 return;
2163         }
2164         while (fgets(line, MAXPATHLEN, fp) != NULL) {
2165                 if (line[strlen(line) - 1] == '\n') {
2166                         line[strlen(line) - 1] = '\0';
2167                 }
2168                 msg->appendOutput(AVO_STRDUP(line));
2169         }
2170         (void) fclose(fp);
2171 }
2172 #endif
2173 
2174 static void
2175 delete_running_struct(Running rp)
2176 {
2177         if ((rp->conditional_cnt > 0) &&
2178             (rp->conditional_targets != NULL)) {
2179                 retmem_mb((char *) rp->conditional_targets);
2180         }
2181 /**/
2182         if ((rp->auto_count > 0) &&
2183             (rp->automatics != NULL)) {
2184                 retmem_mb((char *) rp->automatics);
2185         }
2186 /**/
2187         if(rp->sprodep_value) {
2188                 free_name(rp->sprodep_value);
2189         }
2190         if(rp->sprodep_env) {
2191                 retmem_mb(rp->sprodep_env);
2192         }
2193         retmem_mb((char *) rp);
2194 
2195 }
2196 
2197 #endif
2198