LCOV - code coverage report
Current view: top level - libreoffice/dmake/unix - runargv.c (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 161 264 61.0 %
Date: 2012-12-17 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             : --
       3             : -- SYNOPSIS
       4             : --      Invoke a sub process.
       5             : --
       6             : -- DESCRIPTION
       7             : --  Use the standard methods of executing a sub process.
       8             : --
       9             : -- AUTHOR
      10             : --      Dennis Vadura, dvadura@dmake.wticorp.com
      11             : --
      12             : -- WWW
      13             : --      http://dmake.wticorp.com/
      14             : --
      15             : -- COPYRIGHT
      16             : --      Copyright (c) 1996,1997 by WTI Corp.  All rights reserved.
      17             : --
      18             : --      This program is NOT free software; you can redistribute it and/or
      19             : --      modify it under the terms of the Software License Agreement Provided
      20             : --      in the file <distribution-root>/readme/license.txt.
      21             : --
      22             : -- LOG
      23             : --      Use cvs log to obtain detailed change logs.
      24             : */
      25             : /*
      26             : This file (runargv.c) provides all the parallel process handling routines
      27             : for dmake on unix like operating systems. The following text briefly
      28             : describes the process flow.
      29             : 
      30             : Exec_commands() [make.c] builds the recipes associated to the given target.
      31             :   They are build sequentially in a loop that calls Do_cmnd() for each of them.
      32             : 
      33             : Do_cmnd() [sysintf.c] feeds the given command or command group to runargv().
      34             : 
      35             : The following flowchart decripes the process flow starting with runargv,
      36             : descriptions for each of the functions are following.
      37             : 
      38             :   +--------------------------------+
      39             :   |            runargv             | <+
      40             :   +--------------------------------+  |
      41             :     |               ^                 |
      42             :     |               | returns if      |
      43             :     | calls         | wfc is false    |
      44             :     v               |                 |
      45             :   +--------------------------------+  |
      46             :   |           _add_child           |  |
      47             :   +--------------------------------+  |
      48             :     |               ^                 |
      49             :     | calls if      |                 | if another process
      50             :     | wfc is true   | returns         | is queued:
      51             :     v               |                 | recursive call
      52             :   +--------------------------------+  |
      53             :   |         Wait_for_Child         |  |
      54             :   +--------------------------------+  |
      55             :     |               ^                 |
      56             :     |               | process queue   |
      57             :     | calls         | is empty        |
      58             :     v               |                 |
      59             :   +--------------------------------+  |
      60             :   |        _finished_child         | -+
      61             :   +--------------------------------+
      62             : 
      63             : 
      64             : 
      65             : runargv() [unix/runargv] The runargv function manages up to MAXPROCESS
      66             :   process queues (_procs[i]) for parallel process execution and hands
      67             :   the actual commands down to the operating system.
      68             :   Each of the process queues handles the sequential execution of commands
      69             :   that  belong to that process queue. Usually this means the sequential
      70             :   execution of the recipe lines that belong to one target.
      71             :   Even in non parallel builds (MAXPROCESS==1) child processes are
      72             :   created and handled.
      73             :   If recipes for a target are currently running attach them to the
      74             :   corresponding process queue (_procs[i]) of that target and return.
      75             :   If the maximum number (MAXPROCESS) of concurrently running queues is
      76             :   reached use Wait_for_child(?, -1) to wait for a process queue to become
      77             :   available.
      78             :   New child processes are started using:
      79             :     spawn:       posix_spawnp (POSIX) or spawnvp (cygwin).
      80             :     fork/execvp: Create a client process with fork and run the command
      81             :                  with execvp.
      82             :   The parent calls _add_child() to track the child.
      83             : 
      84             : _add_child(..., wfc) [unix/runargv] creates (or reuses) a process queue
      85             :   and enters the child's parameters.
      86             :   If wfc (wait for completion) is TRUE the function calls
      87             :   Wait_for_child to wait for the whole process queue to be finished.
      88             : 
      89             : Wait_for_child(abort_flg, pqid) [unix/runargv] waits either for the current
      90             :   process from process queue pqid to finish or if the W_WFC attribute is
      91             :   set for all entries of that process queue (recursively) to finish.
      92             :   All finished processes are handled by calling _finished_child() for each
      93             :   of them.
      94             :   If pqid == -1 wait for the next process to finish but honor the A_WFC
      95             :   attribute of that process (queue) and wait for the whole queue if needed.
      96             :   If abort_flg is TRUE no further processes will be added to any process
      97             :   queue.
      98             :   If a pqid is given but a process from another process queue finishes
      99             :   first that process is handled and A_WFC is also honored.
     100             :   All finished processes are processed until the process from the given pqid
     101             :   is reached or gone (might have been handled while finishing another process
     102             :   queue).
     103             : 
     104             : _finished_child(pid, status) [unix/runargv] handles the finished child. If
     105             :   there are more commands in the corresponding process queue start the next
     106             :   with runargv().
     107             : */
     108             : 
     109             : #include <signal.h>
     110             : 
     111             : #include "extern.h"
     112             : 
     113             : #ifdef HAVE_WAIT_H
     114             : #  include <wait.h>
     115             : #else
     116             : # ifdef HAVE_SYS_WAIT_H
     117             : #  include <sys/wait.h>
     118             : # endif
     119             : #endif
     120             : 
     121             : #if HAVE_SPAWN_H && ENABLE_SPAWN
     122             : #  include <spawn.h>
     123             : #endif
     124             : 
     125             : #if __CYGWIN__ && ENABLE_SPAWN
     126             : #if HAVE_CYGWIN_PROCESS_H
     127             : #  include <cygwin/process.h>
     128             : #else
     129             : #  include <process.h>
     130             : #endif
     131             : #endif
     132             : 
     133             : #ifdef __EMX__
     134             : #  include <process.h>
     135             : #define _P_NOWAIT P_NOWAIT
     136             : #endif
     137             : 
     138             : #include "sysintf.h"
     139             : #if HAVE_ERRNO_H
     140             : #  include <errno.h>
     141             : #else
     142             :    extern  int  errno;
     143             : #endif
     144             : 
     145             : typedef struct prp {
     146             :    char *prp_cmd;
     147             :    int   prp_group;
     148             :    t_attr prp_attr;
     149             :    int   prp_last;
     150             :    struct prp *prp_next;
     151             : } RCP, *RCPPTR;
     152             : 
     153             : #if defined(USE_CREATEPROCESS)
     154             :    /* MS's HANDLE is basically a (void *) (winnt.h). */
     155             : typedef HANDLE DMHANDLE;
     156             : #else
     157             : typedef int DMHANDLE;
     158             : #endif
     159             : 
     160             : typedef struct pr {
     161             :    int      pr_valid;
     162             :    DMHANDLE pr_pid;
     163             :    DMHANDLE pr_tid;
     164             :    CELLPTR  pr_target;
     165             :    int      pr_ignore;
     166             :    int      pr_last;
     167             :    int      pr_wfc;
     168             :    RCPPTR   pr_recipe;
     169             :    RCPPTR   pr_recipe_end;
     170             :    char        *pr_dir;
     171             : } PR;
     172             : 
     173             : typedef struct tpid {
     174             :    DMHANDLE pid;
     175             :    DMHANDLE tid;
     176             : } TPID;
     177             : 
     178             : const TPID DMNOPID = { (DMHANDLE)-1, (DMHANDLE)0 };
     179             : 
     180             : static PR  *_procs    = NIL(PR); /* Array to hold concurrent processes. */
     181             : static int  _procs_size = 0;     /* Savegard to find MAXPROCESS changes. */
     182             : static int  _proc_cnt = 0;       /* Number of running processes. */
     183             : static int  _abort_flg= FALSE;
     184             : static int  _use_i    = -1;
     185             : #if defined(USE_CREATEPROCESS)
     186             : static HANDLE *_wpList = NIL(HANDLE); /* Array to hold pids to wait for. */
     187             : #endif
     188             : 
     189             : static  int _add_child ANSI((TPID, CELLPTR, int, int, int));
     190             : static  void    _attach_cmd ANSI((char *, int, CELLPTR, t_attr, int));
     191             : static  void    _finished_child ANSI((DMHANDLE, int));
     192             : static  int     _running ANSI((CELLPTR));
     193             : 
     194             : /* Machine/OS dependent helpers. */
     195             : static  int dmwaitnext ANSI((DMHANDLE *, int *));
     196             : static  int dmwaitpid ANSI((int, DMHANDLE *, int *));
     197             : 
     198             : #if defined( USE_SPAWN )
     199             : 
     200             : int terrno; /* Temporarily store errno. */
     201             : 
     202             : static  TPID    dmspawn ANSI((char **));
     203             : 
     204             : static TPID
     205             : dmspawn( argv )
     206             :    char **argv;
     207             : {
     208             :    TPID pid;
     209             : 
     210             :    /* No error output is done here as stdout/stderr might be redirected. */
     211             : #if defined( __CYGWIN__) || defined( __EMX__)
     212             :    pid.pid = spawnvp(_P_NOWAIT, argv[0], (const char**) argv);
     213             :    pid.tid = 0;
     214             : #elif defined(USE_CREATEPROCESS)
     215             :    static STARTUPINFO si;
     216             :    static int initSTARTUPINFO = FALSE;
     217             :    PROCESS_INFORMATION pi;
     218             : 
     219             :    /* si can be reused. */
     220             :    if( initSTARTUPINFO == FALSE ) {
     221             :       initSTARTUPINFO = TRUE;
     222             :       ZeroMemory( &si, sizeof(si) );
     223             :       si.cb = sizeof(si);
     224             :    }
     225             :    ZeroMemory( &pi, sizeof(pi) );
     226             : 
     227             :    /* Start the child process. CreateProcess() parameters:
     228             :     * No module name (use command line).
     229             :     * Command line. This fails if the path to the program contains spaces.
     230             :     * Process handle not inheritable.
     231             :     * Thread handle not inheritable.
     232             :     * Set handle inheritance (stdout, stderr, etc.) to TRUE.
     233             :     * No creation flags.
     234             :     * Use parent's environment block.
     235             :     * Use parent's starting directory.
     236             :     * Pointer to STARTUPINFO structure.
     237             :     * Pointer to PROCESS_INFORMATION structure. */
     238             :    if( CreateProcess(NULL, argv[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) ) {
     239             :       pid.pid = pi.hProcess;
     240             :       pid.tid = pi.hThread;
     241             :    } else {
     242             :       fprintf(stderr, "CreateProcess failed (%d).\n", GetLastError() );
     243             :       pid.pid = (DMHANDLE)-1;
     244             :    }
     245             : #else   /* Non cygwin, OS/2, MinGW and MSC */
     246             :    int tpid;
     247             :    if (posix_spawnp (&tpid, argv[0], NULL, NULL, argv, (char *)NULL))
     248             :       tpid = -1; /* posix_spawn failed */
     249             : 
     250             :    pid.pid = tpid;
     251             :    pid.tid = 0;
     252             : #endif  /* __CYGWIN__ */
     253             :    return pid;
     254             : }
     255             : 
     256             : #endif /* USE_SPAWN */
     257             : 
     258             : static int
     259         412 : dmwaitnext( wid, status )
     260             :      DMHANDLE *wid; /* Id we waited for. */
     261             :      int *status;   /* status of the finished process. */
     262             :      /* return 1 if a process finished, -1 if there
     263             :       * was nothing to wait for (ECHILD) and -2 for other errors. */
     264             : {
     265             : 
     266             : #if !defined(USE_CREATEPROCESS)
     267             :    /* Here might be the culprit for the famous OOo build hang. If
     268             :     * cygwin manages to "loose" a process and none else is left the
     269             :     * wait() will wait forever. */
     270         412 :    *wid = wait(status);
     271             : 
     272             :    /* If ECHILD is set from waitpid/wait then no child was left. */
     273         412 :    if( *wid  == -1 ) {
     274           0 :      int realErr = errno; // fprintf can pollute errno
     275           0 :      fprintf(stderr, "%s:  Internal Error: wait() failed: %d -  %s\n",
     276           0 :              Pname, errno, strerror(errno) );
     277           0 :       if( realErr != ECHILD ) {
     278             :      /* Wait was interrupted or a child was terminated (SIGCHLD) */
     279           0 :      return -2;
     280             :       } else {
     281           0 :      return -1;
     282             :       }
     283             :    }
     284             : #else
     285             :    DWORD pEvent;
     286             :    DWORD dwExitCode;
     287             :    int i;
     288             :    int numProc = 0;
     289             : 
     290             :    *status = 0;
     291             : 
     292             :    /* Create a list of possible objects to wait for. */
     293             :    for( i=0; i<Max_proc; i++ ) {
     294             :       if(_procs[i].pr_valid) {
     295             :      _wpList[numProc++] = _procs[i].pr_pid;
     296             :       }
     297             :    }
     298             :    if( numProc == 0 ) {
     299             :       fprintf(stderr, "%s:  Internal Error: dmwaitnext() failed: "
     300             :           "Nothing to wait for.\n", Pname );
     301             :       return -1;
     302             :    }
     303             : 
     304             :    /* Wait ... */
     305             :    /* number of objects in array, array of objects,
     306             :     * wait for any object, wait for the next child to finish */
     307             :    pEvent = WaitForMultipleObjects( numProc, _wpList, FALSE, INFINITE);
     308             : 
     309             :    if( pEvent >= 0 && pEvent < WAIT_OBJECT_0 + numProc ) {
     310             :       *wid = _wpList[pEvent - WAIT_OBJECT_0];
     311             :       for( i=0; i<Max_proc && _procs[i].pr_pid != *wid; i++ )
     312             :      ;
     313             :       if( i == Max_proc )
     314             :      Fatal("Internal Error: Process not in pq !");
     315             : 
     316             :       GetExitCodeProcess(*wid, &dwExitCode);
     317             :       if(dwExitCode == STILL_ACTIVE) {
     318             :      /* Process did not terminate -> force it, with exit code 1. */
     319             :      TerminateProcess(*wid, 1);
     320             :      dwExitCode = 1;
     321             :      fprintf(stderr, "%s:  Internal Error: Process still running - "
     322             :          "terminate it!\n", Pname );
     323             :       }
     324             : 
     325             :       /* Close process and thread handles. */
     326             :       CloseHandle( *wid );
     327             :       CloseHandle( _procs[i].pr_tid );
     328             :       *status = dwExitCode;
     329             :    }
     330             :    else {
     331             :       int err = GetLastError();
     332             :       LPVOID lpMsgBuf;
     333             : 
     334             :       FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
     335             :              FORMAT_MESSAGE_FROM_SYSTEM |
     336             :              FORMAT_MESSAGE_IGNORE_INSERTS,
     337             :              NULL,
     338             :              err,
     339             :              MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
     340             :              (LPTSTR) &lpMsgBuf,
     341             :              0, NULL );
     342             : 
     343             :       fprintf(stderr, "%s:  Internal Error: WaitForMultipleObjects() (%d) failed:"
     344             :           " %d -  %s\n", Pname, numProc, err, lpMsgBuf);
     345             :       LocalFree(lpMsgBuf);
     346             : 
     347             :       /* No way to identify something comparable to ECHILD, always return -2.*/
     348             :       return -2;
     349             :    }
     350             : 
     351             : #endif
     352         412 :    return 1;
     353             : }
     354             : 
     355             : 
     356             : static int
     357         384 : dmwaitpid( pqid, wid, status )
     358             :      int pqid;      /* Process queue to wait for. */
     359             :      DMHANDLE *wid; /* Id we waited for. */
     360             :      int *status;   /* status of the finished process. */
     361             :      /* return 1 if the process finished, 0 if it didn't finish yet, -1 if there
     362             :       * was nothing to wait for (ECHILD) and -2 for other errors. */
     363             : {
     364             : 
     365             : #if !defined(USE_CREATEPROCESS)
     366         384 :    *wid = waitpid(_procs[pqid].pr_pid, status, WNOHANG);
     367             : 
     368             :    /* Process still running. */
     369         384 :    if( *wid  == 0 ) {
     370         384 :       *status = 0;
     371         384 :       return 0;
     372             :    }
     373             :    /* If ECHILD is set from waitpid/wait then no child was left. */
     374           0 :    if( *wid  == -1 ) {
     375           0 :      int realErr = errno; // fprintf can pollute errno
     376           0 :       fprintf(stderr, "%s:  Internal Error: waitpid() failed: %d -  %s\n",
     377           0 :           Pname, errno, strerror(errno) );
     378           0 :       if(realErr != ECHILD) {
     379             :      /* Wait was interrupted or a child was terminated (SIGCHLD) */
     380           0 :      return -2;
     381             :       } else {
     382           0 :      return -1;
     383             :       }
     384             :    }
     385             : #else
     386             :    DWORD pEvent;
     387             :    DWORD dwExitCode;
     388             : 
     389             :    *wid = _procs[pqid].pr_pid;
     390             :    *status = 0;
     391             : 
     392             :    /* Wait ... (Check status and return) */
     393             :    pEvent = WaitForSingleObject(*wid, 0);
     394             : 
     395             :    if( pEvent == WAIT_OBJECT_0 ) {
     396             :       GetExitCodeProcess(*wid, &dwExitCode);
     397             :       if(dwExitCode == STILL_ACTIVE) {
     398             :      /* Process did not terminate -> force it, with exit code 1. */
     399             :      TerminateProcess(*wid, 1);
     400             :      dwExitCode = 1;
     401             :      fprintf(stderr, "%s:  Internal Error: Process still running - "
     402             :          "terminate it!\n", Pname );
     403             :       }
     404             : 
     405             :       /* Close process and thread handles. */
     406             :       CloseHandle( *wid );
     407             :       CloseHandle( _procs[pqid].pr_tid );
     408             :       *status = dwExitCode;
     409             :    }
     410             :    else if( pEvent == WAIT_TIMEOUT ) {
     411             :       return 0;
     412             :    }
     413             :    else {
     414             :       int err = GetLastError();
     415             :       LPVOID lpMsgBuf;
     416             : 
     417             :       FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
     418             :              FORMAT_MESSAGE_FROM_SYSTEM |
     419             :              FORMAT_MESSAGE_IGNORE_INSERTS,
     420             :              NULL,
     421             :              err,
     422             :              MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
     423             :              (LPTSTR) &lpMsgBuf,
     424             :              0, NULL );
     425             : 
     426             :       fprintf(stderr, "%s:  Internal Error: WaitForSingleObject() failed:"
     427             :           " %d -  %s\n", Pname, err, lpMsgBuf);
     428             :       LocalFree(lpMsgBuf);
     429             : 
     430             :       /* No way to identify something comparable to ECHILD, always return -2.*/
     431             :       return -2;
     432             :    }
     433             : #endif
     434             : 
     435           0 :    return 1;
     436             : }
     437             : 
     438             : 
     439             : #if ! HAVE_STRERROR
     440             : static char *
     441             : private_strerror (errnum)
     442             :      int errnum;
     443             : {
     444             : #ifndef __APPLE__
     445             : # if defined(arm32) || defined(linux) || defined(__FreeBSD__) || \
     446             :      defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
     447             :   extern  const char * const sys_errlist[];
     448             : # else
     449             :   extern  char *sys_errlist[];
     450             : # endif
     451             : #endif
     452             :   extern int sys_nerr;
     453             : 
     454             :   if (errnum > 0 && errnum <= sys_nerr)
     455             :     return sys_errlist[errnum];
     456             :   return "Unknown system error";
     457             : }
     458             : #define strerror private_strerror
     459             : #endif /* HAVE_STRERROR */
     460             : 
     461             : PUBLIC int
     462        2190 : runargv(target, group, last, cmnd_attr, cmd)/*
     463             : ==============================================
     464             :   Execute the command given by cmd.
     465             : 
     466             :   Return 0 if the command executed and finished or
     467             :          1 if the command started and is running.
     468             :  */
     469             : CELLPTR target;
     470             : int group;
     471             : int last;
     472             : t_attr  cmnd_attr; /* Attributes for current cmnd. */
     473             : char  **cmd; /* Simulate a reference to *cmd. */
     474             : {
     475        2190 :    int  ignore = (cmnd_attr & A_IGNORE)!= 0; /* Ignore errors ('-'). */
     476        2190 :    int  shell  = (cmnd_attr & A_SHELL) != 0; /* Use shell ('+'). */
     477        2190 :    int  mute = (cmnd_attr & A_MUTE) != 0; /* Mute output ('@@'). */
     478        2190 :    int  wfc = (cmnd_attr & A_WFC) != 0; /* Wait for completion. */
     479             : 
     480             :    TPID  pid;
     481        2190 :    int  st_pq = 0; /* Current _exec_shell target process index */
     482        2190 :    char *tcmd = *cmd; /* For saver/easier string arithmetic on *cmd. */
     483             :    char         **argv;
     484             : 
     485        2190 :    int old_stdout = -1; /* For shell escapes and */
     486        2190 :    int old_stderr = -1; /* @@-recipe silencing.  */
     487        2190 :    int internal = 0; /* Used to indicate internal command. */
     488             : 
     489             :    DB_ENTER( "runargv" );
     490             : 
     491             :    /* Special handling for the shell function macro is required. If the
     492             :     * currend command is called as part of a shell escape in a recipe make
     493             :     * sure that all previous recipe lines of this target have finished. */
     494        2190 :    if( Is_exec_shell ) {
     495         384 :       if( (st_pq = _running(Shell_exec_target)) != -1 ) {
     496             :      RCPPTR rp;
     497             :      /* Add WFC to _procs[st_pq]. */
     498           0 :      _procs[st_pq].pr_wfc = TRUE;
     499             :      /* Set also the A_WFC flag in the recipe attributes. */
     500           0 :      for( rp = _procs[st_pq].pr_recipe ; rp != NIL(RCP); rp = rp->prp_next )
     501           0 :         rp->prp_attr |= A_WFC;
     502             : 
     503           0 :      Wait_for_child(FALSE, st_pq);
     504             :       }
     505             :    } else {
     506        1806 :       if( _running(target) != -1 /*&& Max_proc != 1*/ ) {
     507             :      /* The command will be executed when the previous recipe
     508             :       * line completes. */
     509           6 :      _attach_cmd( *cmd, group, target, cmnd_attr, last );
     510           6 :      DB_RETURN( 1 );
     511             :       }
     512             :    }
     513             : 
     514             :    /* If all process array entries are used wait until we get a free
     515             :     * slot. For Max_proc == 1 this forces sequential execution. */
     516        4368 :    while( _proc_cnt == Max_proc ) {
     517           0 :       Wait_for_child(FALSE, -1);
     518             :    }
     519             : 
     520             :    /* Return immediately for empty line or noop command. */
     521        3278 :    if ( !*tcmd ||               /* empty line */
     522        1776 :     ( strncmp(tcmd, "noop", 4) == 0 &&  /* noop command */
     523        1192 :       (iswhite(tcmd[4]) || tcmd[4] == '\0')) ) {
     524        1772 :       internal = 1;
     525             :    }
     526         422 :    else if( !shell &&  /* internal echo only if not in shell */
     527          10 :         strncmp(tcmd, "echo", 4) == 0 &&
     528           0 :         (iswhite(tcmd[4]) || tcmd[4] == '\0') ) {
     529           0 :       int nl = 1;
     530             : 
     531           0 :       tcmd = tcmd+4;
     532           0 :       while( iswhite(*tcmd) ) ++tcmd;
     533           0 :       if ( strncmp(tcmd,"-n",2 ) == 0) {
     534           0 :      nl = 0;
     535           0 :      tcmd = tcmd+2;
     536           0 :      while( iswhite(*tcmd) ) ++tcmd;
     537             :       }
     538             : 
     539             :       /* redirect output for _exec_shell / @@-recipes. */
     540           0 :       if( Is_exec_shell ) {
     541             :      /* Add error checking? */
     542           0 :      old_stdout = dup(1);
     543           0 :      dup2( fileno(stdout_redir), 1 );
     544             :       }
     545           0 :       if( mute ) {
     546           0 :      old_stderr = dup(2);
     547           0 :      dup2( zerofd, 2 );
     548             : 
     549           0 :      if( !Is_exec_shell ) {
     550           0 :         old_stdout = dup(1);
     551           0 :         dup2( zerofd, 1 );
     552             :      }
     553             :       }
     554             : 
     555           0 :       printf("%s%s", tcmd, nl ? "\n" : "");
     556           0 :       fflush(stdout);
     557             : 
     558             :       /* Restore stdout/stderr if needed. */
     559           0 :       if( old_stdout != -1 ) {
     560           0 :      dup2(old_stdout, 1);
     561           0 :      close(old_stdout);
     562           0 :      if( old_stderr != -1 ) {
     563           0 :         dup2(old_stderr, 2);
     564           0 :         close(old_stderr);
     565             :       }
     566             :       }
     567             : 
     568           0 :       internal = 1;
     569             :    }
     570        2184 :    if ( internal ) {
     571             :       /* Use _add_child() / _finished_child() with internal command. */
     572        1772 :       int cur_proc = _add_child(DMNOPID, target, ignore, last, FALSE);
     573        1772 :       _finished_child( (DMHANDLE)-cur_proc, 0 );
     574        1772 :       DB_RETURN( 0 );
     575             :    }
     576             : 
     577             :    /* Pack cmd in argument vector. */
     578         412 :    argv = Pack_argv( group, shell, cmd );
     579             : 
     580             :    /* Really spawn or fork a child. */
     581             : #if defined( USE_SPAWN )
     582             :    /* As no other children are started while the output is redirected this
     583             :     * is save. */
     584             :    if( Is_exec_shell ) {
     585             :       /* Add error checking? */
     586             :       old_stdout = dup(1);
     587             :       dup2( fileno(stdout_redir), 1 );
     588             :    }
     589             :    if( mute ) {
     590             :       old_stderr = dup(2);
     591             :       dup2( zerofd, 2 );
     592             : 
     593             :       if( !Is_exec_shell ) {
     594             :      old_stdout = dup(1);
     595             :      dup2( zerofd, 1 );
     596             :       }
     597             :    }
     598             : 
     599             :    pid = dmspawn( argv );
     600             :    terrno = errno;
     601             : 
     602             :    if( old_stdout != -1 ) {
     603             :       dup2(old_stdout, 1);
     604             :       close(old_stdout);
     605             :       if( old_stderr != -1 ) {
     606             :      dup2(old_stderr, 2);
     607             :      close(old_stderr);
     608             :       }
     609             :    }
     610             :    if(pid.pid == (DMHANDLE)-1) {
     611             :       /* spawn failed */
     612             :       int cur_proc;
     613             : 
     614             :       fprintf(stderr, "%s:  Error executing '%s': %s",
     615             :           Pname, argv[0], strerror(terrno) );
     616             :       if( ignore||Continue ) {
     617             :      fprintf(stderr, " (Ignored)" );
     618             :       }
     619             :       fprintf(stderr, "\n");
     620             : 
     621             :       /* Use _add_child() / _finished_child() to treat the failure
     622             :        * gracefully, if so requested. */
     623             :       cur_proc = _add_child(DMNOPID, target, ignore, last, FALSE);
     624             :       _finished_child((DMHANDLE)cur_proc, SIGTERM);
     625             : 
     626             :       /* _finished_child() aborts dmake if we are not told to
     627             :        * ignore errors. If we reach the this point return 0 as
     628             :        * errors are obviously ignored and indicate that the process
     629             :        * finished. */
     630             :       DB_RETURN( 0 );
     631             :    } else {
     632             :       _add_child(pid, target, ignore, last, wfc);
     633             :    }
     634             : #else  /* USE_SPAWN */
     635             : 
     636         412 :    fflush(stdout);
     637         412 :    switch( pid.pid = fork() ){
     638             : 
     639             :    case -1: /* fork failed */
     640           0 :       Fatal("fork failed: %s: %s", argv[0], strerror( errno ));
     641             : 
     642             :    case 0:  /* child */
     643             :       /* redirect output for _exec_shell / @@-recipes. */
     644           0 :       if( Is_exec_shell ) {
     645             :      /* Add error checking? */
     646           0 :      old_stdout = dup(1);
     647           0 :      dup2( fileno(stdout_redir), 1 );
     648             :       }
     649           0 :       if( mute ) {
     650           0 :      old_stderr = dup(2);
     651           0 :      dup2( zerofd, 2 );
     652             : 
     653           0 :      if( !Is_exec_shell ) {
     654           0 :         old_stdout = dup(1);
     655           0 :         dup2( zerofd, 1 );
     656             :      }
     657             :       }
     658           0 :       execvp(argv[0], argv);
     659             :       /* restoring output to catch potential error output if execvp()
     660             :        * failed. */
     661           0 :       if( old_stdout != -1 ) {
     662           0 :      dup2(old_stdout, 1);
     663           0 :      close(old_stdout);
     664           0 :      if( old_stderr != -1 ) {
     665           0 :         dup2(old_stderr, 2);
     666           0 :         close(old_stderr);
     667             :      }
     668             :       }
     669           0 :       fprintf(stderr, "%s:  Error executing '%s': %s",
     670           0 :           Pname, argv[0], strerror(errno) );
     671           0 :       if( ignore||Continue ) {
     672           0 :      fprintf(stderr, " (Ignored)" );
     673             :       }
     674           0 :       fprintf(stderr, "\n");
     675             : 
     676           0 :       kill(getpid(), SIGTERM);
     677             :       /*NOTREACHED*/
     678           0 :       Fatal("\nInternal Error - kill could't kill child %d.\n", getpid());
     679             : 
     680             :    default: /* parent */
     681         412 :       _add_child(pid, target, ignore, last, wfc);
     682             :    }
     683             : 
     684             : #endif  /* USE_SPAWN */
     685             : 
     686             :    /* If wfc is set this command must have been finished. */
     687         412 :    if( wfc ) {
     688         384 :       DB_RETURN( 0 );
     689             :    } else {
     690          28 :       DB_RETURN( 1 );
     691             :    }
     692             : }
     693             : 
     694             : 
     695             : PUBLIC int
     696         416 : Wait_for_child( abort_flg, pqid )/*
     697             : ===================================
     698             :   Wait for the next processes from process queue pqid to finish. All finished
     699             :   processes are handled by calling  _finished_child() for each of them.
     700             :   If pqid == -1 wait for the next process to finish.
     701             :   If abort_flg is TRUE no further processes will be added to any process
     702             :   queue. The A_WFC attribute is honored, see the documentation at the top
     703             :   of this file.
     704             :   Return 0 if we successfully waited for a process and -1 if there was nothing
     705             :   to wait for.
     706             : */
     707             : int abort_flg;
     708             : int pqid;
     709             : {
     710             :    DMHANDLE pid;
     711             :    DMHANDLE wid;
     712             :    int status;
     713             :    int waitret; /* return value of the dmwait functions. */
     714             :    /* Never wait for internal commands. */
     715             :    int waitchild;
     716         416 :    int is_exec_shell_status = Is_exec_shell;
     717             : 
     718         416 :    if( !_procs ) {
     719             :       /* No process was ever created, i.e. _procs is not yet initialized.
     720             :        * Nothing to wait for. */
     721           4 :      return -1;
     722             :    }
     723             : 
     724         412 :    if( pqid > Max_proc ) Fatal("Internal Error: pqid > Max_proc !");
     725             : 
     726         412 :    if( pqid == -1 ) {
     727             :       /* Check if there is something to wait for. */
     728             :       int i;
     729          28 :       for( i=0; i<Max_proc && !_procs[i].pr_valid; i++ )
     730             :      ;
     731          28 :       if( i == Max_proc )
     732           0 :      return(-1);
     733             : 
     734          28 :       pid = (DMHANDLE)-1;
     735          28 :       waitchild = FALSE;
     736             :    }
     737             :    else {
     738             :       /* Check if pqid is active. */
     739         384 :       if( !_procs[pqid].pr_valid ) {
     740             :      /* Make this an error? */
     741           0 :      Warning("Internal Warning: pqid is not active!?");
     742           0 :      return(-1);
     743             :       }
     744             : 
     745         384 :       pid = _procs[pqid].pr_pid;
     746         384 :       waitchild = _procs[pqid].pr_wfc;
     747             :    }
     748             : 
     749             : 
     750             :    /* It is impossible that processes that were started from _exec_shell
     751             :     * have follow-up commands in its process entry. Unset Is_exec_shell
     752             :     * to prevent piping of child processes that are started from the
     753             :     * _finished_child subroutine and reset to its original value when
     754             :     * leaving this function. */
     755         412 :    Is_exec_shell = FALSE;
     756             : 
     757             :    do {
     758             :       /* Wait for the next process to finish. */
     759         412 :       if( (pid != (DMHANDLE)-1) && (waitret = dmwaitpid(pqid, &wid, &status)) != 0 ) {
     760             :      /* if dmwaitpid returns 0 this means that pid didn't finish yet.
     761             :       * In this case just handle the next finished process in the
     762             :       * following "else". If an error is returned (waitret < 0) the else
     763             :       * clause is not evaluated and the error is handled in the following
     764             :       * lines. If a process was waited for (waitret == 0) also proceed to
     765             :       * the following lines. */
     766             :      ;
     767             :       }
     768             :       else {
     769         412 :      waitret = dmwaitnext(&wid, &status);
     770             :      /* If we get an error tell the error handling routine below that we
     771             :       * were not waiting for a specific pid. */
     772         412 :      if( waitret < 0 ) {
     773           0 :         pid = (DMHANDLE)-1;
     774             :      }
     775             :       }
     776             : 
     777             :       /* If ECHILD is set from waitpid/wait then no child was left. */
     778         412 :       if( waitret < 0 ) {
     779           0 :      if(waitret == -2) {
     780             :         /* Wait was interrupted or a child was terminated (SIGCHLD) */
     781           0 :         if ( in_quit() ) {
     782             :            /* We're already terminating, just continue. */
     783           0 :            return 0;
     784             :         } else {
     785           0 :            Fatal( "dmake was interrupted or a child terminated. "
     786             :               "Stopping all children ..." );
     787             :         }
     788             :      } else {
     789             :         /* The child we were waiting for is missing or no child is
     790             :          * left to wait for. */
     791           0 :         if( pid != (DMHANDLE)-1 ) {
     792             :            /* If we know the pid disable the pq entry. */
     793           0 :            if( _procs[pqid].pr_valid ) {
     794           0 :           _procs[pqid].pr_valid = 0;
     795           0 :                   _procs[pqid].pr_recipe = NIL(RCP);
     796           0 :           _proc_cnt--;
     797             :            }
     798             :         } else {
     799             :            /* otherwise disable all remaining pq's. As we don't know
     800             :         * which pid failed there is no gracefull way to terminate. */
     801             :            int i;
     802           0 :            for( i=0; i<Max_proc; i++ ) {
     803           0 :           _procs[i].pr_valid = 0;
     804           0 :                   _procs[i].pr_recipe = NIL(RCP);
     805             :                }
     806           0 :            _proc_cnt = 0;
     807             :         }
     808             :         /* The pid we were waiting for or any of the remaining children
     809             :          * (pid == -1) is missing. This should not happen and means
     810             :          * that the process got lost or was treated elsewhere. */
     811           0 :         Fatal( "Internal Error: Child is missing but still listed in _procs[x] %d: %s\n"
     812             :            "\nTemporary or .ERRREMOVE targets might not have been removed!\n",
     813           0 :            errno, strerror( errno ) );
     814             :      }
     815             :       }
     816             : 
     817         412 :       _abort_flg = abort_flg;
     818         412 :       _finished_child(wid, status);
     819         412 :       _abort_flg = FALSE;
     820         412 :       if( waitchild ) {
     821             :      /* If pid != wid the process we're waiting for might have been
     822             :       * finished from a "Wait_for_child(FALSE, -1)" call from
     823             :       * _finished_child() -> runargv(). */
     824         384 :      if( pid != wid ) {
     825           0 :         if( !_procs[pqid].pr_valid || _procs[pqid].pr_pid != pid ) {
     826             :            /* Someone finished pid, no need to wait further. */
     827           0 :            waitchild = FALSE;
     828             :         }
     829             :      }
     830             :      else
     831             :         /* We finished pid, no need to wait further. */
     832         384 :         waitchild = FALSE;
     833             :       }
     834             :    }
     835         412 :    while( waitchild );
     836             : 
     837         412 :    Is_exec_shell = is_exec_shell_status;
     838         412 :    return(0);
     839             : }
     840             : 
     841             : 
     842             : PUBLIC void
     843           4 : Clean_up_processes()
     844             : {
     845           4 :     if( _procs != NIL(PR) )
     846             :     {
     847             :       register int i;
     848           0 :       for( i=0; i<Max_proc; i++ )
     849           0 :         if( _procs[i].pr_valid )
     850             :         {
     851             :         #if !defined(USE_CREATEPROCESS)
     852             :             int ret;
     853           0 :             if( (ret = kill(_procs[i].pr_pid, SIGTERM)) )
     854             :             {
     855           0 :                 fprintf(stderr, "Killing of pid %d from pq[%d] failed with: %s - %d ret: %d\n",
     856           0 :                         _procs[i].pr_pid, i, strerror(errno), SIGTERM, ret );
     857             :             }
     858             :         #else
     859             :             TerminateProcess(_procs[i].pr_pid, 1);
     860             :         #endif
     861             :         }
     862             :     }
     863           4 : }
     864             : 
     865             : 
     866             : static int
     867        2184 : _add_child( pid, target, ignore, last, wfc )/*
     868             : ==============================================
     869             :   Creates/amend a process queue entry and enters the child parameters.
     870             :   The pid == -1 represents an internal command and the function returns
     871             :   the used process array index. For non-internal commands the function
     872             :   returns -1.
     873             :   If wfc (wait for completion) is TRUE the function calls
     874             :   Wait_for_child to wait for the whole process queue to be finished.
     875             : */
     876             : TPID    pid;
     877             : CELLPTR target;
     878             : int ignore;
     879             : int     last;
     880             : int     wfc;
     881             : {
     882             :    register int i;
     883             :    register PR *pp;
     884             : 
     885             :    /* Never change MAXPROCESS after _procs is allocated. */
     886        2184 :    if( _procs_size != Max_proc ) {
     887             :      /* If procs was never initialize this is OK, do it now. */
     888         184 :      if( _procs == NIL(PR) ) {
     889         184 :        _procs_size = Max_proc;
     890         184 :        TALLOC( _procs, Max_proc, PR );
     891             : #if defined(USE_CREATEPROCESS)
     892             :        TALLOC( _wpList, Max_proc, HANDLE );
     893             : 
     894             :        /* Signed int values are cast to DMHANDLE in various places, use this
     895             :         * sanity check to verify that DMHANDLE is large enough. */
     896             :        if( sizeof(int) > sizeof(DMHANDLE) )
     897             :          Fatal( "Internal Error: Check type of DMHANDLE!" );
     898             : #endif
     899             :      }
     900             :      else {
     901           0 :        Fatal( "MAXPROCESS changed from `%d' to `%d' after a command was executed!", _procs_size, Max_proc );
     902             :      }
     903             :    }
     904             : 
     905        2184 :    if( Measure & M_RECIPE )
     906           0 :       Do_profile_output( "s", M_RECIPE, target );
     907             : 
     908             :    /* If _use_i ! =-1 then this function is called by _finished_child() ( through runargv() ),
     909             :       and we re-use the process queue number given by _use_i. */
     910        2184 :    if( (i = _use_i) == -1 ) {
     911        2180 :      for( i=0; i<Max_proc; i++ )
     912        2180 :        if( !_procs[i].pr_valid )
     913        2178 :          break;
     914             :    }
     915             : 
     916        2184 :    pp = &(_procs[i]);
     917             : 
     918        2184 :    pp->pr_valid  = 1;
     919        2184 :    pp->pr_pid    = pid.pid;
     920        2184 :    pp->pr_tid    = pid.tid;
     921        2184 :    pp->pr_target = target;
     922        2184 :    pp->pr_ignore = ignore;
     923        2184 :    pp->pr_last   = last;
     924        2184 :    pp->pr_wfc    = wfc;
     925             : 
     926        2184 :    if( pp->pr_dir != NIL(char) )
     927         516 :      FREE(pp->pr_dir);
     928        2184 :    pp->pr_dir    = DmStrDup(Get_current_dir());
     929             : 
     930        2184 :    Current_target = NIL(CELL);
     931             : 
     932        2184 :    _proc_cnt++;
     933             : 
     934        2184 :    if( pid.pid != (DMHANDLE)-1 ) {
     935             :       /* Wait for each recipe to finish if wfc is TRUE. This
     936             :        * basically forces sequential execution. */
     937         412 :       if( wfc ) {
     938         384 :      Wait_for_child( FALSE, i );
     939             :       }
     940             : 
     941         412 :       return -1;
     942             :    } else
     943        1772 :       return i;
     944             : }
     945             : 
     946             : 
     947             : static void
     948        2184 : _finished_child(cid, status)/*
     949             : ==============================
     950             :   Handle process array entry for finished child. This can be a finished
     951             :   process or a finished internal command depending on the content of cid.
     952             :   For cid >= 1 the value of cid is used as the pid to of the finished
     953             :   process and for cid < 1 -cid is used as the process array index of the
     954             :   internal command.
     955             : */
     956             : DMHANDLE cid;
     957             : int status;
     958             : {
     959             :   register int i;
     960             :   char     *dir;
     961             : 
     962        2184 :   if((int)cid < 1) { /* Force int. */
     963             :     /* internal command */
     964        1772 :     i = -((int)cid);
     965             :   }
     966             :   else {
     967         418 :     for( i=0; i<Max_proc; i++ )
     968         418 :       if( _procs[i].pr_valid && _procs[i].pr_pid == cid )
     969         412 :         break;
     970             : 
     971             :     /* Some children we didn't make esp true if using /bin/sh to execute a
     972             :      * a pipe and feed the output as a makefile into dmake. */
     973         412 :     if( i == Max_proc ) {
     974           0 :       Warning("Internal Warning: finished pid %d is not in pq!?", cid);
     975        2184 :       return;
     976             :     }
     977             :   }
     978             : 
     979             :   /* Not a running process anymore, the next runargv() will not use
     980             :    * _attach_cmd(). */
     981        2184 :   _procs[i].pr_valid = 0;
     982             : 
     983        2184 :   if( Measure & M_RECIPE )
     984           0 :     Do_profile_output( "e", M_RECIPE, _procs[i].pr_target );
     985             : 
     986        2184 :   _proc_cnt--;
     987        2184 :   dir = DmStrDup(Get_current_dir());
     988        2184 :   Set_dir( _procs[i].pr_dir );
     989             : 
     990        2190 :   if( _procs[i].pr_recipe != NIL(RCP) && !_abort_flg ) {
     991           6 :     RCPPTR rp = _procs[i].pr_recipe;
     992             : 
     993             : 
     994           6 :     Current_target = _procs[i].pr_target;
     995           6 :     Handle_result( status, _procs[i].pr_ignore, FALSE, _procs[i].pr_target );
     996           6 :     Current_target = NIL(CELL);
     997             : 
     998           6 :     if ( _procs[i].pr_target->ce_attr & A_ERROR ) {
     999           0 :       _procs[i].pr_last = TRUE;
    1000           0 :       goto ABORT_REMAINDER_OF_RECIPE;
    1001             :     }
    1002             : 
    1003           6 :     _procs[i].pr_recipe = rp->prp_next;
    1004             : 
    1005           6 :     _use_i = i;
    1006             :     /* Run next recipe line. The rp->prp_attr propagates a possible
    1007             :      * wfc condition. */
    1008           6 :     runargv( _procs[i].pr_target, rp->prp_group,
    1009             :              rp->prp_last, rp->prp_attr, &rp->prp_cmd );
    1010           6 :     _use_i = -1;
    1011             : 
    1012           6 :     FREE( rp->prp_cmd );
    1013           6 :     FREE( rp );
    1014             : 
    1015             :     /* If all process queues are used wait for the next process to
    1016             :      * finish. Is this really needed here? */
    1017           6 :     if( _proc_cnt == Max_proc ) {
    1018           3 :       Wait_for_child( FALSE, -1 );
    1019             :     }
    1020             :   }
    1021             :   else {
    1022             :     /* empty the queue on abort. */
    1023        2178 :     if( _abort_flg )
    1024           0 :       _procs[i].pr_recipe = NIL(RCP);
    1025             : 
    1026        2178 :     Handle_result(status,_procs[i].pr_ignore,_abort_flg,_procs[i].pr_target);
    1027             : 
    1028             :   ABORT_REMAINDER_OF_RECIPE:
    1029        2178 :     if( _procs[i].pr_last ) {
    1030        1668 :       FREE(_procs[i].pr_dir ); _procs[i].pr_dir = NIL(char); /* Set in _add_child() */
    1031             : 
    1032        1668 :       if( !Doing_bang ) {
    1033             :         /* Update_time_stamp() triggers the deletion of intermediate
    1034             :          * targets.  This starts a new process queue, so we have to
    1035             :          * clear the _use_i variable. */
    1036        1668 :         int my_use_i = _use_i;
    1037             : 
    1038        1668 :         _use_i = -1;
    1039        1668 :         Update_time_stamp( _procs[i].pr_target );
    1040        1668 :         _use_i =  my_use_i;
    1041             :       }
    1042             :     }
    1043             :   }
    1044             : 
    1045        2184 :   Set_dir(dir);
    1046        2184 :   FREE(dir);
    1047             : }
    1048             : 
    1049             : 
    1050             : static int
    1051        2190 : _running( cp )/*
    1052             : ================
    1053             :   Check if target exists in process array AND is running. Return its
    1054             :   process array index if it is running, return -1 otherwise.
    1055             : */
    1056             : CELLPTR cp;
    1057             : {
    1058             :    register int i;
    1059             : 
    1060        2190 :    if( !_procs ) return( -1 );
    1061             : 
    1062        6010 :    for( i=0; i<Max_proc; i++ )
    1063        4025 :       if( _procs[i].pr_valid &&
    1064          15 :       _procs[i].pr_target == cp  )
    1065           6 :      break;
    1066             : 
    1067        2006 :    return( i == Max_proc ? -1 : i );
    1068             : }
    1069             : 
    1070             : 
    1071             : static void
    1072           6 : _attach_cmd( cmd, group, cp, cmnd_attr, last )/*
    1073             : ================================================
    1074             :   Attach to an active process queue. Inherit wfc setting. */
    1075             : char    *cmd;
    1076             : int group;
    1077             : CELLPTR cp;
    1078             : t_attr  cmnd_attr;
    1079             : int     last;
    1080             : {
    1081             :    register int i;
    1082             :    RCPPTR rp;
    1083             : 
    1084          10 :    for( i=0; i<Max_proc; i++ )
    1085          20 :       if( _procs[i].pr_valid &&
    1086          10 :       _procs[i].pr_target == cp  )
    1087           6 :      break;
    1088             : 
    1089           6 :    TALLOC( rp, 1, RCP );
    1090           6 :    rp->prp_cmd   = DmStrDup(cmd);
    1091           6 :    rp->prp_attr = cmnd_attr;
    1092             :    /* Inherit wfc from process queue. */
    1093           6 :    if( _procs[i].pr_wfc )
    1094           0 :       rp->prp_attr |= A_WFC;
    1095           6 :    rp->prp_group = group;
    1096           6 :    rp->prp_last  = last;
    1097             : 
    1098           6 :    if( _procs[i].pr_recipe == NIL(RCP) )
    1099           4 :       _procs[i].pr_recipe = _procs[i].pr_recipe_end = rp;
    1100             :    else {
    1101           2 :       _procs[i].pr_recipe_end->prp_next = rp;
    1102           2 :       _procs[i].pr_recipe_end = rp;
    1103             :    }
    1104           6 : }

Generated by: LCOV version 1.10