Ignore:
Timestamp:
Sep 14, 2016, 3:36:15 PM (9 years ago)
Author:
bird
Message:

rewrote kmk_redirect to skip the separate process. Added chache invalidation after directory deletion for addressing kmk rebuild and fetching.

File:
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/kmk/kmkbuiltin/common-env-and-cwd-opt.c

    r2899 r2912  
    11/* $Id$ */
    22/** @file
    3  * kMk Builtin command - submit job to a kWorker.
     3 * kMk Builtin command - Commmon environment and CWD option handling code.
    44 */
    55
     
    2727*   Header Files                                                               *
    2828*******************************************************************************/
    29 #ifdef __APPLE__
    30 # define _POSIX_C_SOURCE 1 /* 10.4 sdk and unsetenv */
    31 #endif
    32 #include "make.h"
    33 #include "job.h"
    34 #include "variable.h"
    35 #include "pathstuff.h"
     29#include "config.h"
    3630#include <stdio.h>
    3731#include <stdlib.h>
    3832#include <string.h>
    39 #include <errno.h>
    40 #include <assert.h>
    41 #ifdef HAVE_ALLOCA_H
    42 # include <alloca.h>
    43 #endif
    44 #if defined(_MSC_VER)
    45 # include <ctype.h>
    46 # include <io.h>
    47 # include <direct.h>
    48 # include <process.h>
    49 #else
    50 # include <unistd.h>
    51 #endif
    52 #ifdef KBUILD_OS_WINDOWS
    53 # include "sub_proc.h"
    54 #endif
    55 
    56 #include "kbuild.h"
     33
    5734#include "kmkbuiltin.h"
    5835#include "err.h"
    59 
    60 
    61 /*********************************************************************************************************************************
    62 *   Defined Constants And Macros                                                                                                 *
    63 *********************************************************************************************************************************/
    64 /** Hashes a pid. */
    65 #define KWORKER_PID_HASH(a_pid) ((size_t)(a_pid) % 61)
    66 
    67 
    68 /*********************************************************************************************************************************
    69 *   Structures and Typedefs                                                                                                      *
    70 *********************************************************************************************************************************/
    71 typedef struct WORKERINSTANCE *PWORKERINSTANCE;
    72 typedef struct WORKERINSTANCE
    73 {
    74     /** Pointer to the next worker instance. */
    75     PWORKERINSTANCE         pNext;
    76     /** Pointer to the previous worker instance. */
    77     PWORKERINSTANCE         pPrev;
    78     /** Pointer to the next worker with the same pid hash slot. */
    79     PWORKERINSTANCE         pNextPidHash;
    80     /** 32 or 64. */
    81     unsigned                cBits;
    82     /** The process ID of the kWorker process. */
    83     pid_t                   pid;
    84     union
    85     {
    86         struct
    87         {
    88             /** The exit code. */
    89             int32_t         rcExit;
    90             /** Set to 1 if the worker is exiting. */
    91             uint8_t         bWorkerExiting;
    92             uint8_t         abUnused[3];
    93         } s;
    94         uint8_t             ab[8];
    95     } Result;
    96     /** Number of result bytes read alread.  */
    97     size_t                  cbResultRead;
    98 
    99 #ifdef KBUILD_OS_WINDOWS
    100     /** The process handle. */
    101     HANDLE                  hProcess;
    102     /** The bi-directional pipe we use to talk to the kWorker process. */
    103     HANDLE                  hPipe;
    104     /** For overlapped read (have valid event semaphore). */
    105     OVERLAPPED              OverlappedRead;
    106 #else
    107     /** The socket descriptor we use to talk to the kWorker process. */
    108     int                     fdSocket;
    109 #endif
    110 
    111     /** What it's busy with.  NULL if idle. */
    112     struct child           *pBusyWith;
    113 } WORKERINSTANCE;
    114 
    115 
    116 typedef struct WORKERLIST
    117 {
    118     /** The head of the list.  NULL if empty. */
    119     PWORKERINSTANCE         pHead;
    120     /** The tail of the list.  NULL if empty. */
    121     PWORKERINSTANCE         pTail;
    122     /** Number of list entries. */
    123     size_t                  cEntries;
    124 } WORKERLIST;
    125 typedef WORKERLIST *PWORKERLIST;
    126 
    127 
    128 /*********************************************************************************************************************************
    129 *   Global Variables                                                                                                             *
    130 *********************************************************************************************************************************/
    131 /** List of idle worker.*/
    132 static WORKERLIST           g_IdleList;
    133 /** List of busy workers. */
    134 static WORKERLIST           g_BusyList;
    135 /** PID hash table for the workers.
    136  * @sa KWORKER_PID_HASH() */
    137 static PWORKERINSTANCE      g_apPidHash[61];
    138 
    139 #ifdef KBUILD_OS_WINDOWS
    140 /** For naming the pipes.
    141  * Also indicates how many worker instances we've spawned. */
    142 static unsigned             g_uWorkerSeqNo = 0;
    143 #endif
    144 /** Set if we've registred the atexit handler already. */
    145 static int                  g_fAtExitRegistered = 0;
    146 
    147 /** @var g_cArchBits
    148  * The bit count of the architecture this binary is compiled for. */
    149 /** @var g_szArch
    150  * The name of the architecture this binary is compiled for. */
    151 /** @var g_cArchBits
    152  * The bit count of the alternative architecture. */
    153 /** @var g_szAltArch
    154  * The name of the alternative architecture. */
    155 #if defined(KBUILD_ARCH_AMD64)
    156 static unsigned             g_cArchBits    = 64;
    157 static char const           g_szArch[]     = "amd64";
    158 static unsigned             g_cAltArchBits = 32;
    159 static char const           g_szAltArch[]  = "x86";
    160 #elif defined(KBUILD_ARCH_X86)
    161 static unsigned             g_cArchBits    = 32;
    162 static char const           g_szArch[]     = "x86";
    163 static unsigned             g_cAltArchBits = 64;
    164 static char const           g_szAltArch[]  = "amd64";
    165 #else
    166 # error "Port me!"
    167 #endif
    168 
    169 
    170 
    171 /**
    172  * Unlinks a worker instance from a list.
    173  *
    174  * @param   pList               The list.
    175  * @param   pWorker             The worker.
    176  */
    177 static void kSubmitListUnlink(PWORKERLIST pList, PWORKERINSTANCE pWorker)
    178 {
    179     PWORKERINSTANCE pNext = pWorker->pNext;
    180     PWORKERINSTANCE pPrev = pWorker->pPrev;
    181 
    182     if (pNext)
    183     {
    184         assert(pNext->pPrev == pWorker);
    185         pNext->pPrev = pPrev;
    186     }
    187     else
    188     {
    189         assert(pList->pTail == pWorker);
    190         pList->pTail = pPrev;
    191     }
    192 
    193     if (pPrev)
    194     {
    195         assert(pPrev->pNext == pWorker);
    196         pPrev->pNext = pNext;
    197     }
    198     else
    199     {
    200         assert(pList->pHead == pWorker);
    201         pList->pHead = pNext;
    202     }
    203 
    204     assert(!pList->pHead || pList->pHead->pPrev == NULL);
    205     assert(!pList->pTail || pList->pTail->pNext == NULL);
    206 
    207     assert(pList->cEntries > 0);
    208     pList->cEntries--;
    209 
    210     pWorker->pNext = NULL;
    211     pWorker->pPrev = NULL;
    212 }
    213 
    214 
    215 /**
    216  * Appends a worker instance to the tail of a list.
    217  *
    218  * @param   pList               The list.
    219  * @param   pWorker             The worker.
    220  */
    221 static void kSubmitListAppend(PWORKERLIST pList, PWORKERINSTANCE pWorker)
    222 {
    223     PWORKERINSTANCE pTail = pList->pTail;
    224 
    225     assert(pTail != pWorker);
    226     assert(pList->pHead != pWorker);
    227 
    228     pWorker->pNext = NULL;
    229     pWorker->pPrev = pTail;
    230     if (pTail != NULL)
    231     {
    232         assert(pTail->pNext == NULL);
    233         pTail->pNext = pWorker;
    234     }
    235     else
    236     {
    237         assert(pList->pHead == NULL);
    238         pList->pHead = pWorker;
    239     }
    240     pList->pTail = pWorker;
    241 
    242     assert(pList->pHead->pPrev == NULL);
    243     assert(pList->pTail->pNext == NULL);
    244 
    245     pList->cEntries++;
    246 }
    247 
    248 
    249 /**
    250  * Remove worker from the process ID hash table.
    251  *
    252  * @param   pWorker             The worker.
    253  */
    254 static void kSubmitPidHashRemove(PWORKERINSTANCE pWorker)
    255 {
    256     size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
    257     if (g_apPidHash[idxHash] == pWorker)
    258         g_apPidHash[idxHash] = pWorker->pNext;
    259     else
    260     {
    261         PWORKERINSTANCE pPrev = g_apPidHash[idxHash];
    262         while (pPrev && pPrev->pNext != pWorker)
    263             pPrev = pPrev->pNext;
    264         assert(pPrev != NULL);
    265         if (pPrev)
    266             pPrev->pNext = pWorker->pNext;
    267     }
    268     pWorker->pid = -1;
    269 }
    270 
    271 
    272 /**
    273  * Looks up a worker by its process ID.
    274  *
    275  * @returns Pointer to the worker instance if found. NULL if not.
    276  * @param   pid                 The process ID of the worker.
    277  */
    278 static PWORKERINSTANCE kSubmitFindWorkerByPid(pid_t pid)
    279 {
    280     PWORKERINSTANCE pWorker = g_apPidHash[KWORKER_PID_HASH(pid)];
    281     while (pWorker && pWorker->pid != pid)
    282         pWorker = pWorker->pNextPidHash;
    283     return pWorker;
    284 }
    285 
    286 
    287 /**
    288  * Creates a new worker process.
    289  *
    290  * @returns 0 on success, non-zero value on failure.
    291  * @param   pWorker             The worker structure.  Caller does the linking
    292  *                              (as we might be reusing an existing worker
    293  *                              instance because a worker shut itself down due
    294  *                              to high resource leak level).
    295  * @param   cVerbosity          The verbosity level.
    296  */
    297 static int kSubmitSpawnWorker(PWORKERINSTANCE pWorker, int cVerbosity)
    298 {
    299 #if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
    300     static const char s_szWorkerName[] = "kWorker.exe";
    301 #else
    302     static const char s_szWorkerName[] = "kWorker";
    303 #endif
    304     const char     *pszBinPath = get_kbuild_bin_path();
    305     size_t const    cchBinPath = strlen(pszBinPath);
    306     size_t          cchExectuable;
    307     size_t const    cbExecutableBuf = GET_PATH_MAX;
    308     PATH_VAR(szExecutable);
    309 #define TUPLE(a_sz)     a_sz, sizeof(a_sz) - 1
    310     struct variable *pVarVolatile = lookup_variable(TUPLE("PATH_OUT"));
    311     if (pVarVolatile)
    312     { /* likely */ }
    313     else
    314     {
    315         pVarVolatile = lookup_variable(TUPLE("PATH_OUT_BASE"));
    316         if (!pVarVolatile)
    317             warn("Neither PATH_OUT_BASE nor PATH_OUT was found.");
    318     }
    319 
    320     /*
    321      * Construct the executable path.
    322      */
    323     if (   pWorker->cBits == g_cArchBits
    324         ?  cchBinPath + 1 + sizeof(s_szWorkerName) <= cbExecutableBuf
    325         :  cchBinPath + 1 - sizeof(g_szArch) + sizeof(g_szAltArch) + sizeof(s_szWorkerName) <= cbExecutableBuf )
    326     {
    327 #ifdef KBUILD_OS_WINDOWS
    328         static DWORD        s_fDenyRemoteClients = ~(DWORD)0;
    329         wchar_t             wszPipeName[64];
    330         HANDLE              hWorkerPipe;
    331         SECURITY_ATTRIBUTES SecAttrs = { /*nLength:*/ sizeof(SecAttrs), /*pAttrs:*/ NULL, /*bInheritHandle:*/ TRUE };
    332 #else
    333         int                 aiPair[2] = { -1, -1 };
    334 #endif
    335 
    336         memcpy(szExecutable, pszBinPath, cchBinPath);
    337         cchExectuable = cchBinPath;
    338 
    339         /* Replace the arch bin directory extension with the alternative one if requested. */
    340         if (pWorker->cBits != g_cArchBits)
    341         {
    342             if (   cchBinPath < sizeof(g_szArch)
    343                 || memcmp(&szExecutable[cchBinPath - sizeof(g_szArch) + 1], g_szArch, sizeof(g_szArch) - 1) != 0)
    344                 return errx(1, "KBUILD_BIN_PATH does not end with main architecture (%s) as expected: %s", pszBinPath, g_szArch);
    345             cchExectuable -= sizeof(g_szArch) - 1;
    346             memcpy(&szExecutable[cchExectuable], g_szAltArch, sizeof(g_szAltArch) - 1);
    347             cchExectuable += sizeof(g_szAltArch) - 1;
    348         }
    349 
    350         /* Append a slash and the worker name. */
    351         szExecutable[cchExectuable++] = '/';
    352         memcpy(&szExecutable[cchExectuable], s_szWorkerName, sizeof(s_szWorkerName));
    353 
    354 #ifdef KBUILD_OS_WINDOWS
    355         /*
    356          * Create the bi-directional pipe.  Worker end is marked inheritable, our end is not.
    357          */
    358         if (s_fDenyRemoteClients == ~(DWORD)0)
    359             s_fDenyRemoteClients = GetVersion() >= 0x60000 ? PIPE_REJECT_REMOTE_CLIENTS : 0;
    360         _snwprintf(wszPipeName, sizeof(wszPipeName), L"\\\\.\\pipe\\kmk-%u-kWorker-%u", getpid(), g_uWorkerSeqNo++);
    361         hWorkerPipe = CreateNamedPipeW(wszPipeName,
    362                                        PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE /* win2k sp2+ */,
    363                                        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | s_fDenyRemoteClients,
    364                                        1 /* cMaxInstances */,
    365                                        64 /*cbOutBuffer*/,
    366                                        65536 /*cbInBuffer*/,
    367                                        0 /*cMsDefaultTimeout -> 50ms*/,
    368                                        &SecAttrs /* inherit */);
    369         if (hWorkerPipe != INVALID_HANDLE_VALUE)
    370         {
    371             pWorker->hPipe = CreateFileW(wszPipeName,
    372                                          GENERIC_READ | GENERIC_WRITE,
    373                                          0 /* dwShareMode - no sharing */,
    374                                          NULL /*pSecAttr - no inherit */,
    375                                          OPEN_EXISTING,
    376                                          FILE_FLAG_OVERLAPPED,
    377                                          NULL /*hTemplate*/);
    378             if (pWorker->hPipe != INVALID_HANDLE_VALUE)
    379             {
    380                 pWorker->OverlappedRead.hEvent = CreateEventW(NULL /*pSecAttrs - no inherit*/, TRUE /*bManualReset*/,
    381                                                               TRUE /*bInitialState*/, NULL /*pwszName*/);
    382                 if (pWorker->OverlappedRead.hEvent != NULL)
    383                 {
    384                     char        szHandleArg[32];
    385                     const char *apszArgs[6] =
    386                     {
    387                         szExecutable, "--pipe", szHandleArg,
    388                         pVarVolatile ? "--volatile" : NULL, pVarVolatile ? pVarVolatile->value : NULL,
    389                         NULL
    390                     };
    391                     _snprintf(szHandleArg, sizeof(szHandleArg), "%p", hWorkerPipe);
    392 
    393                     /*
    394                      * Create the worker process.
    395                      */
    396                     pWorker->hProcess = (HANDLE) _spawnve(_P_NOWAIT, szExecutable, apszArgs, environ);
    397                     if ((intptr_t)pWorker->hProcess != -1)
    398                     {
    399                         CloseHandle(hWorkerPipe);
    400                         pWorker->pid = GetProcessId(pWorker->hProcess);
    401                         if (cVerbosity > 0)
    402                             fprintf(stderr, "kSubmit: created %d bit worker %d\n", pWorker->cBits, pWorker->pid);
    403                         return 0;
    404                     }
    405                     err(1, "_spawnve(,%s,,)", szExecutable);
    406                     CloseHandle(pWorker->OverlappedRead.hEvent);
    407                     pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE;
    408                 }
    409                 else
    410                     errx(1, "CreateEventW failed: %u", GetLastError());
    411                 CloseHandle(pWorker->hPipe);
    412                 pWorker->hPipe = INVALID_HANDLE_VALUE;
    413             }
    414             else
    415                 errx(1, "Opening named pipe failed: %u", GetLastError());
    416             CloseHandle(hWorkerPipe);
    417         }
    418         else
    419             errx(1, "CreateNamedPipeW failed: %u", GetLastError());
    420 
    421 #else
    422         /*
    423          * Create a socket pair.
    424          */
    425         if (socketpair(AF_LOCAL, SOCK_STREAM, 0, aiPair) == 0)
    426         {
    427             pWorker->fdSocket = aiPair[1];
    428         }
    429         else
    430             err(1, "socketpair");
    431 #endif
    432     }
    433     else
    434         errx(1, "KBUILD_BIN_PATH is too long");
    435     return -1;
    436 }
    437 
    438 
    439 /**
    440  * Selects an idle worker or spawns a new one.
    441  *
    442  * @returns Pointer to the selected worker instance.  NULL on error.
    443  * @param   pWorker             The idle worker instance to respawn.
    444  *                              On failure this will be freed!
    445  * @param   cBitsWorker         The worker bitness - 64 or 32.
    446  */
    447 static int kSubmitRespawnWorker(PWORKERINSTANCE pWorker, int cVerbosity)
    448 {
    449     /*
    450      * Clean up after the old worker.
    451      */
    452 #ifdef KBUILD_OS_WINDOWS
    453     DWORD   rcWait;
    454 
    455     /* Close the pipe handle first, breaking the pipe in case it's not already
    456        busted up.  Close the event semaphore too before waiting for the process. */
    457     if (pWorker->hPipe != INVALID_HANDLE_VALUE)
    458     {
    459         if (!CloseHandle(pWorker->hPipe))
    460             warnx("CloseHandle(pWorker->hPipe): %u", GetLastError());
    461         pWorker->hPipe = INVALID_HANDLE_VALUE;
    462     }
    463 
    464     if (!CloseHandle(pWorker->OverlappedRead.hEvent))
    465         warnx("CloseHandle(pWorker->OverlappedRead.hEvent): %u", GetLastError());
    466     pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE;
    467 
    468     /* It's probably shutdown already, if not give it 10 milliseconds before
    469        we terminate it forcefully. */
    470     rcWait = WaitForSingleObject(pWorker->hProcess, 10);
    471     if (rcWait != WAIT_OBJECT_0)
    472     {
    473         BOOL fRc = TerminateProcess(pWorker->hProcess, 127);
    474         rcWait = WaitForSingleObject(pWorker->hProcess, 100);
    475         if (rcWait != WAIT_OBJECT_0)
    476             warnx("WaitForSingleObject returns %u (and TerminateProcess %d)", rcWait, fRc);
    477     }
    478 
    479     if (!CloseHandle(pWorker->hProcess))
    480         warnx("CloseHandle(pWorker->hProcess): %u", GetLastError());
    481     pWorker->hProcess = INVALID_HANDLE_VALUE;
    482 
    483 #else
    484     pid_t   pidWait;
    485     int     rc;
    486 
    487     if (pWorker->fdSocket != -1)
    488     {
    489         if (close(pWorker->fdSocket) != 0)
    490             warn("close(pWorker->fdSocket)");
    491         pWorker->fdSocket = -1;
    492     }
    493 
    494     kill(pWorker->pid, SIGTERM);
    495     pidWait = waitpid(pWorker->pid, &rc, 0);
    496     if (pidWait != pWorker->pid)
    497         warn("waitpid(pWorker->pid,,0)");
    498 #endif
    499 
    500     /*
    501      * Unlink it from the hash table.
    502      */
    503     kSubmitPidHashRemove(pWorker);
    504 
    505     /*
    506      * Respawn it.
    507      */
    508     if (kSubmitSpawnWorker(pWorker, cVerbosity) == 0)
    509     {
    510         /*
    511          * Insert it into the process ID hash table and idle list.
    512          */
    513         size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
    514         pWorker->pNextPidHash = g_apPidHash[idxHash];
    515         g_apPidHash[idxHash] = pWorker;
    516         return 0;
    517     }
    518 
    519     kSubmitListUnlink(&g_IdleList, pWorker);
    520     free(pWorker);
    521     return -1;
    522 }
    523 
    524 
    525 /**
    526  * Selects an idle worker or spawns a new one.
    527  *
    528  * @returns Pointer to the selected worker instance.  NULL on error.
    529  * @param   cBitsWorker         The worker bitness - 64 or 32.
    530  */
    531 static PWORKERINSTANCE kSubmitSelectWorkSpawnNewIfNecessary(unsigned cBitsWorker, int cVerbosity)
    532 {
    533     /*
    534      * Lookup up an idle worker.
    535      */
    536     PWORKERINSTANCE pWorker = g_IdleList.pHead;
    537     while (pWorker)
    538     {
    539         if (pWorker->cBits == cBitsWorker)
    540             return pWorker;
    541         pWorker = pWorker->pNext;
    542     }
    543 
    544     /*
    545      * Create a new worker instance.
    546      */
    547     pWorker = (PWORKERINSTANCE)xcalloc(sizeof(*pWorker));
    548     pWorker->cBits = cBitsWorker;
    549     if (kSubmitSpawnWorker(pWorker, cVerbosity) == 0)
    550     {
    551         /*
    552          * Insert it into the process ID hash table and idle list.
    553          */
    554         size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
    555         pWorker->pNextPidHash = g_apPidHash[idxHash];
    556         g_apPidHash[idxHash] = pWorker;
    557 
    558         kSubmitListAppend(&g_IdleList, pWorker);
    559         return pWorker;
    560     }
    561 
    562     free(pWorker);
    563     return NULL;
    564 }
    565 
    566 
    567 /**
    568  * Composes a JOB mesage for a worker.
    569  *
    570  * @returns Pointer to the message.
    571  * @param   pszExecutable       The executable to run.
    572  * @param   papszArgs           The argument vector.
    573  * @param   papszEnvVars        The environment vector.
    574  * @param   pszCwd              The current directory.
    575  * @param   fWatcomBrainDamage  The wcc/wcc386 workaround.
    576  * @param   papszPostCmdArgs    The post command and it's arguments.
    577  * @param   cPostCmdArgs        Number of post command argument, including the
    578  *                              command.  Zero if no post command scheduled.
    579  * @param   pcbMsg              Where to return the message length.
    580  */
    581 static void *kSubmitComposeJobMessage(const char *pszExecutable, char **papszArgs, char **papszEnvVars,
    582                                       const char *pszCwd, int fWatcomBrainDamage,
    583                                       char **papszPostCmdArgs, uint32_t cPostCmdArgs, uint32_t *pcbMsg)
    584 {
    585     size_t   cbTmp;
    586     uint32_t i;
    587     uint32_t cbMsg;
    588     uint32_t cArgs;
    589     uint32_t cEnvVars;
    590     uint8_t *pbMsg;
    591     uint8_t *pbCursor;
    592 
    593     /*
    594      * Adjust input.
    595      */
    596     if (!pszExecutable)
    597         pszExecutable = papszArgs[0];
    598 
    599     /*
    600      * Calculate the message length first.
    601      */
    602     cbMsg  = sizeof(cbMsg);
    603     cbMsg += sizeof("JOB");
    604     cbMsg += strlen(pszExecutable) + 1;
    605     cbMsg += strlen(pszCwd) + 1;
    606 
    607     cbMsg += sizeof(cArgs);
    608     for (i = 0; papszArgs[i] != NULL; i++)
    609         cbMsg += 1 + strlen(papszArgs[i]) + 1;
    610     cArgs  = i;
    611 
    612     cbMsg += sizeof(cArgs);
    613     for (i = 0; papszEnvVars[i] != NULL; i++)
    614         cbMsg += strlen(papszEnvVars[i]) + 1;
    615     cEnvVars = i;
    616 
    617     cbMsg += 1;
    618 
    619     cbMsg += sizeof(cPostCmdArgs);
    620     for (i = 0; i < cPostCmdArgs; i++)
    621         cbMsg += strlen(papszPostCmdArgs[i]) + 1;
    622 
    623     /*
    624      * Compose the message.
    625      */
    626     pbMsg = pbCursor = xmalloc(cbMsg);
    627 
    628     /* header */
    629     memcpy(pbCursor, &cbMsg, sizeof(cbMsg));
    630     pbCursor += sizeof(cbMsg);
    631     memcpy(pbCursor, "JOB", sizeof("JOB"));
    632     pbCursor += sizeof("JOB");
    633 
    634     /* executable. */
    635     cbTmp = strlen(pszExecutable) + 1;
    636     memcpy(pbCursor, pszExecutable, cbTmp);
    637     pbCursor += cbTmp;
    638 
    639     /* cwd */
    640     cbTmp = strlen(pszCwd) + 1;
    641     memcpy(pbCursor, pszCwd, cbTmp);
    642     pbCursor += cbTmp;
    643 
    644     /* argument */
    645     memcpy(pbCursor, &cArgs, sizeof(cArgs));
    646     pbCursor += sizeof(cArgs);
    647     for (i = 0; papszArgs[i] != NULL; i++)
    648     {
    649         *pbCursor++ = 0; /* Argument expansion flags (MSC, EMX). */
    650         cbTmp = strlen(papszArgs[i]) + 1;
    651         memcpy(pbCursor, papszArgs[i], cbTmp);
    652         pbCursor += cbTmp;
    653     }
    654     assert(i == cArgs);
    655 
    656     /* environment */
    657     memcpy(pbCursor, &cEnvVars, sizeof(cEnvVars));
    658     pbCursor += sizeof(cEnvVars);
    659     for (i = 0; papszEnvVars[i] != NULL; i++)
    660     {
    661         cbTmp = strlen(papszEnvVars[i]) + 1;
    662         memcpy(pbCursor, papszEnvVars[i], cbTmp);
    663         pbCursor += cbTmp;
    664     }
    665     assert(i == cEnvVars);
    666 
    667     /* flags */
    668     *pbCursor++ = fWatcomBrainDamage != 0;
    669 
    670     /* post command */
    671     memcpy(pbCursor, &cPostCmdArgs, sizeof(cPostCmdArgs));
    672     pbCursor += sizeof(cPostCmdArgs);
    673     for (i = 0; i < cPostCmdArgs; i++)
    674     {
    675         cbTmp = strlen(papszPostCmdArgs[i]) + 1;
    676         memcpy(pbCursor, papszPostCmdArgs[i], cbTmp);
    677         pbCursor += cbTmp;
    678     }
    679     assert(i == cPostCmdArgs);
    680 
    681     assert(pbCursor - pbMsg == (size_t)cbMsg);
    682 
    683     /*
    684      * Done.
    685      */
    686     *pcbMsg = cbMsg;
    687     return pbMsg;
    688 }
    689 
    690 
    691 /**
    692  * Sends the job message to the given worker, respawning the worker if
    693  * necessary.
    694  *
    695  * @returns 0 on success, non-zero on failure.
    696  *
    697  * @param   pWorker             The work to send the request to.  The worker is
    698  *                              on the idle list.
    699  * @param   pvMsg               The message to send.
    700  * @param   cbMsg               The size of the message.
    701  * @param   fNoRespawning       Set if
    702  * @param   cVerbosity          The verbosity level.
    703  */
    704 static int kSubmitSendJobMessage(PWORKERINSTANCE pWorker, void const *pvMsg, uint32_t cbMsg, int fNoRespawning, int cVerbosity)
    705 {
    706     int cRetries;
    707 
    708     /*
    709      * Respawn the worker if it stopped by itself and we closed the pipe already.
    710      */
    711 #ifdef KBUILD_OS_WINDOWS
    712     if (pWorker->hPipe == INVALID_HANDLE_VALUE)
    713 #else
    714     if (pWorker->fdSocket == -1)
    715 #endif
    716     {
    717         if (!fNoRespawning)
    718         {
    719             if (cVerbosity > 0)
    720                 fprintf(stderr,  "kSubmit: Respawning worker (#1)...\n");
    721             if (kSubmitRespawnWorker(pWorker, cVerbosity) != 0)
    722                 return 2;
    723         }
    724 
    725     }
    726 
    727     /*
    728      * Restart-on-broken-pipe loop. Necessary?
    729      */
    730     for (cRetries = !fNoRespawning ? 1 : 0; ; cRetries--)
    731     {
    732         /*
    733          * Try write the message.
    734          */
    735         uint32_t        cbLeft = cbMsg;
    736         uint8_t const  *pbLeft = (uint8_t const  *)pvMsg;
    737 #ifdef KBUILD_OS_WINDOWS
    738         DWORD           dwErr;
    739         DWORD           cbWritten;
    740         while (WriteFile(pWorker->hPipe, pbLeft, cbLeft, &cbWritten, NULL /*pOverlapped*/))
    741         {
    742             assert(cbWritten <= cbLeft);
    743             cbLeft -= cbWritten;
    744             if (!cbLeft)
    745                 return 0;
    746 
    747             /* This scenario shouldn't really ever happen. But just in case... */
    748             pbLeft += cbWritten;
    749         }
    750         dwErr = GetLastError();
    751         if (   (   dwErr != ERROR_BROKEN_PIPE
    752                 && dwErr != ERROR_NO_DATA)
    753             || cRetries <= 0)
    754             return errx(1, "Error writing to worker: %u", dwErr);
    755 #else
    756         ssize_t cbWritten
    757         while ((cbWritten = write(pWorker->fdSocket, pbLeft, cbLeft)) >= 0)
    758         {
    759             assert(cbWritten <= cbLeft);
    760             cbLeft -= cbWritten;
    761             if (!cbLeft)
    762                 return 0;
    763 
    764             pbLeft += cbWritten;
    765         }
    766         if (  (   errno != EPIPE
    767                && errno != ENOTCONN
    768                && errno != ECONNRESET))
    769             || cRetries <= 0)
    770             return err(1, "Error writing to worker");
    771 # error "later"
    772 #endif
    773 
    774         /*
    775          * Broken connection. Try respawn the worker.
    776          */
    777         if (cVerbosity > 0)
    778             fprintf(stderr,  "kSubmit: Respawning worker (#2)...\n");
    779         if (kSubmitRespawnWorker(pWorker, cVerbosity) != 0)
    780             return 2;
    781     }
    782 }
    783 
    784 
    785 /**
    786  * Closes the connection on a worker that said it is going to exit now.
    787  *
    788  * This is a way of dealing with imperfect resource management in the worker, it
    789  * will monitor it a little and trigger a respawn when it looks bad.
    790  *
    791  * This function just closes the pipe / socket connection to the worker.  The
    792  * kSubmitSendJobMessage function will see this a trigger a respawn the next
    793  * time the worker is engaged.  This will usually mean there's a little delay in
    794  * which the process can terminate without us having to actively wait for it.
    795  *
    796  * @param   pWorker             The worker instance.
    797  */
    798 static void kSubmitCloseConnectOnExitingWorker(PWORKERINSTANCE pWorker)
    799 {
    800 #ifdef KBUILD_OS_WINDOWS
    801     if (!CloseHandle(pWorker->hPipe))
    802         warnx("CloseHandle(pWorker->hPipe): %u", GetLastError());
    803     pWorker->hPipe = INVALID_HANDLE_VALUE;
    804 #else
    805     if (close(pWorker->fdSocket) != 0)
    806         warn("close(pWorker->fdSocket)");
    807     pWorker->fdSocket = -1;
    808 #endif
    809 }
    810 
    811 
    812 #ifdef KBUILD_OS_WINDOWS
    813 
    814 /**
    815  * Handles read failure.
    816  *
    817  * @returns Exit code.
    818  * @param   pWorker             The worker instance.
    819  * @param   dwErr               The error code.
    820  * @param   pszWhere            Where it failed.
    821  */
    822 static int kSubmitWinReadFailed(PWORKERINSTANCE pWorker, DWORD dwErr, const char *pszWhere)
    823 {
    824     DWORD dwExitCode;
    825 
    826     if (pWorker->cbResultRead == 0)
    827         errx(1, "%s/ReadFile failed: %u", pszWhere, dwErr);
    828     else
    829         errx(1, "%s/ReadFile failed: %u (read %u bytes)", pszWhere, dwErr, pWorker->cbResultRead);
    830     assert(dwErr != 0);
    831 
    832     /* Complete the result. */
    833     pWorker->Result.s.rcExit         = 127;
    834     pWorker->Result.s.bWorkerExiting = 1;
    835     pWorker->cbResultRead            = sizeof(pWorker->Result);
    836 
    837     if (GetExitCodeProcess(pWorker->hProcess, &dwExitCode))
    838     {
    839         if (dwExitCode != 0)
    840             pWorker->Result.s.rcExit = dwExitCode;
    841     }
    842 
    843     return dwErr != 0 ? (int)(dwErr & 0x7fffffff) : 0x7fffffff;
    844 
    845 }
    846 
    847 
    848 /**
    849  * Used by
    850  * @returns 0 if we got the whole result, -1 if I/O is pending, and windows last
    851  *          error on ReadFile failure.
    852  * @param   pWorker             The worker instance.
    853  */
    854 static int kSubmitReadMoreResultWin(PWORKERINSTANCE pWorker, const char *pszWhere)
    855 {
    856     /*
    857      * Set up the result read, telling the sub_proc.c unit about it.
    858      */
    859     while (pWorker->cbResultRead < sizeof(pWorker->Result))
    860     {
    861         DWORD cbRead = 0;
    862 
    863         BOOL fRc = ResetEvent(pWorker->OverlappedRead.hEvent);
    864         assert(fRc); (void)fRc;
    865 
    866         pWorker->OverlappedRead.Offset     = 0;
    867         pWorker->OverlappedRead.OffsetHigh = 0;
    868 
    869         if (!ReadFile(pWorker->hPipe, &pWorker->Result.ab[pWorker->cbResultRead],
    870                      sizeof(pWorker->Result) - pWorker->cbResultRead,
    871                      &cbRead,
    872                      &pWorker->OverlappedRead))
    873         {
    874             DWORD dwErr = GetLastError();
    875             if (dwErr == ERROR_IO_PENDING)
    876                 return -1;
    877             return kSubmitWinReadFailed(pWorker, dwErr, pszWhere);
    878         }
    879 
    880         pWorker->cbResultRead += cbRead;
    881         assert(pWorker->cbResultRead <= sizeof(pWorker->Result));
    882     }
    883     return 0;
    884 }
    885 
    886 #endif /* KBUILD_OS_WINDOWS */
    887 
    888 /**
    889  * Marks the worker active.
    890  *
    891  * On windows this involves setting up the async result read and telling
    892  * sub_proc.c about the process.
    893  *
    894  * @returns Exit code.
    895  * @param   pWorker             The worker instance to mark as active.
    896  * @param   cVerbosity          The verbosity level.
    897  * @param   pChild              The kmk child to associate the job with.
    898  * @param   pPidSpawned         If @a *pPidSpawned is non-zero if the child is
    899  *                              running, otherwise the worker is already done
    900  *                              and we've returned the exit code of the job.
    901  */
    902 static int kSubmitMarkActive(PWORKERINSTANCE pWorker, int cVerbosity, struct child *pChild, pid_t *pPidSpawned)
    903 {
    904 #ifdef KBUILD_OS_WINDOWS
    905     int rc;
    906 #endif
    907 
    908     pWorker->cbResultRead = 0;
    909 
    910 #ifdef KBUILD_OS_WINDOWS
    911     /*
    912      * Setup the async result read on windows.  If we're slow and the worker
    913      * very fast, this may actually get the result immediately.
    914      */
    915 l_again:
    916     rc = kSubmitReadMoreResultWin(pWorker, "kSubmitMarkActive");
    917     if (rc == -1)
    918     {
    919         if (process_kmk_register_submit(pWorker->OverlappedRead.hEvent, (intptr_t)pWorker, pPidSpawned) == 0)
    920         { /* likely */ }
    921         else
    922         {
    923             /* We need to do the waiting here because sub_proc.c has too much to do. */
    924             warnx("Too many processes for sub_proc.c to handle!");
    925             WaitForSingleObject(pWorker->OverlappedRead.hEvent, INFINITE);
    926             goto l_again;
    927         }
    928     }
    929     else
    930     {
    931         assert(rc == 0 || pWorker->Result.s.rcExit != 0);
    932         if (pWorker->Result.s.bWorkerExiting)
    933             kSubmitCloseConnectOnExitingWorker(pWorker);
    934         *pPidSpawned = 0;
    935         return pWorker->Result.s.rcExit;
    936     }
    937 #endif
    938 
    939     /*
    940      * Mark it busy and move it to the active instance.
    941      */
    942     pWorker->pBusyWith = pChild;
    943 #ifndef KBUILD_OS_WINDOWS
    944     *pPidSpawned = pWorker->pid;
    945 #endif
    946 
    947     kSubmitListUnlink(&g_IdleList, pWorker);
    948     kSubmitListAppend(&g_BusyList, pWorker);
    949     return 0;
    950 }
    951 
    952 
    953 #ifdef KBUILD_OS_WINDOWS
    954 
    955 /**
    956  * Retrieve the worker child result.
    957  *
    958  * If incomplete, we restart the ReadFile operation like kSubmitMarkActive does.
    959  *
    960  * @returns 0 on success, -1 if ReadFile was restarted.
    961  * @param   pvUser              The worker instance.
    962  * @param   prcExit             Where to return the exit code.
    963  * @param   piSigNo             Where to return the signal number.
    964  */
    965 int kSubmitSubProcGetResult(intptr_t pvUser, int *prcExit, int *piSigNo)
    966 {
    967     PWORKERINSTANCE pWorker = (PWORKERINSTANCE)pvUser;
    968 
    969     /*
    970      * Get the overlapped result.  There should be one since we're here
    971      * because of a satisfied WaitForMultipleObject.
    972      */
    973     DWORD cbRead = 0;
    974     if (GetOverlappedResult(pWorker->hPipe, &pWorker->OverlappedRead, &cbRead, TRUE))
    975     {
    976         pWorker->cbResultRead += cbRead;
    977         assert(pWorker->cbResultRead <= sizeof(pWorker->Result));
    978 
    979         /* More to be read? */
    980         while (pWorker->cbResultRead < sizeof(pWorker->Result))
    981         {
    982             int rc = kSubmitReadMoreResultWin(pWorker, "kSubmitSubProcGetResult/more");
    983             if (rc == -1)
    984                 return -1;
    985             assert(rc == 0 || pWorker->Result.s.rcExit != 0);
    986         }
    987         assert(pWorker->cbResultRead == sizeof(pWorker->Result));
    988     }
    989     else
    990     {
    991         DWORD dwErr = GetLastError();
    992         kSubmitWinReadFailed(pWorker, dwErr, "kSubmitSubProcGetResult/result");
    993     }
    994 
    995     /*
    996      * Okay, we've got a result.
    997      */
    998     *prcExit = pWorker->Result.s.rcExit;
    999     switch (pWorker->Result.s.rcExit)
    1000     {
    1001         default:                                *piSigNo = 0; break;
    1002         case CONTROL_C_EXIT:                    *piSigNo = SIGINT; break;
    1003         case STATUS_INTEGER_DIVIDE_BY_ZERO:     *piSigNo = SIGFPE; break;
    1004         case STATUS_ACCESS_VIOLATION:           *piSigNo = SIGSEGV; break;
    1005         case STATUS_PRIVILEGED_INSTRUCTION:
    1006         case STATUS_ILLEGAL_INSTRUCTION:        *piSigNo = SIGILL; break;
    1007     }
    1008     if (pWorker->Result.s.bWorkerExiting)
    1009         kSubmitCloseConnectOnExitingWorker(pWorker);
    1010 
    1011     return 0;
    1012 }
    1013 
    1014 
    1015 int kSubmitSubProcKill(intptr_t pvUser, int iSignal)
    1016 {
    1017     return -1;
    1018 }
    1019 
    1020 
    1021 /**
    1022  * Called by process_cleanup when it's done with the worker.
    1023  *
    1024  * @param   pvUser              The worker instance.
    1025  */
    1026 void kSubmitSubProcCleanup(intptr_t pvUser)
    1027 {
    1028     PWORKERINSTANCE pWorker = (PWORKERINSTANCE)pvUser;
    1029     kSubmitListUnlink(&g_BusyList, pWorker);
    1030     kSubmitListAppend(&g_IdleList, pWorker);
    1031 }
    1032 
    1033 #endif /* KBUILD_OS_WINDOWS */
    1034 
    1035 
    1036 /**
    1037  * atexit callback that trigger worker termination.
    1038  */
    1039 static void kSubmitAtExitCallback(void)
    1040 {
    1041     PWORKERINSTANCE pWorker;
    1042     DWORD           msStartTick;
    1043     DWORD           cKillRaids = 0;
    1044 
    1045     /*
    1046      * Tell all the workers to exit by breaking the connection.
    1047      */
    1048     for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1049         kSubmitCloseConnectOnExitingWorker(pWorker);
    1050     for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1051         kSubmitCloseConnectOnExitingWorker(pWorker);
    1052 
    1053     /*
    1054      * Wait a little while for them to stop.
    1055      */
    1056     Sleep(0);
    1057     msStartTick = GetTickCount();
    1058     for (;;)
    1059     {
    1060         /*
    1061          * Collect handles of running processes.
    1062          */
    1063         PWORKERINSTANCE apWorkers[MAXIMUM_WAIT_OBJECTS];
    1064         HANDLE          ahHandles[MAXIMUM_WAIT_OBJECTS];
    1065         DWORD           cHandles = 0;
    1066 
    1067         for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1068             if (pWorker->hProcess != INVALID_HANDLE_VALUE)
    1069             {
    1070                 if (cHandles < MAXIMUM_WAIT_OBJECTS)
    1071                 {
    1072                     apWorkers[cHandles] = pWorker;
    1073                     ahHandles[cHandles] = pWorker->hProcess;
    1074                 }
    1075                 cHandles++;
    1076             }
    1077         for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1078             if (pWorker->hProcess != INVALID_HANDLE_VALUE)
    1079             {
    1080                 if (cHandles < MAXIMUM_WAIT_OBJECTS)
    1081                 {
    1082                     apWorkers[cHandles] = pWorker;
    1083                     ahHandles[cHandles] = pWorker->hProcess;
    1084                 }
    1085                 cHandles++;
    1086             }
    1087         if (cHandles == 0)
    1088             return;
    1089 
    1090         /*
    1091          * Wait for the processes.
    1092          */
    1093         for (;;)
    1094         {
    1095             DWORD cMsElapsed = GetTickCount() - msStartTick;
    1096             DWORD dwWait = WaitForMultipleObjects(cHandles <= MAXIMUM_WAIT_OBJECTS ? cHandles : MAXIMUM_WAIT_OBJECTS,
    1097                                                   ahHandles, FALSE /*bWaitAll*/,
    1098                                                   cMsElapsed < 1000 ? 1000 - cMsElapsed + 16 : 16);
    1099             if (   dwWait >= WAIT_OBJECT_0
    1100                 && dwWait <= WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS)
    1101             {
    1102                 size_t idx = dwWait - WAIT_OBJECT_0;
    1103                 CloseHandle(apWorkers[idx]->hProcess);
    1104                 apWorkers[idx]->hProcess = INVALID_HANDLE_VALUE;
    1105 
    1106                 if (cHandles <= MAXIMUM_WAIT_OBJECTS)
    1107                 {
    1108                     /* Restart the wait with the worker removed, or quit if it was the last worker. */
    1109                     cHandles--;
    1110                     if (!cHandles)
    1111                         return;
    1112                     if (idx != cHandles)
    1113                     {
    1114                         apWorkers[idx] = apWorkers[cHandles];
    1115                         ahHandles[idx] = ahHandles[cHandles];
    1116                     }
    1117                     continue;
    1118                 }
    1119                 /* else: Reconstruct the wait array so we get maximum coverage. */
    1120             }
    1121             else if (dwWait == WAIT_TIMEOUT)
    1122             {
    1123                 /* Terminate the whole bunch. */
    1124                 cKillRaids++;
    1125                 if (cKillRaids <= 2)
    1126                 {
    1127                     fprintf(stderr, "kmk/kSubmit: Killing %u lingering worker processe(s)!\n", cHandles);
    1128                     for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1129                         if (pWorker->hProcess != INVALID_HANDLE_VALUE)
    1130                             TerminateProcess(pWorker->hProcess, WAIT_TIMEOUT);
    1131                     for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1132                         if (pWorker->hProcess != INVALID_HANDLE_VALUE)
    1133                             TerminateProcess(pWorker->hProcess, WAIT_TIMEOUT);
    1134                 }
    1135                 else
    1136                 {
    1137                     fprintf(stderr, "kmk/kSubmit: Giving up on the last %u worker processe(s). :-(\n", cHandles);
    1138                     break;
    1139                 }
    1140             }
    1141             else
    1142             {
    1143                 /* Some kind of wait error.  Could be a bad handle, check each and remove
    1144                    bad ones as well as completed ones. */
    1145                 size_t idx;
    1146                 fprintf(stderr, "kmk/kSubmit: WaitForMultipleObjects unexpectedly returned %#u (err=%u)\n",
    1147                         dwWait, GetLastError());
    1148                 for (idx = 0; idx < cHandles; idx++)
    1149                 {
    1150                     dwWait = WaitForSingleObject(ahHandles[idx], 0 /*ms*/);
    1151                     if (dwWait != WAIT_TIMEOUT)
    1152                     {
    1153                         CloseHandle(apWorkers[idx]->hProcess);
    1154                         apWorkers[idx]->hProcess = INVALID_HANDLE_VALUE;
    1155                     }
    1156                 }
    1157             }
    1158             break;
    1159         } /* wait loop */
    1160     } /* outer wait loop */
    1161 }
    116236
    116337
     
    118357 * @param   pszValue            The var=value string to apply.
    118458 */
    1185 static int kSubmitOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
    1186                             int cVerbosity, const char *pszValue)
     59int kBuiltinOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, int cVerbosity, const char *pszValue)
    118760{
    118861    const char *pszEqual = strchr(pszValue, '=');
     
    120073            {
    120174                if (cVerbosity > 0)
    1202                     fprintf(stderr, "kSubmit: replacing '%s' with '%s'\n", papszEnv[iEnvVar], pszValue);
     75                    warnx("replacing '%s' with '%s'", papszEnv[iEnvVar], pszValue);
    120376                free(papszEnv[iEnvVar]);
    1204                 papszEnv[iEnvVar] = xstrdup(pszValue);
     77                papszEnv[iEnvVar] = strdup(pszValue);
     78                if (!papszEnv[iEnvVar])
     79                    return errx(1, "out of memory!");
    120580                break;
    120681            }
     
    121287            {
    121388                *pcAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf;
    1214                 *ppapszEnv = papszEnv = (char **)xrealloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0]));
    1215             }
    1216             papszEnv[cEnvVars++] = xstrdup(pszValue);
    1217             papszEnv[cEnvVars]   = NULL;
     89                papszEnv = (char **)realloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0]));
     90                if (!papszEnv)
     91                    return errx(1, "out of memory!");
     92                *ppapszEnv = papszEnv;
     93            }
     94            papszEnv[cEnvVars] = strdup(pszValue);
     95            if (!papszEnv[cEnvVars])
     96                return errx(1, "out of memory!");
     97            papszEnv[++cEnvVars]   = NULL;
    121898            *pcEnvVars = cEnvVars;
    121999            if (cVerbosity > 0)
    1220                 fprintf(stderr, "kSubmit: added '%s'\n", papszEnv[iEnvVar]);
     100                warnx("added '%s'", papszEnv[iEnvVar]);
    1221101        }
    1222102        else
     
    1228108                {
    1229109                    if (cVerbosity > 0)
    1230                         fprintf(stderr, "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
     110                        warnx("removing duplicate '%s'", papszEnv[iEnvVar]);
    1231111                    free(papszEnv[iEnvVar]);
    1232112                    cEnvVars--;
     
    1255135 * @param   pszVarToRemove      The name of the variable to remove.
    1256136 */
    1257 static int kSubmitOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove)
     137int kBuiltinOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove)
    1258138{
    1259139    if (strchr(pszVarToRemove, '=') == NULL)
     
    1269149            {
    1270150                if (cVerbosity > 0)
    1271                     fprintf(stderr, !cRemoved ? "kSubmit: removing '%s'\n"
    1272                             : "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
     151                    warnx(!cRemoved ? "removing '%s'" : "removing duplicate '%s'", papszEnv[iEnvVar]);
    1273152                free(papszEnv[iEnvVar]);
    1274153                cEnvVars--;
     
    1282161
    1283162        if (cVerbosity > 0 && !cRemoved)
    1284             fprintf(stderr, "kSubmit: not found '%s'\n", pszVarToRemove);
     163            warnx("not found '%s'", pszVarToRemove);
    1285164    }
    1286165    else
     
    1300179 * @param   pszValue            The --chdir value to apply.
    1301180 */
    1302 static int kSubmitOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue)
     181int kBuiltinOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue)
    1303182{
    1304183    size_t cchNewCwd = strlen(pszValue);
     
    1360239}
    1361240
    1362 
    1363 static int usage(FILE *pOut,  const char *argv0)
    1364 {
    1365     fprintf(pOut,
    1366             "usage: %s [-Z|--zap-env] [-E|--set <var=val>] [-U|--unset <var=val>]\n"
    1367             "           [-C|--chdir <dir>] [--wcc-brain-damage]\n"
    1368             "           [-3|--32-bit] [-6|--64-bit] [-v]\n"
    1369             "           [-P|--post-cmd <cmd> [args]] -- <program> [args]\n"
    1370             "   or: %s --help\n"
    1371             "   or: %s --version\n"
    1372             "\n"
    1373             "Options:\n"
    1374             "  -Z, --zap-env, -i, --ignore-environment\n"
    1375             "    Zaps the environment. Position dependent.\n"
    1376             "  -E, --set <var>=[value]\n"
    1377             "    Sets an enviornment variable putenv fashion. Position dependent.\n"
    1378             "  -U, --unset <var>\n"
    1379             "    Removes an environment variable. Position dependent.\n"
    1380             "  -C, --chdir <dir>\n"
    1381             "    Specifies the current directory for the program.  Relative paths\n"
    1382             "    are relative to the previous -C option.  Default is getcwd value.\n"
    1383             "  -3, --32-bit\n"
    1384             "    Selects a 32-bit kWorker process. Default: kmk bit count\n"
    1385             "  -6, --64-bit\n"
    1386             "    Selects a 64-bit kWorker process. Default: kmk bit count\n"
    1387             "  --wcc-brain-damage\n"
    1388             "    Works around wcc and wcc386 (Open Watcom) not following normal\n"
    1389             "    quoting conventions on Windows, OS/2, and DOS.\n"
    1390             "  -v,--verbose\n"
    1391             "    More verbose execution.\n"
    1392             "  -P|--post-cmd <cmd> ...\n"
    1393             "    For running a built-in command on the output, specifying the command\n"
    1394             "    and all it's parameters.  Currently supported commands:\n"
    1395             "        kDepObj\n"
    1396             "  -V,--version\n"
    1397             "    Show the version number.\n"
    1398             "  -h,--help\n"
    1399             "    Show this usage information.\n"
    1400             "\n"
    1401             ,
    1402             argv0, argv0, argv0);
    1403     return 1;
    1404 }
    1405 
    1406 
    1407 int kmk_builtin_kSubmit(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned)
    1408 {
    1409     int             rcExit = 0;
    1410     int             iArg;
    1411     unsigned        cAllocatedEnvVars;
    1412     unsigned        iEnvVar;
    1413     unsigned        cEnvVars;
    1414     char          **papszEnv            = NULL;
    1415     const char     *pszExecutable       = NULL;
    1416     const char     *pszCwd              = NULL;
    1417     int             iPostCmd            = argc;
    1418     int             cPostCmdArgs        = 0;
    1419     unsigned        cBitsWorker         = g_cArchBits;
    1420     int             fWatcomBrainDamage  = 0;
    1421     int             cVerbosity          = 0;
    1422     size_t const    cbCwdBuf            = GET_PATH_MAX;
    1423     PATH_VAR(szCwd);
    1424 
    1425     g_progname = argv[0];
    1426 
    1427     /*
    1428      * Create default program environment.
    1429      */
    1430     if (getcwd_fs(szCwd, cbCwdBuf) != NULL)
    1431     { /* likely */ }
    1432     else
    1433         return err(1, "getcwd_fs failed\n");
    1434 
    1435     papszEnv = pChild->environment;
    1436     if (!papszEnv)
    1437         pChild->environment = papszEnv = target_environment(pChild->file);
    1438     cEnvVars = 0;
    1439     while (papszEnv[cEnvVars] != NULL)
    1440         cEnvVars++;
    1441     cAllocatedEnvVars = cEnvVars;
    1442 
    1443     /*
    1444      * Parse the command line.
    1445      */
    1446     for (iArg = 1; iArg < argc; iArg++)
    1447     {
    1448         const char *pszArg = argv[iArg];
    1449         if (*pszArg == '-')
    1450         {
    1451             char chOpt = *++pszArg;
    1452             pszArg++;
    1453             if (chOpt != '-')
    1454             {
    1455                 if (chOpt != '\0')
    1456                 { /* likely */ }
    1457                 else
    1458                 {
    1459                     errx(1, "Incomplete option: '-'");
    1460                     return usage(stderr, argv[0]);
    1461                 }
    1462             }
    1463             else
    1464             {
    1465                 /* '--' indicates where the bits to execute start. */
    1466                 if (*pszArg == '\0')
    1467                 {
    1468                     iArg++;
    1469                     break;
    1470                 }
    1471 
    1472                 if (   strcmp(pszArg, "wcc-brain-damage") == 0
    1473                     || strcmp(pszArg, "watcom-brain-damage") == 0)
    1474                 {
    1475                     fWatcomBrainDamage = 1;
    1476                     continue;
    1477                 }
    1478 
    1479                 /* convert to short. */
    1480                 if (strcmp(pszArg, "help") == 0)
    1481                     chOpt = 'h';
    1482                 else if (strcmp(pszArg, "version") == 0)
    1483                     chOpt = 'V';
    1484                 else if (strcmp(pszArg, "set") == 0)
    1485                     chOpt = 'E';
    1486                 else if (strcmp(pszArg, "unset") == 0)
    1487                     chOpt = 'U';
    1488                 else if (   strcmp(pszArg, "zap-env") == 0
    1489                          || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ )
    1490                     chOpt = 'Z';
    1491                 else if (strcmp(pszArg, "chdir") == 0)
    1492                     chOpt = 'C';
    1493                 else if (strcmp(pszArg, "post-cmd") == 0)
    1494                     chOpt = 'P';
    1495                 else if (strcmp(pszArg, "32-bit") == 0)
    1496                     chOpt = '3';
    1497                 else if (strcmp(pszArg, "64-bit") == 0)
    1498                     chOpt = '6';
    1499                 else if (strcmp(pszArg, "verbose") == 0)
    1500                     chOpt = 'v';
    1501                 else if (strcmp(pszArg, "executable") == 0)
    1502                     chOpt = 'e';
    1503                 else
    1504                 {
    1505                     errx(1, "Unknown option: '%s'", pszArg - 2);
    1506                     return usage(stderr, argv[0]);
    1507                 }
    1508                 pszArg = "";
    1509             }
    1510 
    1511             do
    1512             {
    1513                 /* Get option value first, if the option takes one. */
    1514                 const char *pszValue = NULL;
    1515                 switch (chOpt)
    1516                 {
    1517                     case 'E':
    1518                     case 'U':
    1519                     case 'C':
    1520                     case 'e':
    1521                         if (*pszArg != '\0')
    1522                             pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
    1523                         else if (++iArg < argc)
    1524                             pszValue = argv[iArg];
    1525                         else
    1526                         {
    1527                             errx(1, "Option -%c requires a value!", chOpt);
    1528                             return usage(stderr, argv[0]);
    1529                         }
    1530                         break;
    1531                 }
    1532 
    1533                 switch (chOpt)
    1534                 {
    1535                     case 'Z':
    1536                     case 'i': /* GNU env compatibility. */
    1537                         for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
    1538                             free(papszEnv[iEnvVar]);
    1539                         papszEnv[0] = NULL;
    1540                         cEnvVars = 0;
    1541                         break;
    1542 
    1543                     case 'E':
    1544                         rcExit = kSubmitOptEnvSet(&papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
    1545                         pChild->environment = papszEnv;
    1546                         if (rcExit == 0)
    1547                             break;
    1548                         return rcExit;
    1549 
    1550                     case 'U':
    1551                         rcExit = kSubmitOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue);
    1552                         if (rcExit == 0)
    1553                             break;
    1554                         return rcExit;
    1555 
    1556                     case 'C':
    1557                         rcExit = kSubmitOptChDir(szCwd, cbCwdBuf, pszValue);
    1558                         if (rcExit == 0)
    1559                             break;
    1560                         return rcExit;
    1561 
    1562                     case 'P':
    1563                         if (cPostCmdArgs > 0)
    1564                             return errx(1, "The -P option can only be used once!");
    1565                         if (*pszArg != '\0')
    1566                             return errx(1, "The cmd part of the -P needs to be a separate argument!");
    1567                         iPostCmd = ++iArg;
    1568                         if (iArg >= argc)
    1569                             return errx(1, "The -P option requires a command following it!");
    1570                         while (iArg < argc && strcmp(argv[iArg], "--") != 0)
    1571                             iArg++;
    1572                         cPostCmdArgs = iArg - iPostCmd;
    1573                         iArg--;
    1574                         break;
    1575 
    1576                     case '3':
    1577                         cBitsWorker = 32;
    1578                         break;
    1579 
    1580                     case '6':
    1581                         cBitsWorker = 64;
    1582                         break;
    1583 
    1584                     case 'e':
    1585                         pszExecutable = pszValue;
    1586                         break;
    1587 
    1588                     case 'v':
    1589                         cVerbosity++;
    1590                         break;
    1591 
    1592                     case 'h':
    1593                         usage(stdout, argv[0]);
    1594                         return 0;
    1595 
    1596                     case 'V':
    1597                         return kbuild_version(argv[0]);
    1598                 }
    1599             } while ((chOpt = *pszArg++) != '\0');
    1600         }
    1601         else
    1602         {
    1603             errx(1, "Unknown argument: '%s'", pszArg);
    1604             return usage(stderr, argv[0]);
    1605         }
    1606     }
    1607 
    1608     /*
    1609      * Check that we've got something to execute.
    1610      */
    1611     if (iArg < argc)
    1612     {
    1613         uint32_t        cbMsg;
    1614         void           *pvMsg   = kSubmitComposeJobMessage(pszExecutable, &argv[iArg], papszEnv, szCwd,
    1615                                                            fWatcomBrainDamage, &argv[iPostCmd], cPostCmdArgs, &cbMsg);
    1616         PWORKERINSTANCE pWorker = kSubmitSelectWorkSpawnNewIfNecessary(cBitsWorker, cVerbosity);
    1617         if (pWorker)
    1618         {
    1619             if (!pszExecutable)
    1620                 pszExecutable = argv[iArg];
    1621 
    1622             rcExit = kSubmitSendJobMessage(pWorker, pvMsg, cbMsg, 0 /*fNoRespawning*/, cVerbosity);
    1623             if (rcExit == 0)
    1624                 rcExit = kSubmitMarkActive(pWorker, cVerbosity, pChild, pPidSpawned);
    1625 
    1626             if (!g_fAtExitRegistered)
    1627                 if (atexit(kSubmitAtExitCallback) == 0)
    1628                     g_fAtExitRegistered = 1;
    1629         }
    1630         else
    1631             rcExit = 1;
    1632         free(pvMsg);
    1633     }
    1634     else
    1635     {
    1636         errx(1, "Nothing to executed!");
    1637         rcExit = usage(stderr, argv[0]);
    1638     }
    1639 
    1640     return rcExit;
    1641 }
    1642 
    1643 
    1644 
Note: See TracChangeset for help on using the changeset viewer.