Changeset 2841


Ignore:
Timestamp:
Aug 26, 2016, 10:04:50 PM (9 years ago)
Author:
bird
Message:

work in progress

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kmk/kmkbuiltin/submit.c

    r2840 r2841  
    3838#include <string.h>
    3939#include <errno.h>
     40#include <assert.h>
    4041#ifdef HAVE_ALLOCA_H
    4142# include <alloca.h>
     
    5051#endif
    5152
     53#include "kbuild.h"
    5254#include "kmkbuiltin.h"
    5355#include "err.h"
    5456
    55 #ifdef __OS2__
    56 # define INCL_BASE
    57 # include <os2.h>
    58 # ifndef LIBPATHSTRICT
    59 #  define LIBPATHSTRICT 3
    60 # endif
    61 #endif
     57
     58/*********************************************************************************************************************************
     59*   Defined Constants And Macros                                                                                                 *
     60*********************************************************************************************************************************/
     61/** Hashes a pid. */
     62#define KWORKER_PID_HASH(a_pid) ((size_t)(a_pid) % 61)
     63
    6264
    6365/*********************************************************************************************************************************
     
    7173    /** Pointer to the previous worker instance. */
    7274    PWORKERINSTANCE         pPrev;
     75    /** Pointer to the next worker with the same pid hash slot. */
     76    PWORKERINSTANCE         pNextPidHash;
    7377    /** 32 or 64. */
    7478    unsigned                cBits;
     79    /** The process ID of the kWorker process. */
     80    pid_t                   pid;
     81#ifdef KBUILD_OS_WINDOWS
    7582    /** The process handle. */
    7683    HANDLE                  hProcess;
    77 
     84    /** The bi-directional pipe we use to talk to the kWorker process. */
     85    HANDLE                  hPipe;
     86    /** For overlapped read (have valid event semaphore). */
     87    OVERLAPPED              OverlappedRead;
     88    /** The 32-bit exit code read bufffer. */
     89    uint32_t                u32ReadResult;
     90#else
     91    /** The socket descriptor we use to talk to the kWorker process. */
     92    int                     fdSocket;
     93#endif
     94
     95    /** What it's busy with.  NULL if idle. */
     96    struct child           *pBusyWith;
    7897} WORKERINSTANCE;
     98
     99
     100typedef struct WORKERLIST
     101{
     102    /** The head of the list.  NULL if empty. */
     103    PWORKERINSTANCE         pHead;
     104    /** The tail of the list.  NULL if empty. */
     105    PWORKERINSTANCE         pTail;
     106    /** Number of list entries. */
     107    size_t                  cEntries;
     108} WORKERLIST;
     109typedef WORKERLIST *PWORKERLIST;
    79110
    80111
     
    82113*   Global Variables                                                                                                             *
    83114*********************************************************************************************************************************/
    84 static PWORKERINSTANCE g_pIdleHead;
    85 static PWORKERINSTANCE g_pIdleTail;
    86 
     115/** List of idle worker.*/
     116static WORKERLIST           g_IdleList;
     117/** List of busy workers. */
     118static WORKERLIST           g_BusyList;
     119/** PID hash table for the workers.
     120 * @sa KWORKER_PID_HASH() */
     121static PWORKERINSTANCE      g_apPidHash[61];
     122
     123#ifdef KBUILD_OS_WINDOWS
     124/** For naming the pipes.
     125 * Also indicates how many worker instances we've spawned. */
     126static unsigned             g_uWorkerSeqNo = 0;
     127#endif
     128
     129/** @var g_cArchBits
     130 * The bit count of the architecture this binary is compiled for. */
     131/** @var g_szArch
     132 * The name of the architecture this binary is compiled for. */
     133/** @var g_cArchBits
     134 * The bit count of the alternative architecture. */
     135/** @var g_szAltArch
     136 * The name of the alternative architecture. */
     137#if defined(KBUILD_ARCH_AMD64)
     138static unsigned             g_cArchBits    = 64;
     139static char const           g_szArch[]     = "amd64";
     140static unsigned             g_cAltArchBits = 32;
     141static char const           g_szAltArch[]  = "x86";
     142#elif defined(KBUILD_ARCH_X86)
     143static unsigned             g_cArchBits    = 32;
     144static char const           g_szArch[]     = "x86";
     145static unsigned             g_cAltArchBits = 64;
     146static char const           g_szAltArch[]  = "amd64";
     147#else
     148# error "Port me!"
     149#endif
     150
     151
     152/**
     153 * Unlinks a worker instance from a list.
     154 *
     155 * @param   pList               The list.
     156 * @param   pWorker             The worker.
     157 */
     158static void kSubmitListUnlink(PWORKERLIST pList, PWORKERINSTANCE pWorker)
     159{
     160    PWORKERINSTANCE pNext = pWorker->pNext;
     161    PWORKERINSTANCE pPrev = pWorker->pPrev;
     162
     163    if (pNext)
     164    {
     165        assert(pNext->pPrev == pWorker);
     166        pNext->pPrev = pPrev;
     167    }
     168    else
     169    {
     170        assert(pList->pHead == pWorker);
     171        pList->pHead = pPrev;
     172    }
     173
     174    if (pPrev)
     175    {
     176        assert(pPrev->pNext == pWorker);
     177        pPrev->pNext = pNext;
     178    }
     179    else
     180    {
     181        assert(pList->pTail == pWorker);
     182        pList->pTail = pNext;
     183    }
     184
     185    assert(pList->cEntries > 0);
     186    pList->cEntries--;
     187
     188    pWorker->pNext = NULL;
     189    pWorker->pPrev = NULL;
     190}
     191
     192
     193/**
     194 * Appends a worker instance to the tail of a list.
     195 *
     196 * @param   pList               The list.
     197 * @param   pWorker             The worker.
     198 */
     199static void kSubmitListAppend(PWORKERLIST pList, PWORKERINSTANCE pWorker)
     200{
     201    PWORKERINSTANCE pTail = pList->pTail;
     202
     203    assert(pTail != pWorker);
     204    assert(pList->pHead != pWorker);
     205
     206    pWorker->pNext = NULL;
     207    pWorker->pPrev = pTail;
     208    if (pTail != NULL)
     209    {
     210        assert(pTail->pNext == NULL);
     211        pTail->pNext = pWorker;
     212    }
     213    else
     214    {
     215        assert(pList->pHead == NULL);
     216        pList->pHead = pWorker;
     217        pList->pTail = pWorker;
     218    }
     219
     220    pList->cEntries++;
     221}
     222
     223
     224/**
     225 * Looks up a worker by its process ID.
     226 *
     227 * @returns Pointer to the worker instance if found. NULL if not.
     228 * @param   pid                 The process ID of the worker.
     229 */
     230static PWORKERINSTANCE kSubmitFindWorkerByPid(pid_t pid)
     231{
     232    PWORKERINSTANCE pWorker = g_apPidHash[KWORKER_PID_HASH(pid)];
     233    while (pWorker && pWorker->pid != pid)
     234        pWorker = pWorker->pNextPidHash;
     235    return pWorker;
     236}
     237
     238
     239/**
     240 * Creates a new worker process.
     241 *
     242 * @returns 0 on success, non-zero value on failure.
     243 * @param   pWorker             The worker structure.  Caller does the linking
     244 *                              (as we might be reusing an existing worker
     245 *                              instance because a worker shut itself down due
     246 *                              to high resource leak level).
     247 * @param   cVerbosity          The verbosity level.
     248 */
     249static int kSubmitSpawnWorker(PWORKERINSTANCE pWorker, int cVerbosity)
     250{
     251#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
     252    static const char s_szWorkerName[] = "kWorker.exe";
     253#else
     254    static const char s_szWorkerName[] = "kWorker";
     255#endif
     256    const char     *pszBinPath = get_kbuild_bin_path();
     257    size_t const    cchBinPath = strlen(pszBinPath);
     258    size_t          cchExectuable;
     259    size_t const    cbExecutableBuf = GET_PATH_MAX;
     260    PATH_VAR(szExecutable);
     261
     262    /*
     263     * Construct the executable path.
     264     */
     265    if (   pWorker->cBits == g_cArchBits
     266        ?  cchBinPath + 1 + sizeof(s_szWorkerName) <= cbExecutableBuf
     267        :  cchBinPath + 1 - sizeof(g_szArch) + sizeof(g_szAltArch) + sizeof(s_szWorkerName) <= cbExecutableBuf )
     268    {
     269#ifdef KBUILD_OS_WINDOWS
     270        static DWORD        s_fDenyRemoteClients = ~(DWORD)0;
     271        wchar_t             wszPipeName[64];
     272        HANDLE              hWorkerPipe;
     273        SECURITY_ATTRIBUTES SecAttrs = { /*nLength:*/ sizeof(SecAttrs), /*pAttrs:*/ NULL, /*bInheritHandle:*/ TRUE };
     274#else
     275        int                 aiPair[2] = { -1, -1 };
     276#endif
     277
     278        memcpy(szExecutable, pszBinPath, cchBinPath);
     279        cchExectuable = cchBinPath;
     280
     281        /* Replace the arch bin directory extension with the alternative one if requested. */
     282        if (pWorker->cBits != g_cArchBits)
     283        {
     284            if (   cchBinPath < sizeof(g_szArch)
     285                || memcmp(&szExecutable[cchBinPath - sizeof(g_szArch) + 1], g_szArch, sizeof(g_szArch) - 1) != 0)
     286                return errx(1, "KBUILD_BIN_PATH does not end with main architecture (%s) as expected: %s", pszBinPath, g_szArch);
     287            cchExectuable -= sizeof(g_szArch) - 1;
     288            memcpy(&szExecutable[cchExectuable], g_szAltArch, sizeof(g_szAltArch) - 1);
     289            cchExectuable += sizeof(g_szAltArch) - 1;
     290        }
     291
     292        /* Append a slash and the worker name. */
     293        szExecutable[cchExectuable++] = '/';
     294        memcpy(&szExecutable[cchExectuable], s_szWorkerName, sizeof(s_szWorkerName));
     295
     296#ifdef KBUILD_OS_WINDOWS
     297        /*
     298         * Create the bi-directional pipe.  Worker end is marked inheritable, our end is not.
     299         */
     300        if (s_fDenyRemoteClients == ~(DWORD)0)
     301            s_fDenyRemoteClients = GetVersion() >= 0x60000 ? PIPE_REJECT_REMOTE_CLIENTS : 0;
     302        _snwprintf(wszPipeName, sizeof(wszPipeName), L"\\\\.\\pipe\\kmk-%u-kWorker-%u", getpid(), g_uWorkerSeqNo++);
     303        hWorkerPipe = CreateNamedPipeW(wszPipeName,
     304                                       PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE /* win2k sp2+ */,
     305                                       PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | s_fDenyRemoteClients,
     306                                       1 /* cMaxInstances */,
     307                                       64 /*cbOutBuffer*/,
     308                                       65536 /*cbInBuffer*/,
     309                                       0 /*cMsDefaultTimeout -> 50ms*/,
     310                                       &SecAttrs /* inherit */);
     311        if (hWorkerPipe != INVALID_HANDLE_VALUE)
     312        {
     313            pWorker->hPipe = CreateFileW(wszPipeName,
     314                                         GENERIC_READ | GENERIC_WRITE,
     315                                         0 /* dwShareMode - no sharing */,
     316                                         NULL /*pSecAttr - no inherit */,
     317                                         OPEN_EXISTING,
     318                                         FILE_FLAG_OVERLAPPED,
     319                                         NULL /*hTemplate*/);
     320            if (pWorker->hPipe != INVALID_HANDLE_VALUE)
     321            {
     322                pWorker->OverlappedRead.hEvent = CreateEventW(NULL /*pSecAttrs - no inherit*/, TRUE /*bManualReset*/,
     323                                                              TRUE /*bInitialState*/, NULL /*pwszName*/);
     324                if (pWorker->OverlappedRead.hEvent != NULL)
     325                {
     326                    char        szHandleArg[16];
     327                    const char *apszArgs[4] = { szExecutable, "--pipe", szHandleArg, NULL };
     328                    _snprintf(szHandleArg, sizeof(szHandleArg), "%p", hWorkerPipe);
     329
     330                    /*
     331                     * Create the worker process.
     332                     */
     333                    pWorker->hProcess = (HANDLE) _spawnve(_P_NOWAIT, szExecutable, apszArgs, environ);
     334                    if ((intptr_t)pWorker->hProcess != -1)
     335                    {
     336                        CloseHandle(hWorkerPipe);
     337                        pWorker->pid = GetProcessId(pWorker->hProcess);
     338                        if (cVerbosity > 0)
     339                            fprintf(stderr, "kSubmit: created %d bit worker %d\n", pWorker->cBits, pWorker->pid);
     340                        return 0;
     341                    }
     342                    err(1, "_spawnve(,%s,,)", szExecutable);
     343                    CloseHandle(pWorker->OverlappedRead.hEvent);
     344                    pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE;
     345                }
     346                else
     347                    errx(1, "CreateEventW failed: %u", GetLastError());
     348                CloseHandle(pWorker->hPipe);
     349                pWorker->hPipe = INVALID_HANDLE_VALUE;
     350            }
     351            else
     352                errx(1, "Opening named pipe failed: %u", GetLastError());
     353            CloseHandle(hWorkerPipe);
     354        }
     355        else
     356            errx(1, "CreateNamedPipeW failed: %u", GetLastError());
     357
     358#else
     359        /*
     360         * Create a socket pair.
     361         */
     362        if (socketpair(AF_LOCAL, SOCK_STREAM, 0, aiPair) == 0)
     363        {
     364            pWorker->fdSocket = aiPair[1];
     365        }
     366        else
     367            err(1, "socketpair");
     368#endif
     369    }
     370    else
     371        errx(1, "KBUILD_BIN_PATH is too long");
     372    return -1;
     373}
     374
     375
     376/**
     377 * Selects an idle worker or spawns a new one.
     378 *
     379 * @returns Pointer to the selected worker instance.  NULL on error.
     380 * @param   pWorker             The idle worker instance to respawn.
     381 *                              On failure this will be freed!
     382 * @param   cBitsWorker         The worker bitness - 64 or 32.
     383 */
     384static int kSubmitRespawnWorker(PWORKERINSTANCE pWorker, int cVerbosity)
     385{
     386    size_t idxHash;
     387
     388    /*
     389     * Clean up after the old worker.
     390     */
     391#ifdef KBUILD_OS_WINDOWS
     392    DWORD   rcWait;
     393
     394    /* Close the pipe handle first, breaking the pipe in case it's not already
     395       busted up.  Close the event semaphore too before waiting for the process. */
     396    if (!CloseHandle(pWorker->hPipe))
     397        warnx("CloseHandle(pWorker->hPipe): %u", GetLastError());
     398    pWorker->hPipe = INVALID_HANDLE_VALUE;
     399
     400    if (!CloseHandle(pWorker->OverlappedRead.hEvent))
     401        warnx("CloseHandle(pWorker->OverlappedRead.hEvent): %u", GetLastError());
     402    pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE;
     403
     404    /* It's probably shutdown already, if not give it 10 milliseconds before
     405       we terminate it forcefully. */
     406    rcWait = WaitForSingleObject(pWorker->hProcess, 10);
     407    if (rcWait != WAIT_OBJECT_0)
     408    {
     409        BOOL fRc = TerminateProcess(pWorker->hProcess, 127);
     410        rcWait = WaitForSingleObject(pWorker->hProcess, 100);
     411        if (rcWait != WAIT_OBJECT_0)
     412            warnx("WaitForSingleObject returns %u (and TerminateProcess %d)", rcWait, fRc);
     413    }
     414
     415    if (!CloseHandle(pWorker->hProcess))
     416        warnx("CloseHandle(pWorker->hProcess): %u", GetLastError());
     417    pWorker->hProcess = INVALID_HANDLE_VALUE;
     418
     419#else
     420    pid_t   pidWait;
     421    int     rc;
     422
     423    if (close(pWorker->fdSocket) != 0)
     424        warn("close(pWorker->fdSocket)");
     425    pWorker->fdSocket = -1;
     426
     427    kill(pWorker->pid, SIGTERM);
     428    pidWait = waitpid(pWorker->pid, &rc, 0);
     429    if (pidWait != pWorker->pid)
     430        warn("waitpid(pWorker->pid,,0)");
     431#endif
     432
     433    /*
     434     * Unlink it from the hash table.
     435     */
     436    idxHash = KWORKER_PID_HASH(pWorker->pid);
     437    if (g_apPidHash[idxHash] == pWorker)
     438        g_apPidHash[idxHash] = pWorker->pNext;
     439    else
     440    {
     441        PWORKERINSTANCE pPrev = g_apPidHash[idxHash];
     442        while (pPrev && pPrev->pNext != pWorker)
     443            pPrev = pPrev->pNext;
     444        assert(pPrev != NULL);
     445        if (pPrev)
     446            pPrev->pNext = pWorker->pNext;
     447    }
     448    pWorker->pid = -1;
     449
     450    /*
     451     * Respawn it.
     452     */
     453    if (kSubmitSpawnWorker(pWorker, cVerbosity) == 0)
     454    {
     455        /*
     456         * Insert it into the process ID hash table and idle list.
     457         */
     458        size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
     459        pWorker->pNextPidHash = g_apPidHash[idxHash];
     460        g_apPidHash[idxHash] = pWorker;
     461        return 0;
     462    }
     463
     464    kSubmitListUnlink(&g_IdleList, pWorker);
     465    free(pWorker);
     466    return -1;
     467}
     468
     469
     470/**
     471 * Selects an idle worker or spawns a new one.
     472 *
     473 * @returns Pointer to the selected worker instance.  NULL on error.
     474 * @param   cBitsWorker         The worker bitness - 64 or 32.
     475 */
     476static PWORKERINSTANCE kSubmitSelectWorkSpawnNewIfNecessary(unsigned cBitsWorker, int cVerbosity)
     477{
     478    /*
     479     * Lookup up an idle worker.
     480     */
     481    PWORKERINSTANCE pWorker = g_IdleList.pHead;
     482    while (pWorker)
     483    {
     484        if (pWorker->cBits == cBitsWorker)
     485            return pWorker;
     486        pWorker = pWorker->pNext;
     487    }
     488
     489    /*
     490     * Create a new worker instance.
     491     */
     492    pWorker = (PWORKERINSTANCE)xcalloc(sizeof(*pWorker));
     493    pWorker->cBits = cBitsWorker;
     494    if (kSubmitSpawnWorker(pWorker, cVerbosity) == 0)
     495    {
     496        /*
     497         * Insert it into the process ID hash table and idle list.
     498         */
     499        size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
     500        pWorker->pNextPidHash = g_apPidHash[idxHash];
     501        g_apPidHash[idxHash] = pWorker;
     502
     503        kSubmitListAppend(&g_IdleList, pWorker);
     504        return pWorker;
     505    }
     506
     507    free(pWorker);
     508    return NULL;
     509}
     510
     511
     512/**
     513 * Composes a JOB mesage for a worker.
     514 *
     515 * @returns Pointer to the message.
     516 * @param   pszExecutable   The executable to run.
     517 * @param   papszArgs       The argument vector.
     518 * @param   papszEnvVars    The environment vector.
     519 * @param   pszCwd          The current directory.
     520 * @param   pcbMsg          Where to return the message length.
     521 */
     522static void *kSubmitComposeJobMessage(const char *pszExecutable, char **papszArgs, char **papszEnvVars,
     523                                      const char *pszCwd, uint32_t *pcbMsg)
     524{
     525    size_t   i;
     526    size_t   cbTmp;
     527    uint32_t cbMsg;
     528    uint8_t *pbMsg;
     529    uint8_t *pbCursor;
     530
     531    /*
     532     * Calculate the message length first.
     533     */
     534    cbMsg  = sizeof(cbMsg);
     535    cbMsg += sizeof("JOB");
     536    cbMsg += strlen(pszExecutable) + 1;
     537
     538    for (i = 0; papszArgs[i] != NULL; i++)
     539        cbMsg += strlen(papszArgs[i]) + 1;
     540    cbMsg += 1;
     541
     542    for (i = 0; papszEnvVars[i] != NULL; i++)
     543        cbMsg += strlen(papszEnvVars[i]) + 1;
     544    cbMsg += 1;
     545
     546    cbMsg += strlen(pszCwd) + 1;
     547
     548    /*
     549     * Compose the message.
     550     */
     551    pbMsg = pbCursor = xmalloc(cbMsg);
     552
     553    memcpy(pbCursor, &cbMsg, sizeof(cbMsg));
     554    pbCursor += sizeof(cbMsg);
     555    memcpy(pbCursor, "JOB", sizeof("JOB"));
     556    pbCursor += sizeof("JOB");
     557
     558    cbTmp = strlen(pszExecutable) + 1;
     559    memcpy(pbCursor, pszExecutable, cbTmp);
     560    pbCursor += cbTmp;
     561
     562    for (i = 0; papszArgs[i] != NULL; i++)
     563    {
     564        cbTmp = strlen(papszArgs[i]) + 1;
     565        memcpy(pbCursor, papszArgs[i], cbTmp);
     566        pbCursor += cbTmp;
     567    }
     568    *pbCursor++ = '\0';
     569
     570    for (i = 0; papszEnvVars[i] != NULL; i++)
     571    {
     572        cbTmp = strlen(papszEnvVars[i]) + 1;
     573        memcpy(pbCursor, papszEnvVars[i], cbTmp);
     574        pbCursor += cbTmp;
     575    }
     576    *pbCursor++ = '\0';
     577
     578    cbTmp = strlen(pszCwd) + 1;
     579    memcpy(pbCursor, pszCwd, cbTmp);
     580    pbCursor += cbTmp;
     581
     582    assert(pbCursor - pbMsg == (size_t)cbMsg);
     583
     584    /* done */
     585    *pcbMsg = cbMsg;
     586    return pbMsg;
     587}
     588
     589
     590/**
     591 * Sends the job message to the given worker, respawning the worker if
     592 * necessary.
     593 *
     594 * @returns 0 on success, non-zero on failure.
     595 *
     596 * @param   pWorker             The work to send the request to.  The worker is
     597 *                              on the idle list.
     598 * @param   pvMsg               The message to send.
     599 * @param   cbMsg               The size of the message.
     600 * @param   cVerbosity          The verbosity level.
     601 */
     602static int kSubmitSendJobMessage(PWORKERINSTANCE pWorker, void const *pvMsg, uint32_t cbMsg, int cVerbosity)
     603{
     604    int cRetries = 1;
     605    for (;; cRetries--)
     606    {
     607        /*
     608         * Try write the message.
     609         */
     610        uint32_t        cbLeft = cbMsg;
     611        uint8_t const  *pbLeft = (uint8_t const  *)pvMsg;
     612#ifdef KBUILD_OS_WINDOWS
     613        DWORD           dwErr;
     614        DWORD           cbWritten;
     615        while (WriteFile(pWorker->hPipe, pbLeft, cbLeft, &cbWritten, NULL /*pOverlapped*/))
     616        {
     617            assert(cbWritten <= cbLeft);
     618            cbLeft -= cbWritten;
     619            if (!cbLeft)
     620                return 0;
     621
     622            /* This scenario shouldn't really ever happen. But just in case... */
     623            pbLeft += cbWritten;
     624        }
     625        dwErr = GetLastError();
     626        if (   (   dwErr != ERROR_BROKEN_PIPE
     627                && dwErr != ERROR_NO_DATA)
     628            || cRetries <= 0)
     629            return errx(1, "Error writing to worker: %u", dwErr);
     630#else
     631        ssize_t cbWritten
     632        while ((cbWritten = write(pWorker->fdSocket, pbLeft, cbLeft)) >= 0)
     633        {
     634            assert(cbWritten <= cbLeft);
     635            cbLeft -= cbWritten;
     636            if (!cbLeft)
     637                return 0;
     638
     639            pbLeft += cbWritten;
     640        }
     641        if (  (   errno != EPIPE
     642               && errno != ENOTCONN
     643               && errno != ECONNRESET))
     644            || cRetries <= 0)
     645            return err(1, "Error writing to worker");
     646# error "later"
     647#endif
     648
     649        /*
     650         * Broken connection. Try respawn the worker.
     651         */
     652        if (kSubmitRespawnWorker(pWorker, cVerbosity) != 0)
     653            return 2;
     654    }
     655}
     656
     657
     658/**
     659 * Handles the --set var=value option.
     660 *
     661 * @returns 0 on success, non-zero exit code on error.
     662 * @param   papszEnv            The environment vector.
     663 * @param   pcEnvVars           Pointer to the variable holding the number of
     664 *                              environment variables held by @a papszEnv.
     665 * @param   pcAllocatedEnvVars  Pointer to the variable holding max size of the
     666 *                              environment vector.
     667 * @param   cVerbosity          The verbosity level.
     668 * @param   pszValue            The var=value string to apply.
     669 */
     670static int kSubmitOptEnvSet(char **papszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
     671                            int cVerbosity, const char *pszValue)
     672{
     673    const char *pszEqual = strchr(pszValue, '=');
     674    if (pszEqual)
     675    {
     676        unsigned iEnvVar;
     677        unsigned cEnvVars = *pcEnvVars;
     678        size_t const cchVar = pszValue - pszEqual;
     679        for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
     680            if (   strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0
     681                && papszEnv[iEnvVar][cchVar] == '=')
     682            {
     683                if (cVerbosity > 0)
     684                    fprintf(stderr, "kSubmit: replacing '%s' with '%s'\n", papszEnv[iEnvVar], pszValue);
     685                free(papszEnv[iEnvVar]);
     686                papszEnv[iEnvVar] = xstrdup(pszValue);
     687                break;
     688            }
     689        if (iEnvVar == cEnvVars)
     690        {
     691            /* Append new variable. We probably need to resize the vector. */
     692            if ((cEnvVars + 2) > *pcAllocatedEnvVars)
     693            {
     694                *pcAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf;
     695                papszEnv = (char **)xrealloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0]));
     696            }
     697            papszEnv[cEnvVars++] = xstrdup(pszValue);
     698            papszEnv[cEnvVars]   = NULL;
     699            *pcEnvVars = cEnvVars;
     700            if (cVerbosity > 0)
     701                fprintf(stderr, "kSubmit: added '%s'\n", papszEnv[iEnvVar]);
     702        }
     703        else
     704        {
     705            /* Check for duplicates. */
     706            for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++)
     707                if (   strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0
     708                    && papszEnv[iEnvVar][cchVar] == '=')
     709                {
     710                    if (cVerbosity > 0)
     711                        fprintf(stderr, "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
     712                    free(papszEnv[iEnvVar]);
     713                    cEnvVars--;
     714                    if (iEnvVar != cEnvVars)
     715                        papszEnv[iEnvVar] = papszEnv[cEnvVars];
     716                    papszEnv[cEnvVars] = NULL;
     717                    iEnvVar--;
     718                }
     719        }
     720    }
     721    else
     722        return errx(1, "Missing '=': -E %s", pszValue);
     723
     724    return 0;
     725}
     726
     727
     728/**
     729 * Handles the --unset var option.
     730 *
     731 * @returns 0 on success, non-zero exit code on error.
     732 * @param   papszEnv            The environment vector.
     733 * @param   pcEnvVars           Pointer to the variable holding the number of
     734 *                              environment variables held by @a papszEnv.
     735 * @param   cVerbosity          The verbosity level.
     736 * @param   pszVarToRemove      The name of the variable to remove.
     737 */
     738static int kSubmitOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove)
     739{
     740    if (strchr(pszVarToRemove, '=') == NULL)
     741    {
     742        unsigned     cRemoved = 0;
     743        size_t const cchVar   = strlen(pszVarToRemove);
     744        unsigned     cEnvVars = *pcEnvVars;
     745        unsigned     iEnvVar;
     746
     747        for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
     748            if (   strncmp(papszEnv[iEnvVar], pszVarToRemove, cchVar) == 0
     749                && papszEnv[iEnvVar][cchVar] == '=')
     750            {
     751                if (cVerbosity > 0)
     752                    fprintf(stderr, !cRemoved ? "kSubmit: removing '%s'\n"
     753                            : "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
     754                free(papszEnv[iEnvVar]);
     755                cEnvVars--;
     756                if (iEnvVar != cEnvVars)
     757                    papszEnv[iEnvVar] = papszEnv[cEnvVars];
     758                papszEnv[cEnvVars] = NULL;
     759                cRemoved++;
     760                iEnvVar--;
     761            }
     762        *pcEnvVars = cEnvVars;
     763
     764        if (cVerbosity > 0 && !cRemoved)
     765            fprintf(stderr, "kSubmit: not found '%s'\n", pszVarToRemove);
     766    }
     767    else
     768        return errx(1, "Found invalid variable name character '=' in: -U %s", pszVarToRemove);
     769    return 0;
     770}
     771
     772
     773
     774/**
     775 * Handles the --chdir dir option.
     776 *
     777 * @returns 0 on success, non-zero exit code on error.
     778 * @param   pszCwd              The CWD buffer.  Contains current CWD on input,
     779 *                              modified by @a pszValue on output.
     780 * @param   cbCwdBuf            The size of the CWD buffer.
     781 * @param   pszValue            The --chdir value to apply.
     782 */
     783static int kSubmitOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue)
     784{
     785    size_t cchNewCwd = strlen(pszValue);
     786    size_t offDst;
     787    if (cchNewCwd)
     788    {
     789#ifdef HAVE_DOS_PATHS
     790        if (*pszValue == '/' || *pszValue == '\\')
     791        {
     792            if (pszValue[1] == '/' || pszValue[1] == '\\')
     793                offDst = 0; /* UNC */
     794            else if (pszCwd[1] == ':' && isalpha(pszCwd[0]))
     795                offDst = 2; /* Take drive letter from CWD. */
     796            else
     797                return errx(1, "UNC relative CWD not implemented: cur='%s' new='%s'", pszCwd, pszValue);
     798        }
     799        else if (   pszValue[1] == ':'
     800                 && isalpha(pszValue[0]))
     801        {
     802            if (pszValue[2] == '/'|| pszValue[2] == '\\')
     803                offDst = 0; /* DOS style absolute path. */
     804            else if (   pszCwd[1] == ':'
     805                     && tolower(pszCwd[0]) == tolower(pszValue[0]) )
     806            {
     807                pszValue += 2; /* Same drive as CWD, append drive relative path from value. */
     808                cchNewCwd -= 2;
     809                offDst = strlen(pszCwd);
     810            }
     811            else
     812            {
     813                /* Get current CWD on the specified drive and append value. */
     814                int iDrive = tolower(pszValue[0]) - 'a' + 1;
     815                if (!_getdcwd(iDrive, pszCwd, cbCwdBuf))
     816                    return err(1, "_getdcwd(%d,,) failed", iDrive);
     817                pszValue += 2;
     818                cchNewCwd -= 2;
     819            }
     820        }
     821#else
     822        if (*pszValue == '/')
     823            offDst = 0;
     824#endif
     825        else
     826            offDst = strlen(pszCwd); /* Relative path, append to the existing CWD value. */
     827
     828        /* Do the copying. */
     829#ifdef HAVE_DOS_PATHS
     830        if (offDst > 0 && pszCwd[offDst - 1] != '/' && pszCwd[offDst - 1] != '\\')
     831#else
     832        if (offDst > 0 && pszCwd[offDst - 1] != '/')
     833#endif
     834             pszCwd[offDst++] = '/';
     835        if (offDst + cchNewCwd >= cbCwdBuf)
     836            return errx(1, "Too long CWD: %*.*s%s", offDst, offDst, pszCwd, pszValue);
     837        memcpy(&pszCwd[offDst], pszValue, cchNewCwd + 1);
     838    }
     839    /* else: relative, no change - quitely ignore. */
     840    return 0;
     841}
    87842
    88843
     
    133888    unsigned        iEnvVar;
    134889    unsigned        cEnvVars;
    135     char          **papszEnv = NULL;
    136     const char     *pszCwd = NULL;
    137     unsigned        cBitsWorker = 0;
    138     int             fWatcomBrainDamage = 0;
    139     int             cVerbosity = 0;
    140     size_t const    cbCwdBuf = GET_PATH_MAX;
     890    char          **papszEnv            = NULL;
     891    const char     *pszExecutable       = NULL;
     892    const char     *pszCwd              = NULL;
     893    unsigned        cBitsWorker         = g_cArchBits;
     894    int             fWatcomBrainDamage  = 0;
     895    int             cVerbosity          = 0;
     896    size_t const    cbCwdBuf            = GET_PATH_MAX;
    141897    PATH_VAR(szCwd);
    142898
     
    215971                else if (strcmp(pszArg, "verbose") == 0)
    216972                    chOpt = 'v';
     973                else if (strcmp(pszArg, "executable") == 0)
     974                    chOpt = 'e';
    217975                else
    218976                {
     
    232990                    case 'U':
    233991                    case 'C':
     992                    case 'e':
    234993                        if (*pszArg != '\0')
    235994                            pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
     
    2551014
    2561015                    case 'E':
    257                     {
    258                         const char *pszEqual = strchr(pszValue, '=');
    259                         if (pszEqual)
    260                         {
    261                             size_t const cchVar = pszValue - pszEqual;
    262                             for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
    263                                 if (   strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0
    264                                     && papszEnv[iEnvVar][cchVar] == '=')
    265                                 {
    266                                     if (cVerbosity > 0)
    267                                         fprintf(stderr, "kSubmit: replacing '%s' with '%s'\n", papszEnv[iEnvVar], pszValue);
    268                                     free(papszEnv[iEnvVar]);
    269                                     papszEnv[iEnvVar] = xstrdup(pszValue);
    270                                     break;
    271                                 }
    272                             if (iEnvVar == cEnvVars)
    273                             {
    274                                 /* Append new variable. We probably need to resize the vector. */
    275                                 if ((cEnvVars + 2) > cAllocatedEnvVars)
    276                                 {
    277                                     cAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf;
    278                                     pChild->environment = papszEnv = (char **)xrealloc(papszEnv,
    279                                                                                        cAllocatedEnvVars * sizeof(papszEnv[0]));
    280                                 }
    281                                 papszEnv[cEnvVars++] = xstrdup(pszValue);
    282                                 papszEnv[cEnvVars]   = NULL;
    283                                 if (cVerbosity > 0)
    284                                     fprintf(stderr, "kSubmit: added '%s'\n", papszEnv[iEnvVar]);
    285                             }
    286                             else
    287                             {
    288                                 /* Check for duplicates. */
    289                                 for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++)
    290                                     if (   strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0
    291                                         && papszEnv[iEnvVar][cchVar] == '=')
    292                                     {
    293                                         if (cVerbosity > 0)
    294                                             fprintf(stderr, "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
    295                                         free(papszEnv[iEnvVar]);
    296                                         cEnvVars--;
    297                                         if (iEnvVar != cEnvVars)
    298                                             papszEnv[iEnvVar] = papszEnv[cEnvVars];
    299                                         papszEnv[cEnvVars] = NULL;
    300                                         iEnvVar--;
    301                                     }
    302                             }
    303                         }
    304                         else
    305                             return errx(1, "Missing '=': -E %s", pszValue);
    306                         break;
    307                     }
     1016                        rcExit = kSubmitOptEnvSet(papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
     1017                        pChild->environment = papszEnv;
     1018                        if (rcExit == 0)
     1019                            break;
     1020                        return rcExit;
    3081021
    3091022                    case 'U':
    310                     {
    311                         if (strchr(pszValue, '=') == NULL)
    312                         {
    313                             unsigned     cRemoved = 0;
    314                             size_t const cchVar = strlen(pszValue);
    315                             for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
    316                                 if (   strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0
    317                                     && papszEnv[iEnvVar][cchVar] == '=')
    318                                 {
    319                                     if (cVerbosity > 0)
    320                                         fprintf(stderr, !cRemoved ? "kSubmit: removing '%s'\n"
    321                                                 : "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
    322                                     free(papszEnv[iEnvVar]);
    323                                     cEnvVars--;
    324                                     if (iEnvVar != cEnvVars)
    325                                         papszEnv[iEnvVar] = papszEnv[cEnvVars];
    326                                     papszEnv[cEnvVars] = NULL;
    327                                     cRemoved++;
    328                                     iEnvVar--;
    329                                 }
    330                             if (cVerbosity > 0 && !cRemoved)
    331                                 fprintf(stderr, "kSubmit: not found '%s'\n", pszValue);
    332                         }
    333                         else
    334                             return errx(1, "Found invalid variable name character '=' in: -U %s", pszValue);
    335                         break;
    336                     }
     1023                        rcExit = kSubmitOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue);
     1024                        if (rcExit == 0)
     1025                            break;
     1026                        return rcExit;
    3371027
    3381028                    case 'C':
    339                     {
    340                         size_t cchNewCwd = strlen(pszValue);
    341                         size_t offDst;
    342                         if (cchNewCwd)
    343                         {
    344 #ifdef HAVE_DOS_PATHS
    345                             if (*pszValue == '/' || *pszValue == '\\')
    346                             {
    347                                 if (pszValue[1] == '/' || pszValue[1] == '\\')
    348                                     offDst = 0; /* UNC */
    349                                 else if (szCwd[1] == ':' && isalpha(szCwd[0]))
    350                                     offDst = 2; /* Take drive letter from CWD. */
    351                                 else
    352                                     return errx(1, "UNC relative CWD not implemented: cur='%s' new='%s'", szCwd, pszValue);
    353                             }
    354                             else if (   pszValue[1] == ':'
    355                                      && isalpha(pszValue[0]))
    356                             {
    357                                 if (pszValue[2] == '/'|| pszValue[2] == '\\')
    358                                     offDst = 0; /* DOS style absolute path. */
    359                                 else if (   szCwd[1] == ':'
    360                                          && tolower(szCwd[0]) == tolower(pszValue[0]) )
    361                                 {
    362                                     pszValue += 2; /* Same drive as CWD, append drive relative path from value. */
    363                                     cchNewCwd -= 2;
    364                                     offDst = strlen(szCwd);
    365                                 }
    366                                 else
    367                                 {
    368                                     /* Get current CWD on the specified drive and append value. */
    369                                     int iDrive = tolower(pszValue[0]) - 'a' + 1;
    370                                     if (!_getdcwd(iDrive, szCwd, cbCwdBuf))
    371                                         return err(1, "_getdcwd(%d,,) failed", iDrive);
    372                                     pszValue += 2;
    373                                     cchNewCwd -= 2;
    374                                 }
    375                             }
    376 #else
    377                             if (*pszValue == '/')
    378                                 offDst = 0;
    379 #endif
    380                             else
    381                                 offDst = strlen(szCwd); /* Relative path, append to the existing CWD value. */
    382 
    383                             /* Do the copying. */
    384 #ifdef HAVE_DOS_PATHS
    385                             if (offDst > 0 && szCwd[offDst - 1] != '/' && szCwd[offDst - 1] != '\\')
    386 #else
    387                             if (offDst > 0 && szCwd[offDst - 1] != '/')
    388 #endif
    389                                  szCwd[offDst++] = '/';
    390                             if (offDst + cchNewCwd >= cbCwdBuf)
    391                                 return errx(1, "Too long CWD: %*.*s%s", offDst, offDst, szCwd, pszValue);
    392                             memcpy(&szCwd[offDst], pszValue, cchNewCwd + 1);
    393                         }
    394                         /* else: relative, no change - quitely ignore. */
    395                         break;
    396                     }
     1029                        rcExit = kSubmitOptChDir(szCwd, cbCwdBuf, pszValue);
     1030                        if (rcExit == 0)
     1031                            break;
     1032                        return rcExit;
    3971033
    3981034                    case '3':
     
    4021038                    case '6':
    4031039                        cBitsWorker = 64;
     1040                        break;
     1041
     1042                    case 'e':
     1043                        pszExecutable = pszValue;
    4041044                        break;
    4051045
     
    4331073    if (iArg < argc)
    4341074    {
    435 
     1075        uint32_t        cbMsg;
     1076        void           *pvMsg   = kSubmitComposeJobMessage(pszExecutable, &argv[iArg], papszEnv, szCwd, &cbMsg);
     1077        PWORKERINSTANCE pWorker = kSubmitSelectWorkSpawnNewIfNecessary(cBitsWorker, cVerbosity);
     1078        if (pWorker)
     1079        {
     1080            rcExit = kSubmitSendJobMessage(pWorker, pvMsg, cbMsg, cVerbosity);
     1081            if (rcExit == 0)
     1082            {
     1083                pWorker->pBusyWith = pChild;
     1084                /** @todo integrate with sub_proc.c / whatever. */
     1085            }
     1086        }
     1087        else
     1088            rcExit = 1;
     1089        free(pvMsg);
    4361090    }
    4371091    else
Note: See TracChangeset for help on using the changeset viewer.