Changeset 3195 for trunk/src/kmk


Ignore:
Timestamp:
Mar 27, 2018, 8:09:23 PM (7 years ago)
Author:
bird
Message:

kmk/win: Catch output from processes spawned by kmk_redirect. Made imagecase threadsafe and made winchildren use it.

Location:
trunk/src/kmk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kmk/kmkbuiltin.h

    r3192 r3195  
    8989    /** The KMK output synchronizer.   */
    9090    struct output *pOut;
     91#if defined(KBUILD_OS_WINDOWS) && !defined(KMK_BUILTIN_STANDALONE)
     92    /** Pointer to the worker thread, if we're on one. */
     93    void *pvWorker;
     94#endif
    9195} KMKBUILTINCTX;
    9296/** Pointer to kmk built-in command execution context. */
  • trunk/src/kmk/kmkbuiltin/redirect.c

    r3192 r3195  
    9292#  define LIBPATHSTRICT 3
    9393# endif
     94#endif
     95
     96#ifndef KMK_BUILTIN_STANDALONE
     97extern void kmk_cache_exec_image_a(const char *); /* imagecache.c */
    9498#endif
    9599
     
    596600
    597601/**
     602 * Registers the child process with a care provider or waits on it to complete.
     603 *
     604 * @returns 0 or non-zero success indicator or child exit code, depending on
     605 *          the value pfIsChildExitCode points to.
     606 * @param   pCtx                The command execution context.
     607 * @param   hProcess            The child process handle.
     608 * @param   cVerbosity          The verbosity level.
     609 * @param   pPidSpawned         Where to return the PID of the spawned child
     610 *                              when we're inside KMK and we're return without
     611 *                              waiting.
     612 * @param   pfIsChildExitCode   Where to indicate whether the return exit code
     613 *                              is from the child or from our setup efforts.
     614 */
     615static int kRedirectPostnatalCareOnWindows(PKMKBUILTINCTX pCtx, HANDLE hProcess, unsigned cVerbosity,
     616                                           pid_t *pPidSpawned, KBOOL *pfIsChildExitCode)
     617{
     618    int   rcExit;
     619    DWORD dwTmp;
     620
     621# ifndef KMK_BUILTIN_STANDALONE
     622    /*
     623     * Try register the child with a childcare provider, i.e. winchildren.c
     624     * or sub_proc.c.
     625     */
     626#  ifndef CONFIG_NEW_WIN_CHILDREN
     627    if (process_kmk_register_redirect(hProcess, pPidSpawned) == 0)
     628#  else
     629    if (   pPidSpawned
     630        && MkWinChildCreateRedirect((intptr_t)hProcess, pPidSpawned) == 0)
     631#  endif
     632    {
     633        if (cVerbosity > 0)
     634            warnx(pCtx, "debug: spawned %d", *pPidSpawned);
     635        *pfIsChildExitCode = K_FALSE;
     636        return 0;
     637    }
     638#  ifndef CONFIG_NEW_WIN_CHILDREN
     639    warn(pCtx, "sub_proc is out of slots, waiting for child...");
     640#  else
     641    if (pPidSpawned)
     642        warn(pCtx, "MkWinChildCreateRedirect failed...");
     643#  endif
     644# endif
     645
     646    /*
     647     * Either the provider is outbooked or we're not in a context (like
     648     * standalone) where we get help with waiting and must to it ourselves
     649     */
     650    dwTmp = WaitForSingleObject(hProcess, INFINITE);
     651    if (dwTmp != WAIT_OBJECT_0)
     652        warnx(pCtx, "WaitForSingleObject failed: %#x\n", dwTmp);
     653
     654    if (GetExitCodeProcess(hProcess, &dwTmp))
     655        rcExit = (int)dwTmp;
     656    else
     657    {
     658        warnx(pCtx, "GetExitCodeProcess failed: %u\n", GetLastError());
     659        TerminateProcess(hProcess, 127);
     660        rcExit = 127;
     661    }
     662
     663    CloseHandle(hProcess);
     664    *pfIsChildExitCode = K_TRUE;
     665    return rcExit;
     666}
     667
     668
     669/**
    598670 * Tries to locate the executable image.
    599671 *
     
    665737
    666738   return pszExecutable;
     739}
     740
     741
     742/**
     743 * Turns the orders into input for nt_child_inject_standard_handles and
     744 * winchildren.c
     745 *
     746 * @returns 0 on success, non-zero on failure.
     747 * @param   pCtx                The command execution context.
     748 * @param   cOrders             Number of file operation orders.
     749 * @param   paOrders            The file operation orders.
     750 * @param   pafReplace          Replace (TRUE) or leave alone (FALSE) indicator
     751 *                              for each of the starndard handles.
     752 * @param   pahChild            Array of standard handles for injecting into the
     753 *                              child.  Parallel to pafReplace.
     754 */
     755static int kRedirectOrderToWindowsHandles(PKMKBUILTINCTX pCtx, unsigned cOrders, REDIRECTORDERS *paOrders,
     756                                          BOOL pafReplace[3], HANDLE pahChild[3])
     757{
     758    int i;
     759    for (i = 0; i < (int)cOrders; i++)
     760    {
     761        int fdTarget = paOrders[i].fdTarget;
     762        assert(fdTarget >= 0 && fdTarget < 3);
     763        switch (paOrders[i].enmOrder)
     764        {
     765            case kRedirectOrder_Open:
     766                if (   (paOrders[i].fOpen & O_APPEND)
     767                    && lseek(paOrders[i].fdSource, 0, SEEK_END) < 0)
     768                    return err(pCtx, 10, "lseek-to-end failed on %d (for %d)", paOrders[i].fdSource, fdTarget);
     769                /* fall thru */
     770            case kRedirectOrder_Dup:
     771                pahChild[fdTarget] = (HANDLE)_get_osfhandle(paOrders[i].fdSource);
     772                if (pahChild[fdTarget] == NULL || pahChild[fdTarget] == INVALID_HANDLE_VALUE)
     773                    return err(pCtx, 10, "_get_osfhandle failed on %d (for %d)", paOrders[i].fdSource, fdTarget);
     774                break;
     775
     776            case kRedirectOrder_Close:
     777                pahChild[fdTarget] = NULL;
     778                break;
     779
     780            default:
     781                assert(0);
     782        }
     783        pafReplace[fdTarget] = TRUE;
     784    }
     785    return 0;
    667786}
    668787
     
    774893                CloseHandle(ProcInfo.hThread);
    775894                *phProcess = ProcInfo.hProcess;
     895# ifndef KMK_BUILTIN_STANDALONE
     896                kmk_cache_exec_image_a(pszExecutable);
     897# endif
    776898                rc = 0;
    777899            }
     
    789911            BOOL   afReplace[3] = { FALSE, FALSE, FALSE };
    790912            HANDLE ahChild[3]   = { NULL,  NULL,  NULL  };
    791             rc = 0;
    792             for (i = 0; i < (int)cOrders; i++)
    793             {
    794                 int fdTarget = paOrders[i].fdTarget;
    795                 assert(fdTarget >= 0 && fdTarget < 3);
    796                 switch (paOrders[i].enmOrder)
    797                 {
    798                     case kRedirectOrder_Open:
    799                         if (   (paOrders[i].fOpen & O_APPEND)
    800                             && lseek(paOrders[i].fdSource, 0, SEEK_END) < 0)
    801                             rc = err(pCtx, 10, "lseek-to-end failed on %d (for %d)", paOrders[i].fdSource, fdTarget);
    802                     case kRedirectOrder_Dup:
    803                         ahChild[fdTarget] = (HANDLE)_get_osfhandle(paOrders[i].fdSource);
    804                         if (ahChild[fdTarget] == NULL || ahChild[fdTarget] == INVALID_HANDLE_VALUE)
    805                             rc = err(pCtx, 10, "_get_osfhandle failed on %d (for %d)", paOrders[i].fdSource, fdTarget);
    806                         break;
    807                     case kRedirectOrder_Close:
    808                         ahChild[fdTarget] = NULL;
    809                         break;
    810                     default:
    811                         assert(0);
    812                 }
    813                 afReplace[fdTarget] = TRUE;
    814             }
     913            rc = kRedirectOrderToWindowsHandles(pCtx, cOrders, paOrders, afReplace, ahChild);
    815914            if (rc == 0)
    816915            {
     
    835934                    CloseHandle(ProcInfo.hThread);
    836935                    *phProcess = ProcInfo.hProcess;
     936# ifndef KMK_BUILTIN_STANDALONE
     937                    kmk_cache_exec_image_a(pszExecutable);
     938# endif
    837939                    rc = 0;
    838940                }
     
    848950    return rc;
    849951}
     952
     953# if !defined(KMK_BUILTIN_STANDALONE) && defined(CONFIG_NEW_WIN_CHILDREN)
     954/**
     955 * Pass the problem on to winchildren.c when we're on one of its workers.
     956 *
     957 * @returns 0 on success, non-zero on failure to create.
     958 * @param   pCtx                The command execution context.
     959 * @param   pszExecutable       The child process executable.
     960 * @param   cArgs               Number of arguments.
     961 * @param   papszArgs           The child argument vector.
     962 * @param   papszEnvVars        The child environment vector.
     963 * @param   pszCwd              The current working directory of the child.
     964 * @param   cOrders             Number of file operation orders.
     965 * @param   paOrders            The file operation orders.
     966 * @param   phProcess           Where to return process handle.
     967 * @param   pfIsChildExitCode   Where to indicate whether the return exit code
     968 *                              is from the child or from our setup efforts.
     969 */
     970static int kRedirectExecProcessWithinOnWorker(PKMKBUILTINCTX pCtx, const char *pszExecutable, int cArgs, char **papszArgs,
     971                                              char **papszEnvVars, const char *pszCwd, unsigned cOrders,
     972                                              REDIRECTORDERS *paOrders, KBOOL *pfIsChildExitCode)
     973{
     974    BOOL   afReplace[3] = { FALSE, FALSE, FALSE };
     975    HANDLE ahChild[3]   = { NULL,  NULL,  NULL  };
     976    int rc = kRedirectOrderToWindowsHandles(pCtx, cOrders, paOrders, afReplace, ahChild);
     977    if (rc == 0)
     978    {
     979        rc = MkWinChildBuiltInExecChild(pCtx->pvWorker, pszExecutable, papszArgs, TRUE /*fQuotedArgv*/,
     980                                        papszEnvVars, pszCwd, afReplace, ahChild);
     981        *pfIsChildExitCode = K_TRUE;
     982    }
     983    return rc;
     984}
     985# endif /* !KMK_BUILTIN_STANDALONE */
    850986
    851987#endif /* KBUILD_OS_WINDOWS */
     
    9641100# ifdef KBUILD_OS_WINDOWS
    9651101                /* Windows is slightly complicated due to handles and winchildren.c. */
    966                 HANDLE hProcess = INVALID_HANDLE_VALUE;
    967                 rcExit = kRedirectCreateProcessWindows(pCtx, pszExecutable, cArgs, papszArgs, papszEnvVars,
    968                                                        pszSavedCwd ? pszCwd : NULL, cOrders, paOrders, &hProcess);
    969                 if (rcExit == 0)
    970                 {
    971 #  ifndef CONFIG_NEW_WIN_CHILDREN
    972                     if (process_kmk_register_redirect(hProcess, pPidSpawned) == 0)
    973 #  else
    974                     if (   pPidSpawned
    975                         && MkWinChildCreateRedirect((intptr_t)hProcess, pPidSpawned) == 0)
     1102                if (pPidSpawned)
     1103                    *pPidSpawned = 0;
     1104#  ifdef CONFIG_NEW_WIN_CHILDREN
     1105                if (pCtx->pvWorker && !pPidSpawned)
     1106                    rcExit = kRedirectExecProcessWithinOnWorker(pCtx, pszExecutable, cArgs, papszArgs, papszEnvVars,
     1107                                                                pszSavedCwd ? pszCwd : NULL, cOrders, paOrders,
     1108                                                                pfIsChildExitCode);
     1109                else
    9761110#  endif
    977                     {
    978                         if (cVerbosity > 0)
    979                             warnx(pCtx, "debug: spawned %d", *pPidSpawned);
    980                     }
    981                     else
    982                     {
    983                         DWORD dwTmp;
    984 #  ifndef CONFIG_NEW_WIN_CHILDREN
    985                         warn(pCtx, "sub_proc is out of slots, waiting for child...");
    986 #  else
    987                         if (pPidSpawned)
    988                             warn(pCtx, "MkWinChildCreateRedirect failed...");
    989 #  endif
    990                         dwTmp = WaitForSingleObject(hProcess, INFINITE);
    991                         if (dwTmp != WAIT_OBJECT_0)
    992                             warnx(pCtx, "WaitForSingleObject failed: %#x\n", dwTmp);
    993 
    994                         if (GetExitCodeProcess(hProcess, &dwTmp))
    995                             rcExit = (int)dwTmp;
    996                         else
    997                         {
    998                             warnx(pCtx, "GetExitCodeProcess failed: %u\n", GetLastError());
    999                             TerminateProcess(hProcess, 127);
    1000                             rcExit = 127;
    1001                         }
    1002 
    1003                         CloseHandle(hProcess);
    1004                         if (pPidSpawned)
    1005                             *pPidSpawned = 0;
    1006                         *pfIsChildExitCode = K_TRUE;
    1007                     }
     1111                {
     1112                    HANDLE hProcess = INVALID_HANDLE_VALUE;
     1113                    rcExit = kRedirectCreateProcessWindows(pCtx, pszExecutable, cArgs, papszArgs, papszEnvVars,
     1114                                                           pszSavedCwd ? pszCwd : NULL, cOrders, paOrders, &hProcess);
     1115                    if (rcExit == 0)
     1116                        rcExit = kRedirectPostnatalCareOnWindows(pCtx, hProcess, cVerbosity, pPidSpawned, pfIsChildExitCode);
    10081117                }
    10091118
     
    10521161                    dwWait = 11;
    10531162                    if (GetExitCodeProcess(hProcess, &dwWait))
     1163                    {
     1164                        *pfIsChildExitCode = K_TRUE;
    10541165                        rcExit = dwWait;
     1166                    }
    10551167                    else
    10561168                        rcExit = errx(pCtx, 11, "GetExitCodeProcess(%s) failed: %u", pszExecutable, GetLastError());
  • trunk/src/kmk/output.c

    r3194 r3195  
    126126            fflush(prevdst);
    127127          prevdst = dst;
    128 # ifdef KBUILD_OS_WINDOWS
     128# if 0 /* for debugging */
     129          while (len > 0)
     130            {
     131              const char *nl = (const char *)memchr (src, '\n', len);
     132              size_t line_len = nl ? nl - (const char *)src + 1 : len;
     133              char *tmp = (char *)xmalloc (2 + line_len + 1);
     134              tmp[0] = '>';
     135              tmp[1] = ' ';
     136              memcpy (&tmp[2], src, line_len);
     137#  ifdef KBUILD_OS_WINDOWS
     138              maybe_con_fwrite (tmp, 2 + line_len, 1, dst);
     139#  else
     140              fwrite (tmp, 2 + line_len, 1, dst);
     141#  endif
     142              free (tmp);
     143              src  = (const char *)src + line_len;
     144              len -= line_len;
     145            }
     146#else
     147#  ifdef KBUILD_OS_WINDOWS
    129148          maybe_con_fwrite (src, len, 1, dst);
    130 # else
     149#  else
    131150          fwrite (src, len, 1, dst);
     151#  endif
    132152# endif
    133153        }
  • trunk/src/kmk/w32/imagecache.c

    r3140 r3195  
    2626/* No GNU coding style here! */
    2727
    28 /*******************************************************************************
    29 *   Header Files                                                               *
    30 *******************************************************************************/
     28/*********************************************************************************************************************************
     29*   Header Files                                                                                                                 *
     30*********************************************************************************************************************************/
    3131#include "makeint.h"
    3232
     
    3434
    3535
    36 /*******************************************************************************
    37 *   Structures and Typedefs                                                    *
    38 *******************************************************************************/
     36/*********************************************************************************************************************************
     37*   Structures and Typedefs                                                                                                      *
     38*********************************************************************************************************************************/
    3939typedef struct EXECCACHEENTRY
    4040{
     
    4242    unsigned                uHash;
    4343    /** The name length. */
    44     unsigned                cchName;
     44    unsigned                cwcName;
    4545    /** Pointer to the next name with the same hash. */
    4646    struct EXECCACHEENTRY  *pNext;
    4747    /** When it was last referenced. */
    4848    unsigned                uLastRef;
    49     /** The module handle. */
     49    /** The module handle, LOAD_LIBRARY_AS_DATAFILE. */
    5050    HMODULE                 hmod1;
    51     /** The module handle. */
     51    /** The module handle, DONT_RESOLVE_DLL_REFERENCES. */
    5252    HMODULE                 hmod2;
    5353    /** The executable path. */
    54     char                    szName[1];
     54    wchar_t                 wszName[1];
    5555} EXECCACHEENTRY;
    5656typedef EXECCACHEENTRY *PEXECCACHEENTRY;
    5757
    58 /*******************************************************************************
    59 *   Global Variables                                                           *
    60 *******************************************************************************/
     58
     59/*********************************************************************************************************************************
     60*   Global Variables                                                                                                             *
     61*********************************************************************************************************************************/
     62/** Critical section serializing all access. */
     63static CRITICAL_SECTION g_CritSect;
     64/** Set if initialized. */
     65static int volatile     g_fInitialized = 0;
    6166/** The number of cached images. */
    6267static unsigned         g_cCached;
     
    6974/** The hash table. */
    7075static PEXECCACHEENTRY  g_apHashTab[EXECCACHE_HASHTAB_SIZE];
     76
     77
     78/** A sleepy approach to do-once. */
     79static void kmk_cache_lazy_init(void)
     80{
     81    if (_InterlockedCompareExchange(&g_fInitialized, -1, 0) == 0)
     82    {
     83        InitializeCriticalSection(&g_CritSect);
     84        _InterlockedExchange(&g_fInitialized, 1);
     85    }
     86    else
     87        while (g_fInitialized != 1)
     88            Sleep(1);
     89}
    7190
    7291
     
    83102   elsewhere. */
    84103
    85 static unsigned execcache_calc_hash(const char *psz, unsigned *pcch)
    86 {
    87     unsigned char  *puch = (unsigned char *)psz;
    88     unsigned        hash = 0;
    89     int             ch;
    90 
    91     while ((ch = *puch++))
     104static unsigned execcache_calc_hash(const wchar_t *pwsz, size_t *pcch)
     105{
     106    wchar_t const  * const pwszStart = pwsz;
     107    unsigned               hash = 0;
     108    int                    ch;
     109
     110    while ((ch = *pwsz++) != L'\0')
    92111        hash = ch + (hash << 6) + (hash << 16) - hash;
    93112
    94     *pcch = (unsigned)(puch - psz - 1);
     113    *pcch = (size_t)(pwsz - pwszStart - 1);
    95114    return hash;
    96115}
    97116
    98 
    99 extern void kmk_cache_exec_image(const char *pszExec)
    100 {
    101     /*
    102      * Lookup the name.
    103      */
    104     unsigned            cchName;
    105     const unsigned      uHash = execcache_calc_hash(pszExec, &cchName);
     117/**
     118 * Caches two memory mappings of the specified image so that it isn't flushed
     119 * from the kernel's cache mananger.
     120 *
     121 * Not sure exactly how much this actually helps, but whatever...
     122 *
     123 * @param   pwszExec    The executable.
     124 */
     125extern void kmk_cache_exec_image_w(const wchar_t *pwszExec)
     126{
     127    /*
     128     * Prepare name lookup and to lazy init.
     129     */
     130    size_t              cwcName;
     131    const unsigned      uHash = execcache_calc_hash(pwszExec, &cwcName);
    106132    PEXECCACHEENTRY    *ppCur = &g_apHashTab[uHash % EXECCACHE_HASHTAB_SIZE];
    107     PEXECCACHEENTRY     pCur  = *ppCur;
     133    PEXECCACHEENTRY     pCur;
     134
     135    if (g_fInitialized != 1)
     136        kmk_cache_lazy_init();
     137
     138    /*
     139     * Do the lookup.
     140     */
     141    EnterCriticalSection(&g_CritSect);
     142    pCur = *ppCur;
    108143    while (pCur)
    109144    {
    110145        if (   pCur->uHash   == uHash
    111             && pCur->cchName == cchName
    112             && !memcmp(pCur->szName, pszExec, cchName))
     146            && pCur->cwcName == cwcName
     147            && !memcmp(pCur->wszName, pwszExec, cwcName * sizeof(wchar_t)))
    113148        {
    114149            pCur->uLastRef = ++g_uNow;
     150            LeaveCriticalSection(&g_CritSect);
    115151            return;
    116152        }
     
    118154        pCur = pCur->pNext;
    119155    }
     156    LeaveCriticalSection(&g_CritSect);
    120157
    121158    /*
    122159     * Not found, create a new entry.
    123160     */
    124     pCur = xmalloc(sizeof(*pCur) + cchName);
     161    pCur = xmalloc(sizeof(*pCur) + cwcName * sizeof(wchar_t));
    125162    pCur->uHash    = uHash;
    126     pCur->cchName  = cchName;
     163    pCur->cwcName  = (unsigned)cwcName;
    127164    pCur->pNext    = NULL;
    128165    pCur->uLastRef = ++g_uNow;
    129     memcpy(pCur->szName, pszExec, cchName + 1);
    130     pCur->hmod1 = LoadLibraryEx(pszExec, NULL, LOAD_LIBRARY_AS_DATAFILE);
     166    memcpy(pCur->wszName, pwszExec, (cwcName + 1) * sizeof(wchar_t));
     167    pCur->hmod1 = LoadLibraryExW(pwszExec, NULL, LOAD_LIBRARY_AS_DATAFILE);
    131168    if (pCur->hmod1 != NULL)
    132         pCur->hmod2 = LoadLibraryEx(pszExec, NULL, DONT_RESOLVE_DLL_REFERENCES);
     169        pCur->hmod2 = LoadLibraryExW(pwszExec, NULL, DONT_RESOLVE_DLL_REFERENCES);
    133170    else
    134171        pCur->hmod2 = NULL;
    135172
    136     *ppCur = pCur;
    137     g_cCached++;
    138 }
    139 
     173    /*
     174     * Insert it.
     175     * Take into account that we might've been racing other threads,
     176     * fortunately we don't evict anything from the cache.
     177     */
     178    EnterCriticalSection(&g_CritSect);
     179    if (*ppCur != NULL)
     180    {
     181        /* Find new end of chain and check for duplicate. */
     182        PEXECCACHEENTRY pCur2 = *ppCur;
     183        while (pCur2)
     184        {
     185            if (   pCur->uHash   == uHash
     186                && pCur->cwcName == cwcName
     187                && !memcmp(pCur->wszName, pwszExec, cwcName * sizeof(wchar_t)))
     188                break;
     189            ppCur = &pCur->pNext;
     190            pCur = pCur->pNext;
     191        }
     192
     193    }
     194    if (*ppCur == NULL)
     195    {
     196        *ppCur = pCur;
     197        g_cCached++;
     198        LeaveCriticalSection(&g_CritSect);
     199    }
     200    else
     201    {
     202        LeaveCriticalSection(&g_CritSect);
     203
     204        if (pCur->hmod1 != NULL)
     205            FreeLibrary(pCur->hmod1);
     206        if (pCur->hmod2 != NULL)
     207            FreeLibrary(pCur->hmod2);
     208        free(pCur);
     209    }
     210}
     211
     212extern void kmk_cache_exec_image_a(const char *pszExec)
     213{
     214    wchar_t wszExec[260];
     215    int cwc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszExec, strlen(pszExec) + 1, wszExec, 260);
     216    if (cwc > 0)
     217        kmk_cache_exec_image_w(wszExec);
     218}
     219
  • trunk/src/kmk/w32/subproc/sub_proc.c

    r3140 r3195  
    4040# include <assert.h>
    4141# include "kmkbuiltin.h"
    42 extern void kmk_cache_exec_image(const char *); /* imagecache.c */
     42extern void kmk_cache_exec_image_a(const char *); /* imagecache.c */
    4343#endif
    4444
     
    844844#ifdef KMK
    845845                if (exec_fname[0])
    846                         kmk_cache_exec_image(exec_fname);
     846                        kmk_cache_exec_image_a(exec_fname);
    847847                else if (exec_path)
    848                         kmk_cache_exec_image(exec_path);
     848                        kmk_cache_exec_image_a(exec_path);
    849849                else if (argv[0])
    850                         kmk_cache_exec_image(argv[0]);
     850                        kmk_cache_exec_image_a(argv[0]);
    851851
    852852                switch (process_priority) {
  • trunk/src/kmk/w32/winchildren.c

    r3192 r3195  
    101101
    102102#include "nt/nt_child_inject_standard_handles.h"
     103
     104#ifndef KMK_BUILTIN_STANDALONE
     105extern void kmk_cache_exec_image_w(const wchar_t *); /* imagecache.c */
     106#endif
    103107
    104108
     
    805809 * Commmon worker for waiting on a child process and retrieving the exit code.
    806810 *
     811 * @returns Child exit code.
    807812 * @param   pWorker             The worker.
    808813 * @param   pChild              The child.
     
    812817 *                              associated with the worker.
    813818 */
    814 static void mkWinChildcareWorkerWaitForProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild, HANDLE hProcess,
    815                                                WCHAR const *pwszJob, BOOL fCatchOutput)
     819static int mkWinChildcareWorkerWaitForProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild, HANDLE hProcess,
     820                                              WCHAR const *pwszJob, BOOL fCatchOutput)
    816821{
    817822    DWORD const msStart = GetTickCount();
     
    850855                    mkWinChildcareWorkerCatchOutput(pChild, &pWorker->StdErr, TRUE /*fFlushing*/);
    851856                }
    852                 return;
     857                return dwExitCode;
    853858            }
    854859        }
     
    891896        if (pChild->iExitCode == 0)
    892897            pChild->iExitCode = -4242;
    893         return;
     898        return pChild->iExitCode;
    894899    }
    895900}
     
    930935 * @param   pwszzEnvironment    The enviornment block.
    931936 */
    932 static int mkWinChildcareWorkerCreateProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild, WCHAR const *pwszImageName,
    933                                              WCHAR const *pwszCommandLine, WCHAR const *pwszzEnvironment)
     937static int mkWinChildcareWorkerCreateProcess(PWINCHILDCAREWORKER pWorker, WCHAR const *pwszImageName,
     938                                             WCHAR const *pwszCommandLine, WCHAR const *pwszzEnvironment, WCHAR const *pwszCwd,
     939                                             BOOL pafReplace[3], HANDLE pahChild[3], BOOL fCatchOutput, HANDLE *phProcess)
    934940{
    935941    PROCESS_INFORMATION ProcInfo;
    936942    STARTUPINFOW        StartupInfo;
    937943    DWORD               fFlags       = CREATE_UNICODE_ENVIRONMENT;
    938     BOOL const          fHaveHandles = pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE
    939                                     || pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE;
     944    BOOL const          fHaveHandles = pafReplace[0] | pafReplace[1] | pafReplace[2];
    940945    BOOL                fRet;
    941946    DWORD               dwErr;
     
    965970    StartupInfo.cbReserved2 = 0;
    966971    if (   !fHaveHandles
    967         && !pChild->u.Process.fCatchOutput)
     972        && !fCatchOutput)
    968973        StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
    969974    else
     
    9991004
    10001005    fRet = CreateProcessW((WCHAR *)pwszImageName, (WCHAR *)pwszCommandLine, NULL /*pProcSecAttr*/, NULL /*pThreadSecAttr*/,
    1001                           FALSE /*fInheritHandles*/, fFlags, (WCHAR *)pwszzEnvironment, NULL /*pwsz*/, &StartupInfo, &ProcInfo);
     1006                          FALSE /*fInheritHandles*/, fFlags, (WCHAR *)pwszzEnvironment, pwszCwd, &StartupInfo, &ProcInfo);
    10021007    dwErr = GetLastError();
    10031008
     
    10061011#endif
    10071012    if (fRet)
    1008         pChild->u.Process.hProcess = ProcInfo.hProcess;
     1013        *phProcess = ProcInfo.hProcess;
    10091014    else
    10101015    {
    10111016        fprintf(stderr, "CreateProcess(%ls) failed: %u\n", pwszImageName, dwErr);
    1012         return pChild->iExitCode = (int)dwErr;
     1017        return (int)dwErr;
    10131018    }
    10141019
     
    10221027         * First do handle inhertiance as that's the most complicated.
    10231028         */
    1024         if (fHaveHandles || pChild->u.Process.fCatchOutput)
    1025         {
    1026             char    szErrMsg[128];
    1027             BOOL    afReplace[3];
    1028             HANDLE  ahChild[3];
    1029 
    1030             afReplace[0] = FALSE;
    1031             ahChild[0]   = INVALID_HANDLE_VALUE;
    1032             if (fHaveHandles)
    1033             {
    1034                 afReplace[1] = pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE;
    1035                 ahChild[1]   = pChild->u.Process.hStdOut;
    1036                 afReplace[2] = pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE;
    1037                 ahChild[2]   = pChild->u.Process.hStdErr;
    1038             }
    1039             else
    1040             {
    1041                 afReplace[1] = TRUE;
    1042                 ahChild[1]   = pWorker->StdOut.hPipeChild;
    1043                 afReplace[2] = TRUE;
    1044                 ahChild[2]   = pWorker->StdErr.hPipeChild;
    1045             }
    1046 
    1047             dwErr = nt_child_inject_standard_handles(ProcInfo.hProcess, afReplace, ahChild, szErrMsg, sizeof(szErrMsg));
     1029        if (fHaveHandles || fCatchOutput)
     1030        {
     1031            char szErrMsg[128];
     1032            if (fCatchOutput)
     1033            {
     1034                if (!pafReplace[1])
     1035                {
     1036                    pafReplace[1] = TRUE;
     1037                    pahChild[1]   = pWorker->StdOut.hPipeChild;
     1038                }
     1039                if (!pafReplace[2])
     1040                {
     1041                    pafReplace[2] = TRUE;
     1042                    pahChild[2]   = pWorker->StdErr.hPipeChild;
     1043                }
     1044            }
     1045            dwErr = nt_child_inject_standard_handles(ProcInfo.hProcess, pafReplace, pahChild, szErrMsg, sizeof(szErrMsg));
    10481046            if (dwErr != 0)
    10491047                fprintf(stderr, "%s\n", szErrMsg);
     
    10941092
    10951093    /*
    1096      * Close unnecessary handles.
    1097      */
    1098     mkWinChildcareWorkerCloseStandardHandles(pChild);
     1094     * Close unnecessary handles and cache the image.
     1095     */
    10991096    CloseHandle(ProcInfo.hThread);
     1097    kmk_cache_exec_image_w(pwszImageName);
     1098    return 0;
     1099}
     1100
     1101/**
     1102 * Converts a argument vector that has already been quoted correctly.
     1103 *
     1104 * The argument vector is typically the result of quote_argv().
     1105 *
     1106 * @returns 0 on success, non-zero on failure.
     1107 * @param   papszArgs           The argument vector to convert.
     1108 * @param   ppwszCommandLine    Where to return the command line.
     1109 */
     1110static int mkWinChildcareWorkerConvertQuotedArgvToCommandline(char **papszArgs, WCHAR **ppwszCommandLine)
     1111{
     1112    WCHAR   *pwszCmdLine;
     1113    WCHAR   *pwszDst;
     1114
     1115    /*
     1116     * Calc length the converted length.
     1117     */
     1118    unsigned cwcNeeded = 1;
     1119    unsigned i = 0;
     1120    const char *pszSrc;
     1121    while ((pszSrc = papszArgs[i]) != NULL)
     1122    {
     1123        int cwcThis = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, -1, NULL, 0);
     1124        if (cwcThis > 0 || *pszSrc == '\0')
     1125            cwcNeeded += cwcThis + 1;
     1126        else
     1127        {
     1128            DWORD dwErr = GetLastError();
     1129            fprintf(stderr, _("MultiByteToWideChar failed to convert argv[%u] (%s): %u\n"), i, pszSrc, dwErr);
     1130            return dwErr;
     1131        }
     1132        i++;
     1133    }
     1134
     1135    /*
     1136     * Allocate and do the conversion.
     1137     */
     1138    pwszCmdLine = pwszDst = (WCHAR *)xmalloc(cwcNeeded * sizeof(WCHAR));
     1139    i = 0;
     1140    while ((pszSrc = papszArgs[i]) != NULL)
     1141    {
     1142        int cwcThis;
     1143        if (i > 0)
     1144        {
     1145            *pwszDst++ = ' ';
     1146            cwcNeeded--;
     1147        }
     1148
     1149        cwcThis = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, -1, pwszDst, cwcNeeded);
     1150        if (!cwcThis && *pszSrc != '\0')
     1151        {
     1152            DWORD dwErr = GetLastError();
     1153            fprintf(stderr, _("MultiByteToWideChar failed to convert argv[%u] (%s): %u\n"), i, pszSrc, dwErr);
     1154            free(pwszCmdLine);
     1155            return dwErr;
     1156        }
     1157        if (cwcThis > 0 && pwszDst[cwcThis - 1] == '\0')
     1158            cwcThis--;
     1159        pwszDst += cwcThis;
     1160        cwcNeeded -= cwcThis;
     1161        i++;
     1162    }
     1163    *pwszDst++ = '\0';
     1164
     1165    *ppwszCommandLine = pwszCmdLine;
    11001166    return 0;
    11011167}
     
    17071773        {
    17081774            fprintf(stderr, "%s: not found!\n", pszArg0);
    1709 //__debugbreak();
    17101775            dwErr = ERROR_FILE_NOT_FOUND;
    17111776        }
     
    19201985        if (rc == 0)
    19211986        {
    1922             rc = mkWinChildcareWorkerCreateProcess(pWorker, pChild, pwszImageName, pwszCommandLine, pwszzEnvironment);
     1987            BOOL afReplace[3] = { FALSE, pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE, pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE };
     1988            HANDLE ahChild[3] = { INVALID_HANDLE_VALUE, pChild->u.Process.hStdOut, pChild->u.Process.hStdErr };
     1989            rc = mkWinChildcareWorkerCreateProcess(pWorker, pwszImageName, pwszCommandLine, pwszzEnvironment,
     1990                                                   NULL /*pwszCwd*/, afReplace, ahChild, pChild->u.Process.fCatchOutput,
     1991                                                   &pChild->u.Process.hProcess);
     1992            mkWinChildcareWorkerCloseStandardHandles(pChild);
    19231993            if (rc == 0)
    19241994            {
     
    19582028{
    19592029    PCKMKBUILTINENTRY pBuiltIn = pChild->u.BuiltIn.pBuiltIn;
    1960     KMKBUILTINCTX Ctx = { pBuiltIn->uName.s.sz, pChild->pMkChild ? &pChild->pMkChild->output : NULL };
     2030    KMKBUILTINCTX Ctx =
     2031    {
     2032        pBuiltIn->uName.s.sz,
     2033        pChild->pMkChild ? &pChild->pMkChild->output : NULL,
     2034        pWorker,
     2035    };
    19612036    if (pBuiltIn->uFnSignature == FN_SIG_MAIN)
    19622037        pChild->iExitCode = pBuiltIn->u.pfnMain(pChild->u.BuiltIn.cArgs, pChild->u.BuiltIn.papszArgs,
     
    19642039    else if (pBuiltIn->uFnSignature == FN_SIG_MAIN_SPAWNS)
    19652040        pChild->iExitCode = pBuiltIn->u.pfnMainSpawns(pChild->u.BuiltIn.cArgs, pChild->u.BuiltIn.papszArgs,
    1966                                                       pChild->u.BuiltIn.papszEnv, &Ctx, pChild->pMkChild, NULL);
     2041                                                      pChild->u.BuiltIn.papszEnv, &Ctx, pChild->pMkChild, NULL /*pPid*/);
    19672042    else
    19682043    {
     
    21152190                PWINCHILD pTailExpect;
    21162191
     2192                pWorker->pCurChild = pChild;
    21172193                switch (pChild->enmType)
    21182194                {
     
    21372213                        assert(0);
    21382214                }
     2215                pWorker->pCurChild = NULL;
    21392216
    21402217                /*
     
    28212898}
    28222899
     2900
     2901/**
     2902 * New interface used by redirect.c for spawning and waitin on a child.
     2903 *
     2904 * This interface is only used when kmk_builtin_redirect is already running on
     2905 * a worker thread.
     2906 *
     2907 * @returns exit status.
     2908 * @param   pvWorker        The worker instance.
     2909 * @param   pszExecutable   The executable image to run.
     2910 * @param   papszArgs       Argument vector.
     2911 * @param   fQuotedArgv     Whether the argument vector is already quoted and
     2912 *                          just need some space to be turned into a command
     2913 *                          line.
     2914 * @param   papszEnvVars    Environment vector.
     2915 * @param   pszCwd          The working directory of the child.  Optional.
     2916 * @param   pafReplace      Which standard handles to replace. Maybe modified!
     2917 * @param   pahReplace      The replacement handles.  Maybe modified!
     2918 *
     2919 */
     2920int MkWinChildBuiltInExecChild(void *pvWorker, const char *pszExecutable, char **papszArgs, BOOL fQuotedArgv,
     2921                               char **papszEnvVars, const char *pszCwd, BOOL pafReplace[3], HANDLE pahReplace[3])
     2922{
     2923    PWINCHILDCAREWORKER pWorker = (PWINCHILDCAREWORKER)pvWorker;
     2924    WCHAR              *pwszSearchPath   = NULL;
     2925    WCHAR              *pwszzEnvironment = NULL;
     2926    WCHAR              *pwszCommandLine  = NULL;
     2927    WCHAR              *pwszImageName    = NULL;
     2928    WCHAR              *pwszCwd          = NULL;
     2929    BOOL                fNeedShell       = FALSE;
     2930    int                 rc;
     2931    assert(pWorker->uMagic == WINCHILDCAREWORKER_MAGIC);
     2932    assert(pWorker->pCurChild != NULL && pWorker->pCurChild->uMagic == WINCHILD_MAGIC);
     2933
     2934    /*
     2935     * Convert the CWD first since it's optional and we don't need to clean
     2936     * up anything here if it fails.
     2937     */
     2938    if (pszCwd)
     2939    {
     2940        size_t cchCwd = strlen(pszCwd);
     2941        int    cwcCwd = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszCwd, cchCwd + 1, NULL, 0);
     2942        pwszCwd = xmalloc((cwcCwd + 1) * sizeof(WCHAR)); /* (+1 in case cwcCwd is 0) */
     2943        cwcCwd = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszCwd, cchCwd + 1, pwszCwd, cwcCwd + 1);
     2944        if (!cwcCwd)
     2945        {
     2946            rc = GetLastError();
     2947            fprintf(stderr, _("MultiByteToWideChar failed to convert CWD (%s): %u\n"), pszCwd, (unsigned)rc);
     2948            return rc;
     2949        }
     2950    }
     2951
     2952    /*
     2953     * Before we search for the image, we convert the environment so we don't
     2954     * have to traverse it twice to find the PATH.
     2955     */
     2956    rc = mkWinChildcareWorkerConvertEnvironment(papszEnvVars ? papszEnvVars : environ, 0/*cbEnvStrings*/,
     2957                                                &pwszzEnvironment, &pwszSearchPath);
     2958    /*
     2959     * Find the executable and maybe checking if it's a shell script, then
     2960     * convert it to a command line.
     2961     */
     2962    if (rc == 0)
     2963        rc = mkWinChildcareWorkerFindImage(pszExecutable, pwszSearchPath, pwszzEnvironment,
     2964                                           NULL /*pszNull*/, &pwszImageName, &fNeedShell);
     2965    if (rc == 0)
     2966    {
     2967        assert(!fNeedShell);
     2968        if (!fQuotedArgv)
     2969            rc = mkWinChildcareWorkerConvertCommandline(papszArgs, 0 /*fFlags*/, &pwszCommandLine);
     2970        else
     2971            rc = mkWinChildcareWorkerConvertQuotedArgvToCommandline(papszArgs, &pwszCommandLine);
     2972
     2973        /*
     2974         * Create the child process.
     2975         */
     2976        if (rc == 0)
     2977        {
     2978            HANDLE hProcess;
     2979            rc = mkWinChildcareWorkerCreateProcess(pWorker, pwszImageName, pwszCommandLine, pwszzEnvironment,
     2980                                                   pwszCwd, pafReplace, pahReplace, TRUE /*fCatchOutput*/, &hProcess);
     2981            if (rc == 0)
     2982            {
     2983                /*
     2984                 * Wait for the child to complete.
     2985                 */
     2986                rc = mkWinChildcareWorkerWaitForProcess(pWorker, pWorker->pCurChild, hProcess, pwszImageName,
     2987                                                        TRUE /*fCatchOutput*/);
     2988                CloseHandle(hProcess);
     2989            }
     2990        }
     2991    }
     2992
     2993    free(pwszCwd);
     2994    free(pwszCommandLine);
     2995    free(pwszImageName);
     2996    free(pwszzEnvironment);
     2997
     2998    return rc;
     2999}
     3000
    28233001#endif /* CONFIG_NEW_WIN_CHILDREN */
    28243002
  • trunk/src/kmk/w32/winchildren.h

    r3172 r3195  
    4141int     MkWinChildCreateSubmit(intptr_t hEvent, void *pvSubmitWorker, pid_t *pPid);
    4242int     MkWinChildCreateRedirect(intptr_t hProcess, pid_t *pPid);
     43# ifdef DECLARE_HANDLE
     44int     MkWinChildBuiltInExecChild(void *pvWorker, const char *pszExecutable, char **papszArgs, BOOL fQuotedArgv,
     45                                   char **papszEnvVars, const char *pszCwd, BOOL pafReplace[3], HANDLE pahReplace[3]);
     46# endif
    4347#endif
    4448int     MkWinChildKill(pid_t pid, int iSignal, struct child *pMkChild);
Note: See TracChangeset for help on using the changeset viewer.