Changeset 3161
- Timestamp:
- Mar 19, 2018, 11:40:35 PM (7 years ago)
- Location:
- trunk/src/kmk
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kmk/kmkbuiltin/redirect.c
r3159 r3161 39 39 #if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2) 40 40 # include <process.h> 41 #endif 42 #if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2) 43 # include <Windows.h> 44 # include <Winternl.h> 41 45 #endif 42 46 #if defined(_MSC_VER) … … 91 95 92 96 97 /********************************************************************************************************************************* 98 * Defined Constants And Macros * 99 *********************************************************************************************************************************/ 93 100 /* String + strlen tuple. */ 94 101 #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 95 108 96 109 … … 156 169 "\n" 157 170 "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" 159 172 "\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" 161 175 "\n" 162 176 "The -Z switch zaps the environment.\n" … … 165 179 "fashion.\n" 166 180 "\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" 169 184 "\n" 170 185 "The --wcc-brain-damage switch is to work around wcc and wcc386 (Open Watcom)\n" … … 439 454 if (fdOpened >= 0) 440 455 { 456 #ifndef KBUILD_OS_WINDOWS 441 457 if ( !kRedirectHasConflict(fdOpened, cOrders, paOrders) 442 # ifdef _MSC_VER458 # ifdef _MSC_VER 443 459 && fdOpened != fdTarget 444 # endif460 # endif 445 461 ) 462 #endif 446 463 { 447 464 #ifndef _MSC_VER … … 510 527 511 528 512 #if ndef USE_POSIX_SPAWN529 #if !defined(USE_POSIX_SPAWN) && !defined(KBUILD_OS_WINDOWS) 513 530 514 531 /** … … 778 795 #endif /* !USE_POSIX_SPAWN */ 779 796 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 */ 810 static 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 */ 885 static 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 */ 780 1128 781 1129 /** … … 802 1150 * is from the child or from our setup efforts. 803 1151 */ 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, 1152 static 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, 806 1155 #ifdef USE_POSIX_SPAWN 807 1156 posix_spawn_file_actions_t *pFileActions, … … 863 1212 } 864 1213 1214 #ifndef KBUILD_OS_WINDOWS 865 1215 /* 866 1216 * Change working directory if so requested. … … 871 1221 rcExit = errx(10, "Failed to change directory to '%s'", pszCwd); 872 1222 } 1223 #endif /* KBUILD_OS_WINDOWS */ 873 1224 if (rcExit == 0) 874 1225 { 875 # ifndef USE_POSIX_SPAWN1226 # if !defined(USE_POSIX_SPAWN) && !defined(KBUILD_OS_WINDOWS) 876 1227 /* 877 1228 * Execute the file orders. 878 1229 */ 879 1230 FILE *pWorkingStdErr = NULL; 880 # if defined(CONFIG_NEW_WIN_CHILDREN) && defined(KMK)1231 # if defined(CONFIG_NEW_WIN_CHILDREN) && defined(KMK) 881 1232 if (cOrders > 0) 882 1233 MkWinChildExclusiveAcquire(); 883 # endif1234 # endif 884 1235 rcExit = kRedirectExecFdOrders(cOrders, paOrders, &pWorkingStdErr); 885 1236 if (rcExit == 0) 886 # endif887 { 888 # ifdef KMK1237 # endif 1238 { 1239 # ifdef KMK 889 1240 /* 890 1241 * We're spawning from within kmk. 891 1242 */ 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 894 1251 HANDLE hProcess = (HANDLE)_spawnvpe(_P_NOWAIT, pszExecutable, papszArgs, papszEnvVars); 895 1252 kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr); 896 # ifdef CONFIG_NEW_WIN_CHILDREN1253 # ifdef CONFIG_NEW_WIN_CHILDREN 897 1254 if (cOrders > 0) 898 1255 MkWinChildExclusiveRelease(); 1256 # endif 1257 if ((intptr_t)hProcess != -1) 899 1258 # endif 900 if ((intptr_t)hProcess != -1) 901 { 902 # ifndef CONFIG_NEW_WIN_CHILDREN 1259 { 1260 # ifndef CONFIG_NEW_WIN_CHILDREN 903 1261 if (process_kmk_register_redirect(hProcess, pPidSpawned) == 0) 904 # else1262 # else 905 1263 if (MkWinChildCreateRedirect((intptr_t)hProcess, pPidSpawned) == 0) 906 # endif1264 # endif 907 1265 { 908 1266 if (cVerbosity > 0) … … 912 1270 { 913 1271 DWORD dwTmp; 914 # ifndef CONFIG_NEW_WIN_CHILDREN1272 # ifndef CONFIG_NEW_WIN_CHILDREN 915 1273 warn("sub_proc is out of slots, waiting for child..."); 916 # else1274 # else 917 1275 warn("MkWinChildCreateRedirect failed..."); 918 # endif1276 # endif 919 1277 dwTmp = WaitForSingleObject(hProcess, INFINITE); 920 1278 if (dwTmp != WAIT_OBJECT_0) … … 935 1293 } 936 1294 } 1295 # if 0 937 1296 else 938 1297 rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable); 1298 # endif 939 1299 940 1300 # elif defined(KBUILD_OS_OS2) … … 951 1311 *pPidSpawned = 0; 952 1312 } 953 # else1313 # else 954 1314 rcExit = posix_spawnp(pPidSpawned, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars); 955 1315 if (rcExit == 0) … … 963 1323 *pPidSpawned = 0; 964 1324 } 965 # endif1325 # endif 966 1326 967 1327 #else /* !KMK */ … … 969 1329 * Spawning from inside the kmk_redirect executable. 970 1330 */ 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) 972 1350 errno = 0; 973 1351 # if defined(KBUILD_OS_WINDOWS) … … 1023 1401 #endif /* !KMK */ 1024 1402 } 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 1026 1404 else if (cOrders > 0) 1027 1405 MkWinChildExclusiveRelease(); … … 1441 1819 else if (fd < 0) 1442 1820 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 1443 1825 else 1444 1826 { … … 1464 1846 else if (fd < 0) 1465 1847 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 1466 1852 else if (*pszEqual != '=') 1467 1853 rcExit = errx(2, "syntax error: expected '=' to follow target descriptor: '-d %s'", pszValue); … … 1608 1994 else if (fd < 0) 1609 1995 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 1610 2000 else 1611 2001 break; … … 1688 2078 * Do the spawning in a separate function (main is far to large as it is by now). 1689 2079 */ 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, 1691 2083 #ifdef USE_POSIX_SPAWN 1692 2084 cOrders, aOrders, &FileActions, cVerbosity, -
trunk/src/kmk/main.c
r3159 r3161 1470 1470 if (!cpus) 1471 1471 cpus = 1; 1472 # ifdef CONFIG_NEW_WIN_CHILDREN 1472 1473 if (cpus > 64) 1473 1474 cpus = 64; /* (wait for multiple objects limit) */ 1475 # endif 1474 1476 return cpus; 1475 1477 … … 3129 3131 setrlimit (RLIMIT_STACK, &stack_limit); 3130 3132 #endif 3131 fprintf(stderr, "respawning 1..\n");3132 fprintf(stderr, "respawning 2..\n");3133 fprintf(stderr, "respawning 3..!\n");3134 3133 # if !defined(WINDOWS32) || !defined(CONFIG_NEW_WIN_CHILDREN) 3135 3134 exec_command ((char **)nargv, environ); -
trunk/src/kmk/w32/winchildren.c
r3159 r3161 76 76 77 77 #include <Windows.h> 78 #include <Winternl.h> 78 79 #include <assert.h> 79 80 #include <process.h> … … 270 271 /** Kernel32!SetThreadGroupAffinity */ 271 272 static BOOL (WINAPI *g_pfnSetThreadGroupAffinity)(HANDLE, CONST GROUP_AFFINITY *, GROUP_AFFINITY *); 273 /** NTDLL!NtQueryInformationProcess */ 274 static NTSTATUS (NTAPI *g_pfnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); 275 /** Set if the windows host is 64-bit. */ 276 static BOOL g_f64BitHost = (K_ARCH_BITS == 64); 272 277 /** Windows version info. 273 278 * @note Putting this before the volatile stuff, hoping to keep it in a … … 289 294 static unsigned volatile g_idxLastChildcareWorker = 0; 290 295 291 /** TemporaryRW lock for serializing kmkbuiltin_redirect and CreateProcess. */296 /** RW lock for serializing kmkbuiltin_redirect and CreateProcess. */ 292 297 static SRWLOCK g_RWLock; 293 298 … … 301 306 void MkWinChildInit(unsigned int cJobSlots) 302 307 { 308 HMODULE hmod; 309 303 310 /* 304 311 * Figure out how many childcare workers first. … … 322 329 323 330 /* 331 * NTDLL imports that we need. 332 */ 333 hmod = GetModuleHandleA("NTDLL.DLL"); 334 *(FARPROC *)&g_pfnNtQueryInformationProcess = GetProcAddress(hmod, "NtQueryInformationProcess"); 335 if (!g_pfnNtQueryInformationProcess) 336 fatal(NILF, 0, _("MkWinChildInit: NtQueryInformationProcess not found")); 337 338 #if K_ARCH_BITS == 32 339 /* 340 * Initialize g_f64BitHost. 341 */ 342 if (!IsWow64Process(GetCurrentProcess(), &g_f64BitHost)) 343 fatal(NILF, INTSTR_LENGTH, _("MkWinChildInit: IsWow64Process failed: %u"), GetLastError()); 344 #elif K_ARCH_BITS == 64 345 assert(g_f64BitHost); 346 #else 347 # error "K_ARCH_BITS is bad/missing" 348 #endif 349 350 /* 324 351 * Figure out how many processor groups there are. 325 352 * For that we need to first figure the windows version. … … 334 361 if (g_VersionInfo.dwMajorVersion >= 6) 335 362 { 336 HMODULEhmod = GetModuleHandleA("KERNEL32.DLL");363 hmod = GetModuleHandleA("KERNEL32.DLL"); 337 364 *(FARPROC *)&g_pfnGetActiveProcessorGroupCount = GetProcAddress(hmod, "GetActiveProcessorGroupCount"); 338 365 *(FARPROC *)&g_pfnGetActiveProcessorCount = GetProcAddress(hmod, "GetActiveProcessorCount"); … … 365 392 } 366 393 367 /* Temporary: */ 394 /* 395 * For serializing with standard file handle manipulation (kmkbuiltin_redirect). 396 */ 368 397 InitializeSRWLock(&g_RWLock); 369 398 } … … 465 494 } 466 495 } 496 497 498 /** 499 * Does the actual process creation given. 500 * 501 * @returns 0 if there is anything to wait on, otherwise non-zero windows error. 502 * @param pWorker The childcare worker. 503 * @param pChild The child. 504 * @param pwszImageName The image path. 505 * @param pwszCommandLine The command line. 506 * @param pwszzEnvironment The enviornment block. 507 */ 508 static int mkWinChildcareWorkerCreateProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild, WCHAR const *pwszImageName, 509 WCHAR const *pwszCommandLine, WCHAR const *pwszzEnvironment) 510 { 511 PROCESS_INFORMATION ProcInfo; 512 STARTUPINFOW StartupInfo; 513 DWORD fFlags = CREATE_UNICODE_ENVIRONMENT; 514 BOOL const fHaveHandles = pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE 515 || pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE; 516 BOOL fRet; 517 DWORD dwErr; 518 #ifdef KMK 519 extern int process_priority; 520 #endif 521 522 /* 523 * Populate startup info. 524 * 525 * Turns out we can get away without passing TRUE for the inherit handles 526 * parameter to CreateProcess when we're not using STARTF_USESTDHANDLES. 527 * At least on NT, which is all worth caring about at this point + context IMO. 528 * 529 * Not inherting the handles is a good thing because it means we won't 530 * accidentally end up with a pipe handle or such intended for a different 531 * child process, potentially causing the EOF/HUP event to be delayed. 532 * 533 * Since the present handle inhertiance requirements only involves standard 534 * output and error, we'll never set the inherit handles flag and instead 535 * do manual handle duplication and planting. 536 */ 537 memset(&StartupInfo, 0, sizeof(StartupInfo)); 538 StartupInfo.cb = sizeof(StartupInfo); 539 GetStartupInfoW(&StartupInfo); 540 if (!fHaveHandles) 541 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES; 542 else 543 { 544 fFlags |= CREATE_SUSPENDED; 545 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES; 546 547 /* Don't pass CRT inheritance info to the child (from our parent actually). */ 548 StartupInfo.cbReserved2 = 0; 549 StartupInfo.lpReserved2 = 0; 550 } 551 552 /* 553 * Flags. 554 */ 555 #ifdef KMK 556 switch (process_priority) 557 { 558 case 1: fFlags |= CREATE_SUSPENDED | IDLE_PRIORITY_CLASS; break; 559 case 2: fFlags |= CREATE_SUSPENDED | BELOW_NORMAL_PRIORITY_CLASS; break; 560 case 3: fFlags |= CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS; break; 561 case 4: fFlags |= CREATE_SUSPENDED | HIGH_PRIORITY_CLASS; break; 562 case 5: fFlags |= CREATE_SUSPENDED | REALTIME_PRIORITY_CLASS; break; 563 } 564 #endif 565 if (g_cProcessorGroups > 1) 566 fFlags |= CREATE_SUSPENDED; 567 568 /* 569 * Try create the process. 570 */ 571 DB(DB_JOBS, ("CreateProcessW(%ls, %ls,,, TRUE, %#x...)\n", pwszImageName, pwszCommandLine, fFlags)); 572 memset(&ProcInfo, 0, sizeof(ProcInfo)); 573 AcquireSRWLockShared(&g_RWLock); 574 575 fRet = CreateProcessW((WCHAR *)pwszImageName, (WCHAR *)pwszCommandLine, NULL /*pProcSecAttr*/, NULL /*pThreadSecAttr*/, 576 FALSE /*fInheritHandles*/, fFlags, (WCHAR *)pwszzEnvironment, NULL /*pwsz*/, &StartupInfo, &ProcInfo); 577 dwErr = GetLastError(); 578 579 ReleaseSRWLockShared(&g_RWLock); 580 if (fRet) 581 pChild->u.Process.hProcess = ProcInfo.hProcess; 582 else 583 { 584 fprintf(stderr, "CreateProcess(%ls) failed: %u\n", pwszImageName, dwErr); 585 return pChild->iExitCode = (int)dwErr; 586 } 587 588 /* 589 * If the child is suspended, we've got some adjustment work to be done. 590 */ 591 dwErr = ERROR_SUCCESS; 592 if (fFlags & CREATE_SUSPENDED) 593 { 594 /* 595 * First do handle inhertiance as that's the most complicated. 596 */ 597 if (fHaveHandles) 598 { 599 /* 600 * Get the PEB address and figure out the child process bit count. 601 */ 602 ULONG cbActual1 = 0; 603 PROCESS_BASIC_INFORMATION BasicInfo = { 0, 0, }; 604 NTSTATUS rcNt = g_pfnNtQueryInformationProcess(ProcInfo.hProcess, ProcessBasicInformation, 605 &BasicInfo, sizeof(BasicInfo), &cbActual1); 606 if (NT_SUCCESS(rcNt)) 607 { 608 /* 609 * Read the user process parameter pointer from the PEB. 610 * 611 * Note! Seems WOW64 processes starts out with a 64-bit PEB and 612 * process parameter block. 613 */ 614 BOOL const f32BitPeb = !g_f64BitHost; 615 ULONG const cbChildPtr = f32BitPeb ? 4 : 8; 616 PVOID pvSrcInPeb = (char *)BasicInfo.PebBaseAddress + (f32BitPeb ? 0x10 : 0x20); 617 char * pbDst = 0; 618 SIZE_T cbActual2 = 0; 619 if (ReadProcessMemory(ProcInfo.hProcess, pvSrcInPeb, &pbDst, cbChildPtr, &cbActual2)) 620 { 621 /* 622 * Duplicate the handles into the child. 623 */ 624 union 625 { 626 ULONGLONG au64Bit[2]; 627 ULONG au32Bit[2]; 628 } WriteBuf; 629 ULONG idx = 0; 630 HANDLE hChildStdOut = INVALID_HANDLE_VALUE; 631 HANDLE hChildStdErr = INVALID_HANDLE_VALUE; 632 633 pbDst += (f32BitPeb ? 0x1c : 0x28); 634 if (pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE) 635 { 636 if (DuplicateHandle(GetCurrentProcess(), pChild->u.Process.hStdOut, ProcInfo.hProcess, 637 &hChildStdOut, 0, TRUE /*fInheritable*/, DUPLICATE_SAME_ACCESS)) 638 { 639 if (f32BitPeb) 640 WriteBuf.au32Bit[idx++] = (DWORD)(uintptr_t)hChildStdOut; 641 else 642 WriteBuf.au64Bit[idx++] = (uintptr_t)hChildStdOut; 643 } 644 else 645 { 646 dwErr = GetLastError(); 647 fprintf(stderr, "Failed to duplicate %p (stdout) into the child: %u\n", 648 pChild->u.Process.hStdOut, dwErr); 649 } 650 } 651 else 652 pbDst += cbChildPtr; 653 654 if (pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE) 655 { 656 if (DuplicateHandle(GetCurrentProcess(), pChild->u.Process.hStdErr, ProcInfo.hProcess, 657 &hChildStdErr, 0, TRUE /*fInheritable*/, DUPLICATE_SAME_ACCESS)) 658 { 659 if (f32BitPeb) 660 WriteBuf.au32Bit[idx++] = (DWORD)(uintptr_t)hChildStdOut; 661 else 662 WriteBuf.au64Bit[idx++] = (uintptr_t)hChildStdOut; 663 } 664 else 665 { 666 dwErr = GetLastError(); 667 fprintf(stderr, "Failed to duplicate %p (stderr) into the child: %u\n", 668 pChild->u.Process.hStdOut, dwErr); 669 } 670 } 671 672 /* 673 * Finally write the handle values into the child. 674 */ 675 if ( idx > 0 676 && !WriteProcessMemory(ProcInfo.hProcess, pbDst, &WriteBuf, idx * cbChildPtr, &cbActual2)) 677 { 678 dwErr = GetLastError(); 679 fprintf(stderr, "Failed to write %p LB %u into child: %u\n", pbDst, idx * cbChildPtr, dwErr); 680 } 681 } 682 else 683 { 684 dwErr = GetLastError(); 685 fprintf(stderr, "Failed to read %p LB %u from the child: %u\n", pvSrcInPeb, cbChildPtr, dwErr); 686 } 687 } 688 else 689 { 690 fprintf(stderr, "NtQueryInformationProcess failed on child: %#x\n", rcNt); 691 dwErr = (DWORD)rcNt; 692 } 693 } 694 695 /* 696 * Assign processor group (ignore failure). 697 */ 698 if (g_cProcessorGroups > 1) 699 { 700 GROUP_AFFINITY Affinity = { ~(ULONG_PTR)0, pWorker->iProcessorGroup, { 0, 0, 0 } }; 701 fRet = g_pfnSetThreadGroupAffinity(ProcInfo.hThread, &Affinity, NULL); 702 assert(fRet); 703 } 704 705 #ifdef KMK 706 /* 707 * Set priority (ignore failure). 708 */ 709 switch (process_priority) 710 { 711 case 1: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_IDLE); break; 712 case 2: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_BELOW_NORMAL); break; 713 case 3: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_NORMAL); break; 714 case 4: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_HIGHEST); break; 715 case 5: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_TIME_CRITICAL); break; 716 default: fRet = TRUE; 717 } 718 assert(fRet); 719 #endif 720 721 /* 722 * Resume the thread if the adjustments succeeded, otherwise kill it. 723 */ 724 if (dwErr == ERROR_SUCCESS) 725 { 726 fRet = ResumeThread(ProcInfo.hThread); 727 assert(fRet); 728 if (!fRet) 729 { 730 dwErr = GetLastError(); 731 fprintf(stderr, "ResumeThread failed on child process: %u\n", dwErr); 732 } 733 } 734 if (dwErr != ERROR_SUCCESS) 735 TerminateProcess(ProcInfo.hProcess, dwErr); 736 } 737 738 /* 739 * Close unnecessary handles. 740 */ 741 if ( pChild->u.Process.fCloseStdOut 742 && pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE) 743 { 744 CloseHandle(pChild->u.Process.hStdOut); 745 pChild->u.Process.hStdOut = INVALID_HANDLE_VALUE; 746 pChild->u.Process.fCloseStdOut = FALSE; 747 } 748 if ( pChild->u.Process.fCloseStdErr 749 && pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE) 750 { 751 CloseHandle(pChild->u.Process.hStdErr); 752 pChild->u.Process.hStdErr = INVALID_HANDLE_VALUE; 753 pChild->u.Process.fCloseStdErr = FALSE; 754 } 755 756 CloseHandle(ProcInfo.hThread); 757 return 0; 758 } 759 467 760 468 761 #define MKWCCWCMD_F_CYGWIN_SHELL 1 … … 1215 1508 static void mkWinChildcareWorkerThreadHandleProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild) 1216 1509 { 1217 PROCESS_INFORMATION ProcInfo;1218 STARTUPINFOW StartupInfo;1219 1510 WCHAR const *pwszPath = NULL; 1220 1511 WCHAR *pwszzEnvironment = NULL; … … 1222 1513 WCHAR *pwszImageName = NULL; 1223 1514 BOOL fNeedShell = FALSE; 1224 DWORD fFlags = CREATE_UNICODE_ENVIRONMENT;1225 BOOL fRet;1226 1515 int rc; 1227 #ifdef KMK1228 extern int process_priority;1229 #endif1230 1516 1231 1517 /* … … 1249 1535 else 1250 1536 rc = mkWinChildcareWorkerConvertCommandlineWithShell(pwszImageName, pChild->u.Process.papszArgs, &pwszCommandLine); 1251 }1252 if (rc == 0)1253 {1254 AcquireSRWLockShared(&g_RWLock); /* temporary */1255 1537 1256 1538 /* 1257 * Populate startup info.1539 * Create the child process. 1258 1540 */ 1259 memset(&StartupInfo, 0, sizeof(StartupInfo)); 1260 StartupInfo.cb = sizeof(StartupInfo); 1261 GetStartupInfoW(&StartupInfo); 1262 if ( pChild->u.Process.hStdErr == INVALID_HANDLE_VALUE 1263 && pChild->u.Process.hStdOut == INVALID_HANDLE_VALUE) 1264 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES; 1265 else 1266 { 1267 StartupInfo.dwFlags |= STARTF_USESTDHANDLES; 1268 StartupInfo.hStdOutput = pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE 1269 ? pChild->u.Process.hStdOut : GetStdHandle(STD_OUTPUT_HANDLE); 1270 StartupInfo.hStdError = pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE 1271 ? pChild->u.Process.hStdErr : GetStdHandle(STD_ERROR_HANDLE); 1272 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); 1273 } 1274 1275 /* 1276 * Flags. 1277 */ 1278 #ifdef KMK 1279 switch (process_priority) 1280 { 1281 case 1: fFlags |= CREATE_SUSPENDED | IDLE_PRIORITY_CLASS; break; 1282 case 2: fFlags |= CREATE_SUSPENDED | BELOW_NORMAL_PRIORITY_CLASS; break; 1283 case 3: fFlags |= CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS; break; 1284 case 4: fFlags |= CREATE_SUSPENDED | HIGH_PRIORITY_CLASS; break; 1285 case 5: fFlags |= CREATE_SUSPENDED | REALTIME_PRIORITY_CLASS; break; 1286 } 1287 #endif 1288 if (g_cProcessorGroups > 1) 1289 fFlags |= CREATE_SUSPENDED; 1290 1291 /* 1292 * Try create the process. 1293 */ 1294 DB(DB_JOBS, ("CreateProcessW(%ls, %ls,,, TRUE, %#x...)\n", pwszImageName, pwszCommandLine, fFlags)); 1295 memset(&ProcInfo, 0, sizeof(ProcInfo)); 1296 fRet = CreateProcessW(pwszImageName, pwszCommandLine, NULL /*pProcSecAttr*/, NULL /*pThreadSecAttr*/, 1297 TRUE /*fInheritHandles*/, fFlags, pwszzEnvironment, NULL /*pwsz*/, &StartupInfo, &ProcInfo); 1298 rc = GetLastError(); 1299 ReleaseSRWLockShared(&g_RWLock); /* temporary */ 1300 if (fRet) 1301 { 1302 /* 1303 * Make thread priority and affinity changes if necessary. 1304 */ 1305 if (fFlags & CREATE_SUSPENDED) 1306 { 1307 BOOL fRet; 1308 if (g_cProcessorGroups > 1) 1309 { 1310 GROUP_AFFINITY Affinity = { ~(ULONG_PTR)0, pWorker->iProcessorGroup, { 0, 0, 0 } }; 1311 fRet = g_pfnSetThreadGroupAffinity(ProcInfo.hThread, &Affinity, NULL); 1312 assert(fRet); 1313 } 1314 #ifdef KMK 1315 switch (process_priority) 1316 { 1317 case 1: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_IDLE); break; 1318 case 2: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_BELOW_NORMAL); break; 1319 case 3: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_NORMAL); break; 1320 case 4: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_HIGHEST); break; 1321 case 5: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_TIME_CRITICAL); break; 1322 default: fRet = TRUE; 1323 } 1324 assert(fRet); 1325 #endif 1326 fRet = ResumeThread(ProcInfo.hThread); 1327 assert(fRet); 1328 (void)fRet; 1329 } 1330 1331 /* 1332 * Close unncessary handles. 1333 */ 1334 CloseHandle(ProcInfo.hThread); 1335 pChild->u.Process.hProcess = ProcInfo.hProcess; 1336 1337 if ( pChild->u.Process.fCloseStdOut 1338 && pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE) 1339 { 1340 CloseHandle(pChild->u.Process.hStdOut); 1341 pChild->u.Process.hStdOut = INVALID_HANDLE_VALUE; 1342 pChild->u.Process.fCloseStdOut = FALSE; 1343 } 1344 if ( pChild->u.Process.fCloseStdErr 1345 && pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE) 1346 { 1347 CloseHandle(pChild->u.Process.hStdErr); 1348 pChild->u.Process.hStdErr = INVALID_HANDLE_VALUE; 1349 pChild->u.Process.fCloseStdErr = FALSE; 1350 } 1351 1352 /* 1353 * Wait for the child to complete. 1354 */ 1355 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, ProcInfo.hProcess); 1541 if (rc == 0) 1542 { 1543 rc = mkWinChildcareWorkerCreateProcess(pWorker, pChild, pwszImageName, pwszCommandLine, pwszzEnvironment); 1544 if (rc == 0) 1545 { 1546 /* 1547 * Wait for the child to complete. 1548 */ 1549 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Process.hProcess); 1550 } 1551 else 1552 pChild->iExitCode = rc; 1356 1553 } 1357 1554 else … … 1513 1710 pChild->pNext = pTailExpect; 1514 1711 pTailActual = _InterlockedCompareExchangePointer(&g_pTailCompletedChildren, pChild, pTailExpect); 1515 if (pTailActual == pTailExpect) 1712 if (pTailActual != pTailExpect) 1713 pTailExpect = pTailActual; 1714 else 1516 1715 { 1517 1716 _InterlockedDecrement(&g_cPendingChildren); … … 2205 2404 } 2206 2405 2207 /** Temporary serialization with kmkbuiltin_redirect. */2406 /** Serialization with kmkbuiltin_redirect. */ 2208 2407 void MkWinChildExclusiveAcquire(void) 2209 2408 { … … 2211 2410 } 2212 2411 2213 /** Temporary serialization with kmkbuiltin_redirect. */2412 /** Serialization with kmkbuiltin_redirect. */ 2214 2413 void MkWinChildExclusiveRelease(void) 2215 2414 {
Note:
See TracChangeset
for help on using the changeset viewer.