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