source: trunk/src/kmk/w32/winchildren.c@ 3168

Last change on this file since 3168 was 3165, checked in by bird, 8 years ago

kmk/win: g_RWLock no longer needed.

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