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