VirtualBox

source: kBuild/trunk/src/kmk/w32/winchildren.c@ 3162

Last change on this file since 3162 was 3162, checked in by bird, 7 years ago

kmk/win: Pretty sure I made this lpReserved2 mistake before. Duh.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 89.1 KB
Line 
1/* $Id: winchildren.c 3162 2018-03-20 03:13:12Z bird $ */
2/** @file
3 * Child process creation and management for kmk.
4 */
5
6/*
7 * Copyright (c) 2018 knut st. osmundsen <[email protected]>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/* No GNU coding style here atm, convert if upstreamed. */
27
28/** @page pg_win_children Windows child process creation and managment
29 *
30 * This new implementation aims at addressing the following:
31 *
32 * 1. Speed up process creation by doing the expensive CreateProcess call
33 * in a worker thread.
34 *
35 * 2. No 64 process limit imposed by WaitForMultipleObjects.
36 *
37 * 3. Better distribute jobs among processor groups.
38 *
39 * 4. Offloading more expensive kmkbuiltin operations to worker threads,
40 * making the main thread focus on managing child processes.
41 *
42 * 5. Output synchronization using reusable pipes [not yet implemented].
43 *
44 *
45 * To be quite honest, the first item (CreateProcess expense) didn't occur to me
46 * at first and was more of a sideeffect discovered along the way. A test
47 * rebuilding IPRT went from 4m52s to 3m19s on a 8 thread system.
48 *
49 * The 2nd and 3rd goals are related to newer build servers that have lots of
50 * CPU threads and various Windows NT (aka NT OS/2 at the time) design choices
51 * made in the late 1980ies.
52 *
53 * WaitForMultipleObjects does not support waiting for more than 64 objects,
54 * unlike poll and select. This is just something everyone ends up having to
55 * work around in the end.
56 *
57 * Affinity masks are uintptr_t sized, so 64-bit hosts can only manage 64
58 * processors and 32-bit only 32. Workaround was introduced with Windows 7
59 * (IIRC) and is called processor groups. The CPU threads are grouped into 1 or
60 * more groups of up to 64 processors. Processes are generally scheduled to a
61 * signle processor group at first, but threads may be changed to be scheduled
62 * on different groups. This code will try distribute children evenly among the
63 * processor groups, using a very simple algorithm (see details in code).
64 *
65 */
66
67
68/*********************************************************************************************************************************
69* Header Files *
70*********************************************************************************************************************************/
71#include "../makeint.h"
72#include "../job.h"
73#include "../debug.h"
74#include "../kmkbuiltin.h"
75#include "winchildren.h"
76
77#include <Windows.h>
78#include <Winternl.h>
79#include <assert.h>
80#include <process.h>
81
82
83/*********************************************************************************************************************************
84* Defined Constants And Macros *
85*********************************************************************************************************************************/
86#define MKWINCHILD_MAX_PATH 1024
87
88/** Checks the UTF-16 environment variable pointed to is the PATH. */
89#define IS_PATH_ENV_VAR(a_cwcVar, a_pwszVar) \
90 ( (a_cwcVar) >= 5 \
91 && (a_pwszVar)[4] == L'=' \
92 && ((a_pwszVar)[0] == L'P' || (a_pwszVar)[0] == L'p') \
93 && ((a_pwszVar)[1] == L'A' || (a_pwszVar)[1] == L'a') \
94 && ((a_pwszVar)[2] == L'T' || (a_pwszVar)[2] == L't') \
95 && ((a_pwszVar)[3] == L'H' || (a_pwszVar)[3] == L'h') )
96
97
98/*********************************************************************************************************************************
99* Structures and Typedefs *
100*********************************************************************************************************************************/
101/**
102 * Child process type.
103 */
104typedef enum WINCHILDTYPE
105{
106 WINCHILDTYPE_INVALID = 0,
107 /** Normal child process. */
108 WINCHILDTYPE_PROCESS,
109#ifdef KMK
110 /** kmkbuiltin command. */
111 WINCHILDTYPE_BUILTIN,
112 /** kSubmit job. */
113 WINCHILDTYPE_SUBMIT,
114 /** kmk_redirect job. */
115 WINCHILDTYPE_REDIRECT,
116#endif
117 /** End of valid child types. */
118 WINCHILDTYPE_END
119} WINCHILDTYPE;
120
121
122/** Pointer to a windows child process. */
123typedef struct WINCHILD *PWINCHILD;
124/**
125 * Windows child process.
126 */
127typedef struct WINCHILD
128{
129 /** Magic / eyecatcher (WINCHILD_MAGIC). */
130 ULONG uMagic;
131 /** Child type. */
132 WINCHILDTYPE enmType;
133 /** Pointer to the next child process. */
134 PWINCHILD pNext;
135 /** The pid for this child. */
136 pid_t pid;
137 /** The make child structure associated with this child. */
138 struct child *pMkChild;
139
140 /** The process exit code. */
141 int iExitCode;
142 /** Kill signal, in case we or someone else killed it. */
143 int iSignal;
144 /** Set if core was dumped. */
145 int fCoreDumped;
146
147 /** Type specific data. */
148 union
149 {
150 /** Data for WINCHILDTYPE_PROCESS. */
151 struct
152 {
153 /** Argument vector (single allocation, strings following array). */
154 char **papszArgs;
155 /** Length of the argument strings. */
156 size_t cbArgsStrings;
157 /** Environment vector. Only a copy if fEnvIsCopy is set. */
158 char **papszEnv;
159 /** If we made a copy of the environment, this is the size of the
160 * strings and terminator string (not in array). This is done to
161 * speed up conversion, since MultiByteToWideChar can handle '\0'. */
162 size_t cbEnvStrings;
163 /** The make shell to use (copy). */
164 char *pszShell;
165 /** Handle to use for standard out. */
166 HANDLE hStdOut;
167 /** Handle to use for standard out. */
168 HANDLE hStdErr;
169 /** Whether to close hStdOut after creating the process. */
170 BOOL fCloseStdOut;
171 /** Whether to close hStdErr after creating the process. */
172 BOOL fCloseStdErr;
173
174 /** Child process handle. */
175 HANDLE hProcess;
176 } Process;
177
178 /** Data for WINCHILDTYPE_SUBMIT. */
179 struct
180 {
181 /** The event we're to wait on (hooked up to a pipe) */
182 HANDLE hEvent;
183 /** Parameter for the cleanup callback. */
184 void *pvSubmitWorker;
185 } Submit;
186
187 /** Data for WINCHILDTYPE_REDIRECT. */
188 struct
189 {
190 /** Child process handle. */
191 HANDLE hProcess;
192 } Redirect;
193 } u;
194
195} WINCHILD;
196/** WINCHILD::uMagic value. */
197#define WINCHILD_MAGIC 0xbabebabeU
198
199
200/**
201 * Data for a windows childcare worker thread.
202 *
203 * We use one worker thread per child, reusing the threads when possible.
204 *
205 * This setup helps avoid the 64-bit handle with the WaitForMultipleObject API.
206 *
207 * It also helps using all CPUs on systems with more than one CPU group
208 * (typically systems with more than 64 CPU threads or/and multiple sockets, or
209 * special configs).
210 *
211 * This helps facilitates using pipes for collecting output child rather
212 * than temporary files. Pipes doesn't involve NTFS and can easily be reused.
213 *
214 * Finally, kBuild specific, this allows running kmkbuiltin_xxxx commands in
215 * threads.
216 */
217typedef struct WINCHILDCAREWORKER
218{
219 /** Magic / eyecatcher (WINCHILDCAREWORKER_MAGIC). */
220 ULONG uMagic;
221 /** The processor group for this worker. */
222 unsigned int iProcessorGroup;
223 /** The thread ID. */
224 unsigned int tid;
225 /** The thread handle. */
226 HANDLE hThread;
227 /** The event the thread is idling on. */
228 HANDLE hEvtIdle;
229 /** Pointer to the current child. */
230 PWINCHILD volatile pCurChild;
231 /** List of children pending execution on this worker.
232 * This is updated atomitically just like g_pTailCompletedChildren. */
233 PWINCHILD volatile pTailTodoChildren;
234 /** TRUE if idle, FALSE if not. */
235 long volatile fIdle;
236} WINCHILDCAREWORKER;
237/** Pointer to a childcare worker thread. */
238typedef WINCHILDCAREWORKER *PWINCHILDCAREWORKER;
239/** WINCHILD::uMagic value. */
240#define WINCHILDCAREWORKER_MAGIC 0xdad0dad0U
241
242
243/*********************************************************************************************************************************
244* Global Variables *
245*********************************************************************************************************************************/
246/** Whether it's initialized or not. */
247static BOOL g_fInitialized = FALSE;
248/** Set when we're shutting down everything. */
249static BOOL volatile g_fShutdown = FALSE;
250/** Event used to wait for children. */
251static HANDLE g_hEvtWaitChildren = INVALID_HANDLE_VALUE;
252/** Number of childcare workers currently in g_papChildCareworkers. */
253static unsigned g_cChildCareworkers = 0;
254/** Maximum number of childcare workers in g_papChildCareworkers. */
255static unsigned g_cChildCareworkersMax = 0;
256/** Pointer to childcare workers. */
257static PWINCHILDCAREWORKER *g_papChildCareworkers = NULL;
258/** The group index for the worker allocator.
259 * This is ever increasing and must be modded by g_cProcessorGroups. */
260static unsigned g_idxProcessorGroupAllocator = 0;
261/** The processor in group index for the worker allocator. */
262static unsigned g_idxProcessorInGroupAllocator = 0;
263/** Number of processor groups in the system. */
264static unsigned g_cProcessorGroups = 1;
265/** Array detailing how many active processors there are in each group. */
266static unsigned const *g_pacProcessorsInGroup = &g_cProcessorGroups;
267/** Kernel32!GetActiveProcessorGroupCount */
268static WORD (WINAPI *g_pfnGetActiveProcessorGroupCount)(VOID);
269/** Kernel32!GetActiveProcessorCount */
270static DWORD (WINAPI *g_pfnGetActiveProcessorCount)(WORD);
271/** Kernel32!SetThreadGroupAffinity */
272static BOOL (WINAPI *g_pfnSetThreadGroupAffinity)(HANDLE, CONST GROUP_AFFINITY *, GROUP_AFFINITY *);
273/** NTDLL!NtQueryInformationProcess */
274static NTSTATUS (NTAPI *g_pfnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
275/** Set if the windows host is 64-bit. */
276static BOOL g_f64BitHost = (K_ARCH_BITS == 64);
277/** Windows version info.
278 * @note Putting this before the volatile stuff, hoping to keep it in a
279 * different cache line than the static bits above. */
280static OSVERSIONINFOA g_VersionInfo = { sizeof(g_VersionInfo), 4, 0, 1381, VER_PLATFORM_WIN32_NT, {0} };
281
282/** Children that has been completed.
283 * This is updated atomically, pushing completed children in LIFO fashion
284 * (thus 'tail'), then hitting g_hEvtWaitChildren if head. */
285static PWINCHILD volatile g_pTailCompletedChildren = NULL;
286
287/** Number of idle pending children.
288 * This is updated before g_hEvtWaitChildren is signalled. */
289static unsigned volatile g_cPendingChildren = 0;
290
291/** Number of idle childcare worker threads. */
292static unsigned volatile g_cIdleChildcareWorkers = 0;
293/** Index of the last idle child careworker (just a hint). */
294static unsigned volatile g_idxLastChildcareWorker = 0;
295
296/** RW lock for serializing kmkbuiltin_redirect and CreateProcess. */
297static SRWLOCK g_RWLock;
298
299
300
301/**
302 * Initializes the windows child module.
303 *
304 * @param cJobSlots The number of job slots.
305 */
306void MkWinChildInit(unsigned int cJobSlots)
307{
308 HMODULE hmod;
309
310 /*
311 * Figure out how many childcare workers first.
312 */
313 static unsigned int const s_cMaxWorkers = 4096;
314 unsigned cWorkers;
315 if (cJobSlots >= 1 && cJobSlots < s_cMaxWorkers)
316 cWorkers = cJobSlots;
317 else
318 cWorkers = s_cMaxWorkers;
319
320 /*
321 * Allocate the array and the child completed event object.
322 */
323 g_papChildCareworkers = (PWINCHILDCAREWORKER *)xcalloc(cWorkers * sizeof(g_papChildCareworkers[0]));
324 g_cChildCareworkersMax = cWorkers;
325
326 g_hEvtWaitChildren = CreateEvent(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pszName*/);
327 if (!g_hEvtWaitChildren)
328 fatal(NILF, INTSTR_LENGTH, _("MkWinChildInit: CreateEvent failed: %u"), GetLastError());
329
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 /*
351 * Figure out how many processor groups there are.
352 * For that we need to first figure the windows version.
353 */
354 if (!GetVersionExA(&g_VersionInfo))
355 {
356 DWORD uRawVer = GetVersion();
357 g_VersionInfo.dwMajorVersion = uRawVer & 0xff;
358 g_VersionInfo.dwMinorVersion = (uRawVer >> 8) & 0xff;
359 g_VersionInfo.dwBuildNumber = (uRawVer >> 16) & 0x7fff;
360 }
361 if (g_VersionInfo.dwMajorVersion >= 6)
362 {
363 hmod = GetModuleHandleA("KERNEL32.DLL");
364 *(FARPROC *)&g_pfnGetActiveProcessorGroupCount = GetProcAddress(hmod, "GetActiveProcessorGroupCount");
365 *(FARPROC *)&g_pfnGetActiveProcessorCount = GetProcAddress(hmod, "GetActiveProcessorCount");
366 *(FARPROC *)&g_pfnSetThreadGroupAffinity = GetProcAddress(hmod, "SetThreadGroupAffinity");
367 if ( g_pfnSetThreadGroupAffinity
368 && g_pfnGetActiveProcessorCount
369 && g_pfnGetActiveProcessorGroupCount)
370 {
371 unsigned int *pacProcessorsInGroup;
372 unsigned iGroup;
373 g_cProcessorGroups = g_pfnGetActiveProcessorGroupCount();
374 if (g_cProcessorGroups == 0)
375 g_cProcessorGroups = 1;
376
377 pacProcessorsInGroup = (unsigned int *)xmalloc(sizeof(g_pacProcessorsInGroup[0]) * g_cProcessorGroups);
378 g_pacProcessorsInGroup = pacProcessorsInGroup;
379 for (iGroup = 0; iGroup < g_cProcessorGroups; iGroup++)
380 pacProcessorsInGroup[iGroup] = g_pfnGetActiveProcessorCount(iGroup);
381
382 /* We shift the starting group with the make nesting level as part of
383 our very simple distribution strategy. */
384 g_idxProcessorGroupAllocator = makelevel;
385 }
386 else
387 {
388 g_pfnSetThreadGroupAffinity = NULL;
389 g_pfnGetActiveProcessorCount = NULL;
390 g_pfnGetActiveProcessorGroupCount = NULL;
391 }
392 }
393
394 /*
395 * For serializing with standard file handle manipulation (kmkbuiltin_redirect).
396 */
397 InitializeSRWLock(&g_RWLock);
398
399 /*
400 * This is dead code that was thought to fix a problem observed doing
401 * `tcc.exe /c "kmk |& tee bld.log"` and leading to a crash in cl.exe
402 * when spawned with fInheritHandles = FALSE, see hStdErr=NULL in the
403 * child. However, it turns out this was probably caused by not clearing
404 * the CRT file descriptor and handle table in the startup info.
405 * Leaving the code here in case it comes in handy after all.
406 */
407#if 0
408 {
409 struct
410 {
411 DWORD uStdHandle;
412 HANDLE hHandle;
413 } aHandles[3] = { { STD_INPUT_HANDLE, NULL }, { STD_OUTPUT_HANDLE, NULL }, { STD_ERROR_HANDLE, NULL } };
414 int i;
415
416 for (i = 0; i < 3; i++)
417 aHandles[i].hHandle = GetStdHandle(aHandles[i].uStdHandle);
418
419 for (i = 0; i < 3; i++)
420 if ( aHandles[i].hHandle == NULL
421 || aHandles[i].hHandle == INVALID_HANDLE_VALUE)
422 {
423 int fd = open("nul", _O_RDWR);
424 if (fd >= 0)
425 {
426 if (_dup2(fd, i) >= 0)
427 {
428 assert((HANDLE)_get_osfhandle(i) != aHandles[i].hHandle);
429 assert((HANDLE)_get_osfhandle(i) == GetStdHandle(aHandles[i].uStdHandle));
430 }
431 else
432 ONNNS(fatal, NILF, "_dup2(%d('nul'), %d) failed: %u (%s)", fd, i, errno, strerror(errno));
433 if (fd != i)
434 close(fd);
435 }
436 else
437 ONNS(fatal, NILF, "open(nul,RW) failed: %u (%s)", i, errno, strerror(errno));
438 }
439 else
440 {
441 int j;
442 for (j = i + 1; j < 3; j++)
443 if (aHandles[j].hHandle == aHandles[i].hHandle)
444 {
445 int fd = _dup(j);
446 if (fd >= 0)
447 {
448 if (_dup2(fd, j) >= 0)
449 {
450 aHandles[j].hHandle = (HANDLE)_get_osfhandle(j);
451 assert(aHandles[j].hHandle != aHandles[i].hHandle);
452 assert(aHandles[j].hHandle == GetStdHandle(aHandles[j].uStdHandle));
453 }
454 else
455 ONNNS(fatal, NILF, "_dup2(%d, %d) failed: %u (%s)", fd, j, errno, strerror(errno));
456 if (fd != j)
457 close(fd);
458 }
459 else
460 ONNS(fatal, NILF, "_dup(%d) failed: %u (%s)", j, errno, strerror(errno));
461 }
462 }
463 }
464#endif
465}
466
467/**
468 * Used by mkWinChildcareWorkerThread() and MkWinChildWait() to get the head
469 * child from a lifo (g_pTailCompletedChildren, pTailTodoChildren).
470 *
471 * @returns Head child.
472 * @param ppTail Pointer to the child variable.
473 * @param pChild Tail child.
474 */
475static PWINCHILD mkWinChildDequeFromLifo(PWINCHILD volatile *ppTail, PWINCHILD pChild)
476{
477 if (pChild->pNext)
478 {
479 PWINCHILD pPrev;
480 do
481 {
482 pPrev = pChild;
483 pChild = pChild->pNext;
484 } while (pChild->pNext);
485 pPrev->pNext = NULL;
486 }
487 else
488 {
489 PWINCHILD const pWantedChild = pChild;
490 pChild = _InterlockedCompareExchangePointer(ppTail, NULL, pWantedChild);
491 if (pChild != pWantedChild)
492 {
493 PWINCHILD pPrev;
494 do
495 {
496 pPrev = pChild;
497 pChild = pChild->pNext;
498 } while (pChild->pNext);
499 pPrev->pNext = NULL;
500 assert(pChild == pWantedChild);
501 }
502 }
503 return pChild;
504}
505
506/**
507 * Duplicates the given UTF-16 string.
508 *
509 * @returns 0
510 * @param pwszSrc The UTF-16 string to duplicate.
511 * @param cwcSrc Length, may include the terminator.
512 * @param ppwszDst Where to return the duplicate.
513 */
514static int mkWinChildDuplicateUtf16String(const WCHAR *pwszSrc, size_t cwcSrc, WCHAR **ppwszDst)
515{
516 size_t cb = sizeof(WCHAR) * cwcSrc;
517 if (cwcSrc > 0 && pwszSrc[cwcSrc - 1] == L'\0')
518 *ppwszDst = (WCHAR *)memcpy(xmalloc(cb), pwszSrc, cb);
519 else
520 {
521 WCHAR *pwszDst = (WCHAR *)xmalloc(cb + sizeof(WCHAR));
522 memcpy(pwszDst, pwszSrc, cb);
523 pwszDst[cwcSrc] = L'\0';
524 *ppwszDst = pwszDst;
525 }
526 return 0;
527}
528
529/**
530 * Commmon worker for waiting on a child process and retrieving the exit code.
531 *
532 * @param pWorker The worker.
533 * @param pChild The child.
534 * @param hProcess The process handle.
535 */
536static void mkWinChildcareWorkerWaitForProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild, HANDLE hProcess)
537{
538 for (;;)
539 {
540 DWORD dwExitCode = -42;
541 DWORD dwStatus = WaitForSingleObject(hProcess, INFINITE);
542 assert(dwStatus != WAIT_FAILED);
543 if (dwStatus == WAIT_OBJECT_0)
544 {
545 DWORD dwExitCode = -42;
546 if (GetExitCodeProcess(hProcess, &dwExitCode))
547 {
548 pChild->iExitCode = (int)dwExitCode;
549 return;
550 }
551 }
552 else if ( dwStatus == WAIT_IO_COMPLETION
553 || dwStatus == WAIT_TIMEOUT /* whatever */)
554 continue; /* however unlikely, these aren't fatal. */
555
556 /* Something failed. */
557 pChild->iExitCode = GetLastError();
558 if (pChild->iExitCode == 0)
559 pChild->iExitCode = -4242;
560 return;
561 }
562}
563
564
565/**
566 * Does the actual process creation given.
567 *
568 * @returns 0 if there is anything to wait on, otherwise non-zero windows error.
569 * @param pWorker The childcare worker.
570 * @param pChild The child.
571 * @param pwszImageName The image path.
572 * @param pwszCommandLine The command line.
573 * @param pwszzEnvironment The enviornment block.
574 */
575static int mkWinChildcareWorkerCreateProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild, WCHAR const *pwszImageName,
576 WCHAR const *pwszCommandLine, WCHAR const *pwszzEnvironment)
577{
578 PROCESS_INFORMATION ProcInfo;
579 STARTUPINFOW StartupInfo;
580 DWORD fFlags = CREATE_UNICODE_ENVIRONMENT;
581 BOOL const fHaveHandles = pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE
582 || pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE;
583 BOOL fRet;
584 DWORD dwErr;
585#ifdef KMK
586 extern int process_priority;
587#endif
588
589 /*
590 * Populate startup info.
591 *
592 * Turns out we can get away without passing TRUE for the inherit handles
593 * parameter to CreateProcess when we're not using STARTF_USESTDHANDLES.
594 * At least on NT, which is all worth caring about at this point + context IMO.
595 *
596 * Not inherting the handles is a good thing because it means we won't
597 * accidentally end up with a pipe handle or such intended for a different
598 * child process, potentially causing the EOF/HUP event to be delayed.
599 *
600 * Since the present handle inhertiance requirements only involves standard
601 * output and error, we'll never set the inherit handles flag and instead
602 * do manual handle duplication and planting.
603 */
604 memset(&StartupInfo, 0, sizeof(StartupInfo));
605 StartupInfo.cb = sizeof(StartupInfo);
606 GetStartupInfoW(&StartupInfo);
607 StartupInfo.lpReserved2 = 0; /* No CRT file handle + descriptor info possible, sorry. */
608 StartupInfo.cbReserved2 = 0;
609 if (!fHaveHandles)
610 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
611 else
612 {
613 fFlags |= CREATE_SUSPENDED;
614 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
615 }
616
617 /*
618 * Flags.
619 */
620#ifdef KMK
621 switch (process_priority)
622 {
623 case 1: fFlags |= CREATE_SUSPENDED | IDLE_PRIORITY_CLASS; break;
624 case 2: fFlags |= CREATE_SUSPENDED | BELOW_NORMAL_PRIORITY_CLASS; break;
625 case 3: fFlags |= CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS; break;
626 case 4: fFlags |= CREATE_SUSPENDED | HIGH_PRIORITY_CLASS; break;
627 case 5: fFlags |= CREATE_SUSPENDED | REALTIME_PRIORITY_CLASS; break;
628 }
629#endif
630 if (g_cProcessorGroups > 1)
631 fFlags |= CREATE_SUSPENDED;
632
633 /*
634 * Try create the process.
635 */
636 DB(DB_JOBS, ("CreateProcessW(%ls, %ls,,, TRUE, %#x...)\n", pwszImageName, pwszCommandLine, fFlags));
637 memset(&ProcInfo, 0, sizeof(ProcInfo));
638 AcquireSRWLockShared(&g_RWLock);
639
640 fRet = CreateProcessW((WCHAR *)pwszImageName, (WCHAR *)pwszCommandLine, NULL /*pProcSecAttr*/, NULL /*pThreadSecAttr*/,
641 FALSE /*fInheritHandles*/, fFlags, (WCHAR *)pwszzEnvironment, NULL /*pwsz*/, &StartupInfo, &ProcInfo);
642 dwErr = GetLastError();
643
644 ReleaseSRWLockShared(&g_RWLock);
645 if (fRet)
646 pChild->u.Process.hProcess = ProcInfo.hProcess;
647 else
648 {
649 fprintf(stderr, "CreateProcess(%ls) failed: %u\n", pwszImageName, dwErr);
650 return pChild->iExitCode = (int)dwErr;
651 }
652
653 /*
654 * If the child is suspended, we've got some adjustment work to be done.
655 */
656 dwErr = ERROR_SUCCESS;
657 if (fFlags & CREATE_SUSPENDED)
658 {
659 /*
660 * First do handle inhertiance as that's the most complicated.
661 */
662 if (fHaveHandles)
663 {
664 /*
665 * Get the PEB address and figure out the child process bit count.
666 */
667 ULONG cbActual1 = 0;
668 PROCESS_BASIC_INFORMATION BasicInfo = { 0, 0, };
669 NTSTATUS rcNt = g_pfnNtQueryInformationProcess(ProcInfo.hProcess, ProcessBasicInformation,
670 &BasicInfo, sizeof(BasicInfo), &cbActual1);
671 if (NT_SUCCESS(rcNt))
672 {
673 /*
674 * Read the user process parameter pointer from the PEB.
675 *
676 * Note! Seems WOW64 processes starts out with a 64-bit PEB and
677 * process parameter block.
678 */
679 BOOL const f32BitPeb = !g_f64BitHost;
680 ULONG const cbChildPtr = f32BitPeb ? 4 : 8;
681 PVOID pvSrcInPeb = (char *)BasicInfo.PebBaseAddress + (f32BitPeb ? 0x10 : 0x20);
682 char * pbDst = 0;
683 SIZE_T cbActual2 = 0;
684 if (ReadProcessMemory(ProcInfo.hProcess, pvSrcInPeb, &pbDst, cbChildPtr, &cbActual2))
685 {
686 /*
687 * Duplicate the handles into the child.
688 */
689 union
690 {
691 ULONGLONG au64Bit[2];
692 ULONG au32Bit[2];
693 } WriteBuf;
694 ULONG idx = 0;
695 HANDLE hChildStdOut = INVALID_HANDLE_VALUE;
696 HANDLE hChildStdErr = INVALID_HANDLE_VALUE;
697
698 pbDst += (f32BitPeb ? 0x1c : 0x28);
699 if (pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE)
700 {
701 if (DuplicateHandle(GetCurrentProcess(), pChild->u.Process.hStdOut, ProcInfo.hProcess,
702 &hChildStdOut, 0, TRUE /*fInheritable*/, DUPLICATE_SAME_ACCESS))
703 {
704 if (f32BitPeb)
705 WriteBuf.au32Bit[idx++] = (DWORD)(uintptr_t)hChildStdOut;
706 else
707 WriteBuf.au64Bit[idx++] = (uintptr_t)hChildStdOut;
708 }
709 else
710 {
711 dwErr = GetLastError();
712 fprintf(stderr, "Failed to duplicate %p (stdout) into the child: %u\n",
713 pChild->u.Process.hStdOut, dwErr);
714 }
715 }
716 else
717 pbDst += cbChildPtr;
718
719 if (pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE)
720 {
721 if (DuplicateHandle(GetCurrentProcess(), pChild->u.Process.hStdErr, ProcInfo.hProcess,
722 &hChildStdErr, 0, TRUE /*fInheritable*/, DUPLICATE_SAME_ACCESS))
723 {
724 if (f32BitPeb)
725 WriteBuf.au32Bit[idx++] = (DWORD)(uintptr_t)hChildStdOut;
726 else
727 WriteBuf.au64Bit[idx++] = (uintptr_t)hChildStdOut;
728 }
729 else
730 {
731 dwErr = GetLastError();
732 fprintf(stderr, "Failed to duplicate %p (stderr) into the child: %u\n",
733 pChild->u.Process.hStdOut, dwErr);
734 }
735 }
736
737 /*
738 * Finally write the handle values into the child.
739 */
740 if ( idx > 0
741 && !WriteProcessMemory(ProcInfo.hProcess, pbDst, &WriteBuf, idx * cbChildPtr, &cbActual2))
742 {
743 dwErr = GetLastError();
744 fprintf(stderr, "Failed to write %p LB %u into child: %u\n", pbDst, idx * cbChildPtr, dwErr);
745 }
746 }
747 else
748 {
749 dwErr = GetLastError();
750 fprintf(stderr, "Failed to read %p LB %u from the child: %u\n", pvSrcInPeb, cbChildPtr, dwErr);
751 }
752 }
753 else
754 {
755 fprintf(stderr, "NtQueryInformationProcess failed on child: %#x\n", rcNt);
756 dwErr = (DWORD)rcNt;
757 }
758 }
759
760 /*
761 * Assign processor group (ignore failure).
762 */
763 if (g_cProcessorGroups > 1)
764 {
765 GROUP_AFFINITY Affinity = { ~(ULONG_PTR)0, pWorker->iProcessorGroup, { 0, 0, 0 } };
766 fRet = g_pfnSetThreadGroupAffinity(ProcInfo.hThread, &Affinity, NULL);
767 assert(fRet);
768 }
769
770#ifdef KMK
771 /*
772 * Set priority (ignore failure).
773 */
774 switch (process_priority)
775 {
776 case 1: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_IDLE); break;
777 case 2: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_BELOW_NORMAL); break;
778 case 3: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_NORMAL); break;
779 case 4: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_HIGHEST); break;
780 case 5: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_TIME_CRITICAL); break;
781 default: fRet = TRUE;
782 }
783 assert(fRet);
784#endif
785
786 /*
787 * Resume the thread if the adjustments succeeded, otherwise kill it.
788 */
789 if (dwErr == ERROR_SUCCESS)
790 {
791 fRet = ResumeThread(ProcInfo.hThread);
792 assert(fRet);
793 if (!fRet)
794 {
795 dwErr = GetLastError();
796 fprintf(stderr, "ResumeThread failed on child process: %u\n", dwErr);
797 }
798 }
799 if (dwErr != ERROR_SUCCESS)
800 TerminateProcess(ProcInfo.hProcess, dwErr);
801 }
802
803 /*
804 * Close unnecessary handles.
805 */
806 if ( pChild->u.Process.fCloseStdOut
807 && pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE)
808 {
809 CloseHandle(pChild->u.Process.hStdOut);
810 pChild->u.Process.hStdOut = INVALID_HANDLE_VALUE;
811 pChild->u.Process.fCloseStdOut = FALSE;
812 }
813 if ( pChild->u.Process.fCloseStdErr
814 && pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE)
815 {
816 CloseHandle(pChild->u.Process.hStdErr);
817 pChild->u.Process.hStdErr = INVALID_HANDLE_VALUE;
818 pChild->u.Process.fCloseStdErr = FALSE;
819 }
820
821 CloseHandle(ProcInfo.hThread);
822 return 0;
823}
824
825
826#define MKWCCWCMD_F_CYGWIN_SHELL 1
827#define MKWCCWCMD_F_MKS_SHELL 2
828#define MKWCCWCMD_F_HAVE_SH 4
829#define MKWCCWCMD_F_HAVE_KASH_C 8 /**< kmk_ash -c "..." */
830
831static int mkWinChildcareWorkerConvertCommandline(char **papszArgs, unsigned fFlags, WCHAR **ppwszCommandLine)
832{
833 struct ARGINFO
834 {
835 size_t cchSrc;
836 size_t cwcDst; /**< converted size w/o terminator. */
837 size_t cwcDstExtra : 24; /**< Only set with fSlowly. */
838 size_t fSlowly : 1;
839 size_t fQuoteIt : 1;
840 size_t fEndSlashes : 1; /**< if escapes needed for trailing backslashes. */
841 size_t fExtraSpace : 1; /**< if kash -c "" needs an extra space before the quote. */
842 } *paArgInfo;
843 size_t cArgs;
844 size_t i;
845 size_t cwcNeeded;
846 WCHAR *pwszDst;
847 WCHAR *pwszCmdLine;
848
849 /*
850 * Count them first so we can allocate an info array of the stack.
851 */
852 cArgs = 0;
853 while (papszArgs[cArgs] != NULL)
854 cArgs++;
855 paArgInfo = (struct ARGINFO *)alloca(sizeof(paArgInfo[0]) * cArgs);
856
857 /*
858 * Preprocess them and calculate the exact command line length.
859 */
860 cwcNeeded = 1;
861 for (i = 0; i < cArgs; i++)
862 {
863 char *pszSrc = papszArgs[i];
864 size_t cchSrc = strlen(pszSrc);
865 paArgInfo[i].cchSrc = cchSrc;
866 if (cchSrc == 0)
867 {
868 /* empty needs quoting. */
869 paArgInfo[i].cwcDst = 2;
870 paArgInfo[i].cwcDstExtra = 0;
871 paArgInfo[i].fSlowly = 0;
872 paArgInfo[i].fQuoteIt = 1;
873 paArgInfo[i].fExtraSpace = 0;
874 paArgInfo[i].fEndSlashes = 0;
875 }
876 else
877 {
878 const char *pszSpace = memchr(pszSrc, ' ', cchSrc);
879 const char *pszTab = memchr(pszSrc, '\t', cchSrc);
880 const char *pszDQuote = memchr(pszSrc, '"', cchSrc);
881 const char *pszEscape = memchr(pszSrc, '\\', cchSrc);
882 int cwcDst = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cchSrc + 1, NULL, 0);
883 if (cwcDst >= 0)
884 --cwcDst;
885 else
886 {
887 DWORD dwErr = GetLastError();
888 fprintf(stderr, _("MultiByteToWideChar failed to convert argv[%u] (%s): %u\n"), i, pszSrc, dwErr);
889 return dwErr;
890 }
891#if 0
892 if (!pszSpace && !pszTab && !pszDQuote && !pszEscape)
893 {
894 /* no special handling needed. */
895 paArgInfo[i].cwcDst = cwcDst;
896 paArgInfo[i].cwcDstExtra = 0;
897 paArgInfo[i].fSlowly = 0;
898 paArgInfo[i].fQuoteIt = 0;
899 paArgInfo[i].fExtraSpace = 0;
900 paArgInfo[i].fEndSlashes = 0;
901 }
902 else if (!pszDQuote && !pszEscape)
903 {
904 /* Just double quote it. */
905 paArgInfo[i].cwcDst = cwcDst + 2;
906 paArgInfo[i].cwcDstExtra = 0;
907 paArgInfo[i].fSlowly = 0;
908 paArgInfo[i].fQuoteIt = 1;
909 paArgInfo[i].fExtraSpace = 0;
910 paArgInfo[i].fEndSlashes = 0;
911 }
912 else
913#endif
914 {
915 /* Complicated, need to scan the string to figure out what to do. */
916 size_t cwcDstExtra;
917 int cBackslashes;
918 char ch;
919
920 paArgInfo[i].fQuoteIt = 0;
921 paArgInfo[i].fSlowly = 1;
922 paArgInfo[i].fExtraSpace = 0;
923 paArgInfo[i].fEndSlashes = 0;
924
925 cwcDstExtra = 0;
926 cBackslashes = 0;
927 while ((ch = *pszSrc++) != '\0')
928 {
929 switch (ch)
930 {
931 default:
932 cBackslashes = 0;
933 break;
934
935 case '\\':
936 cBackslashes++;
937 break;
938
939 case '"':
940 if (fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_MKS_SHELL))
941 cwcDstExtra += 1;
942 else
943 cwcDstExtra += 1 + cBackslashes;
944 break;
945
946 case ' ':
947 case '\t':
948 if (!paArgInfo[i].fQuoteIt)
949 {
950 paArgInfo[i].fQuoteIt = 1;
951 cwcDstExtra += 2;
952 }
953 cBackslashes = 0;
954 break;
955 }
956 }
957
958 if ( cBackslashes > 0
959 && paArgInfo[i].fQuoteIt
960 && !(fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_MKS_SHELL)))
961 {
962 cwcDstExtra += cBackslashes;
963 paArgInfo[i].fEndSlashes = 1;
964 }
965
966 paArgInfo[i].cwcDst = cwcDst + cwcDstExtra;
967 paArgInfo[i].cwcDstExtra = cwcDstExtra;
968 }
969 }
970
971 if ( (fFlags & MKWCCWCMD_F_HAVE_KASH_C)
972 && paArgInfo[i].fQuoteIt)
973 {
974 paArgInfo[i].fExtraSpace = 1;
975 paArgInfo[i].cwcDst++;
976 paArgInfo[i].cwcDstExtra++;
977 }
978
979 cwcNeeded += (i != 0) + paArgInfo[i].cwcDst;
980 }
981
982 /*
983 * Allocate the result buffer and do the actual conversion.
984 */
985 pwszDst = pwszCmdLine = (WCHAR *)xmalloc(sizeof(WCHAR) * cwcNeeded);
986 for (i = 0; i < cArgs; i++)
987 {
988 char *pszSrc = papszArgs[i];
989 size_t cwcDst = paArgInfo[i].cwcDst;
990
991 if (i != 0)
992 *pwszDst++ = L' ';
993
994 if (paArgInfo[i].fQuoteIt)
995 {
996 *pwszDst++ = L'"';
997 cwcDst -= 2;
998 }
999
1000 if (!paArgInfo[i].fSlowly)
1001 {
1002 int cwcDst2 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, paArgInfo[i].cchSrc, pwszDst, cwcDst + 1);
1003 assert(cwcDst2 >= 0);
1004 pwszDst += cwcDst;
1005 }
1006 else
1007 {
1008 /* Do the conversion into the end of the output buffer, then move
1009 it up to where it should be char by char. */
1010 size_t cBackslashes;
1011 size_t cwcLeft = paArgInfo[i].cwcDst - paArgInfo[i].cwcDstExtra;
1012 WCHAR volatile *pwchSlowSrc = pwszDst + paArgInfo[i].cwcDstExtra;
1013 WCHAR volatile *pwchSlowDst = pwszDst;
1014 int cwcDst2 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, paArgInfo[i].cchSrc,
1015 (WCHAR *)pwchSlowSrc, cwcLeft + 1);
1016 assert(cwcDst2 >= 0);
1017
1018 cBackslashes = 0;
1019 while (cwcLeft-- > 0)
1020 {
1021 WCHAR wcSrc = *pwchSlowSrc++;
1022 if (wcSrc != L'\\' && wcSrc != L'"')
1023 cBackslashes = 0;
1024 else if (wcSrc == L'\\')
1025 cBackslashes++;
1026 else if ( (fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_HAVE_SH))
1027 == (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_HAVE_SH))
1028 *pwchSlowDst++ = L'"'; /* cygwin: '"' instead of '\\', no escaped slashes. */
1029 else
1030 {
1031 if (!(fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_MKS_SHELL)))
1032 cBackslashes = 1;
1033 while (cBackslashes-- > 0)
1034 *pwchSlowDst++ = L'\\';
1035 }
1036 *pwchSlowDst++ = wcSrc;
1037 }
1038
1039 if (paArgInfo[i].fEndSlashes)
1040 while (cBackslashes-- > 0)
1041 *pwchSlowDst++ = L'\\';
1042
1043 pwszDst += cwcDst;
1044 assert(pwszDst == (WCHAR *)pwchSlowDst);
1045 }
1046
1047 if (paArgInfo[i].fExtraSpace)
1048 *pwszDst++ = L' ';
1049 if (paArgInfo[i].fQuoteIt)
1050 *pwszDst++ = L'"';
1051 }
1052 *pwszDst = L'\0';
1053 *ppwszCommandLine = pwszCmdLine;
1054 return 0;
1055}
1056
1057static int mkWinChildcareWorkerConvertCommandlineWithShell(const WCHAR *pwszShell, char **papszArgs, WCHAR **ppwszCommandLine)
1058{
1059 return -2;
1060}
1061
1062/**
1063 * Searches the environment block for the PATH variable.
1064 *
1065 * @returns Pointer to the path in the block or ".".
1066 * @param pwszzEnv The UTF-16 environment block to search.
1067 */
1068static const WCHAR *mkWinChildcareWorkerFindPathValue(const WCHAR *pwszzEnv)
1069{
1070 while (*pwszzEnv)
1071 {
1072 size_t cwcVar = wcslen(pwszzEnv);
1073 if (!IS_PATH_ENV_VAR(cwcVar, pwszzEnv))
1074 pwszzEnv += cwcVar + 1;
1075 else if (cwcVar > 5)
1076 return &pwszzEnv[5];
1077 else
1078 break;
1079 }
1080 return L".";
1081}
1082
1083/**
1084 * Checks if we need to had this executable file to the shell.
1085 *
1086 * @returns TRUE if it's shell fooder, FALSE if we think windows can handle it.
1087 * @param hFile Handle to the file in question
1088 */
1089static BOOL mkWinChildcareWorkerCheckIfNeedShell(HANDLE hFile)
1090{
1091 /*
1092 * Read the first 512 bytes and check for an executable image header.
1093 */
1094 union
1095 {
1096 DWORD dwSignature;
1097 WORD wSignature;
1098 BYTE ab[128];
1099 } uBuf;
1100 DWORD cbRead;
1101 uBuf.dwSignature = 0;
1102 if ( ReadFile(hFile, &uBuf, sizeof(uBuf), &cbRead, NULL /*pOverlapped*/)
1103 && cbRead == sizeof(uBuf))
1104 {
1105 if (uBuf.wSignature == IMAGE_DOS_SIGNATURE)
1106 return FALSE;
1107 if (uBuf.dwSignature == IMAGE_NT_SIGNATURE)
1108 return FALSE;
1109 if ( uBuf.wSignature == IMAGE_OS2_SIGNATURE /* NE */
1110 || uBuf.wSignature == 0x5d4c /* LX */
1111 || uBuf.wSignature == IMAGE_OS2_SIGNATURE_LE /* LE */)
1112 return FALSE;
1113 }
1114 return TRUE;
1115}
1116
1117
1118/**
1119 * Tries to locate the image file, searching the path and maybe falling back on
1120 * the shell in case it knows more (think cygwin with its own view of the file
1121 * system).
1122 *
1123 * This will also check for shell script, falling back on the shell too to
1124 * handle those.
1125 *
1126 * @returns 0 on success, windows error code on failure.
1127 * @param pszArg0 The first argument.
1128 * @param pwszPath The path if mkWinChildcareWorkerConvertEnvironment
1129 * found it.
1130 * @param pwszzEnv The environment block, in case we need to look for
1131 * the path.
1132 * @param pszShell The shell.
1133 * @param ppwszImagePath Where to return the pointer to the image path. This
1134 * could be the shell.
1135 * @param pfNeedShell Where to return shell vs direct execution indicator.
1136 */
1137static int mkWinChildcareWorkerFindImage(char const *pszArg0, WCHAR const *pwszPath, WCHAR const *pwszzEnv,
1138 const char *pszShell, WCHAR **ppwszImagePath, BOOL *pfNeedShell)
1139{
1140 /** @todo Slap a cache on this code. We usually end up executing the same
1141 * stuff over and over again (e.g. compilers, linkers, etc).
1142 * Hitting the file system is slow on windows. */
1143
1144 /*
1145 * Convert pszArg0 to unicode so we can work directly on that.
1146 */
1147 WCHAR wszArg0[MKWINCHILD_MAX_PATH + 4]; /* +4 for painless '.exe' appending */
1148 DWORD dwErr;
1149 size_t cbArg0 = strlen(pszArg0) + 1;
1150 int const cwcArg0 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszArg0, cbArg0, wszArg0, MKWINCHILD_MAX_PATH);
1151 if (cwcArg0 > 0)
1152 {
1153 HANDLE hFile = INVALID_HANDLE_VALUE;
1154 WCHAR wszPathBuf[MKWINCHILD_MAX_PATH + 4]; /* +4 for painless '.exe' appending */
1155 int cwc;
1156
1157 /*
1158 * If there isn't an .exe suffix, we may have to add one.
1159 * Also we ASSUME that .exe suffixes means no hash bang detection needed.
1160 */
1161 int const fHasExeSuffix = cwcArg0 > CSTRLEN(".exe")
1162 && wszArg0[cwcArg0 - 4] == '.'
1163 && (wszArg0[cwcArg0 - 3] == L'e' || wszArg0[cwcArg0 - 3] == L'E')
1164 && (wszArg0[cwcArg0 - 2] == L'x' || wszArg0[cwcArg0 - 2] == L'X')
1165 && (wszArg0[cwcArg0 - 1] == L'e' || wszArg0[cwcArg0 - 1] == L'E');
1166
1167 /*
1168 * If there isn't any path specified, we need to search the PATH env.var.
1169 */
1170 int const fHasPath = wszArg0[1] == L':'
1171 || wszArg0[0] == L'\\'
1172 || wszArg0[0] == L'/'
1173 || wmemchr(wszArg0, L'/', cwcArg0)
1174 || wmemchr(wszArg0, L'\\', cwcArg0);
1175
1176 /* Before we do anything, flip UNIX slashes to DOS ones. */
1177 WCHAR *pwc = wszArg0;
1178 while ((pwc = wcschr(pwc, L'/')) != NULL)
1179 *pwc++ = L'\\';
1180
1181 /* Don't need to set this all the time... */
1182 *pfNeedShell = FALSE;
1183
1184 /*
1185 * If any kind of path is specified in arg0, we will not search the
1186 * PATH env.var and can limit ourselves to maybe slapping a .exe on to it.
1187 */
1188 if (fHasPath)
1189 {
1190 /*
1191 * If relative to a CWD, turn it into an absolute one.
1192 */
1193 unsigned cwcPath = cwcArg0;
1194 WCHAR *pwszPath = wszArg0;
1195 if ( *pwszPath != L'\\'
1196 && (pwszPath[1] != ':' || pwszPath[2] != L'\\') )
1197 {
1198 DWORD cwcAbsPath = GetFullPathNameW(wszArg0, MKWINCHILD_MAX_PATH, wszPathBuf, NULL);
1199 if (cwcAbsPath > 0)
1200 {
1201 cwcAbsPath = cwcPath + 1; /* include terminator, like MultiByteToWideChar does. */
1202 pwszPath = wszPathBuf;
1203 }
1204 }
1205
1206 /*
1207 * If there is an exectuable path, we only need to check that it exists.
1208 */
1209 if (fHasExeSuffix)
1210 {
1211 DWORD dwAttribs = GetFileAttributesW(pwszPath);
1212 if (dwAttribs != INVALID_FILE_ATTRIBUTES)
1213 return mkWinChildDuplicateUtf16String(pwszPath, cwcPath + 4, ppwszImagePath);
1214 }
1215 else
1216 {
1217 /*
1218 * No suffix, so try open it first to see if it's shell fooder.
1219 * Otherwise, append a .exe suffix and check if it exists.
1220 */
1221 hFile = CreateFileW(pwszPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
1222 NULL /*pSecAttr*/, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1223 if (hFile != INVALID_HANDLE_VALUE)
1224 {
1225 *pfNeedShell = mkWinChildcareWorkerCheckIfNeedShell(hFile);
1226 CloseHandle(hFile);
1227 if (!*pfNeedShell)
1228 return mkWinChildDuplicateUtf16String(pwszPath, cwcPath, ppwszImagePath);
1229 }
1230 /* Append the .exe suffix and check if it exists. */
1231 else
1232 {
1233 DWORD dwAttribs;
1234 pwszPath[cwcPath - 1] = L'.';
1235 pwszPath[cwcPath ] = L'e';
1236 pwszPath[cwcPath + 1] = L'x';
1237 pwszPath[cwcPath + 2] = L'e';
1238 pwszPath[cwcPath + 3] = L'\0';
1239 dwAttribs = GetFileAttributesW(pwszPath);
1240 if (dwAttribs != INVALID_FILE_ATTRIBUTES)
1241 return mkWinChildDuplicateUtf16String(pwszPath, cwcPath + 4, ppwszImagePath);
1242 }
1243 }
1244 }
1245 /*
1246 * No path, need to search the PATH env.var. for the executable, maybe
1247 * adding an .exe suffix while do so if that is missing.
1248 */
1249 else
1250 {
1251 BOOL fSearchedCwd = FALSE;
1252 if (!pwszPath)
1253 pwszPath = mkWinChildcareWorkerFindPathValue(pwszzEnv);
1254 for (;;)
1255 {
1256 size_t cwcCombined;
1257
1258 /*
1259 * Find the end of the current PATH component.
1260 */
1261 size_t cwcSkip;
1262 WCHAR wcEnd;
1263 size_t cwcComponent = 0;
1264 WCHAR wc;
1265 while ((wc = pwszPath[cwcComponent]) != L'\0')
1266 {
1267 if (wc != ';' && wc != ':')
1268 { /* likely */ }
1269 else if (wc == ';')
1270 break;
1271 else if (cwcComponent != pwszPath[cwcComponent] != L'"' ? 1 : 2)
1272 break;
1273 cwcComponent++;
1274 }
1275 wcEnd = wc;
1276
1277 /* Trim leading spaces and double quotes. */
1278 while ( cwcComponent > 0
1279 && ((wc = *pwszPath) == L'"' || wc == L' ' || wc == L'\t'))
1280 {
1281 pwszPath++;
1282 cwcComponent--;
1283 }
1284 cwcSkip = cwcComponent;
1285
1286 /* Trim trailing spaces & double quotes. */
1287 while ( cwcComponent > 0
1288 && ((wc = pwszPath[cwcComponent - 1]) == L'"' || wc == L' ' || wc == L'\t'))
1289 cwcComponent--;
1290
1291 /*
1292 * Skip empty components. Join the component and the filename, making sure to
1293 * resolve any CWD relative stuff first.
1294 */
1295 cwcCombined = cwcComponent + 1 + cwcArg0;
1296 if (cwcComponent > 0 && cwcCombined <= MKWINCHILD_MAX_PATH)
1297 {
1298 DWORD dwAttribs;
1299
1300 /* Copy the component into wszPathBuf, maybe abspath'ing it. */
1301 DWORD cwcAbsPath = 0;
1302 if ( *pwszPath != L'\\'
1303 && (pwszPath[1] != ':' || pwszPath[2] != L'\\') )
1304 {
1305 WCHAR const wcSaved = pwszPath[cwcCombined];
1306 *(WCHAR *)&pwszPath[cwcCombined] = '\0'; /* Pointing to our converted buffer, so this is okay for now. */
1307 cwcAbsPath = GetFullPathNameW(pwszPath, MKWINCHILD_MAX_PATH, wszPathBuf, NULL);
1308 *(WCHAR *)&pwszPath[cwcCombined] = wcSaved;
1309 if (cwcAbsPath > 0 && cwcAbsPath + 1 + cwcArg0 <= MKWINCHILD_MAX_PATH)
1310 cwcCombined = cwcAbsPath + 1 + cwcArg0;
1311 else
1312 cwcAbsPath = 0;
1313 }
1314 if (cwcAbsPath == 0)
1315 {
1316 memcpy(wszPathBuf, pwszPath, cwcComponent);
1317 cwcAbsPath = cwcComponent;
1318 }
1319
1320 /* Append the filename. */
1321 if ((wc = wszPathBuf[cwcAbsPath - 1]) == L'\\' || wc == L'/' || wc == L':')
1322 {
1323 memcpy(&wszPathBuf[cwcAbsPath], wszArg0, cwcArg0 * sizeof(WCHAR));
1324 cwcCombined--;
1325 }
1326 else
1327 {
1328 wszPathBuf[cwcAbsPath] = L'\\';
1329 memcpy(&wszPathBuf[cwcAbsPath + 1], wszArg0, cwcArg0 * sizeof(WCHAR));
1330 }
1331 assert(wszPathBuf[cwcCombined - 1] == L'\0');
1332
1333 /* DOS slash conversion */
1334 pwc = wszPathBuf;
1335 while ((pwc = wcschr(pwc, L'/')) != NULL)
1336 *pwc++ = L'\\';
1337
1338 /*
1339 * Search with exe suffix first.
1340 */
1341 if (!fHasExeSuffix)
1342 {
1343 wszPathBuf[cwcCombined - 1] = L'.';
1344 wszPathBuf[cwcCombined ] = L'e';
1345 wszPathBuf[cwcCombined + 1] = L'x';
1346 wszPathBuf[cwcCombined + 2] = L'e';
1347 wszPathBuf[cwcCombined + 3] = L'\0';
1348 }
1349 dwAttribs = GetFileAttributesW(wszPathBuf);
1350 if ( dwAttribs != INVALID_FILE_ATTRIBUTES
1351 && !(dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
1352 return mkWinChildDuplicateUtf16String(wszPathBuf, cwcCombined + (fHasExeSuffix ? 0 : 4), ppwszImagePath);
1353 if (!fHasExeSuffix)
1354 {
1355 wszPathBuf[cwcCombined - 1] = L'\0';
1356
1357 /*
1358 * Check if the file exists w/o the added '.exe' suffix. If it does,
1359 * we need to check if we can pass it to CreateProcess or need the shell.
1360 */
1361 hFile = CreateFileW(wszPathBuf, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
1362 NULL /*pSecAttr*/, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1363 if (hFile != INVALID_HANDLE_VALUE)
1364 {
1365 *pfNeedShell = mkWinChildcareWorkerCheckIfNeedShell(hFile);
1366 CloseHandle(hFile);
1367 if (!*pfNeedShell)
1368 return mkWinChildDuplicateUtf16String(wszPathBuf, cwcCombined, ppwszImagePath);
1369 break;
1370 }
1371 }
1372 }
1373
1374 /*
1375 * Advance to the next component.
1376 */
1377 if (wcEnd != '\0')
1378 pwszPath += cwcSkip + 1;
1379 else if (fSearchedCwd)
1380 break;
1381 else
1382 {
1383 fSearchedCwd = TRUE;
1384 pwszPath = L".";
1385 }
1386 }
1387 }
1388
1389 /*
1390 * We need the shell. It will take care of finding/reporting missing
1391 * image files and such.
1392 */
1393 *pfNeedShell = TRUE;
1394 cwc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszShell, strlen(pszShell), wszPathBuf, MKWINCHILD_MAX_PATH);
1395 if (cwc > 0)
1396 return mkWinChildDuplicateUtf16String(wszPathBuf, cwc, ppwszImagePath);
1397 dwErr = GetLastError();
1398 }
1399 else
1400 {
1401 dwErr = GetLastError();
1402 fprintf(stderr, _("MultiByteToWideChar failed to convert argv[0] (%s): %u\n"), pszArg0, dwErr);
1403 }
1404 return dwErr == ERROR_INSUFFICIENT_BUFFER ? ERROR_FILENAME_EXCED_RANGE : dwErr;
1405}
1406
1407/**
1408 * Creates the environment block.
1409 *
1410 * @returns 0 on success, windows error code on failure.
1411 * @param papszEnv The environment vector to convert.
1412 * @param cbEnvStrings The size of the environment strings, iff they are
1413 * sequential in a block. Otherwise, zero.
1414 * @param ppwszEnv Where to return the pointer to the environment
1415 * block.
1416 * @param ppwszPath Where to return the pointer to the path value within
1417 * the environment block. This will not be set if
1418 * cbEnvStrings is non-zero, more efficient to let
1419 * mkWinChildcareWorkerFindImage() search when needed.
1420 */
1421static int mkWinChildcareWorkerConvertEnvironment(char **papszEnv, size_t cbEnvStrings,
1422 WCHAR **ppwszEnv, WCHAR const **ppwszPath)
1423{
1424 DWORD dwErr;
1425 int cwcRc;
1426 int cwcDst;
1427 WCHAR *pwszzDst;
1428
1429 *ppwszPath = NULL;
1430
1431 /*
1432 * We've got a little optimization here with help from mkWinChildCopyStringArray.
1433 */
1434 if (cbEnvStrings)
1435 {
1436 cwcDst = cbEnvStrings + 32;
1437 pwszzDst = (WCHAR *)xmalloc(cwcDst * sizeof(WCHAR));
1438 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, papszEnv[0], cbEnvStrings, pwszzDst, cwcDst);
1439 if (cwcRc != 0)
1440 {
1441 *ppwszEnv = pwszzDst;
1442 return 0;
1443 }
1444
1445 /* Resize the allocation and try again. */
1446 dwErr = GetLastError();
1447 if (dwErr == ERROR_INSUFFICIENT_BUFFER)
1448 {
1449 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, papszEnv[0], cbEnvStrings, NULL, 0);
1450 if (cwcRc > 0)
1451 cwcDst = cwcRc + 32;
1452 else
1453 cwcDst *= 2;
1454 pwszzDst = (WCHAR *)xrealloc(pwszzDst, cwcDst);
1455 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, papszEnv[0], cbEnvStrings, pwszzDst, cwcDst);
1456 if (cwcRc != 0)
1457 {
1458 *ppwszEnv = pwszzDst;
1459 return 0;
1460 }
1461 dwErr = GetLastError();
1462 }
1463 fprintf(stderr, _("MultiByteToWideChar failed to convert environment block: %u\n"), dwErr);
1464 }
1465 /*
1466 * Need to convert it string by string.
1467 */
1468 else
1469 {
1470 size_t offPathValue = ~(size_t)0;
1471 size_t offDst;
1472
1473 /*
1474 * Estimate the size first.
1475 */
1476 size_t cEnvVars;
1477 size_t cwcDst = 32;
1478 size_t iVar = 0;
1479 const char *pszSrc;
1480 while ((pszSrc = papszEnv[iVar]) != NULL)
1481 {
1482 cwcDst += strlen(pszSrc) + 1;
1483 iVar++;
1484 }
1485 cEnvVars = iVar;
1486
1487 /* Allocate estimated WCHARs and convert the variables one by one, reallocating
1488 the block as needed. */
1489 pwszzDst = (WCHAR *)xmalloc(cwcDst * sizeof(WCHAR));
1490 cwcDst--; /* save one wchar for the terminating empty string. */
1491 offDst = 0;
1492 for (iVar = 0; iVar < cEnvVars; iVar++)
1493 {
1494 size_t cwcLeft = cwcDst - offDst;
1495 size_t const cbSrc = strlen(pszSrc = papszEnv[iVar]) + 1;
1496 assert(cwcDst >= offDst);
1497
1498
1499 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cbSrc, &pwszzDst[offDst], cwcLeft);
1500 if (cwcRc > 0)
1501 { /* likely */ }
1502 else
1503 {
1504 dwErr = GetLastError();
1505 if (dwErr == ERROR_INSUFFICIENT_BUFFER)
1506 {
1507 /* Need more space. So, calc exacly how much and resize the block accordingly. */
1508 size_t cbSrc2 = cbSrc;
1509 size_t iVar2 = iVar;
1510 cwcLeft = 1;
1511 for (;;)
1512 {
1513 size_t cwcRc2 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cbSrc, NULL, 0);
1514 if (cwcRc2 > 0)
1515 cwcLeft += cwcRc2;
1516 else
1517 cwcLeft += cbSrc * 4;
1518
1519 /* advance */
1520 iVar2++;
1521 if (iVar2 >= cEnvVars)
1522 break;
1523 pszSrc = papszEnv[iVar2];
1524 cbSrc2 = strlen(pszSrc) + 1;
1525 }
1526 pszSrc = papszEnv[iVar];
1527
1528 /* Grow the allocation and repeat the conversion. */
1529 if (offDst + cwcLeft > cwcDst + 1)
1530 {
1531 cwcDst = offDst + cwcLeft;
1532 pwszzDst = (WCHAR *)xrealloc(pwszzDst, cwcDst * sizeof(WCHAR));
1533 cwcDst--; /* save one wchar for the terminating empty string. */
1534 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cbSrc, &pwszzDst[offDst], cwcLeft - 1);
1535 if (cwcRc <= 0)
1536 dwErr = GetLastError();
1537 }
1538 }
1539 if (cwcRc <= 0)
1540 {
1541 fprintf(stderr, _("MultiByteToWideChar failed to convert environment string #%u (%s): %u\n"),
1542 iVar, pszSrc, dwErr);
1543 free(pwszzDst);
1544 return dwErr;
1545 }
1546 }
1547
1548 /* Look for the PATH. */
1549 if ( offPathValue == ~(size_t)0
1550 && IS_PATH_ENV_VAR(cwcRc, &pwszzDst[offDst]) )
1551 offPathValue = offDst + 4 + 1;
1552
1553 /* Advance. */
1554 offDst += cwcRc;
1555 }
1556 pwszzDst[offDst++] = '\0';
1557
1558 if (offPathValue != ~(size_t)0)
1559 *ppwszPath = &pwszzDst[offPathValue];
1560 *ppwszEnv = pwszzDst;
1561 return 0;
1562 }
1563 free(pwszzDst);
1564 return dwErr;
1565}
1566
1567/**
1568 * Childcare worker: handle regular process.
1569 *
1570 * @param pWorker The worker.
1571 * @param pChild The kSubmit child.
1572 */
1573static void mkWinChildcareWorkerThreadHandleProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
1574{
1575 WCHAR const *pwszPath = NULL;
1576 WCHAR *pwszzEnvironment = NULL;
1577 WCHAR *pwszCommandLine = NULL;
1578 WCHAR *pwszImageName = NULL;
1579 BOOL fNeedShell = FALSE;
1580 int rc;
1581
1582 /*
1583 * First we convert the environment so we get the PATH we need to
1584 * search for the executable.
1585 */
1586 rc = mkWinChildcareWorkerConvertEnvironment(pChild->u.Process.papszEnv ? pChild->u.Process.papszEnv : environ,
1587 pChild->u.Process.cbEnvStrings,
1588 &pwszzEnvironment, &pwszPath);
1589 /*
1590 * Find the executable and maybe checking if it's a shell script, then
1591 * convert it to a command line.
1592 */
1593 if (rc == 0)
1594 rc = mkWinChildcareWorkerFindImage(pChild->u.Process.papszArgs[0], pwszzEnvironment, pwszPath,
1595 pChild->u.Process.pszShell, &pwszImageName, &fNeedShell);
1596 if (rc == 0)
1597 {
1598 if (!fNeedShell)
1599 rc = mkWinChildcareWorkerConvertCommandline(pChild->u.Process.papszArgs, 0 /*fFlags*/, &pwszCommandLine);
1600 else
1601 rc = mkWinChildcareWorkerConvertCommandlineWithShell(pwszImageName, pChild->u.Process.papszArgs, &pwszCommandLine);
1602
1603 /*
1604 * Create the child process.
1605 */
1606 if (rc == 0)
1607 {
1608 rc = mkWinChildcareWorkerCreateProcess(pWorker, pChild, pwszImageName, pwszCommandLine, pwszzEnvironment);
1609 if (rc == 0)
1610 {
1611 /*
1612 * Wait for the child to complete.
1613 */
1614 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Process.hProcess);
1615 }
1616 else
1617 pChild->iExitCode = rc;
1618 }
1619 else
1620 pChild->iExitCode = rc;
1621 }
1622 else
1623 pChild->iExitCode = rc;
1624 free(pwszCommandLine);
1625 free(pwszImageName);
1626 free(pwszzEnvironment);
1627}
1628
1629#ifdef KMK
1630
1631/**
1632 * Childcare worker: handle builtin command.
1633 *
1634 * @param pWorker The worker.
1635 * @param pChild The kSubmit child.
1636 */
1637static void mkWinChildcareWorkerThreadHandleBuiltin(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
1638{
1639 /** @todo later. */
1640__debugbreak();
1641}
1642
1643/**
1644 * Childcare worker: handle kSubmit job.
1645 *
1646 * @param pWorker The worker.
1647 * @param pChild The kSubmit child.
1648 */
1649static void mkWinChildcareWorkerThreadHandleSubmit(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
1650{
1651 void *pvSubmitWorker = pChild->u.Submit.pvSubmitWorker;
1652 for (;;)
1653 {
1654 int iExitCode = -42;
1655 int iSignal = -1;
1656 DWORD dwStatus = WaitForSingleObject(pChild->u.Submit.hEvent, INFINITE);
1657 assert(dwStatus != WAIT_FAILED);
1658
1659 if (kSubmitSubProcGetResult((intptr_t)pvSubmitWorker, &iExitCode, &iSignal) == 0)
1660 {
1661 pChild->iExitCode = iExitCode;
1662 pChild->iSignal = iSignal;
1663 /* Cleanup must be done on the main thread. */
1664 return;
1665 }
1666
1667 if (pChild->iSignal != 0)
1668 kSubmitSubProcKill((intptr_t)pvSubmitWorker, pChild->iSignal);
1669 }
1670}
1671
1672/**
1673 * Childcare worker: handle kmk_redirect process.
1674 *
1675 * @param pWorker The worker.
1676 * @param pChild The redirect child.
1677 */
1678static void mkWinChildcareWorkerThreadHandleRedirect(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
1679{
1680 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Redirect.hProcess);
1681}
1682
1683#endif /* KMK */
1684
1685/**
1686 * Childcare worker thread.
1687 *
1688 * @returns 0
1689 * @param pvUser The worker instance.
1690 */
1691static unsigned int __stdcall mkWinChildcareWorkerThread(void *pvUser)
1692{
1693 PWINCHILDCAREWORKER pWorker = (PWINCHILDCAREWORKER)pvUser;
1694 assert(pWorker->uMagic == WINCHILDCAREWORKER_MAGIC);
1695
1696 /*
1697 * Adjust process group if necessary.
1698 */
1699 if (g_cProcessorGroups > 1)
1700 {
1701 GROUP_AFFINITY Affinity = { ~(ULONG_PTR)0, pWorker->iProcessorGroup, { 0, 0, 0 } };
1702 BOOL fRet = g_pfnSetThreadGroupAffinity(GetCurrentThread(), &Affinity, NULL);
1703 assert(fRet); (void)fRet;
1704 }
1705
1706 /*
1707 * Work loop.
1708 */
1709 while (!g_fShutdown)
1710 {
1711 /*
1712 * Try go idle.
1713 */
1714 PWINCHILD pChild = pWorker->pTailTodoChildren;
1715 if (!pChild)
1716 {
1717 _InterlockedExchange(&pWorker->fIdle, TRUE);
1718 pChild = pWorker->pTailTodoChildren;
1719 if (!pChild)
1720 {
1721 DWORD dwStatus;
1722
1723 _InterlockedIncrement((long *)&g_cIdleChildcareWorkers);
1724 dwStatus = WaitForSingleObject(pWorker->hEvtIdle, INFINITE);
1725 _InterlockedExchange(&pWorker->fIdle, FALSE);
1726 _InterlockedDecrement((long *)&g_cIdleChildcareWorkers);
1727
1728 assert(dwStatus != WAIT_FAILED);
1729 if (dwStatus == WAIT_FAILED)
1730 Sleep(20);
1731
1732 pChild = pWorker->pTailTodoChildren;
1733 }
1734 else
1735 _InterlockedExchange(&pWorker->fIdle, FALSE);
1736 }
1737 if (pChild)
1738 {
1739 /*
1740 * We got work to do. First job is to deque the job.
1741 */
1742 pChild = mkWinChildDequeFromLifo(&pWorker->pTailTodoChildren, pChild);
1743 assert(pChild);
1744 if (pChild)
1745 {
1746 PWINCHILD pTailExpect;
1747
1748 switch (pChild->enmType)
1749 {
1750 case WINCHILDTYPE_PROCESS:
1751 mkWinChildcareWorkerThreadHandleProcess(pWorker, pChild);
1752 break;
1753#ifdef KMK
1754 case WINCHILDTYPE_BUILTIN:
1755 mkWinChildcareWorkerThreadHandleBuiltin(pWorker, pChild);
1756 break;
1757 case WINCHILDTYPE_SUBMIT:
1758 mkWinChildcareWorkerThreadHandleSubmit(pWorker, pChild);
1759 break;
1760 case WINCHILDTYPE_REDIRECT:
1761 mkWinChildcareWorkerThreadHandleRedirect(pWorker, pChild);
1762 break;
1763#endif
1764 default:
1765 assert(0);
1766 }
1767
1768 /*
1769 * Move the child to the completed list.
1770 */
1771 pTailExpect = NULL;
1772 for (;;)
1773 {
1774 PWINCHILD pTailActual;
1775 pChild->pNext = pTailExpect;
1776 pTailActual = _InterlockedCompareExchangePointer(&g_pTailCompletedChildren, pChild, pTailExpect);
1777 if (pTailActual != pTailExpect)
1778 pTailExpect = pTailActual;
1779 else
1780 {
1781 _InterlockedDecrement(&g_cPendingChildren);
1782 if (pTailExpect)
1783 break;
1784 if (SetEvent(g_hEvtWaitChildren))
1785 break;
1786 fprintf(stderr, "SetEvent(g_hEvtWaitChildren=%p) failed: %u\n", g_hEvtWaitChildren, GetLastError());
1787 break;
1788 }
1789 }
1790 }
1791 }
1792 }
1793
1794 _endthreadex(0);
1795 return 0;
1796}
1797
1798/**
1799 * Creates another childcare worker.
1800 *
1801 * @returns The new worker, if we succeeded.
1802 */
1803static PWINCHILDCAREWORKER mkWinChildcareCreateWorker(void)
1804{
1805 PWINCHILDCAREWORKER pWorker = (PWINCHILDCAREWORKER)xcalloc(sizeof(*pWorker));
1806 pWorker->uMagic = WINCHILDCAREWORKER_MAGIC;
1807 pWorker->hEvtIdle = CreateEvent(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pszName*/);
1808 if (pWorker->hEvtIdle)
1809 {
1810 /* Before we start the thread, assign it to a processor group. */
1811 if (g_cProcessorGroups > 1)
1812 {
1813 unsigned int cMaxInGroup;
1814 unsigned int cInGroup;
1815 unsigned int iGroup = g_idxProcessorGroupAllocator % g_cProcessorGroups;
1816 pWorker->iProcessorGroup = iGroup;
1817
1818 /* Advance. We employ a very simple strategy that does 50% in
1819 each group for each group cycle. Odd processor counts are
1820 caught in odd group cycles. The init function selects the
1821 starting group based on make nesting level to avoid stressing
1822 out the first group. */
1823 cInGroup = ++g_idxProcessorInGroupAllocator;
1824 cMaxInGroup = g_pacProcessorsInGroup[iGroup];
1825 if ( !(cMaxInGroup & 1)
1826 || !((g_idxProcessorGroupAllocator / g_cProcessorGroups) & 1))
1827 cMaxInGroup /= 2;
1828 else
1829 cMaxInGroup = cMaxInGroup / 2 + 1;
1830 if (cInGroup >= cMaxInGroup)
1831 {
1832 g_idxProcessorInGroupAllocator = 0;
1833 g_idxProcessorGroupAllocator++;
1834 }
1835 }
1836
1837 /* Try start the thread. */
1838 pWorker->hThread = (HANDLE)_beginthreadex(NULL, 0 /*cbStack*/, mkWinChildcareWorkerThread, pWorker,
1839 0, &pWorker->tid);
1840 if (pWorker->hThread != NULL)
1841 {
1842 g_papChildCareworkers[g_cChildCareworkers++] = pWorker;
1843 return pWorker;
1844 }
1845 CloseHandle(pWorker->hEvtIdle);
1846 }
1847 pWorker->uMagic = ~WINCHILDCAREWORKER_MAGIC;
1848 free(pWorker);
1849 return NULL;
1850}
1851
1852/**
1853 * Helper for copying argument and environment vectors.
1854 *
1855 * @returns Single alloc block copy.
1856 * @param papszSrc The source vector.
1857 * @param pcbStrings Where to return the size of the strings & terminator.
1858 */
1859static char **mkWinChildCopyStringArray(char **papszSrc, size_t *pcbStrings)
1860{
1861 const char *psz;
1862 char **papszDstArray;
1863 char *pszDstStr;
1864 size_t i;
1865
1866 /* Calc sizes first. */
1867 size_t cbStrings = 1; /* (one extra for terminator string) */
1868 size_t cStrings = 0;
1869 while ((psz = papszSrc[cStrings]) != NULL)
1870 {
1871 cbStrings += strlen(psz) + 1;
1872 cStrings++;
1873 }
1874 *pcbStrings = cbStrings;
1875
1876 /* Allocate destination. */
1877 papszDstArray = (char **)xmalloc(cbStrings + (cStrings + 1) * sizeof(papszDstArray[0]));
1878 pszDstStr = (char *)&papszDstArray[cStrings + 1];
1879
1880 /* Copy it. */
1881 for (i = 0; i < cStrings; i++)
1882 {
1883 size_t cbString = strlen(papszSrc[i]) + 1;
1884 papszDstArray[i] = (char *)memcpy(pszDstStr, papszSrc[i], cbString);
1885 pszDstStr += cbString;
1886 }
1887 *pszDstStr = '\0';
1888 assert(&pszDstStr[1] - papszDstArray[0] == cbStrings);
1889 papszDstArray[i] = NULL;
1890 return papszDstArray;
1891}
1892
1893/**
1894 * Allocate and init a WINCHILD.
1895 *
1896 * @returns The new windows child structure.
1897 * @param enmType The child type.
1898 */
1899static PWINCHILD mkWinChildNew(WINCHILDTYPE enmType)
1900{
1901 PWINCHILD pChild = xcalloc(sizeof(*pChild));
1902 pChild->enmType = enmType;
1903 pChild->fCoreDumped = 0;
1904 pChild->iSignal = 0;
1905 pChild->iExitCode = 222222;
1906 pChild->uMagic = WINCHILD_MAGIC;
1907 pChild->pid = (intptr_t)pChild;
1908 return pChild;
1909}
1910
1911/**
1912 * Destructor for WINCHILD.
1913 *
1914 * @param pChild The child structure to destroy.
1915 */
1916static void mkWinChildDelete(PWINCHILD pChild)
1917{
1918 assert(pChild->uMagic == WINCHILD_MAGIC);
1919 pChild->uMagic = ~WINCHILD_MAGIC;
1920
1921 switch (pChild->enmType)
1922 {
1923 case WINCHILDTYPE_PROCESS:
1924 {
1925 if (pChild->u.Process.papszArgs)
1926 {
1927 free(pChild->u.Process.papszArgs);
1928 pChild->u.Process.papszArgs = NULL;
1929 }
1930 if (pChild->u.Process.cbEnvStrings && pChild->u.Process.papszEnv)
1931 {
1932 free(pChild->u.Process.papszEnv);
1933 pChild->u.Process.papszEnv = NULL;
1934 }
1935 if (pChild->u.Process.pszShell)
1936 {
1937 free(pChild->u.Process.pszShell);
1938 pChild->u.Process.pszShell = NULL;
1939 }
1940 if (pChild->u.Process.hProcess)
1941 {
1942 CloseHandle(pChild->u.Process.hProcess);
1943 pChild->u.Process.hProcess = NULL;
1944 }
1945 if ( pChild->u.Process.fCloseStdOut
1946 && pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE)
1947 {
1948 CloseHandle(pChild->u.Process.hStdOut);
1949 pChild->u.Process.hStdOut = INVALID_HANDLE_VALUE;
1950 pChild->u.Process.fCloseStdOut = FALSE;
1951 }
1952 if ( pChild->u.Process.fCloseStdErr
1953 && pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE)
1954 {
1955 CloseHandle(pChild->u.Process.hStdErr);
1956 pChild->u.Process.hStdErr = INVALID_HANDLE_VALUE;
1957 pChild->u.Process.fCloseStdErr = FALSE;
1958 }
1959 break;
1960 }
1961
1962#ifdef KMK
1963
1964 case WINCHILDTYPE_BUILTIN:
1965 assert(0);
1966 break;
1967
1968 case WINCHILDTYPE_SUBMIT:
1969 if (pChild->u.Submit.pvSubmitWorker)
1970 {
1971 kSubmitSubProcCleanup((intptr_t)pChild->u.Submit.pvSubmitWorker);
1972 pChild->u.Submit.pvSubmitWorker = NULL;
1973 }
1974 break;
1975
1976 case WINCHILDTYPE_REDIRECT:
1977 if (pChild->u.Redirect.hProcess)
1978 {
1979 CloseHandle(pChild->u.Redirect.hProcess);
1980 pChild->u.Redirect.hProcess = NULL;
1981 }
1982 break;
1983
1984#endif /* KMK */
1985
1986 default:
1987 assert(0);
1988 }
1989
1990 free(pChild);
1991}
1992
1993/**
1994 * Queues the child with a worker, creating new workers if necessary.
1995 *
1996 * @returns 0 on success, windows error code on failure (child destroyed).
1997 * @param pChild The child.
1998 * @param pPid Where to return the PID (optional).
1999 */
2000static int mkWinChildPushToCareWorker(PWINCHILD pChild, pid_t *pPid)
2001{
2002 PWINCHILDCAREWORKER pWorker = NULL;
2003 PWINCHILD pOldChild;
2004 PWINCHILD pCurChild;
2005
2006 /*
2007 * There are usually idle workers around, except for at the start.
2008 */
2009 if (g_cIdleChildcareWorkers > 0)
2010 {
2011 /*
2012 * Try the idle hint first and move forward from it.
2013 */
2014 unsigned int const cWorkers = g_cChildCareworkers;
2015 unsigned int iHint = g_idxLastChildcareWorker;
2016 unsigned int i;
2017 for (i = iHint; i < cWorkers; i++)
2018 {
2019 PWINCHILDCAREWORKER pPossibleWorker = g_papChildCareworkers[i];
2020 if (pPossibleWorker->fIdle)
2021 {
2022 pWorker = pPossibleWorker;
2023 break;
2024 }
2025 }
2026 if (!pWorker)
2027 {
2028 /* Scan from the start. */
2029 if (iHint > cWorkers)
2030 iHint = cWorkers;
2031 for (i = 0; i < iHint; i++)
2032 {
2033 PWINCHILDCAREWORKER pPossibleWorker = g_papChildCareworkers[i];
2034 if (pPossibleWorker->fIdle)
2035 {
2036 pWorker = pPossibleWorker;
2037 break;
2038 }
2039 }
2040 }
2041 }
2042 if (!pWorker)
2043 {
2044 /*
2045 * Try create more workers if we haven't reached the max yet.
2046 */
2047 if (g_cChildCareworkers < g_cChildCareworkersMax)
2048 pWorker = mkWinChildcareCreateWorker();
2049
2050 /*
2051 * Queue it with an existing worker. Look for one without anthing extra scheduled.
2052 */
2053 if (!pWorker)
2054 {
2055 unsigned int i = g_cChildCareworkers;
2056 if (i == 0)
2057 fatal(NILF, 0, _("Failed to create worker threads for managing child processes!\n"));
2058 pWorker = g_papChildCareworkers[--i];
2059 if (pWorker->pTailTodoChildren)
2060 while (i-- > 0)
2061 {
2062 PWINCHILDCAREWORKER pPossibleWorker = g_papChildCareworkers[i];
2063 if (!pPossibleWorker->pTailTodoChildren)
2064 {
2065 pWorker = pPossibleWorker;
2066 break;
2067 }
2068 }
2069 }
2070 }
2071
2072 /*
2073 * Do the queueing.
2074 */
2075 pOldChild = NULL;
2076 for (;;)
2077 {
2078 pChild->pNext = pOldChild;
2079 pCurChild = _InterlockedCompareExchangePointer((void **)&pWorker->pTailTodoChildren, pChild, pOldChild);
2080 if (pCurChild == pOldChild)
2081 {
2082 DWORD volatile dwErr;
2083 _InterlockedIncrement(&g_cPendingChildren);
2084 if ( !pWorker->fIdle
2085 || SetEvent(pWorker->hEvtIdle))
2086 {
2087 *pPid = pChild->pid;
2088 return 0;
2089 }
2090
2091 _InterlockedDecrement(&g_cPendingChildren);
2092 dwErr = GetLastError();
2093 assert(0);
2094 mkWinChildDelete(pChild);
2095 return dwErr ? dwErr : -1;
2096 }
2097 pOldChild = pCurChild;
2098 }
2099}
2100
2101/**
2102 * Creates a regular child process (job.c).
2103 *
2104 * Will copy the information and push it to a childcare thread that does the
2105 * actual process creation.
2106 *
2107 * @returns 0 on success, windows status code on failure.
2108 * @param papszArgs The arguments.
2109 * @param papszEnv The environment (optional).
2110 * @param pszShell The SHELL variable value (optional).
2111 * @param pMkChild The make child structure (optional).
2112 * @param pPid Where to return the pid.
2113 */
2114int MkWinChildCreate(char **papszArgs, char **papszEnv, const char *pszShell, struct child *pMkChild, pid_t *pPid)
2115{
2116 PWINCHILD pChild = mkWinChildNew(WINCHILDTYPE_PROCESS);
2117 pChild->pMkChild = pMkChild;
2118
2119 pChild->u.Process.papszArgs = mkWinChildCopyStringArray(papszArgs, &pChild->u.Process.cbArgsStrings);
2120 if ( !papszEnv
2121 || !pMkChild
2122 || pMkChild->environment == papszEnv)
2123 {
2124 pChild->u.Process.cbEnvStrings = 0;
2125 pChild->u.Process.papszEnv = papszEnv;
2126 }
2127 else
2128 pChild->u.Process.papszEnv = mkWinChildCopyStringArray(papszEnv, &pChild->u.Process.cbEnvStrings);
2129 if (pszShell)
2130 pChild->u.Process.pszShell = xstrdup(pszShell);
2131 pChild->u.Process.hStdOut = INVALID_HANDLE_VALUE;
2132 pChild->u.Process.hStdErr = INVALID_HANDLE_VALUE;
2133
2134 return mkWinChildPushToCareWorker(pChild, pPid);
2135}
2136
2137/**
2138 * Creates a chile process with a pipe hooked up to stdout.
2139 *
2140 * @returns 0 on success, non-zero on failure.
2141 * @param papszArgs The argument vector.
2142 * @param papszEnv The environment vector (optional).
2143 * @param fdErr File descriptor to hook up to stderr.
2144 * @param pPid Where to return the pid.
2145 * @param pfdReadPipe Where to return the read end of the pipe.
2146 */
2147int MkWinChildCreateWithStdOutPipe(char **papszArgs, char **papszEnv, int fdErr, pid_t *pPid, int *pfdReadPipe)
2148{
2149 /*
2150 * Create the pipe.
2151 */
2152 HANDLE hReadPipe;
2153 HANDLE hWritePipe;
2154 if (CreatePipe(&hReadPipe, &hWritePipe, NULL, 0 /* default size */))
2155 {
2156 if (SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT /* clear */ , HANDLE_FLAG_INHERIT /*set*/))
2157 {
2158 int fdReadPipe = _open_osfhandle((intptr_t)hReadPipe, O_RDONLY);
2159 if (fdReadPipe >= 0)
2160 {
2161 PWINCHILD pChild;
2162 int rc;
2163
2164 /*
2165 * Get a handle for fdErr. Ignore failure.
2166 */
2167 HANDLE hStdErr = INVALID_HANDLE_VALUE;
2168 if (fdErr >= 0)
2169 {
2170 HANDLE hNative = (HANDLE)_get_osfhandle(fdErr);
2171 if (!DuplicateHandle(GetCurrentProcess(), hNative, GetCurrentProcess(),
2172 &hStdErr, 0 /*DesiredAccess*/, TRUE /*fInherit*/, DUPLICATE_SAME_ACCESS))
2173 {
2174 ONN(error, NILF, _("DuplicateHandle failed on stderr descriptor (%u): %u\n"), fdErr, GetLastError());
2175 hStdErr = INVALID_HANDLE_VALUE;
2176 }
2177 }
2178
2179 /*
2180 * Push it off to the worker thread.
2181 */
2182 pChild = mkWinChildNew(WINCHILDTYPE_PROCESS);
2183 pChild->u.Process.papszArgs = mkWinChildCopyStringArray(papszArgs, &pChild->u.Process.cbArgsStrings);
2184 pChild->u.Process.papszEnv = mkWinChildCopyStringArray(papszEnv ? papszEnv : environ,
2185 &pChild->u.Process.cbEnvStrings);
2186 //if (pszShell)
2187 // pChild->u.Process.pszShell = xstrdup(pszShell);
2188 pChild->u.Process.hStdOut = hWritePipe;
2189 pChild->u.Process.hStdErr = hStdErr;
2190 pChild->u.Process.fCloseStdErr = TRUE;
2191 pChild->u.Process.fCloseStdOut = TRUE;
2192
2193 rc = mkWinChildPushToCareWorker(pChild, pPid);
2194 if (rc == 0)
2195 *pfdReadPipe = fdReadPipe;
2196 else
2197 {
2198 ON(error, NILF, _("mkWinChildPushToCareWorker failed on pipe: %d\n"), rc);
2199 close(fdReadPipe);
2200 *pfdReadPipe = -1;
2201 *pPid = -1;
2202 }
2203 return rc;
2204 }
2205
2206 ON(error, NILF, _("_open_osfhandle failed on pipe: %u\n"), errno);
2207 }
2208 else
2209 ON(error, NILF, _("SetHandleInformation failed on pipe: %u\n"), GetLastError());
2210 if (hReadPipe != INVALID_HANDLE_VALUE)
2211 CloseHandle(hReadPipe);
2212 CloseHandle(hWritePipe);
2213 }
2214 else
2215 ON(error, NILF, _("CreatePipe failed: %u\n"), GetLastError());
2216 *pfdReadPipe = -1;
2217 *pPid = -1;
2218 return -1;
2219}
2220
2221#ifdef KMK
2222
2223/**
2224 * Interface used by kSubmit.c for registering stuff to wait on.
2225 *
2226 * @returns 0 on success, windows status code on failure.
2227 * @param hEvent The event object handle to wait on.
2228 * @param pvSubmitWorker The argument to pass back to kSubmit to clean up.
2229 * @param pPid Where to return the pid.
2230 */
2231int MkWinChildCreateSubmit(intptr_t hEvent, void *pvSubmitWorker, pid_t *pPid)
2232{
2233 PWINCHILD pChild = mkWinChildNew(WINCHILDTYPE_SUBMIT);
2234 pChild->u.Submit.hEvent = (HANDLE)hEvent;
2235 pChild->u.Submit.pvSubmitWorker = pvSubmitWorker;
2236 return mkWinChildPushToCareWorker(pChild, pPid);
2237}
2238
2239/**
2240 * Interface used by redirect.c for registering stuff to wait on.
2241 *
2242 * @returns 0 on success, windows status code on failure.
2243 * @param hProcess The process object to wait on.
2244 * @param pPid Where to return the pid.
2245 */
2246int MkWinChildCreateRedirect(intptr_t hProcess, pid_t *pPid)
2247{
2248 PWINCHILD pChild = mkWinChildNew(WINCHILDTYPE_REDIRECT);
2249 pChild->u.Redirect.hProcess = (HANDLE)hProcess;
2250 return mkWinChildPushToCareWorker(pChild, pPid);
2251}
2252
2253#endif /* CONFIG_NEW_WIN_CHILDREN */
2254
2255/**
2256 * Interface used to kill process when processing Ctrl-C and fatal errors.
2257 *
2258 * @returns 0 on success, -1+errno on error.
2259 * @param pid The process to kill (PWINCHILD).
2260 * @param iSignal What to kill it with.
2261 * @param pMkChild The make child structure for validation.
2262 */
2263int MkWinChildKill(pid_t pid, int iSignal, struct child *pMkChild)
2264{
2265 PWINCHILD pChild = (PWINCHILD)pid;
2266 if (pChild)
2267 {
2268 assert(pChild->uMagic == WINCHILD_MAGIC);
2269 if (pChild->uMagic == WINCHILD_MAGIC)
2270 {
2271 switch (pChild->enmType)
2272 {
2273 case WINCHILDTYPE_PROCESS:
2274 assert(pChild->pMkChild == pMkChild);
2275 TerminateProcess(pChild->u.Process.hProcess, DBG_TERMINATE_PROCESS);
2276 pChild->iSignal = iSignal;
2277 break;
2278
2279#ifdef KMK
2280
2281 case WINCHILDTYPE_SUBMIT:
2282 {
2283 pChild->iSignal = iSignal;
2284 SetEvent(pChild->u.Submit.hEvent);
2285 break;
2286 }
2287
2288 case WINCHILDTYPE_REDIRECT:
2289 TerminateProcess(pChild->u.Redirect.hProcess, DBG_TERMINATE_PROCESS);
2290 pChild->iSignal = iSignal;
2291 break;
2292
2293 case WINCHILDTYPE_BUILTIN:
2294 break;
2295
2296#endif /* KMK */
2297
2298 default:
2299 assert(0);
2300 }
2301 }
2302 }
2303 return -1;
2304}
2305
2306/**
2307 * Wait for a child process to complete
2308 *
2309 * @returns 0 on success, windows error code on failure.
2310 * @param fBlock Whether to block.
2311 * @param pPid Where to return the pid if a child process
2312 * completed. This is set to zero if none.
2313 * @param piExitCode Where to return the exit code.
2314 * @param piSignal Where to return the exit signal number.
2315 * @param pfCoreDumped Where to return the core dumped indicator.
2316 * @param ppMkChild Where to return the associated struct child pointer.
2317 */
2318int MkWinChildWait(int fBlock, pid_t *pPid, int *piExitCode, int *piSignal, int *pfCoreDumped, struct child **ppMkChild)
2319{
2320 PWINCHILD pChild;
2321
2322 *pPid = 0;
2323 *piExitCode = -222222;
2324 *pfCoreDumped = 0;
2325 *ppMkChild = NULL;
2326
2327 /*
2328 * Wait if necessary.
2329 */
2330 if (fBlock && !g_pTailCompletedChildren && g_cPendingChildren > 0)
2331 {
2332 DWORD dwStatus = WaitForSingleObject(g_hEvtWaitChildren, INFINITE);
2333 if (dwStatus == WAIT_FAILED)
2334 return (int)GetLastError();
2335 }
2336
2337 /*
2338 * Try unlink the last child in the LIFO.
2339 */
2340 pChild = g_pTailCompletedChildren;
2341 if (!pChild)
2342 return 0;
2343 pChild = mkWinChildDequeFromLifo(&g_pTailCompletedChildren, pChild);
2344 assert(pChild);
2345
2346 /*
2347 * Set return values and ditch the child structure.
2348 */
2349 *pPid = pChild->pid;
2350 *piExitCode = pChild->iExitCode;
2351 *pfCoreDumped = pChild->fCoreDumped;
2352 *ppMkChild = pChild->pMkChild;
2353 switch (pChild->enmType)
2354 {
2355 case WINCHILDTYPE_PROCESS:
2356 break;
2357#ifdef KMK
2358 case WINCHILDTYPE_BUILTIN:
2359 break;
2360 case WINCHILDTYPE_SUBMIT:
2361 break;
2362 case WINCHILDTYPE_REDIRECT:
2363 break;
2364#endif /* KMK */
2365 default:
2366 assert(0);
2367 }
2368 mkWinChildDelete(pChild);
2369
2370#ifdef KMK
2371 /* Flush the volatile directory cache. */
2372 dir_cache_invalid_after_job();
2373#endif
2374 return 0;
2375}
2376
2377/**
2378 * Get the child completed event handle.
2379 *
2380 * Needed when w32os.c is waiting for a job token to become available, given
2381 * that completed children is the typical source of these tokens (esp. for kmk).
2382 *
2383 * @returns Event handle.
2384 */
2385intptr_t MkWinChildGetCompleteEventHandle(void)
2386{
2387 return (intptr_t)g_hEvtWaitChildren;
2388}
2389
2390/**
2391 * Emulate execv() for restarting kmk after one ore more makefiles has been
2392 * made.
2393 *
2394 * Does not return.
2395 *
2396 * @param papszArgs The arguments.
2397 * @param papszEnv The environment.
2398 */
2399void MkWinChildReExecMake(char **papszArgs, char **papszEnv)
2400{
2401 PROCESS_INFORMATION ProcInfo;
2402 STARTUPINFOW StartupInfo;
2403 WCHAR *pwszCommandLine;
2404 WCHAR *pwszzEnvironment;
2405 WCHAR *pwszPathIgnored;
2406 int rc;
2407
2408 /*
2409 * Get the executable name.
2410 */
2411 WCHAR wszImageName[MKWINCHILD_MAX_PATH];
2412 DWORD cwcImageName = GetModuleFileNameW(GetModuleHandle(NULL), wszImageName, MKWINCHILD_MAX_PATH);
2413 if (cwcImageName == 0)
2414 ON(fatal, NILF, _("MkWinChildReExecMake: GetModuleFileName failed: %u\n"), GetLastError());
2415
2416 /*
2417 * Create the command line and environment.
2418 */
2419 rc = mkWinChildcareWorkerConvertCommandline(papszArgs, 0 /*fFlags*/, &pwszCommandLine);
2420 if (rc != 0)
2421 ON(fatal, NILF, _("MkWinChildReExecMake: mkWinChildcareWorkerConvertCommandline failed: %u\n"), rc);
2422
2423 rc = mkWinChildcareWorkerConvertEnvironment(papszEnv ? papszEnv : environ, 0 /*cbEnvStrings*/,
2424 &pwszzEnvironment, &pwszPathIgnored);
2425 if (rc != 0)
2426 ON(fatal, NILF, _("MkWinChildReExecMake: mkWinChildcareWorkerConvertEnvironment failed: %u\n"), rc);
2427
2428
2429 /*
2430 * Fill out the startup info and try create the process.
2431 */
2432 memset(&ProcInfo, 0, sizeof(ProcInfo));
2433 memset(&StartupInfo, 0, sizeof(StartupInfo));
2434 StartupInfo.cb = sizeof(StartupInfo);
2435 GetStartupInfoW(&StartupInfo);
2436 if (!CreateProcessW(wszImageName, pwszCommandLine, NULL /*pProcSecAttr*/, NULL /*pThreadSecAttr*/,
2437 TRUE /*fInheritHandles*/, CREATE_UNICODE_ENVIRONMENT, pwszzEnvironment, NULL /*pwsz*/,
2438 &StartupInfo, &ProcInfo))
2439 ON(fatal, NILF, _("MkWinChildReExecMake: CreateProcessW failed: %u\n"), GetLastError());
2440 CloseHandle(ProcInfo.hThread);
2441
2442 /*
2443 * Wait for it to complete and forward the status code to our parent.
2444 */
2445 for (;;)
2446 {
2447 DWORD dwExitCode = -2222;
2448 DWORD dwStatus = WaitForSingleObject(ProcInfo.hProcess, INFINITE);
2449 if ( dwStatus == WAIT_IO_COMPLETION
2450 || dwStatus == WAIT_TIMEOUT /* whatever */)
2451 continue; /* however unlikely, these aren't fatal. */
2452
2453 /* Get the status code and terminate. */
2454 if (dwStatus == WAIT_OBJECT_0)
2455 {
2456 if (!GetExitCodeProcess(ProcInfo.hProcess, &dwExitCode))
2457 {
2458 ON(fatal, NILF, _("MkWinChildReExecMake: GetExitCodeProcess failed: %u\n"), GetLastError());
2459 dwExitCode = -2222;
2460 }
2461 }
2462 else if (dwStatus)
2463 dwExitCode = dwStatus;
2464
2465 CloseHandle(ProcInfo.hProcess);
2466 for (;;)
2467 exit(dwExitCode);
2468 }
2469}
2470
2471#if 0 /* no longer needed */
2472/** Serialization with kmkbuiltin_redirect. */
2473void MkWinChildExclusiveAcquire(void)
2474{
2475 AcquireSRWLockExclusive(&g_RWLock);
2476}
2477
2478/** Serialization with kmkbuiltin_redirect. */
2479void MkWinChildExclusiveRelease(void)
2480{
2481 ReleaseSRWLockExclusive(&g_RWLock);
2482}
2483#endif
2484
2485/**
2486 * Implementation of the CLOSE_ON_EXEC macro.
2487 *
2488 * @returns errno value.
2489 * @param fd The file descriptor to hide from children.
2490 */
2491int MkWinChildUnrelatedCloseOnExec(int fd)
2492{
2493 if (fd >= 0)
2494 {
2495 HANDLE hNative = (HANDLE)_get_osfhandle(fd);
2496 if (hNative != INVALID_HANDLE_VALUE && hNative != NULL)
2497 {
2498 if (SetHandleInformation(hNative, HANDLE_FLAG_INHERIT /*clear*/ , 0 /*set*/))
2499 return 0;
2500 }
2501 return errno;
2502 }
2503 return EINVAL;
2504}
2505
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette