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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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
Note: See TracChangeset for help on using the changeset viewer.