Ignore:
Timestamp:
Mar 19, 2018, 11:40:35 PM (7 years ago)
Author:
bird
Message:

kmk/win: More child process work, focusing on making kmk_builtin_redirect not requiring the shared lock.

File:
1 edited

Legend:

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

    r3159 r3161  
    3939#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
    4040# include <process.h>
     41#endif
     42#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
     43# include <Windows.h>
     44# include <Winternl.h>
    4145#endif
    4246#if defined(_MSC_VER)
     
    9195
    9296
     97/*********************************************************************************************************************************
     98*   Defined Constants And Macros                                                                                                 *
     99*********************************************************************************************************************************/
    93100/* String + strlen tuple. */
    94101#define TUPLE(a_sz)     a_sz, sizeof(a_sz) - 1
     102
     103/** Only standard handles on windows. */
     104#ifdef KBUILD_OS_WINDOWS
     105# define ONLY_TARGET_STANDARD_HANDLES
     106#endif
     107
    95108
    96109
     
    156169            "\n"
    157170            "The -d switch duplicate the right hand file descriptor (src-fd) to the left\n"
    158             "hand side one (fd).\n"
     171            "hand side one (fd). The latter is limited to standard handles on windows.\n"
    159172            "\n"
    160             "The -c switch will close the specified file descriptor.\n"
     173            "The -c switch will close the specified file descriptor. Limited to standard\n"
     174            "handles on windows.\n"
    161175            "\n"
    162176            "The -Z switch zaps the environment.\n"
     
    165179            "fashion.\n"
    166180            "\n"
    167             "The -C switch is for changing the current directory. This takes immediate\n"
    168             "effect, so be careful where you put it.\n"
     181            "The -C switch is for changing the current directory.  Please specify an\n"
     182            "absolute program path as it's platform dependent whether this takes effect\n"
     183            "before or after the executable is located.\n"
    169184            "\n"
    170185            "The --wcc-brain-damage switch is to work around wcc and wcc386 (Open Watcom)\n"
     
    439454        if (fdOpened >= 0)
    440455        {
     456#ifndef KBUILD_OS_WINDOWS
    441457            if (   !kRedirectHasConflict(fdOpened, cOrders, paOrders)
    442 #ifdef _MSC_VER
     458# ifdef _MSC_VER
    443459                && fdOpened != fdTarget
    444 #endif
     460# endif
    445461                )
     462#endif
    446463            {
    447464#ifndef _MSC_VER
     
    510527
    511528
    512 #ifndef USE_POSIX_SPAWN
     529#if !defined(USE_POSIX_SPAWN) && !defined(KBUILD_OS_WINDOWS)
    513530
    514531/**
     
    778795#endif /* !USE_POSIX_SPAWN */
    779796
     797#ifdef KBUILD_OS_WINDOWS
     798
     799/**
     800 * Tries to locate the executable image.
     801 *
     802 * This isn't quite perfect yet...
     803 *
     804 * @returns pszExecutable or pszBuf with valid string.
     805 * @param   pszExecutable   The specified executable.
     806 * @param   pszBuf          Buffer to return a modified path in.
     807 * @param   cbBuf           Size of return buffer.
     808 * @param   pszPath         The search path.
     809 */
     810static const char *kRedirectCreateProcessWindowsFindImage(const char *pszExecutable, char *pszBuf, size_t cbBuf,
     811                                                          const char *pszPath)
     812{
     813    /*
     814     * Analyze the name.
     815     */
     816    size_t const cchExecutable = strlen(pszExecutable);
     817    BOOL         fHavePath = FALSE;
     818    BOOL         fHaveSuffix = FALSE;
     819    size_t       off = cchExecutable;
     820    while (off > 0)
     821    {
     822        char ch = pszExecutable[--off];
     823        if (ch == '.')
     824        {
     825            fHaveSuffix = TRUE;
     826            break;
     827        }
     828        if (ch == '\\' || ch == '/' || ch == ':')
     829        {
     830            fHavePath = TRUE;
     831            break;
     832        }
     833    }
     834    if (!fHavePath)
     835        while (off > 0)
     836        {
     837            char ch = pszExecutable[--off];
     838            if (ch == '\\' || ch == '/' || ch == ':')
     839            {
     840                fHavePath = TRUE;
     841                break;
     842            }
     843        }
     844   /*
     845    * If no path, search the path value.
     846    */
     847   if (!fHavePath)
     848   {
     849       char *pszFilename;
     850       DWORD cchFound = SearchPathA(pszPath, pszExecutable, fHaveSuffix ? NULL : ".exe", cbBuf, pszBuf, &pszFilename);
     851       if (cchFound)
     852           return pszBuf;
     853   }
     854
     855   /*
     856    * If no suffix, try add .exe.
     857    */
     858   if (   !fHaveSuffix
     859       && GetFileAttributesA(pszExecutable) == INVALID_FILE_ATTRIBUTES
     860       && cchExecutable + 4 < cbBuf)
     861   {
     862       memcpy(pszBuf, pszExecutable, cchExecutable);
     863       memcpy(&pszBuf[cchExecutable], ".exe", 5);
     864       if (GetFileAttributesA(pszBuf) != INVALID_FILE_ATTRIBUTES)
     865           return pszBuf;
     866   }
     867
     868   return pszExecutable;
     869}
     870
     871/**
     872 * Alternative approach on windows that use CreateProcess and doesn't require
     873 * any serialization wrt handles and CWD.
     874 *
     875 * @returns 0 on success, non-zero on failure to create.
     876 * @param   pszExecutable       The child process executable.
     877 * @param   cArgs               Number of arguments.
     878 * @param   papszArgs           The child argument vector.
     879 * @param   papszEnvVars        The child environment vector.
     880 * @param   pszCwd              The current working directory of the child.
     881 * @param   cOrders             Number of file operation orders.
     882 * @param   paOrders            The file operation orders.
     883 * @param   phProcess           Where to return process handle.
     884 */
     885static kRedirectCreateProcessWindows(const char *pszExecutable, int cArgs, char **papszArgs, char **papszEnvVars,
     886                                     const char *pszCwd, unsigned cOrders, REDIRECTORDERS *paOrders, HANDLE *phProcess)
     887{
     888    static NTSTATUS (NTAPI *s_pfnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG) = NULL;
     889    size_t cbArgs;
     890    char  *pszCmdLine;
     891    size_t cbEnv;
     892    char  *pszzEnv;
     893    char  *pch;
     894    int    i;
     895    int    rc;
     896
     897    /*
     898     * Determin host bitness and APIs while we can still easily return.
     899     */
     900#if K_ARCH_BITS == 32
     901    BOOL   f64BitHost = TRUE;
     902    if (!IsWow64Process(GetCurrentProcess(), &f64BitHost))
     903        return errx(9, "IsWow64Process failed: %u", GetLastError());
     904#elif K_ARCH_BITS == 64
     905    BOOL const f64BitHost = TRUE;
     906#else
     907# error "K_ARCH_BITS is bad/missing"
     908#endif
     909    if (cOrders > 0 && !s_pfnNtQueryInformationProcess)
     910    {
     911        *(FARPROC *)&s_pfnNtQueryInformationProcess = GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtQueryInformationProcess");
     912        if (!s_pfnNtQueryInformationProcess)
     913            return errx(9, "NtQueryInformationProcess not found!");
     914    }
     915
     916    /*
     917     * Start by making the the command line.  We just need to put spaces
     918     * between the arguments since quote_argv don't the quoting already.
     919     */
     920    cbArgs = 0;
     921    for (i = 0; i < cArgs; i++)
     922        cbArgs += strlen(papszArgs[i]) + 1;
     923    pszCmdLine = pch = (char *)malloc(cbArgs);
     924    if (!pszCmdLine)
     925        return errx(9, "out of memory!");
     926    for (i = 0; i < cArgs; i++)
     927    {
     928        size_t cch;
     929        if (i != 0)
     930            *pch++ = ' ';
     931        cch = strlen(papszArgs[i]);
     932        memcpy(pch, papszArgs[i], cch);
     933        pch += cch;
     934    }
     935    *pch++ = '\0';
     936    assert(pch - pszCmdLine == cbArgs);
     937
     938    /*
     939     * The environment vector is also simple.
     940     */
     941    cbEnv = 0;
     942    for (i = 0; papszEnvVars[i]; i++)
     943        cbEnv += strlen(papszEnvVars[i]) + 1;
     944    cbEnv++;
     945    pszzEnv = pch = (char *)malloc(cbEnv);
     946    if (pszzEnv)
     947    {
     948        char                szAbsExe[1024];
     949        const char         *pszPathVal = NULL;
     950        STARTUPINFOA        StartupInfo;
     951        PROCESS_INFORMATION ProcInfo = { NULL, NULL, 0, 0 };
     952
     953        for (i = 0; papszEnvVars[i]; i++)
     954        {
     955            size_t cbSrc = strlen(papszEnvVars[i]) + 1;
     956            memcpy(pch, papszEnvVars[i], cbSrc);
     957            if (   !pszPathVal
     958                && cbSrc >= 5
     959                && pch[4] == '='
     960                && (pch[0] == 'P' || pch[0] == 'p')
     961                && (pch[1] == 'A' || pch[1] == 'a')
     962                && (pch[2] == 'T' || pch[2] == 't')
     963                && (pch[3] == 'H' || pch[3] == 'h'))
     964                pszPathVal = &pch[5];
     965            pch += cbSrc;
     966        }
     967        *pch++ = '\0';
     968        assert(pch - pszzEnv == cbEnv);
     969
     970        /*
     971         * Locate the executable.
     972         */
     973        pszExecutable = kRedirectCreateProcessWindowsFindImage(pszExecutable, szAbsExe, sizeof(szAbsExe), pszPathVal);
     974
     975        /*
     976         * Do basic startup info preparation.
     977         */
     978        memset(&StartupInfo, 0, sizeof(StartupInfo));
     979        StartupInfo.cb = sizeof(StartupInfo);
     980        GetStartupInfoA(&StartupInfo);
     981        StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
     982
     983        /*
     984         * If there are no redirection orders, we're good.
     985         */
     986        if (!cOrders)
     987        {
     988            if (CreateProcessA(pszExecutable, pszCmdLine, NULL /*pProcAttrs*/, NULL /*pThreadAttrs*/,
     989                               FALSE /*fInheritHandles*/, 0 /*fFlags*/, pszzEnv, pszCwd, &StartupInfo, &ProcInfo))
     990            {
     991                CloseHandle(ProcInfo.hThread);
     992                *phProcess = ProcInfo.hProcess;
     993                rc = 0;
     994            }
     995            else
     996                rc = errx(10, "CreateProcessA(%s) failed: %u", pszExecutable, GetLastError());
     997        }
     998        else
     999        {
     1000            /*
     1001             * Execute the orders, ending up with three handles we need to
     1002             * implant into the guest process.
     1003             *
     1004             * This isn't 100% perfect wrt O_APPEND, but it'll have to do for now.
     1005             */
     1006            BOOL   afReplace[3] = { FALSE, FALSE, FALSE };
     1007            HANDLE ahChild[3]   = { NULL,  NULL,  NULL  };
     1008            rc = 0;
     1009            for (i = 0; i < (int)cOrders; i++)
     1010            {
     1011                int fdTarget = paOrders[i].fdTarget;
     1012                assert(fdTarget >= 0 && fdTarget < 3);
     1013                switch (paOrders[i].enmOrder)
     1014                {
     1015                    case kRedirectOrder_Open:
     1016                        if (   (paOrders[i].fOpen & O_APPEND)
     1017                            && lseek(paOrders[i].fdSource, 0, SEEK_END) < 0)
     1018                            rc = err(10, "lseek-to-end failed on %d (for %d)", paOrders[i].fdSource, fdTarget);
     1019                    case kRedirectOrder_Dup:
     1020                        ahChild[fdTarget] = (HANDLE)_get_osfhandle(paOrders[i].fdSource);
     1021                        if (ahChild[fdTarget] == NULL || ahChild[fdTarget] == INVALID_HANDLE_VALUE)
     1022                            rc = err(10, "_get_osfhandle failed on %d (for %d)", paOrders[i].fdSource, fdTarget);
     1023                        break;
     1024                    case kRedirectOrder_Close:
     1025                        ahChild[fdTarget] = NULL;
     1026                        break;
     1027                    default:
     1028                        assert(0);
     1029                }
     1030                afReplace[fdTarget] = TRUE;
     1031            }
     1032            if (rc == 0)
     1033            {
     1034                /*
     1035                 * Start the process in suspended animation so we can inject handles.
     1036                 *
     1037                 * We clear the reserved 2 pointer + size to avoid passing the wrong
     1038                 * filehandle info to the child.  We may later want to generate this.
     1039                 */
     1040                StartupInfo.cbReserved2 = 0;
     1041                StartupInfo.lpReserved2 = 0;
     1042
     1043                if (CreateProcessA(pszExecutable, pszCmdLine, NULL /*pProcAttrs*/, NULL /*pThreadAttrs*/,
     1044                                   FALSE /*fInheritHandles*/, CREATE_SUSPENDED, pszzEnv, pszCwd, &StartupInfo, &ProcInfo))
     1045                {
     1046                    /*
     1047                     * Figure out where we need to write the handles.
     1048                     */
     1049                    ULONG                     cbActual1 = 0;
     1050                    PROCESS_BASIC_INFORMATION BasicInfo = { 0, 0, };
     1051                    NTSTATUS rcNt = s_pfnNtQueryInformationProcess(ProcInfo.hProcess, ProcessBasicInformation,
     1052                                                                   &BasicInfo, sizeof(BasicInfo), &cbActual1);
     1053                    if (NT_SUCCESS(rcNt))
     1054                    {
     1055                        BOOL const   f32BitPeb  = !f64BitHost;
     1056                        ULONG  const cbChildPtr = f32BitPeb ? 4 : 8;
     1057                        PVOID        pvSrcInPeb = (char *)BasicInfo.PebBaseAddress + (f32BitPeb ? 0x10 : 0x20);
     1058                        char *       pbDst      = 0;
     1059                        SIZE_T       cbActual2  = 0;
     1060                        if (ReadProcessMemory(ProcInfo.hProcess, pvSrcInPeb, &pbDst, cbChildPtr, &cbActual2))
     1061                        {
     1062                            union
     1063                            {
     1064                                KU32 au32[3];
     1065                                KU64 au64[3];
     1066                            } uBuf;
     1067                            memset(&uBuf, 0, sizeof(uBuf));
     1068                            pbDst += f32BitPeb ? 0x18 : 0x20;
     1069                            if (   (afReplace[0] && afReplace[1] && afReplace[2])
     1070                                || ReadProcessMemory(ProcInfo.hProcess, pbDst, &uBuf, cbChildPtr * 3, &cbActual2))
     1071                            {
     1072                                for (i = 0; i < 3; i++)
     1073                                    if (afReplace[i])
     1074                                    {
     1075                                        HANDLE hInChild = INVALID_HANDLE_VALUE;
     1076                                        if (   ahChild[i] == NULL /* just closed*/
     1077                                            || DuplicateHandle(GetCurrentProcess(), ahChild[i], ProcInfo.hProcess, &hInChild,
     1078                                                               0, TRUE /*fInheriable*/, DUPLICATE_SAME_ACCESS))
     1079                                        {
     1080                                            if (f32BitPeb)
     1081                                                uBuf.au32[i] = (KU32)(uintptr_t)hInChild;
     1082                                            else
     1083                                                uBuf.au64[i] = (uintptr_t)hInChild;
     1084                                        }
     1085                                        else
     1086                                            rc = errx(10, "Error duplicating %p into the child: %u", ahChild[i], GetLastError());
     1087                                    }
     1088                                if (    rc == 0
     1089                                    && !WriteProcessMemory(ProcInfo.hProcess, pbDst, &uBuf, cbChildPtr * 3, &cbActual2))
     1090                                    rc = errx(10, "Error writing standard handles at %p LB %u in the child: %u",
     1091                                              pbDst, cbChildPtr * 3, GetLastError());
     1092                            }
     1093                            else
     1094                                rc = errx(10, "Error reading %p LB %u from the child: %u",
     1095                                          pbDst, cbChildPtr * 3, GetLastError());
     1096                        }
     1097                        else
     1098                            rc = errx(10, "Error reading %p LB %u from the child: %u", pvSrcInPeb, cbChildPtr);
     1099                    }
     1100                    else
     1101                        rc = errx(10, "NtQueryInformationProcess failed: %#x", rcNt);
     1102
     1103                    /* Resume the bugger... */
     1104                    if (   rc == 0
     1105                        && !ResumeThread(ProcInfo.hThread))
     1106                        rc = errx(10, "ResumeThread failed: %u", GetLastError());
     1107
     1108                    /* .. or kill it. */
     1109                    if (rc != 0)
     1110                        TerminateProcess(ProcInfo.hProcess, rc);
     1111
     1112                    CloseHandle(ProcInfo.hThread);
     1113                    *phProcess = ProcInfo.hProcess;
     1114                    rc = 0;
     1115                }
     1116                else
     1117                    rc = errx(10, "CreateProcessA(%s) failed: %u", pszExecutable, GetLastError());
     1118            }
     1119        }
     1120        free(pszzEnv);
     1121    }
     1122    else
     1123        rc = errx(9, "out of memory!");
     1124    free(pszCmdLine);
     1125    return rc;
     1126}
     1127#endif /* KBUILD_OS_WINDOWS */
    7801128
    7811129/**
     
    8021150 *                              is from the child or from our setup efforts.
    8031151 */
    804 static int kRedirectDoSpawn(const char *pszExecutable, int cArgs, char **papszArgs, int fWatcomBrainDamage, char **papszEnvVars,
    805                             const char *pszCwd, const char *pszSavedCwd, unsigned cOrders, REDIRECTORDERS *paOrders,
     1152static int kRedirectDoSpawn(const char *pszExecutable, int cArgs, char **papszArgs, int fWatcomBrainDamage,
     1153                            char **papszEnvVars, const char *pszCwd, const char *pszSavedCwd,
     1154                            unsigned cOrders, REDIRECTORDERS *paOrders,
    8061155#ifdef USE_POSIX_SPAWN
    8071156                            posix_spawn_file_actions_t *pFileActions,
     
    8631212        }
    8641213
     1214#ifndef KBUILD_OS_WINDOWS
    8651215        /*
    8661216         * Change working directory if so requested.
     
    8711221                rcExit = errx(10, "Failed to change directory to '%s'", pszCwd);
    8721222        }
     1223#endif /* KBUILD_OS_WINDOWS */
    8731224        if (rcExit == 0)
    8741225        {
    875 #ifndef USE_POSIX_SPAWN
     1226# if !defined(USE_POSIX_SPAWN) && !defined(KBUILD_OS_WINDOWS)
    8761227            /*
    8771228             * Execute the file orders.
    8781229             */
    8791230            FILE *pWorkingStdErr = NULL;
    880 # if defined(CONFIG_NEW_WIN_CHILDREN) && defined(KMK)
     1231#  if defined(CONFIG_NEW_WIN_CHILDREN) && defined(KMK)
    8811232            if (cOrders > 0)
    8821233                MkWinChildExclusiveAcquire();
    883 # endif
     1234#  endif
    8841235            rcExit = kRedirectExecFdOrders(cOrders, paOrders, &pWorkingStdErr);
    8851236            if (rcExit == 0)
    886 #endif
    887             {
    888 #ifdef KMK
     1237# endif
     1238            {
     1239# ifdef KMK
    8891240                /*
    8901241                 * We're spawning from within kmk.
    8911242                 */
    892 #if defined(KBUILD_OS_WINDOWS)
    893                 /* Windows is slightly complicated due to handles and sub_proc.c. */
     1243# if defined(KBUILD_OS_WINDOWS)
     1244                /* Windows is slightly complicated due to handles and winchildren.c. */
     1245#  if 1
     1246                HANDLE hProcess = INVALID_HANDLE_VALUE;
     1247                rcExit = kRedirectCreateProcessWindows(pszExecutable, cArgs, papszArgs, papszEnvVars, pszCwd,
     1248                                                       cOrders, paOrders, &hProcess);
     1249                if (rcExit == 0)
     1250#  else
    8941251                HANDLE  hProcess = (HANDLE)_spawnvpe(_P_NOWAIT, pszExecutable, papszArgs, papszEnvVars);
    8951252                kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
    896 # ifdef CONFIG_NEW_WIN_CHILDREN
     1253#  ifdef CONFIG_NEW_WIN_CHILDREN
    8971254                if (cOrders > 0)
    8981255                    MkWinChildExclusiveRelease();
     1256#  endif
     1257                if ((intptr_t)hProcess != -1)
    8991258# endif
    900                 if ((intptr_t)hProcess != -1)
    901                 {
    902 # ifndef CONFIG_NEW_WIN_CHILDREN
     1259                {
     1260#  ifndef CONFIG_NEW_WIN_CHILDREN
    9031261                    if (process_kmk_register_redirect(hProcess, pPidSpawned) == 0)
    904 # else
     1262#  else
    9051263                    if (MkWinChildCreateRedirect((intptr_t)hProcess, pPidSpawned) == 0)
    906 # endif
     1264#  endif
    9071265                    {
    9081266                        if (cVerbosity > 0)
     
    9121270                    {
    9131271                        DWORD dwTmp;
    914 # ifndef CONFIG_NEW_WIN_CHILDREN
     1272#  ifndef CONFIG_NEW_WIN_CHILDREN
    9151273                        warn("sub_proc is out of slots, waiting for child...");
    916 # else
     1274#  else
    9171275                        warn("MkWinChildCreateRedirect failed...");
    918 # endif
     1276#  endif
    9191277                        dwTmp = WaitForSingleObject(hProcess, INFINITE);
    9201278                        if (dwTmp != WAIT_OBJECT_0)
     
    9351293                    }
    9361294                }
     1295#  if 0
    9371296                else
    9381297                    rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
     1298#  endif
    9391299
    9401300# elif defined(KBUILD_OS_OS2)
     
    9511311                    *pPidSpawned = 0;
    9521312                }
    953 #else
     1313# else
    9541314                rcExit = posix_spawnp(pPidSpawned, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars);
    9551315                if (rcExit == 0)
     
    9631323                    *pPidSpawned = 0;
    9641324                }
    965 #endif
     1325# endif
    9661326
    9671327#else  /* !KMK */
     
    9691329                 * Spawning from inside the kmk_redirect executable.
    9701330                 */
    971 # if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
     1331# ifdef KBUILD_OS_WINDOWS
     1332                HANDLE hProcess = INVALID_HANDLE_VALUE;
     1333                rcExit = kRedirectCreateProcessWindows(pszExecutable, cArgs, papszArgs, papszEnvVars, pszCwd,
     1334                                                       cOrders, paOrders, &hProcess);
     1335                if (rcExit == 0)
     1336                {
     1337                    DWORD dwWait;
     1338                    do
     1339                        dwWait = WaitForSingleObject(hProcess, INFINITE);
     1340                    while (dwWait == WAIT_IO_COMPLETION || dwWait == WAIT_TIMEOUT);
     1341
     1342                    dwWait = 11;
     1343                    if (GetExitCodeProcess(hProcess, &dwWait))
     1344                        rcExit = dwWait;
     1345                    else
     1346                        rcExit = errx(11, "GetExitCodeProcess(%s) failed: %u", pszExecutable, GetLastError());
     1347                }
     1348
     1349#elif defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
    9721350                errno  = 0;
    9731351#  if defined(KBUILD_OS_WINDOWS)
     
    10231401#endif /* !KMK */
    10241402            }
    1025 #if defined(CONFIG_NEW_WIN_CHILDREN) && defined(KBUILD_OS_WINDOWS) && defined(KMK)
     1403#if defined(CONFIG_NEW_WIN_CHILDREN) && defined(KBUILD_OS_WINDOWS) && defined(KMK) && 0
    10261404            else if (cOrders > 0)
    10271405                MkWinChildExclusiveRelease();
     
    14411819                else if (fd < 0)
    14421820                    rcExit = errx(2, "error: negative fd %d (%s)", fd, pszValue);
     1821#ifdef ONLY_TARGET_STANDARD_HANDLES
     1822                else if (fd > 2)
     1823                    rcExit = errx(2, "error: %d is not a standard descriptor number", fd);
     1824#endif
    14431825                else
    14441826                {
     
    14641846                else if (fd < 0)
    14651847                    rcExit = errx(2, "error: negative target descriptor %d ('-d %s')", fd, pszValue);
     1848#ifdef ONLY_TARGET_STANDARD_HANDLES
     1849                else if (fd > 2)
     1850                    rcExit = errx(2, "error: target %d is not a standard descriptor number", fd);
     1851#endif
    14661852                else if (*pszEqual != '=')
    14671853                    rcExit = errx(2, "syntax error: expected '=' to follow target descriptor: '-d %s'", pszValue);
     
    16081994                        else if (fd < 0)
    16091995                            rcExit = errx(2, "error: negative fd %d (%s)", fd, argv[iArg]);
     1996#ifdef ONLY_TARGET_STANDARD_HANDLES
     1997                        else if (fd > 2)
     1998                            rcExit = errx(2, "error: %d is not a standard descriptor number", fd);
     1999#endif
    16102000                        else
    16112001                            break;
     
    16882078         * Do the spawning in a separate function (main is far to large as it is by now).
    16892079         */
    1690         rcExit = kRedirectDoSpawn(pszExecutable, argc - iArg, &argv[iArg], fWatcomBrainDamage, papszEnvVars, szCwd, pszSavedCwd,
     2080        rcExit = kRedirectDoSpawn(pszExecutable, argc - iArg, &argv[iArg], fWatcomBrainDamage,
     2081                                  papszEnvVars,
     2082                                  szCwd, pszSavedCwd,
    16912083#ifdef USE_POSIX_SPAWN
    16922084                                  cOrders, aOrders, &FileActions, cVerbosity,
Note: See TracChangeset for help on using the changeset viewer.