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

Last change on this file since 3170 was 3169, checked in by bird, 8 years ago

kmk/win: Run kDepObj on worker thread.

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