VirtualBox

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

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

winchildren: Display messages every 15 or 30 secons when processes keep on running. Strighten out the PATH searching. Use the directory cache when resolving the executable image, now that kFsCache have basic locking in place. Be more careful in mkWinChildCopyStringArray to make sure the strings are always zero terminated, even if the input is volatile/corrupted for some reason (parnaoia).

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

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