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

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

kmk/winchildren: PATH searching fixes.

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