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

Last change on this file since 3670 was 3667, checked in by bird, 5 months ago

kmk: More debugging code for the mysterious 'kmk: write error: stdout' problem...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 140.2 KB
Line 
1/* $Id: winchildren.c 3667 2025-02-21 14:36:48Z 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.
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 * @section sec_win_children_av Remarks on Microsoft Defender and other AV
67 *
68 * Part of the motivation for writing this code was horrible CPU utilization on
69 * a brand new AMD Threadripper 1950X system with lots of memory and SSDs,
70 * running 64-bit Windows 10 build 16299.
71 *
72 * Turns out Microsoft defender adds some overhead to CreateProcess
73 * and other stuff:
74 * - Old make with CreateProcess on main thread:
75 * - With runtime defender enabled: 14 min 6 seconds
76 * - With runtime defender disabled: 4 min 49 seconds
77 * - New make with CreateProcess on worker thread (this code):
78 * - With runtime defender enabled: 6 min 29 seconds
79 * - With runtime defender disabled: 4 min 36 seconds
80 * - With runtime defender disabled out dir only: 5 min 59 seconds
81 *
82 * See also kWorker / kSubmit for more bickering about AV & disk encryption.
83 */
84
85
86/*********************************************************************************************************************************
87* Header Files *
88*********************************************************************************************************************************/
89#include <Windows.h>
90#include <Winternl.h>
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 <assert.h>
100#include <process.h>
101#include <intrin.h>
102
103#include "nt/nt_child_inject_standard_handles.h"
104#include "console.h"
105
106#ifndef KMK_BUILTIN_STANDALONE
107extern void kmk_cache_exec_image_w(const wchar_t *); /* imagecache.c */
108#endif
109
110/* Option values from main.c: */
111extern const char *win_job_object_mode;
112extern const char *win_job_object_name;
113extern int win_job_object_no_kill;
114
115
116/*********************************************************************************************************************************
117* Defined Constants And Macros *
118*********************************************************************************************************************************/
119#define MKWINCHILD_MAX_PATH 1024
120
121#define MKWINCHILD_DO_SET_PROCESSOR_GROUP
122
123/** Checks the UTF-16 environment variable pointed to is the PATH. */
124#define IS_PATH_ENV_VAR(a_cwcVar, a_pwszVar) \
125 ( (a_cwcVar) >= 5 \
126 && (a_pwszVar)[4] == L'=' \
127 && ((a_pwszVar)[0] == L'P' || (a_pwszVar)[0] == L'p') \
128 && ((a_pwszVar)[1] == L'A' || (a_pwszVar)[1] == L'a') \
129 && ((a_pwszVar)[2] == L'T' || (a_pwszVar)[2] == L't') \
130 && ((a_pwszVar)[3] == L'H' || (a_pwszVar)[3] == L'h') )
131
132
133/*********************************************************************************************************************************
134* Structures and Typedefs *
135*********************************************************************************************************************************/
136/** Pointer to a childcare worker thread. */
137typedef struct WINCHILDCAREWORKER *PWINCHILDCAREWORKER;
138/** Pointer to a windows child process. */
139typedef struct WINCHILD *PWINCHILD;
140
141
142/**
143 * Child process type.
144 */
145typedef enum WINCHILDTYPE
146{
147 WINCHILDTYPE_INVALID = 0,
148 /** Normal child process. */
149 WINCHILDTYPE_PROCESS,
150#ifdef KMK
151 /** kmkbuiltin command. */
152 WINCHILDTYPE_BUILT_IN,
153 /** kmkbuiltin_append result write out. */
154 WINCHILDTYPE_APPEND,
155 /** kSubmit job. */
156 WINCHILDTYPE_SUBMIT,
157 /** kmk_redirect job. */
158 WINCHILDTYPE_REDIRECT,
159#endif
160 /** End of valid child types. */
161 WINCHILDTYPE_END
162} WINCHILDTYPE;
163
164
165/**
166 * Windows child process.
167 */
168typedef struct WINCHILD
169{
170 /** Magic / eyecatcher (WINCHILD_MAGIC). */
171 ULONG uMagic;
172 /** Child type. */
173 WINCHILDTYPE enmType;
174 /** Pointer to the next child process. */
175 PWINCHILD pNext;
176 /** The pid for this child. */
177 pid_t pid;
178 /** The make child structure associated with this child. */
179 struct child *pMkChild;
180
181 /** The process exit code. */
182 int iExitCode;
183 /** Kill signal, in case we or someone else killed it. */
184 int iSignal;
185 /** Set if core was dumped. */
186 int fCoreDumped;
187 /** Set if the a child process is a candidate for cl.exe where we supress
188 * annoying source name output. */
189 BOOL fProbableClExe;
190 /** The worker executing this child. */
191 PWINCHILDCAREWORKER pWorker;
192
193 /** Type specific data. */
194 union
195 {
196 /** Data for WINCHILDTYPE_PROCESS. */
197 struct
198 {
199 /** Argument vector (single allocation, strings following array). */
200 char **papszArgs;
201 /** Length of the argument strings. */
202 size_t cbArgsStrings;
203 /** Environment vector. Only a copy if fEnvIsCopy is set. */
204 char **papszEnv;
205 /** If we made a copy of the environment, this is the size of the
206 * strings and terminator string (not in array). This is done to
207 * speed up conversion, since MultiByteToWideChar can handle '\0'. */
208 size_t cbEnvStrings;
209 /** The make shell to use (copy). */
210 char *pszShell;
211 /** Handle to use for standard out. */
212 HANDLE hStdOut;
213 /** Handle to use for standard out. */
214 HANDLE hStdErr;
215 /** Whether to close hStdOut after creating the process. */
216 BOOL fCloseStdOut;
217 /** Whether to close hStdErr after creating the process. */
218 BOOL fCloseStdErr;
219 /** Whether to catch output from the process. */
220 BOOL fCatchOutput;
221
222 /** Child process handle. */
223 HANDLE hProcess;
224 } Process;
225
226 /** Data for WINCHILDTYPE_BUILT_IN. */
227 struct
228 {
229 /** The built-in command. */
230 PCKMKBUILTINENTRY pBuiltIn;
231 /** Number of arguments. */
232 int cArgs;
233 /** Argument vector (single allocation, strings following array). */
234 char **papszArgs;
235 /** Environment vector. Only a copy if fEnvIsCopy is set. */
236 char **papszEnv;
237 } BuiltIn;
238
239 /** Data for WINCHILDTYPE_APPEND. */
240 struct
241 {
242 /** The filename. */
243 char *pszFilename;
244 /** How much to append. */
245 size_t cbAppend;
246 /** What to append. */
247 char *pszAppend;
248 /** Whether to truncate the file. */
249 int fTruncate;
250 } Append;
251
252 /** Data for WINCHILDTYPE_SUBMIT. */
253 struct
254 {
255 /** The event we're to wait on (hooked up to a pipe) */
256 HANDLE hEvent;
257 /** Parameter for the cleanup callback. */
258 void *pvSubmitWorker;
259 /** Standard output catching pipe. Optional. */
260 PWINCCWPIPE pStdOut;
261 /** Standard error catching pipe. Optional. */
262 PWINCCWPIPE pStdErr;
263 } Submit;
264
265 /** Data for WINCHILDTYPE_REDIRECT. */
266 struct
267 {
268 /** Child process handle. */
269 HANDLE hProcess;
270 } Redirect;
271 } u;
272
273} WINCHILD;
274/** WINCHILD::uMagic value. */
275#define WINCHILD_MAGIC 0xbabebabeU
276
277
278/**
279 * Data for a windows childcare worker thread.
280 *
281 * We use one worker thread per child, reusing the threads when possible.
282 *
283 * This setup helps avoid the 64-bit handle with the WaitForMultipleObject API.
284 *
285 * It also helps using all CPUs on systems with more than one CPU group
286 * (typically systems with more than 64 CPU threads or/and multiple sockets, or
287 * special configs).
288 *
289 * This helps facilitates using pipes for collecting output child rather
290 * than temporary files. Pipes doesn't involve NTFS and can easily be reused.
291 *
292 * Finally, kBuild specific, this allows running kmkbuiltin_xxxx commands in
293 * threads.
294 */
295typedef struct WINCHILDCAREWORKER
296{
297 /** Magic / eyecatcher (WINCHILDCAREWORKER_MAGIC). */
298 ULONG uMagic;
299 /** The worker index. */
300 unsigned int idxWorker;
301 /** The processor group for this worker. */
302 unsigned int iProcessorGroup;
303 /** The thread ID. */
304 unsigned int tid;
305 /** The thread handle. */
306 HANDLE hThread;
307 /** The event the thread is idling on. */
308 HANDLE hEvtIdle;
309 /** The pipe catching standard output from a child. */
310 PWINCCWPIPE pStdOut;
311 /** The pipe catching standard error from a child. */
312 PWINCCWPIPE pStdErr;
313
314 /** Pointer to the current child. */
315 PWINCHILD volatile pCurChild;
316 /** List of children pending execution on this worker.
317 * This is updated atomitically just like g_pTailCompletedChildren. */
318 PWINCHILD volatile pTailTodoChildren;
319 /** TRUE if idle, FALSE if not. */
320 long volatile fIdle;
321} WINCHILDCAREWORKER;
322/** WINCHILD::uMagic value. */
323#define WINCHILDCAREWORKER_MAGIC 0xdad0dad0U
324
325
326/*********************************************************************************************************************************
327* Global Variables *
328*********************************************************************************************************************************/
329/** Whether it's initialized or not. */
330static BOOL g_fInitialized = FALSE;
331/** Set when we're shutting down everything. */
332static BOOL volatile g_fShutdown = FALSE;
333/** Event used to wait for children. */
334static HANDLE g_hEvtWaitChildren = INVALID_HANDLE_VALUE;
335/** Number of childcare workers currently in g_papChildCareworkers. */
336static unsigned g_cChildCareworkers = 0;
337/** Maximum number of childcare workers in g_papChildCareworkers. */
338static unsigned g_cChildCareworkersMax = 0;
339/** Pointer to childcare workers. */
340static PWINCHILDCAREWORKER *g_papChildCareworkers = NULL;
341/** The processor group allocator state. */
342static MKWINCHILDCPUGROUPALLOCSTATE g_ProcessorGroupAllocator;
343/** Number of processor groups in the system. */
344static unsigned g_cProcessorGroups = 1;
345/** Array detailing how many active processors there are in each group. */
346static unsigned const *g_pacProcessorsInGroup = &g_cProcessorGroups;
347/** Info from the GetLogicalProcessorInformationEx call - can be NULL. */
348static GROUP_RELATIONSHIP const *g_pGroupInfo = NULL;
349
350/** Kernel32!GetActiveProcessorGroupCount */
351static WORD (WINAPI *g_pfnGetActiveProcessorGroupCount)(VOID);
352/** Kernel32!GetActiveProcessorCount */
353static DWORD (WINAPI *g_pfnGetActiveProcessorCount)(WORD);
354/** Kernel32!GetLogicalProcessorInformationEx */
355static DWORD (WINAPI *g_pfnGetLogicalProcessorInformationEx)(LOGICAL_PROCESSOR_RELATIONSHIP,
356 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD);
357/** Kernel32!SetThreadGroupAffinity */
358static BOOL (WINAPI *g_pfnSetThreadGroupAffinity)(HANDLE, CONST GROUP_AFFINITY *, GROUP_AFFINITY *);
359/** Kernel32!SetThreadSelectedCpuSetMasks (windows 11+). */
360static BOOL (WINAPI *g_pfnSetThreadSelectedCpuSetMasks)(HANDLE, PGROUP_AFFINITY, USHORT);
361/** Kernel32!SetProcessDefaultCpuSetMasks (windows 11+). */
362static BOOL (WINAPI *g_pfnSetProcessDefaultCpuSetMasks)(HANDLE, PGROUP_AFFINITY, USHORT);
363/** NTDLL!NtQueryInformationProcess */
364static NTSTATUS (NTAPI *g_pfnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
365
366/** Set if the windows host is 64-bit. */
367static BOOL g_f64BitHost = (K_ARCH_BITS == 64);
368/** Windows version info.
369 * @note Putting this before the volatile stuff, hoping to keep it in a
370 * different cache line than the static bits above. */
371static OSVERSIONINFOA g_VersionInfo = { sizeof(g_VersionInfo), 4, 0, 1381, VER_PLATFORM_WIN32_NT, {0} };
372
373/** Children that has been completed.
374 * This is updated atomically, pushing completed children in LIFO fashion
375 * (thus 'tail'), then hitting g_hEvtWaitChildren if head. */
376static PWINCHILD volatile g_pTailCompletedChildren = NULL;
377
378/** Number of idle pending children.
379 * This is updated before g_hEvtWaitChildren is signalled. */
380static unsigned volatile g_cPendingChildren = 0;
381
382/** Number of idle childcare worker threads. */
383static unsigned volatile g_cIdleChildcareWorkers = 0;
384/** Index of the last idle child careworker (just a hint). */
385static unsigned volatile g_idxLastChildcareWorker = 0;
386
387#ifdef WITH_RW_LOCK
388/** RW lock for serializing kmkbuiltin_redirect and CreateProcess. */
389static SRWLOCK g_RWLock;
390#endif
391
392/** The job object for this make instance, if we created/opened one. */
393static HANDLE g_hJob = NULL;
394
395
396/*********************************************************************************************************************************
397* Internal Functions *
398*********************************************************************************************************************************/
399static void mkWinChildInitJobObjectAssociation(void);
400
401#if K_ARCH_BITS == 32 && !defined(_InterlockedCompareExchangePointer)
402/** _InterlockedCompareExchangePointer is missing? (VS2010) */
403K_INLINE void *_InterlockedCompareExchangePointer(void * volatile *ppvDst, void *pvNew, void *pvOld)
404{
405 return (void *)_InterlockedCompareExchange((long volatile *)ppvDst, (intptr_t)pvNew, (intptr_t)pvOld);
406}
407#endif
408
409
410/**
411 * Initializes the windows child module.
412 *
413 * @param cJobSlots The number of job slots.
414 */
415void MkWinChildInit(unsigned int cJobSlots)
416{
417 HMODULE hmod;
418
419 /*
420 * Figure out how many childcare workers first.
421 */
422 static unsigned int const s_cMaxWorkers = 4096;
423 unsigned cWorkers;
424 if (cJobSlots >= 1 && cJobSlots < s_cMaxWorkers)
425 cWorkers = cJobSlots;
426 else
427 cWorkers = s_cMaxWorkers;
428
429 /*
430 * Allocate the array and the child completed event object.
431 */
432 g_papChildCareworkers = (PWINCHILDCAREWORKER *)xcalloc(cWorkers * sizeof(g_papChildCareworkers[0]));
433 g_cChildCareworkersMax = cWorkers;
434
435 g_hEvtWaitChildren = CreateEvent(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pszName*/);
436 if (!g_hEvtWaitChildren)
437 fatal(NILF, INTSTR_LENGTH, _("MkWinChildInit: CreateEvent failed: %u"), GetLastError());
438
439 /*
440 * NTDLL imports that we need.
441 */
442 hmod = GetModuleHandleA("NTDLL.DLL");
443 *(FARPROC *)&g_pfnNtQueryInformationProcess = GetProcAddress(hmod, "NtQueryInformationProcess");
444 if (!g_pfnNtQueryInformationProcess)
445 fatal(NILF, 0, _("MkWinChildInit: NtQueryInformationProcess not found"));
446
447#if K_ARCH_BITS == 32
448 /*
449 * Initialize g_f64BitHost.
450 */
451 if (!IsWow64Process(GetCurrentProcess(), &g_f64BitHost))
452 fatal(NILF, INTSTR_LENGTH, _("MkWinChildInit: IsWow64Process failed: %u"), GetLastError());
453#elif K_ARCH_BITS == 64
454 assert(g_f64BitHost);
455#else
456# error "K_ARCH_BITS is bad/missing"
457#endif
458
459 /*
460 * Figure out how many processor groups there are.
461 * For that we need to first figure the windows version.
462 */
463 if (!GetVersionExA(&g_VersionInfo))
464 {
465 DWORD uRawVer = GetVersion();
466 g_VersionInfo.dwMajorVersion = uRawVer & 0xff;
467 g_VersionInfo.dwMinorVersion = (uRawVer >> 8) & 0xff;
468 g_VersionInfo.dwBuildNumber = (uRawVer >> 16) & 0x7fff;
469 }
470 if (g_VersionInfo.dwMajorVersion >= 6)
471 {
472 hmod = GetModuleHandleA("KERNEL32.DLL");
473 *(FARPROC *)&g_pfnGetActiveProcessorGroupCount = GetProcAddress(hmod, "GetActiveProcessorGroupCount");
474 *(FARPROC *)&g_pfnGetActiveProcessorCount = GetProcAddress(hmod, "GetActiveProcessorCount");
475 *(FARPROC *)&g_pfnSetThreadGroupAffinity = GetProcAddress(hmod, "SetThreadGroupAffinity");
476 *(FARPROC *)&g_pfnGetLogicalProcessorInformationEx = GetProcAddress(hmod, "GetLogicalProcessorInformationEx");
477 *(FARPROC *)&g_pfnSetThreadSelectedCpuSetMasks = GetProcAddress(hmod, "SetThreadSelectedCpuSetMasks"); /* W11 */
478 *(FARPROC *)&g_pfnSetProcessDefaultCpuSetMasks = GetProcAddress(hmod, "SetProcessDefaultCpuSetMasks"); /* W11 */
479 if ( g_pfnSetThreadGroupAffinity
480 && g_pfnGetActiveProcessorCount
481 && g_pfnGetActiveProcessorGroupCount
482 && g_pfnGetLogicalProcessorInformationEx)
483 {
484 unsigned int *pacProcessorsInGroup;
485 unsigned iGroup;
486 g_cProcessorGroups = g_pfnGetActiveProcessorGroupCount();
487 if (g_cProcessorGroups == 0)
488 g_cProcessorGroups = 1;
489
490 pacProcessorsInGroup = (unsigned int *)xmalloc(sizeof(g_pacProcessorsInGroup[0]) * g_cProcessorGroups);
491 g_pacProcessorsInGroup = pacProcessorsInGroup;
492 for (iGroup = 0; iGroup < g_cProcessorGroups; iGroup++)
493 pacProcessorsInGroup[iGroup] = g_pfnGetActiveProcessorCount(iGroup);
494
495 /* Get active process group affinity masks so we can set them correctly. */
496 {
497 DWORD cbNeeded = cbNeeded = offsetof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, Group)
498 + offsetof(GROUP_RELATIONSHIP, GroupInfo)
499 + sizeof(PROCESSOR_GROUP_INFO) * (g_cProcessorGroups + 4);
500 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX pInfo = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)xcalloc(cbNeeded);
501 BOOL fRet = g_pfnGetLogicalProcessorInformationEx(RelationGroup, pInfo, &cbNeeded);
502 if (!fRet && GetLastError() == ERROR_BUFFER_OVERFLOW)
503 {
504 free(pInfo);
505 pInfo = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)xcalloc(cbNeeded);
506 fRet = g_pfnGetLogicalProcessorInformationEx(RelationGroup, pInfo, &cbNeeded);
507 }
508 if ( fRet
509 && pInfo->Group.ActiveGroupCount > 0
510 && pInfo->Group.ActiveGroupCount <= pInfo->Group.MaximumGroupCount)
511 {
512 g_pGroupInfo = &pInfo->Group; /* leaked */
513 pInfo = NULL;
514 }
515 else
516 free(pInfo);
517 }
518
519 /* Iff this is windows 11 or later, the default policy should be that
520 threads can be scheduled in any CPU group, so reset any prior crap
521 to make sure this remains the case for us and our children.
522
523 This means we can skip g_pfnSetThreadGroupAffinity the calls later. */
524 if (g_pfnSetProcessDefaultCpuSetMasks)
525 g_pfnSetProcessDefaultCpuSetMasks(GetCurrentProcess(), NULL, 0);
526 if (g_pfnSetThreadSelectedCpuSetMasks)
527 g_pfnSetThreadSelectedCpuSetMasks(GetCurrentThread(), NULL, 0);
528
529 MkWinChildInitCpuGroupAllocator(&g_ProcessorGroupAllocator);
530 }
531 else
532 {
533 g_pfnSetThreadGroupAffinity = NULL;
534 g_pfnGetActiveProcessorCount = NULL;
535 g_pfnGetActiveProcessorGroupCount = NULL;
536 g_pfnGetLogicalProcessorInformationEx = NULL;
537 }
538 }
539
540#ifdef WITH_RW_LOCK
541 /*
542 * For serializing with standard file handle manipulation (kmkbuiltin_redirect).
543 */
544 InitializeSRWLock(&g_RWLock);
545#endif
546
547 /*
548 * Associate with a job object.
549 */
550 mkWinChildInitJobObjectAssociation();
551
552 /*
553 * This is dead code that was thought to fix a problem observed doing
554 * `tcc.exe /c "kmk |& tee bld.log"` and leading to a crash in cl.exe
555 * when spawned with fInheritHandles = FALSE, see hStdErr=NULL in the
556 * child. However, it turns out this was probably caused by not clearing
557 * the CRT file descriptor and handle table in the startup info.
558 * Leaving the code here in case it comes in handy after all.
559 */
560#if 0
561 {
562 struct
563 {
564 DWORD uStdHandle;
565 HANDLE hHandle;
566 } aHandles[3] = { { STD_INPUT_HANDLE, NULL }, { STD_OUTPUT_HANDLE, NULL }, { STD_ERROR_HANDLE, NULL } };
567 int i;
568
569 for (i = 0; i < 3; i++)
570 aHandles[i].hHandle = GetStdHandle(aHandles[i].uStdHandle);
571
572 for (i = 0; i < 3; i++)
573 if ( aHandles[i].hHandle == NULL
574 || aHandles[i].hHandle == INVALID_HANDLE_VALUE)
575 {
576 int fd = open("nul", _O_RDWR);
577 if (fd >= 0)
578 {
579 if (_dup2(fd, i) >= 0)
580 {
581 assert((HANDLE)_get_osfhandle(i) != aHandles[i].hHandle);
582 assert((HANDLE)_get_osfhandle(i) == GetStdHandle(aHandles[i].uStdHandle));
583 }
584 else
585 ONNNS(fatal, NILF, "_dup2(%d('nul'), %d) failed: %u (%s)", fd, i, errno, strerror(errno));
586 if (fd != i)
587 close(fd);
588 }
589 else
590 ONNS(fatal, NILF, "open(nul,RW) failed: %u (%s)", i, errno, strerror(errno));
591 }
592 else
593 {
594 int j;
595 for (j = i + 1; j < 3; j++)
596 if (aHandles[j].hHandle == aHandles[i].hHandle)
597 {
598 int fd = _dup(j);
599 if (fd >= 0)
600 {
601 if (_dup2(fd, j) >= 0)
602 {
603 aHandles[j].hHandle = (HANDLE)_get_osfhandle(j);
604 assert(aHandles[j].hHandle != aHandles[i].hHandle);
605 assert(aHandles[j].hHandle == GetStdHandle(aHandles[j].uStdHandle));
606 }
607 else
608 ONNNS(fatal, NILF, "_dup2(%d, %d) failed: %u (%s)", fd, j, errno, strerror(errno));
609 if (fd != j)
610 close(fd);
611 }
612 else
613 ONNS(fatal, NILF, "_dup(%d) failed: %u (%s)", j, errno, strerror(errno));
614 }
615 }
616 }
617#endif
618}
619
620/**
621 * Create or open a job object for this make instance and its children.
622 *
623 * Depending on the --job-object=mode value, we typically create/open a job
624 * object here if we're the root make instance. The job object is then
625 * typically configured to kill all remaining processes when the root make
626 * terminates, so that there aren't any stuck processes around messing up
627 * subsequent builds. This is very handy on build servers.
628 *
629 * If we're it no-kill mode, the job object is pretty pointless for manual
630 * cleanup as the job object becomes invisible (or something) when the last
631 * handle to it closes, i.e. g_hJob. On windows 8 and later it looks
632 * like any orphaned children are immediately assigned to the parent job
633 * object. Too bad for kmk_kill and such.
634 *
635 * win_job_object_mode values: login, root, each, none
636 */
637static void mkWinChildInitJobObjectAssociation(void)
638{
639 BOOL fCreate = TRUE;
640 char szJobName[128];
641 const char *pszJobName = win_job_object_name;
642
643 /* Skip if disabled. */
644 if (strcmp(win_job_object_mode, "none") == 0)
645 return;
646
647 /* Skip if not root make instance, unless we're having one job object
648 per make instance. */
649 if ( makelevel != 0
650 && strcmp(win_job_object_mode, "each") != 0)
651 return;
652
653 /* Format the the default job object name if --job-object-name
654 wasn't given. */
655 if (!pszJobName || *pszJobName == '\0')
656 {
657 pszJobName = szJobName;
658 if (strcmp(win_job_object_mode, "login") == 0)
659 {
660 /* Use the AuthenticationId like mspdbsrv.exe does. */
661 HANDLE hToken;
662 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
663 {
664 TOKEN_STATISTICS TokenStats;
665 DWORD cbRet = 0;
666 memset(&TokenStats, 0, sizeof(TokenStats));
667 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
668 snprintf(szJobName, sizeof(szJobName), "kmk-job-obj-login-%08x.%08x",
669 (unsigned)TokenStats.AuthenticationId.HighPart, (unsigned)TokenStats.AuthenticationId.LowPart);
670 else
671 {
672 ONN(message, 0, _("GetTokenInformation failed: %u (cbRet=%u)"), GetLastError(), cbRet);
673 return;
674 }
675 CloseHandle(hToken);
676 }
677 else
678 {
679 ON(message, 0, _("OpenProcessToken failed: %u"), GetLastError());
680 return;
681 }
682 }
683 else
684 {
685 SYSTEMTIME Now = {0};
686 GetSystemTime(&Now);
687 snprintf(szJobName, sizeof(szJobName), "kmk-job-obj-%04u-%02u-%02uT%02u-%02u-%02uZ%u",
688 Now.wYear, Now.wMonth, Now.wDay, Now.wHour, Now.wMinute, Now.wSecond, getpid());
689 }
690 }
691
692 /* In login mode and when given a job object name, we try open it first. */
693 if ( win_job_object_name
694 || strcmp(win_job_object_mode, "login") == 0)
695 {
696 g_hJob = OpenJobObjectA(JOB_OBJECT_ASSIGN_PROCESS, win_job_object_no_kill /*bInheritHandle*/, pszJobName);
697 if (g_hJob)
698 fCreate = FALSE;
699 else
700 {
701 DWORD dwErr = GetLastError();
702 if (dwErr != ERROR_PATH_NOT_FOUND && dwErr != ERROR_FILE_NOT_FOUND)
703 {
704 OSN(message, 0, _("OpenJobObjectA(,,%s) failed: %u"), pszJobName, GetLastError());
705 return;
706 }
707 }
708 }
709
710 if (fCreate)
711 {
712 SECURITY_ATTRIBUTES SecAttr = { sizeof(SecAttr), NULL, TRUE /*bInheritHandle*/ };
713 g_hJob = CreateJobObjectA(win_job_object_no_kill ? &SecAttr : NULL, pszJobName);
714 if (g_hJob)
715 {
716 /* We need to set the BREAKAWAY_OK flag, as we don't want make CreateProcess
717 fail if someone tries to break way. Also set KILL_ON_JOB_CLOSE unless
718 --job-object-no-kill is given. */
719 JOBOBJECT_EXTENDED_LIMIT_INFORMATION Info;
720 DWORD cbActual = 0;
721 memset(&Info, 0, sizeof(Info));
722 if (QueryInformationJobObject(g_hJob, JobObjectExtendedLimitInformation, &Info, sizeof(Info), &cbActual))
723 {
724 Info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK;
725 if (!win_job_object_no_kill)
726 Info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
727 else
728 Info.BasicLimitInformation.LimitFlags &= ~JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
729 if (!SetInformationJobObject(g_hJob, JobObjectExtendedLimitInformation, &Info, sizeof(Info)))
730 OSSN(message, 0, _("SetInformationJobObject(%s,JobObjectExtendedLimitInformation,{%s},) failed: %u"),
731 pszJobName, win_job_object_mode, GetLastError());
732 }
733 else
734 OSN(message, 0, _("QueryInformationJobObject(%s,JobObjectExtendedLimitInformation,,,) failed: %u"),
735 pszJobName, GetLastError());
736 }
737 else
738 {
739 OSN(message, 0, _("CreateJobObjectA(NULL,%s) failed: %u"), pszJobName, GetLastError());
740 return;
741 }
742 }
743
744 /* Make it our job object. */
745 if (!(AssignProcessToJobObject(g_hJob, GetCurrentProcess())))
746 OSN(message, 0, _("AssignProcessToJobObject(%s, me) failed: %u"), pszJobName, GetLastError());
747}
748
749/**
750 * Used by mkWinChildcareWorkerThread() and MkWinChildWait() to get the head
751 * child from a lifo (g_pTailCompletedChildren, pTailTodoChildren).
752 *
753 * @returns Head child.
754 * @param ppTail Pointer to the child variable.
755 * @param pChild Tail child.
756 */
757static PWINCHILD mkWinChildDequeFromLifo(PWINCHILD volatile *ppTail, PWINCHILD pChild)
758{
759 if (pChild->pNext)
760 {
761 PWINCHILD pPrev;
762 do
763 {
764 pPrev = pChild;
765 pChild = pChild->pNext;
766 } while (pChild->pNext);
767 pPrev->pNext = NULL;
768 }
769 else
770 {
771 PWINCHILD const pWantedChild = pChild;
772 pChild = _InterlockedCompareExchangePointer(ppTail, NULL, pWantedChild);
773 if (pChild != pWantedChild)
774 {
775 PWINCHILD pPrev;
776 do
777 {
778 pPrev = pChild;
779 pChild = pChild->pNext;
780 } while (pChild->pNext);
781 pPrev->pNext = NULL;
782 assert(pChild == pWantedChild);
783 }
784 }
785 return pChild;
786}
787
788/**
789 * Output error message while running on a worker thread.
790 *
791 * @returns -1
792 * @param pWorker The calling worker. Mainly for getting the
793 * current child and its stderr output unit. Pass
794 * NULL if the output should go thru the child
795 * stderr buffering.
796 * @param iType The error type:
797 * - 0: more of a info directly to stdout,
798 * - 1: child related error,
799 * - 2: child related error for immedate release.
800 * @param pszFormat The message format string.
801 * @param ... Argument for the message.
802 */
803static int MkWinChildError(PWINCHILDCAREWORKER pWorker, int iType, const char *pszFormat, ...)
804{
805 /*
806 * Format the message into stack buffer.
807 */
808 char szMsg[4096];
809 int cchMsg;
810 int cchPrefix;
811 va_list va;
812
813 /* Compose the prefix, being paranoid about it not exceeding the buffer in any way. */
814 const char *pszInfix = iType == 0 ? "info: " : "error: ";
815 const char *pszProgram = program;
816 if (strlen(pszProgram) > 80)
817 {
818#ifdef KMK
819 pszProgram = "kmk";
820#else
821 pszProgram = "gnumake";
822#endif
823 }
824 if (makelevel == 0)
825 cchPrefix = snprintf(szMsg, sizeof(szMsg) / 2, "%s: %s", pszProgram, pszInfix);
826 else
827 cchPrefix = snprintf(szMsg, sizeof(szMsg) / 2, "%s[%u]: %s", pszProgram, makelevel, pszInfix);
828 assert(cchPrefix < sizeof(szMsg) / 2 && cchPrefix > 0);
829
830 /* Format the user specified message. */
831 va_start(va, pszFormat);
832 cchMsg = vsnprintf(&szMsg[cchPrefix], sizeof(szMsg) - 2 - cchPrefix, pszFormat, va);
833 va_end(va);
834 szMsg[sizeof(szMsg) - 2] = '\0';
835 cchMsg = strlen(szMsg);
836
837 /* Make sure there's a newline at the end of it (we reserved space for that). */
838 if (cchMsg <= 0 || szMsg[cchMsg - 1] != '\n')
839 {
840 szMsg[cchMsg++] = '\n';
841 szMsg[cchMsg] = '\0';
842 }
843
844#ifdef CONFIG_WITH_OUTPUT_IN_MEMORY
845 /*
846 * Try use the stderr of the current child of the worker.
847 */
848 if ( iType != 0
849 && iType != 3
850 && pWorker)
851 {
852 PWINCHILD pChild = pWorker->pCurChild;
853 if (pChild)
854 {
855 struct child *pMkChild = pChild->pMkChild;
856 if (pMkChild)
857 {
858 output_write_text(&pMkChild->output, 1 /*is_err*/, szMsg, cchMsg);
859 return -1;
860 }
861 }
862 }
863#endif
864
865 /*
866 * Fallback to writing directly to stderr.
867 */
868 maybe_con_fwrite(szMsg, cchMsg, 1, iType == 0 ? stdout : stderr);
869 return -1;
870}
871
872/**
873 * Duplicates the given UTF-16 string.
874 *
875 * @returns 0
876 * @param pwszSrc The UTF-16 string to duplicate.
877 * @param cwcSrc Length, may include the terminator.
878 * @param ppwszDst Where to return the duplicate.
879 */
880static int mkWinChildDuplicateUtf16String(const WCHAR *pwszSrc, size_t cwcSrc, WCHAR **ppwszDst)
881{
882 size_t cb = sizeof(WCHAR) * cwcSrc;
883 if (cwcSrc > 0 && pwszSrc[cwcSrc - 1] == L'\0')
884 *ppwszDst = (WCHAR *)memcpy(xmalloc(cb), pwszSrc, cb);
885 else
886 {
887 WCHAR *pwszDst = (WCHAR *)xmalloc(cb + sizeof(WCHAR));
888 memcpy(pwszDst, pwszSrc, cb);
889 pwszDst[cwcSrc] = L'\0';
890 *ppwszDst = pwszDst;
891 }
892 return 0;
893}
894
895
896/**
897 * Used to flush data we're read but not yet written at the termination of a
898 * process.
899 *
900 * @param pChild The child.
901 * @param pPipe The pipe.
902 */
903static void mkWinChildcareWorkerFlushUnwritten(PWINCHILD pChild, PWINCCWPIPE pPipe)
904{
905 DWORD cbUnwritten = pPipe->offPendingRead - pPipe->cbWritten;
906 assert(pPipe->cbWritten <= pPipe->cbBuffer - 16);
907 assert(pPipe->offPendingRead <= pPipe->cbBuffer - 16);
908 if (cbUnwritten)
909 {
910#ifdef CONFIG_WITH_OUTPUT_IN_MEMORY
911 if (pChild && pChild->pMkChild)
912 {
913 output_write_bin(&pChild->pMkChild->output, pPipe->iWhich == 2, &pPipe->pbBuffer[pPipe->cbWritten], cbUnwritten);
914 pPipe->cbWritten += cbUnwritten;
915 }
916 else
917#endif
918 {
919 DWORD cbWritten = 0;
920 if (WriteFile(GetStdHandle(pPipe->iWhich == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE),
921 &pPipe->pbBuffer[pPipe->cbWritten], cbUnwritten, &cbWritten, NULL))
922 pPipe->cbWritten += cbWritten <= cbUnwritten ? cbWritten : cbUnwritten; /* paranoia */
923 }
924 pPipe->fHaveWrittenOut = TRUE;
925 }
926}
927
928/**
929 * This logic mirrors kwSandboxConsoleFlushAll.
930 *
931 * @returns TRUE if it looks like a CL.EXE source line, otherwise FALSE.
932 * @param pPipe The pipe.
933 * @param offStart The start of the output in the pipe buffer.
934 * @param offEnd The end of the output in the pipe buffer.
935 */
936static BOOL mkWinChildcareWorkerIsClExeSourceLine(PWINCCWPIPE pPipe, DWORD offStart, DWORD offEnd)
937{
938 if (offEnd < offStart + 2)
939 return FALSE;
940 if (offEnd - offStart > 80)
941 return FALSE;
942
943 if ( pPipe->pbBuffer[offEnd - 2] != '\r'
944 || pPipe->pbBuffer[offEnd - 1] != '\n')
945 return FALSE;
946
947 offEnd -= 2;
948 while (offEnd-- > offStart)
949 {
950 char ch = pPipe->pbBuffer[offEnd];
951 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
952 { /* likely */ }
953 else
954 return FALSE;
955 }
956
957 return TRUE;
958}
959
960/**
961 * Adds output to the given standard output for the child.
962 *
963 * There is no pending read when this function is called, so we're free to
964 * reshuffle the buffer if desirable.
965 *
966 * @param pChild The child. Optional (kSubmit).
967 * @param iWhich Which standard descriptor number.
968 * @param cbNewData How much more output was caught.
969 */
970static void mkWinChildcareWorkerCaughtMoreOutput(PWINCHILD pChild, PWINCCWPIPE pPipe, DWORD cbNewData)
971{
972 DWORD offStart = pPipe->cbWritten;
973 assert(offStart <= pPipe->offPendingRead);
974 assert(offStart <= pPipe->cbBuffer - 16);
975 assert(pPipe->offPendingRead <= pPipe->cbBuffer - 16);
976 if (cbNewData > 0)
977 {
978 DWORD offRest;
979
980 /* Move offPendingRead ahead by cbRead. */
981 pPipe->offPendingRead += cbNewData;
982 assert(pPipe->offPendingRead <= pPipe->cbBuffer);
983 if (pPipe->offPendingRead > pPipe->cbBuffer)
984 pPipe->offPendingRead = pPipe->cbBuffer;
985
986 /* Locate the last newline in the buffer. */
987 offRest = pPipe->offPendingRead;
988 while (offRest > offStart && pPipe->pbBuffer[offRest - 1] != '\n')
989 offRest--;
990
991 /* If none were found and we've less than 16 bytes left in the buffer, try
992 find a word boundrary to flush on instead. */
993 if ( offRest <= offStart
994 && pPipe->cbBuffer - pPipe->offPendingRead + offStart < 16)
995 {
996 offRest = pPipe->offPendingRead;
997 while ( offRest > offStart
998 && isalnum(pPipe->pbBuffer[offRest - 1]))
999 offRest--;
1000 if (offRest == offStart)
1001 offRest = pPipe->offPendingRead;
1002 }
1003 /* If this is a potential CL.EXE process, we will keep the source
1004 filename unflushed and maybe discard it at the end. */
1005 else if ( pChild
1006 && pChild->fProbableClExe
1007 && pPipe->iWhich == 1
1008 && offRest == pPipe->offPendingRead
1009 && mkWinChildcareWorkerIsClExeSourceLine(pPipe, offStart, offRest))
1010 offRest = offStart;
1011
1012 if (offRest > offStart)
1013 {
1014 /* Write out offStart..offRest. */
1015 DWORD cbToWrite = offRest - offStart;
1016#ifdef CONFIG_WITH_OUTPUT_IN_MEMORY
1017 if (pChild && pChild->pMkChild)
1018 {
1019 output_write_bin(&pChild->pMkChild->output, pPipe->iWhich == 2, &pPipe->pbBuffer[offStart], cbToWrite);
1020 offStart += cbToWrite;
1021 pPipe->cbWritten = offStart;
1022 }
1023 else
1024#endif
1025 {
1026 DWORD cbWritten = 0;
1027 if (WriteFile(GetStdHandle(pPipe->iWhich == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE),
1028 &pPipe->pbBuffer[offStart], cbToWrite, &cbWritten, NULL))
1029 {
1030 offStart += cbWritten <= cbToWrite ? cbWritten : cbToWrite; /* paranoia */
1031 pPipe->cbWritten = offStart;
1032 }
1033 }
1034 pPipe->fHaveWrittenOut = TRUE;
1035 }
1036 }
1037
1038 /* Shuffle the data to the front of the buffer. */
1039 if (offStart > 0)
1040 {
1041 DWORD cbUnwritten = pPipe->offPendingRead - offStart;
1042 if (cbUnwritten > 0)
1043 memmove(pPipe->pbBuffer, &pPipe->pbBuffer[offStart], cbUnwritten);
1044 pPipe->offPendingRead -= pPipe->cbWritten;
1045 pPipe->cbWritten = 0;
1046 }
1047}
1048
1049/**
1050 * Catches output from the given pipe.
1051 *
1052 * @param pChild The child. Optional (kSubmit).
1053 * @param pPipe The pipe.
1054 * @param fDraining Set if we're draining the pipe after the process
1055 * terminated.
1056 */
1057static void mkWinChildcareWorkerCatchOutput(PWINCHILD pChild, PWINCCWPIPE pPipe, BOOL fDraining)
1058{
1059 /*
1060 * Deal with already pending read.
1061 */
1062 if (pPipe->fReadPending)
1063 {
1064 DWORD cbRead = 0;
1065 if (GetOverlappedResult(pPipe->hPipeMine, &pPipe->Overlapped, &cbRead, !fDraining))
1066 {
1067 mkWinChildcareWorkerCaughtMoreOutput(pChild, pPipe, cbRead);
1068 pPipe->fReadPending = FALSE;
1069 }
1070 else if (fDraining && GetLastError() == ERROR_IO_INCOMPLETE)
1071 return;
1072 else
1073 {
1074 MkWinChildError(pChild ? pChild->pWorker : NULL, 2, "GetOverlappedResult failed: %u\n", GetLastError());
1075 pPipe->fReadPending = FALSE;
1076 if (fDraining)
1077 return;
1078 }
1079 }
1080
1081 /*
1082 * Read data till one becomes pending.
1083 */
1084 for (;;)
1085 {
1086 DWORD cbRead;
1087
1088 memset(&pPipe->Overlapped, 0, sizeof(pPipe->Overlapped));
1089 pPipe->Overlapped.hEvent = pPipe->hEvent;
1090 ResetEvent(pPipe->hEvent);
1091
1092 assert(pPipe->offPendingRead < pPipe->cbBuffer);
1093 SetLastError(0);
1094 cbRead = 0;
1095 if (!ReadFile(pPipe->hPipeMine, &pPipe->pbBuffer[pPipe->offPendingRead],
1096 pPipe->cbBuffer - pPipe->offPendingRead, &cbRead, &pPipe->Overlapped))
1097 {
1098 DWORD dwErr = GetLastError();
1099 if (dwErr == ERROR_IO_PENDING)
1100 pPipe->fReadPending = TRUE;
1101 else
1102 MkWinChildError(pChild ? pChild->pWorker : NULL, 2,
1103 "ReadFile failed on standard %s: %u\n",
1104 pPipe->iWhich == 1 ? "output" : "error", GetLastError());
1105 return;
1106 }
1107
1108 mkWinChildcareWorkerCaughtMoreOutput(pChild, pPipe, cbRead);
1109 }
1110}
1111
1112/**
1113 * Makes sure the output pipes are drained and pushed to output.
1114 *
1115 * @param pChild The child. Optional (kSubmit).
1116 * @param pStdOut The standard output pipe structure.
1117 * @param pStdErr The standard error pipe structure.
1118 */
1119void MkWinChildcareWorkerDrainPipes(PWINCHILD pChild, PWINCCWPIPE pStdOut, PWINCCWPIPE pStdErr)
1120{
1121 mkWinChildcareWorkerCatchOutput(pChild, pStdOut, TRUE /*fDraining*/);
1122 mkWinChildcareWorkerCatchOutput(pChild, pStdErr, TRUE /*fDraining*/);
1123
1124 /* Drop lone 'source.c' line from CL.exe, but only if no other output at all. */
1125 if ( pChild
1126 && pChild->fProbableClExe
1127 && !pStdOut->fHaveWrittenOut
1128 && !pStdErr->fHaveWrittenOut
1129 && pStdErr->cbWritten == pStdErr->offPendingRead
1130 && pStdOut->cbWritten < pStdOut->offPendingRead
1131 && mkWinChildcareWorkerIsClExeSourceLine(pStdOut, pStdOut->cbWritten, pStdOut->offPendingRead))
1132 {
1133 if (!pStdOut->fReadPending)
1134 pStdOut->cbWritten = pStdOut->offPendingRead = 0;
1135 else
1136 pStdOut->cbWritten = pStdOut->offPendingRead;
1137 }
1138 else
1139 {
1140 mkWinChildcareWorkerFlushUnwritten(pChild, pStdOut);
1141 mkWinChildcareWorkerFlushUnwritten(pChild, pStdErr);
1142 }
1143}
1144
1145/**
1146 * Commmon worker for waiting on a child process and retrieving the exit code.
1147 *
1148 * @returns Child exit code.
1149 * @param pWorker The worker.
1150 * @param pChild The child.
1151 * @param hProcess The process handle.
1152 * @param pwszJob The job name.
1153 * @param fCatchOutput Set if we need to work the output pipes
1154 * associated with the worker.
1155 */
1156static int mkWinChildcareWorkerWaitForProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild, HANDLE hProcess,
1157 WCHAR const *pwszJob, BOOL fCatchOutput)
1158{
1159 DWORD const msStart = GetTickCount();
1160 DWORD msNextMsg = msStart + 15000;
1161
1162 /* Reset the written indicators on the pipes before we start loop. */
1163 pWorker->pStdOut->fHaveWrittenOut = FALSE;
1164 pWorker->pStdErr->fHaveWrittenOut = FALSE;
1165
1166 for (;;)
1167 {
1168 /*
1169 * Do the waiting and output catching.
1170 */
1171 DWORD dwStatus;
1172 if (!fCatchOutput)
1173 dwStatus = WaitForSingleObject(hProcess, 15001 /*ms*/);
1174 else
1175 {
1176 HANDLE ahHandles[3] = { hProcess, pWorker->pStdOut->hEvent, pWorker->pStdErr->hEvent };
1177 dwStatus = WaitForMultipleObjects(3, ahHandles, FALSE /*fWaitAll*/, 1000 /*ms*/);
1178 if (dwStatus == WAIT_OBJECT_0 + 1)
1179 mkWinChildcareWorkerCatchOutput(pChild, pWorker->pStdOut, FALSE /*fDraining*/);
1180 else if (dwStatus == WAIT_OBJECT_0 + 2)
1181 mkWinChildcareWorkerCatchOutput(pChild, pWorker->pStdErr, FALSE /*fDraining*/);
1182 }
1183 assert(dwStatus != WAIT_FAILED);
1184
1185 /*
1186 * Get the exit code and return if the process was signalled as done.
1187 */
1188 if (dwStatus == WAIT_OBJECT_0)
1189 {
1190 DWORD dwExitCode = -42;
1191 if (GetExitCodeProcess(hProcess, &dwExitCode))
1192 {
1193 pChild->iExitCode = (int)dwExitCode;
1194 if (fCatchOutput)
1195 MkWinChildcareWorkerDrainPipes(pChild, pWorker->pStdOut, pWorker->pStdErr);
1196 return dwExitCode;
1197 }
1198 }
1199 /*
1200 * Loop again if just a timeout or pending output?
1201 * Put out a message every 15 or 30 seconds if the job takes a while.
1202 */
1203 else if ( dwStatus == WAIT_TIMEOUT
1204 || dwStatus == WAIT_OBJECT_0 + 1
1205 || dwStatus == WAIT_OBJECT_0 + 2
1206 || dwStatus == WAIT_IO_COMPLETION)
1207 {
1208 DWORD msNow = GetTickCount();
1209 if (msNow >= msNextMsg)
1210 {
1211 if ( !pChild->pMkChild
1212 || !pChild->pMkChild->recursive) /* ignore make recursions */
1213 {
1214 if ( !pChild->pMkChild
1215 || !pChild->pMkChild->file
1216 || !pChild->pMkChild->file->name)
1217 MkWinChildError(NULL, 0, "Pid %u ('%ls') still running after %u seconds\n",
1218 GetProcessId(hProcess), pwszJob, (msNow - msStart) / 1000);
1219 else
1220 MkWinChildError(NULL, 0, "Target '%s' (pid %u) still running after %u seconds\n",
1221 pChild->pMkChild->file->name, GetProcessId(hProcess), (msNow - msStart) / 1000);
1222 }
1223
1224 /* After 15s, 30s, 60s, 120s, 180s, ... */
1225 if (msNextMsg == msStart + 15000)
1226 msNextMsg += 15000;
1227 else
1228 msNextMsg += 30000;
1229 }
1230 continue;
1231 }
1232
1233 /* Something failed. */
1234 pChild->iExitCode = GetLastError();
1235 if (pChild->iExitCode == 0)
1236 pChild->iExitCode = -4242;
1237 return pChild->iExitCode;
1238 }
1239}
1240
1241
1242/**
1243 * Closes standard handles that need closing before destruction.
1244 *
1245 * @param pChild The child (WINCHILDTYPE_PROCESS).
1246 */
1247static void mkWinChildcareWorkerCloseStandardHandles(PWINCHILD pChild)
1248{
1249 if ( pChild->u.Process.fCloseStdOut
1250 && pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE)
1251 {
1252 CloseHandle(pChild->u.Process.hStdOut);
1253 pChild->u.Process.hStdOut = INVALID_HANDLE_VALUE;
1254 pChild->u.Process.fCloseStdOut = FALSE;
1255 }
1256 if ( pChild->u.Process.fCloseStdErr
1257 && pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE)
1258 {
1259 CloseHandle(pChild->u.Process.hStdErr);
1260 pChild->u.Process.hStdErr = INVALID_HANDLE_VALUE;
1261 pChild->u.Process.fCloseStdErr = FALSE;
1262 }
1263}
1264
1265
1266/**
1267 * Does the actual process creation.
1268 *
1269 * @returns 0 if there is anything to wait on, otherwise non-zero windows error.
1270 * @param pWorker The childcare worker.
1271 * @param pChild The child.
1272 * @param pwszImageName The image path.
1273 * @param pwszCommandLine The command line.
1274 * @param pwszzEnvironment The enviornment block.
1275 */
1276static int mkWinChildcareWorkerCreateProcess(PWINCHILDCAREWORKER pWorker, WCHAR const *pwszImageName,
1277 WCHAR const *pwszCommandLine, WCHAR const *pwszzEnvironment, WCHAR const *pwszCwd,
1278 BOOL pafReplace[3], HANDLE pahChild[3], BOOL fCatchOutput, HANDLE *phProcess)
1279{
1280 PROCESS_INFORMATION ProcInfo;
1281 STARTUPINFOW StartupInfo;
1282 DWORD fFlags = CREATE_UNICODE_ENVIRONMENT;
1283 BOOL const fHaveHandles = pafReplace[0] | pafReplace[1] | pafReplace[2];
1284 BOOL fRet;
1285 DWORD dwErr;
1286#ifdef KMK
1287 extern int process_priority;
1288#endif
1289#ifdef DEBUG_STDOUT_CLOSE_ISSUE
1290 my_check_stdout("mkWinChildcareWorkerCreateProcess/entry");
1291#endif
1292
1293 /*
1294 * Populate startup info.
1295 *
1296 * Turns out we can get away without passing TRUE for the inherit handles
1297 * parameter to CreateProcess when we're not using STARTF_USESTDHANDLES.
1298 * At least on NT, which is all worth caring about at this point + context IMO.
1299 *
1300 * Not inherting the handles is a good thing because it means we won't
1301 * accidentally end up with a pipe handle or such intended for a different
1302 * child process, potentially causing the EOF/HUP event to be delayed.
1303 *
1304 * Since the present handle inhertiance requirements only involves standard
1305 * output and error, we'll never set the inherit handles flag and instead
1306 * do manual handle duplication and planting.
1307 */
1308 memset(&StartupInfo, 0, sizeof(StartupInfo));
1309 StartupInfo.cb = sizeof(StartupInfo);
1310 GetStartupInfoW(&StartupInfo);
1311 StartupInfo.lpReserved2 = 0; /* No CRT file handle + descriptor info possible, sorry. */
1312 StartupInfo.cbReserved2 = 0;
1313 if ( !fHaveHandles
1314 && !fCatchOutput)
1315 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
1316 else
1317 {
1318 fFlags |= CREATE_SUSPENDED;
1319 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
1320 }
1321
1322 /*
1323 * Flags.
1324 */
1325#ifdef KMK
1326 switch (process_priority)
1327 {
1328 case 1: fFlags |= CREATE_SUSPENDED | IDLE_PRIORITY_CLASS; break;
1329 case 2: fFlags |= CREATE_SUSPENDED | BELOW_NORMAL_PRIORITY_CLASS; break;
1330 case 3: fFlags |= CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS; break;
1331 case 4: fFlags |= CREATE_SUSPENDED | HIGH_PRIORITY_CLASS; break;
1332 case 5: fFlags |= CREATE_SUSPENDED | REALTIME_PRIORITY_CLASS; break;
1333 }
1334#endif
1335 if (g_cProcessorGroups > 1)
1336 fFlags |= CREATE_SUSPENDED;
1337
1338 /*
1339 * Try create the process.
1340 */
1341 DB(DB_JOBS, ("CreateProcessW(%ls, %ls,,, TRUE, %#x...)\n", pwszImageName, pwszCommandLine, fFlags));
1342 memset(&ProcInfo, 0, sizeof(ProcInfo));
1343#ifdef WITH_RW_LOCK
1344 AcquireSRWLockShared(&g_RWLock);
1345#endif
1346
1347 fRet = CreateProcessW((WCHAR *)pwszImageName, (WCHAR *)pwszCommandLine, NULL /*pProcSecAttr*/, NULL /*pThreadSecAttr*/,
1348 FALSE /*fInheritHandles*/, fFlags, (WCHAR *)pwszzEnvironment, pwszCwd, &StartupInfo, &ProcInfo);
1349 dwErr = GetLastError();
1350
1351#ifdef WITH_RW_LOCK
1352 ReleaseSRWLockShared(&g_RWLock);
1353#endif
1354 if (fRet)
1355 *phProcess = ProcInfo.hProcess;
1356 else
1357 {
1358 MkWinChildError(pWorker, 1, "CreateProcess(%ls) failed: %u\n", pwszImageName, dwErr);
1359 return (int)dwErr;
1360 }
1361
1362 /*
1363 * If the child is suspended, we've got some adjustment work to be done.
1364 */
1365 dwErr = ERROR_SUCCESS;
1366 if (fFlags & CREATE_SUSPENDED)
1367 {
1368 /*
1369 * First do handle inhertiance as that's the most complicated.
1370 */
1371 if (fHaveHandles || fCatchOutput)
1372 {
1373 char szErrMsg[128];
1374 if (fCatchOutput)
1375 {
1376 if (!pafReplace[1])
1377 {
1378 pafReplace[1] = TRUE;
1379 pahChild[1] = pWorker->pStdOut->hPipeChild;
1380 }
1381 if (!pafReplace[2])
1382 {
1383 pafReplace[2] = TRUE;
1384 pahChild[2] = pWorker->pStdErr->hPipeChild;
1385 }
1386 }
1387 dwErr = nt_child_inject_standard_handles(ProcInfo.hProcess, pafReplace, pahChild, szErrMsg, sizeof(szErrMsg));
1388 if (dwErr != 0)
1389 MkWinChildError(pWorker, 1, "%s\n", szErrMsg);
1390 }
1391
1392 /*
1393 * Assign processor group (ignore failure).
1394 */
1395#ifdef MKWINCHILD_DO_SET_PROCESSOR_GROUP
1396 if (g_cProcessorGroups > 1 && !g_pfnSetProcessDefaultCpuSetMasks)
1397 {
1398 GROUP_AFFINITY Affinity = { 0, pWorker->iProcessorGroup, { 0, 0, 0 } };
1399 if (g_pGroupInfo && pWorker->iProcessorGroup < g_pGroupInfo->ActiveGroupCount)
1400 Affinity.Mask = g_pGroupInfo->GroupInfo[pWorker->iProcessorGroup].ActiveProcessorMask;
1401 fRet = g_pfnSetThreadGroupAffinity(ProcInfo.hThread, &Affinity, NULL);
1402 assert(fRet);
1403 }
1404#endif
1405
1406#ifdef KMK
1407 /*
1408 * Set priority (ignore failure).
1409 */
1410 switch (process_priority)
1411 {
1412 case 1: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_IDLE); break;
1413 case 2: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_BELOW_NORMAL); break;
1414 case 3: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_NORMAL); break;
1415 case 4: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_HIGHEST); break;
1416 case 5: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_TIME_CRITICAL); break;
1417 default: fRet = TRUE;
1418 }
1419 assert(fRet);
1420#endif
1421
1422 /*
1423 * Inject the job object if we're in a non-killing mode, to postpone
1424 * the closing of the job object and maybe make it more useful.
1425 */
1426 if (win_job_object_no_kill && g_hJob)
1427 {
1428 HANDLE hWhatever = INVALID_HANDLE_VALUE;
1429 DuplicateHandle(GetCurrentProcess(), g_hJob, ProcInfo.hProcess, &hWhatever, GENERIC_ALL,
1430 TRUE /*bInheritHandle*/, DUPLICATE_SAME_ACCESS);
1431 }
1432
1433 /*
1434 * Resume the thread if the adjustments succeeded, otherwise kill it.
1435 */
1436 if (dwErr == ERROR_SUCCESS)
1437 {
1438 fRet = ResumeThread(ProcInfo.hThread);
1439 assert(fRet);
1440 if (!fRet)
1441 {
1442 dwErr = GetLastError();
1443 MkWinChildError(pWorker, 1, "ResumeThread failed on child process: %u\n", dwErr);
1444 }
1445 }
1446 if (dwErr != ERROR_SUCCESS)
1447 TerminateProcess(ProcInfo.hProcess, dwErr);
1448 }
1449
1450 /*
1451 * Close unnecessary handles and cache the image.
1452 */
1453 CloseHandle(ProcInfo.hThread);
1454 kmk_cache_exec_image_w(pwszImageName);
1455#ifdef DEBUG_STDOUT_CLOSE_ISSUE
1456 my_check_stdout("mkWinChildcareWorkerCreateProcess/tail");
1457#endif
1458 return 0;
1459}
1460
1461/**
1462 * Converts a argument vector that has already been quoted correctly.
1463 *
1464 * The argument vector is typically the result of quote_argv().
1465 *
1466 * @returns 0 on success, non-zero on failure.
1467 * @param pWorker The childcare worker.
1468 * @param papszArgs The argument vector to convert.
1469 * @param ppwszCommandLine Where to return the command line.
1470 */
1471static int mkWinChildcareWorkerConvertQuotedArgvToCommandline(PWINCHILDCAREWORKER pWorker, char **papszArgs,
1472 WCHAR **ppwszCommandLine)
1473{
1474 WCHAR *pwszCmdLine;
1475 WCHAR *pwszDst;
1476
1477 /*
1478 * Calc length the converted length.
1479 */
1480 unsigned cwcNeeded = 1;
1481 unsigned i = 0;
1482 const char *pszSrc;
1483 while ((pszSrc = papszArgs[i]) != NULL)
1484 {
1485 int cwcThis = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, -1, NULL, 0);
1486 if (cwcThis > 0 || *pszSrc == '\0')
1487 cwcNeeded += cwcThis + 1;
1488 else
1489 {
1490 DWORD dwErr = GetLastError();
1491 MkWinChildError(pWorker, 1, _("MultiByteToWideChar failed to convert argv[%u] (%s): %u\n"), i, pszSrc, dwErr);
1492 return dwErr;
1493 }
1494 i++;
1495 }
1496
1497 /*
1498 * Allocate and do the conversion.
1499 */
1500 pwszCmdLine = pwszDst = (WCHAR *)xmalloc(cwcNeeded * sizeof(WCHAR));
1501 i = 0;
1502 while ((pszSrc = papszArgs[i]) != NULL)
1503 {
1504 int cwcThis;
1505 if (i > 0)
1506 {
1507 *pwszDst++ = ' ';
1508 cwcNeeded--;
1509 }
1510
1511 cwcThis = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, -1, pwszDst, cwcNeeded);
1512 if (!cwcThis && *pszSrc != '\0')
1513 {
1514 DWORD dwErr = GetLastError();
1515 MkWinChildError(pWorker, 1, _("MultiByteToWideChar failed to convert argv[%u] (%s): %u\n"), i, pszSrc, dwErr);
1516 free(pwszCmdLine);
1517 return dwErr;
1518 }
1519 if (cwcThis > 0 && pwszDst[cwcThis - 1] == '\0')
1520 cwcThis--;
1521 pwszDst += cwcThis;
1522 cwcNeeded -= cwcThis;
1523 i++;
1524 }
1525 *pwszDst++ = '\0';
1526
1527 *ppwszCommandLine = pwszCmdLine;
1528 return 0;
1529}
1530
1531
1532#define MKWCCWCMD_F_CYGWIN_SHELL 1
1533#define MKWCCWCMD_F_MKS_SHELL 2
1534#define MKWCCWCMD_F_HAVE_SH 4
1535#define MKWCCWCMD_F_HAVE_KASH_C 8 /**< kmk_ash -c "..." */
1536
1537/*
1538 * @param pWorker The childcare worker if on one, otherwise NULL.
1539 */
1540static int mkWinChildcareWorkerConvertCommandline(PWINCHILDCAREWORKER pWorker, char **papszArgs, unsigned fFlags,
1541 WCHAR **ppwszCommandLine)
1542{
1543 struct ARGINFO
1544 {
1545 size_t cchSrc;
1546 size_t cwcDst; /**< converted size w/o terminator. */
1547 size_t cwcDstExtra : 24; /**< Only set with fSlowly. */
1548 size_t fSlowly : 1;
1549 size_t fQuoteIt : 1;
1550 size_t fEndSlashes : 1; /**< if escapes needed for trailing backslashes. */
1551 size_t fExtraSpace : 1; /**< if kash -c "" needs an extra space before the quote. */
1552 } *paArgInfo;
1553 size_t cArgs;
1554 size_t i;
1555 size_t cwcNeeded;
1556 WCHAR *pwszDst;
1557 WCHAR *pwszCmdLine;
1558
1559 /*
1560 * Count them first so we can allocate an info array of the stack.
1561 */
1562 cArgs = 0;
1563 while (papszArgs[cArgs] != NULL)
1564 cArgs++;
1565 paArgInfo = (struct ARGINFO *)alloca(sizeof(paArgInfo[0]) * cArgs);
1566
1567 /*
1568 * Preprocess them and calculate the exact command line length.
1569 */
1570 cwcNeeded = 1;
1571 for (i = 0; i < cArgs; i++)
1572 {
1573 char *pszSrc = papszArgs[i];
1574 size_t cchSrc = strlen(pszSrc);
1575 paArgInfo[i].cchSrc = cchSrc;
1576 if (cchSrc == 0)
1577 {
1578 /* empty needs quoting. */
1579 paArgInfo[i].cwcDst = 2;
1580 paArgInfo[i].cwcDstExtra = 0;
1581 paArgInfo[i].fSlowly = 0;
1582 paArgInfo[i].fQuoteIt = 1;
1583 paArgInfo[i].fExtraSpace = 0;
1584 paArgInfo[i].fEndSlashes = 0;
1585 }
1586 else
1587 {
1588 const char *pszSpace = memchr(pszSrc, ' ', cchSrc);
1589 const char *pszTab = memchr(pszSrc, '\t', cchSrc);
1590 const char *pszDQuote = memchr(pszSrc, '"', cchSrc);
1591 const char *pszEscape = memchr(pszSrc, '\\', cchSrc);
1592 int cwcDst = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cchSrc + 1, NULL, 0);
1593 if (cwcDst >= 0)
1594 --cwcDst;
1595 else
1596 {
1597 DWORD dwErr = GetLastError();
1598 MkWinChildError(pWorker, 1, _("MultiByteToWideChar failed to convert argv[%u] (%s): %u\n"), i, pszSrc, dwErr);
1599 return dwErr;
1600 }
1601#if 0
1602 if (!pszSpace && !pszTab && !pszDQuote && !pszEscape)
1603 {
1604 /* no special handling needed. */
1605 paArgInfo[i].cwcDst = cwcDst;
1606 paArgInfo[i].cwcDstExtra = 0;
1607 paArgInfo[i].fSlowly = 0;
1608 paArgInfo[i].fQuoteIt = 0;
1609 paArgInfo[i].fExtraSpace = 0;
1610 paArgInfo[i].fEndSlashes = 0;
1611 }
1612 else if (!pszDQuote && !pszEscape)
1613 {
1614 /* Just double quote it. */
1615 paArgInfo[i].cwcDst = cwcDst + 2;
1616 paArgInfo[i].cwcDstExtra = 0;
1617 paArgInfo[i].fSlowly = 0;
1618 paArgInfo[i].fQuoteIt = 1;
1619 paArgInfo[i].fExtraSpace = 0;
1620 paArgInfo[i].fEndSlashes = 0;
1621 }
1622 else
1623#endif
1624 {
1625 /* Complicated, need to scan the string to figure out what to do. */
1626 size_t cwcDstExtra;
1627 int cBackslashes;
1628 char ch;
1629
1630 paArgInfo[i].fQuoteIt = 0;
1631 paArgInfo[i].fSlowly = 1;
1632 paArgInfo[i].fExtraSpace = 0;
1633 paArgInfo[i].fEndSlashes = 0;
1634
1635 cwcDstExtra = 0;
1636 cBackslashes = 0;
1637 while ((ch = *pszSrc++) != '\0')
1638 {
1639 switch (ch)
1640 {
1641 default:
1642 cBackslashes = 0;
1643 break;
1644
1645 case '\\':
1646 cBackslashes++;
1647 break;
1648
1649 case '"':
1650 if (fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_MKS_SHELL))
1651 cwcDstExtra += 1; /* just an extra '"' */
1652 else
1653 cwcDstExtra += 1 + cBackslashes; /* extra '\\' for the '"' and for each preceeding slash. */
1654 cBackslashes = 0;
1655 break;
1656
1657 case ' ':
1658 case '\t':
1659 if (!paArgInfo[i].fQuoteIt)
1660 {
1661 paArgInfo[i].fQuoteIt = 1;
1662 cwcDstExtra += 2;
1663 }
1664 cBackslashes = 0;
1665 break;
1666 }
1667 }
1668
1669 /* If we're quoting the argument and it ends with trailing '\\', it/they must be escaped. */
1670 if ( cBackslashes > 0
1671 && paArgInfo[i].fQuoteIt
1672 && !(fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_MKS_SHELL)))
1673 {
1674 cwcDstExtra += cBackslashes;
1675 paArgInfo[i].fEndSlashes = 1;
1676 }
1677
1678 paArgInfo[i].cwcDst = cwcDst + cwcDstExtra;
1679 paArgInfo[i].cwcDstExtra = cwcDstExtra;
1680 }
1681 }
1682
1683 if ( (fFlags & MKWCCWCMD_F_HAVE_KASH_C)
1684 && paArgInfo[i].fQuoteIt)
1685 {
1686 paArgInfo[i].fExtraSpace = 1;
1687 paArgInfo[i].cwcDst++;
1688 paArgInfo[i].cwcDstExtra++;
1689 }
1690
1691 cwcNeeded += (i != 0) + paArgInfo[i].cwcDst;
1692 }
1693
1694 /*
1695 * Allocate the result buffer and do the actual conversion.
1696 */
1697 pwszDst = pwszCmdLine = (WCHAR *)xmalloc(sizeof(WCHAR) * cwcNeeded);
1698 for (i = 0; i < cArgs; i++)
1699 {
1700 char *pszSrc = papszArgs[i];
1701 size_t cwcDst = paArgInfo[i].cwcDst;
1702
1703 if (i != 0)
1704 *pwszDst++ = L' ';
1705
1706 if (paArgInfo[i].fQuoteIt)
1707 {
1708 *pwszDst++ = L'"';
1709 cwcDst -= 2;
1710 }
1711
1712 if (!paArgInfo[i].fSlowly)
1713 {
1714 int cwcDst2 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, paArgInfo[i].cchSrc, pwszDst, cwcDst + 1);
1715 assert(cwcDst2 >= 0);
1716 pwszDst += cwcDst;
1717 }
1718 else
1719 {
1720 /* Do the conversion into the end of the output buffer, then move
1721 it up to where it should be char by char. */
1722 int cBackslashes;
1723 size_t cwcLeft = paArgInfo[i].cwcDst - paArgInfo[i].cwcDstExtra;
1724 WCHAR volatile *pwchSlowSrc = pwszDst + paArgInfo[i].cwcDstExtra;
1725 WCHAR volatile *pwchSlowDst = pwszDst;
1726 int cwcDst2 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, paArgInfo[i].cchSrc,
1727 (WCHAR *)pwchSlowSrc, cwcLeft + 1);
1728 assert(cwcDst2 >= 0);
1729
1730 cBackslashes = 0;
1731 while (cwcLeft-- > 0)
1732 {
1733 WCHAR wcSrc = *pwchSlowSrc++;
1734 if (wcSrc != L'\\' && wcSrc != L'"')
1735 cBackslashes = 0;
1736 else if (wcSrc == L'\\')
1737 cBackslashes++;
1738 else if ( (fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_HAVE_SH))
1739 == (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_HAVE_SH))
1740 {
1741 *pwchSlowDst++ = L'"'; /* cygwin: '"' instead of '\\', no escaped slashes. */
1742 cBackslashes = 0;
1743 }
1744 else
1745 {
1746 if (!(fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_MKS_SHELL)))
1747 cBackslashes += 1; /* one extra escape the '"' and one for each preceeding slash. */
1748 while (cBackslashes > 0)
1749 {
1750 *pwchSlowDst++ = L'\\';
1751 cBackslashes--;
1752 }
1753 }
1754 *pwchSlowDst++ = wcSrc;
1755 assert((uintptr_t)pwchSlowDst <= (uintptr_t)pwchSlowSrc);
1756 }
1757
1758 if (paArgInfo[i].fEndSlashes)
1759 while (cBackslashes-- > 0)
1760 *pwchSlowDst++ = L'\\';
1761
1762 pwszDst += cwcDst;
1763 assert(pwszDst == (WCHAR *)pwchSlowDst);
1764 }
1765
1766 if (paArgInfo[i].fExtraSpace)
1767 *pwszDst++ = L' ';
1768 if (paArgInfo[i].fQuoteIt)
1769 *pwszDst++ = L'"';
1770 }
1771 *pwszDst = L'\0';
1772 *ppwszCommandLine = pwszCmdLine;
1773 return 0;
1774}
1775
1776static int mkWinChildcareWorkerConvertCommandlineWithShell(PWINCHILDCAREWORKER pWorker, const WCHAR *pwszShell, char **papszArgs,
1777 WCHAR **ppwszCommandLine)
1778{
1779 MkWinChildError(pWorker, 1, "%s: not found!\n", papszArgs[0]);
1780//__debugbreak();
1781 return ERROR_FILE_NOT_FOUND;
1782}
1783
1784/**
1785 * Searches the environment block for the PATH variable.
1786 *
1787 * @returns Pointer to the path in the block or "." in pwszPathFallback.
1788 * @param pwszzEnv The UTF-16 environment block to search.
1789 * @param pwszPathFallback Fallback.
1790 */
1791static const WCHAR *mkWinChildcareWorkerFindPathValue(const WCHAR *pwszzEnv, WCHAR pwszPathFallback[4])
1792{
1793 while (*pwszzEnv)
1794 {
1795 size_t cwcVar = wcslen(pwszzEnv);
1796 if (!IS_PATH_ENV_VAR(cwcVar, pwszzEnv))
1797 pwszzEnv += cwcVar + 1;
1798 else if (cwcVar > 5)
1799 return &pwszzEnv[5];
1800 else
1801 break;
1802 }
1803 pwszPathFallback[0] = L'.';
1804 pwszPathFallback[1] = L'\0';
1805 return pwszPathFallback;
1806}
1807
1808/**
1809 * Checks if we need to had this executable file to the shell.
1810 *
1811 * @returns TRUE if it's shell fooder, FALSE if we think windows can handle it.
1812 * @param hFile Handle to the file in question
1813 */
1814static BOOL mkWinChildcareWorkerCheckIfNeedShell(HANDLE hFile)
1815{
1816 /*
1817 * Read the first 512 bytes and check for an executable image header.
1818 */
1819 union
1820 {
1821 DWORD dwSignature;
1822 WORD wSignature;
1823 BYTE ab[128];
1824 } uBuf;
1825 DWORD cbRead;
1826 uBuf.dwSignature = 0;
1827 if ( ReadFile(hFile, &uBuf, sizeof(uBuf), &cbRead, NULL /*pOverlapped*/)
1828 && cbRead == sizeof(uBuf))
1829 {
1830 if (uBuf.wSignature == IMAGE_DOS_SIGNATURE)
1831 return FALSE;
1832 if (uBuf.dwSignature == IMAGE_NT_SIGNATURE)
1833 return FALSE;
1834 if ( uBuf.wSignature == IMAGE_OS2_SIGNATURE /* NE */
1835 || uBuf.wSignature == 0x5d4c /* LX */
1836 || uBuf.wSignature == IMAGE_OS2_SIGNATURE_LE /* LE */)
1837 return FALSE;
1838 }
1839 return TRUE;
1840}
1841
1842/**
1843 * Checks if the image path looks like microsoft CL.exe.
1844 *
1845 * @returns TRUE / FALSE.
1846 * @param pwszImagePath The executable image path to evalutate.
1847 * @param cwcImagePath The length of the image path.
1848 */
1849static BOOL mkWinChildIsProbableClExe(WCHAR const *pwszImagePath, size_t cwcImagePath)
1850{
1851 assert(pwszImagePath[cwcImagePath] == '\0');
1852 return cwcImagePath > 7
1853 && (pwszImagePath[cwcImagePath - 7] == L'/' || pwszImagePath[cwcImagePath - 7] == L'\\')
1854 && (pwszImagePath[cwcImagePath - 6] == L'c' || pwszImagePath[cwcImagePath - 6] == L'C')
1855 && (pwszImagePath[cwcImagePath - 5] == L'l' || pwszImagePath[cwcImagePath - 5] == L'L')
1856 && pwszImagePath[cwcImagePath - 4] == L'.'
1857 && (pwszImagePath[cwcImagePath - 3] == L'e' || pwszImagePath[cwcImagePath - 3] == L'E')
1858 && (pwszImagePath[cwcImagePath - 2] == L'x' || pwszImagePath[cwcImagePath - 2] == L'X')
1859 && (pwszImagePath[cwcImagePath - 1] == L'e' || pwszImagePath[cwcImagePath - 1] == L'E');
1860}
1861
1862/**
1863 * Temporary workaround for seemingly buggy kFsCache.c / dir-nt-bird.c.
1864 *
1865 * Something is not invalidated / updated correctly!
1866 */
1867static BOOL mkWinChildcareWorkerIsRegularFileW(PWINCHILDCAREWORKER pWorker, wchar_t const *pwszPath)
1868{
1869 BOOL fRet = FALSE;
1870#ifdef KMK
1871 if (utf16_regular_file_p(pwszPath))
1872 fRet = TRUE;
1873 else
1874#endif
1875 {
1876 /* Don't believe the cache. */
1877 DWORD dwAttr = GetFileAttributesW(pwszPath);
1878 if (dwAttr != INVALID_FILE_ATTRIBUTES)
1879 {
1880 if (!(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
1881 {
1882#ifdef KMK
1883 extern void dir_cache_invalid_volatile(void);
1884 dir_cache_invalid_volatile();
1885 if (utf16_regular_file_p(pwszPath))
1886 MkWinChildError(pWorker, 1, "kFsCache was out of sync! pwszPath=%S\n", pwszPath);
1887 else
1888 {
1889 dir_cache_invalid_all();
1890 if (utf16_regular_file_p(pwszPath))
1891 MkWinChildError(pWorker, 1, "kFsCache was really out of sync! pwszPath=%S\n", pwszPath);
1892 else
1893 MkWinChildError(pWorker, 1, "kFsCache is really out of sync!! pwszPath=%S\n", pwszPath);
1894 }
1895#endif
1896 fRet = TRUE;
1897 }
1898 }
1899 }
1900 return fRet;
1901}
1902
1903
1904/**
1905 * Tries to locate the image file, searching the path and maybe falling back on
1906 * the shell in case it knows more (think cygwin with its own view of the file
1907 * system).
1908 *
1909 * This will also check for shell script, falling back on the shell too to
1910 * handle those.
1911 *
1912 * @returns 0 on success, windows error code on failure.
1913 * @param pWorker The childcare worker.
1914 * @param pszArg0 The first argument.
1915 * @param pwszSearchPath In case mkWinChildcareWorkerConvertEnvironment
1916 * had a chance of locating the search path already.
1917 * @param pwszzEnv The environment block, in case we need to look for
1918 * the path.
1919 * @param pszShell The shell.
1920 * @param ppwszImagePath Where to return the pointer to the image path. This
1921 * could be the shell.
1922 * @param pfNeedShell Where to return shell vs direct execution indicator.
1923 * @param pfProbableClExe Where to return an indicator of probably CL.EXE.
1924 */
1925static int mkWinChildcareWorkerFindImage(PWINCHILDCAREWORKER pWorker, char const *pszArg0, WCHAR *pwszSearchPath,
1926 WCHAR const *pwszzEnv, const char *pszShell,
1927 WCHAR **ppwszImagePath, BOOL *pfNeedShell, BOOL *pfProbableClExe)
1928{
1929 /** @todo Slap a cache on this code. We usually end up executing the same
1930 * stuff over and over again (e.g. compilers, linkers, etc).
1931 * Hitting the file system is slow on windows. */
1932
1933 /*
1934 * Convert pszArg0 to unicode so we can work directly on that.
1935 */
1936 WCHAR wszArg0[MKWINCHILD_MAX_PATH + 4]; /* +4 for painless '.exe' appending */
1937 DWORD dwErr;
1938 size_t cbArg0 = strlen(pszArg0) + 1;
1939 int const cwcArg0 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszArg0, cbArg0, wszArg0, MKWINCHILD_MAX_PATH);
1940 if (cwcArg0 > 0)
1941 {
1942 HANDLE hFile = INVALID_HANDLE_VALUE;
1943 WCHAR wszPathBuf[MKWINCHILD_MAX_PATH + 4]; /* +4 for painless '.exe' appending */
1944 int cwc;
1945
1946 /*
1947 * If there isn't an .exe suffix, we may have to add one.
1948 * Also we ASSUME that .exe suffixes means no hash bang detection needed.
1949 */
1950 int const fHasExeSuffix = cwcArg0 > CSTRLEN(".exe")
1951 && wszArg0[cwcArg0 - 4] == '.'
1952 && (wszArg0[cwcArg0 - 3] == L'e' || wszArg0[cwcArg0 - 3] == L'E')
1953 && (wszArg0[cwcArg0 - 2] == L'x' || wszArg0[cwcArg0 - 2] == L'X')
1954 && (wszArg0[cwcArg0 - 1] == L'e' || wszArg0[cwcArg0 - 1] == L'E');
1955
1956 /*
1957 * If there isn't any path specified, we need to search the PATH env.var.
1958 */
1959 int const fHasPath = wszArg0[1] == L':'
1960 || wszArg0[0] == L'\\'
1961 || wszArg0[0] == L'/'
1962 || wmemchr(wszArg0, L'/', cwcArg0)
1963 || wmemchr(wszArg0, L'\\', cwcArg0);
1964
1965 /* Before we do anything, flip UNIX slashes to DOS ones. */
1966 WCHAR *pwc = wszArg0;
1967 while ((pwc = wcschr(pwc, L'/')) != NULL)
1968 *pwc++ = L'\\';
1969
1970 /* Don't need to set these all the time... */
1971 *pfNeedShell = FALSE;
1972 *pfProbableClExe = FALSE;
1973
1974 /*
1975 * If any kind of path is specified in arg0, we will not search the
1976 * PATH env.var and can limit ourselves to maybe slapping a .exe on to it.
1977 */
1978 if (fHasPath)
1979 {
1980 /*
1981 * If relative to a CWD, turn it into an absolute one.
1982 */
1983 unsigned cwcPath = cwcArg0;
1984 WCHAR *pwszPath = wszArg0;
1985 if ( *pwszPath != L'\\'
1986 && (pwszPath[1] != ':' || pwszPath[2] != L'\\') )
1987 {
1988 DWORD cwcAbsPath = GetFullPathNameW(wszArg0, MKWINCHILD_MAX_PATH, wszPathBuf, NULL);
1989 if (cwcAbsPath > 0)
1990 {
1991 cwcPath = cwcAbsPath + 1; /* include terminator, like MultiByteToWideChar does. */
1992 pwszPath = wszPathBuf;
1993 }
1994 }
1995
1996 /*
1997 * Check with .exe suffix first.
1998 * We don't open .exe files and look for hash bang stuff, we just
1999 * assume they are executable images that CreateProcess can deal with.
2000 */
2001 if (!fHasExeSuffix)
2002 {
2003 pwszPath[cwcPath - 1] = L'.';
2004 pwszPath[cwcPath ] = L'e';
2005 pwszPath[cwcPath + 1] = L'x';
2006 pwszPath[cwcPath + 2] = L'e';
2007 pwszPath[cwcPath + 3] = L'\0';
2008 }
2009
2010 if (mkWinChildcareWorkerIsRegularFileW(pWorker, pwszPath))
2011 {
2012 *pfProbableClExe = mkWinChildIsProbableClExe(pwszPath, cwcPath + 4 - 1);
2013 return mkWinChildDuplicateUtf16String(pwszPath, cwcPath + 4, ppwszImagePath);
2014 }
2015
2016 /*
2017 * If no suffix was specified, try without .exe too, but now we need
2018 * to see if it's for the shell or CreateProcess.
2019 */
2020 if (!fHasExeSuffix)
2021 {
2022 pwszPath[cwcPath - 1] = L'\0';
2023#ifdef KMK
2024 if (mkWinChildcareWorkerIsRegularFileW(pWorker, pwszPath))
2025#endif
2026 {
2027 hFile = CreateFileW(pwszPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
2028 NULL /*pSecAttr*/, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2029 if (hFile != INVALID_HANDLE_VALUE)
2030 {
2031 *pfNeedShell = mkWinChildcareWorkerCheckIfNeedShell(hFile);
2032 CloseHandle(hFile);
2033 if (!*pfNeedShell)
2034 {
2035 *pfProbableClExe = mkWinChildIsProbableClExe(pwszPath, cwcPath - 1);
2036 return mkWinChildDuplicateUtf16String(pwszPath, cwcPath, ppwszImagePath);
2037 }
2038 }
2039 }
2040 }
2041 }
2042 /*
2043 * No path, need to search the PATH env.var. for the executable, maybe
2044 * adding an .exe suffix while do so if that is missing.
2045 */
2046 else
2047 {
2048 BOOL fSearchedCwd = FALSE;
2049 WCHAR wszPathFallback[4];
2050 if (!pwszSearchPath)
2051 pwszSearchPath = (WCHAR *)mkWinChildcareWorkerFindPathValue(pwszzEnv, wszPathFallback);
2052
2053 for (;;)
2054 {
2055 size_t cwcCombined;
2056
2057 /*
2058 * Find the end of the current PATH component.
2059 */
2060 size_t cwcSkip;
2061 WCHAR wcEnd;
2062 size_t cwcComponent = 0;
2063 WCHAR wc;
2064 while ((wc = pwszSearchPath[cwcComponent]) != L'\0')
2065 {
2066 if (wc != ';' && wc != ':')
2067 { /* likely */ }
2068 else if (wc == ';')
2069 break;
2070 else if (cwcComponent != (pwszSearchPath[cwcComponent] != L'"' ? 1 : 2))
2071 break;
2072 cwcComponent++;
2073 }
2074 wcEnd = wc;
2075
2076 /* Trim leading spaces and double quotes. */
2077 while ( cwcComponent > 0
2078 && ((wc = *pwszSearchPath) == L'"' || wc == L' ' || wc == L'\t'))
2079 {
2080 pwszSearchPath++;
2081 cwcComponent--;
2082 }
2083 cwcSkip = cwcComponent;
2084
2085 /* Trim trailing spaces & double quotes. */
2086 while ( cwcComponent > 0
2087 && ((wc = pwszSearchPath[cwcComponent - 1]) == L'"' || wc == L' ' || wc == L'\t'))
2088 cwcComponent--;
2089
2090 /*
2091 * Skip empty components. Join the component and the filename, making sure to
2092 * resolve any CWD relative stuff first.
2093 */
2094 cwcCombined = cwcComponent + 1 + cwcArg0;
2095 if (cwcComponent > 0 && cwcCombined <= MKWINCHILD_MAX_PATH)
2096 {
2097 /* Copy the component into wszPathBuf, maybe abspath'ing it. */
2098 DWORD cwcAbsPath = 0;
2099 if ( *pwszSearchPath != L'\\'
2100 && (pwszSearchPath[1] != ':' || pwszSearchPath[2] != L'\\') )
2101 {
2102 /* To save an extra buffer + copying, we'll temporarily modify the PATH
2103 value in our converted UTF-16 environment block. */
2104 WCHAR const wcSaved = pwszSearchPath[cwcComponent];
2105 pwszSearchPath[cwcComponent] = L'\0';
2106 cwcAbsPath = GetFullPathNameW(pwszSearchPath, MKWINCHILD_MAX_PATH, wszPathBuf, NULL);
2107 pwszSearchPath[cwcComponent] = wcSaved;
2108 if (cwcAbsPath > 0 && cwcAbsPath + 1 + cwcArg0 <= MKWINCHILD_MAX_PATH)
2109 cwcCombined = cwcAbsPath + 1 + cwcArg0;
2110 else
2111 cwcAbsPath = 0;
2112 }
2113 if (cwcAbsPath == 0)
2114 {
2115 memcpy(wszPathBuf, pwszSearchPath, cwcComponent * sizeof(WCHAR));
2116 cwcAbsPath = cwcComponent;
2117 }
2118
2119 /* Append the filename. */
2120 if ((wc = wszPathBuf[cwcAbsPath - 1]) == L'\\' || wc == L'/' || wc == L':')
2121 {
2122 memcpy(&wszPathBuf[cwcAbsPath], wszArg0, cwcArg0 * sizeof(WCHAR));
2123 cwcCombined--;
2124 }
2125 else
2126 {
2127 wszPathBuf[cwcAbsPath] = L'\\';
2128 memcpy(&wszPathBuf[cwcAbsPath + 1], wszArg0, cwcArg0 * sizeof(WCHAR));
2129 }
2130 assert(wszPathBuf[cwcCombined - 1] == L'\0');
2131
2132 /* DOS slash conversion */
2133 pwc = wszPathBuf;
2134 while ((pwc = wcschr(pwc, L'/')) != NULL)
2135 *pwc++ = L'\\';
2136
2137 /*
2138 * Search with exe suffix first.
2139 */
2140 if (!fHasExeSuffix)
2141 {
2142 wszPathBuf[cwcCombined - 1] = L'.';
2143 wszPathBuf[cwcCombined ] = L'e';
2144 wszPathBuf[cwcCombined + 1] = L'x';
2145 wszPathBuf[cwcCombined + 2] = L'e';
2146 wszPathBuf[cwcCombined + 3] = L'\0';
2147 }
2148 if (mkWinChildcareWorkerIsRegularFileW(pWorker, wszPathBuf))
2149 {
2150 *pfProbableClExe = mkWinChildIsProbableClExe(wszPathBuf, cwcCombined + (fHasExeSuffix ? 0 : 4) - 1);
2151 return mkWinChildDuplicateUtf16String(wszPathBuf, cwcCombined + (fHasExeSuffix ? 0 : 4), ppwszImagePath);
2152 }
2153 if (!fHasExeSuffix)
2154 {
2155 wszPathBuf[cwcCombined - 1] = L'\0';
2156#ifdef KMK
2157 if (mkWinChildcareWorkerIsRegularFileW(pWorker, wszPathBuf))
2158#endif
2159 {
2160 /*
2161 * Check if the file exists w/o the added '.exe' suffix. If it does,
2162 * we need to check if we can pass it to CreateProcess or need the shell.
2163 */
2164 hFile = CreateFileW(wszPathBuf, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
2165 NULL /*pSecAttr*/, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
2166 if (hFile != INVALID_HANDLE_VALUE)
2167 {
2168 *pfNeedShell = mkWinChildcareWorkerCheckIfNeedShell(hFile);
2169 CloseHandle(hFile);
2170 if (!*pfNeedShell)
2171 {
2172 *pfProbableClExe = mkWinChildIsProbableClExe(wszPathBuf, cwcCombined - 1);
2173 return mkWinChildDuplicateUtf16String(wszPathBuf, cwcCombined, ppwszImagePath);
2174 }
2175 break;
2176 }
2177 }
2178 }
2179 }
2180
2181 /*
2182 * Advance to the next component.
2183 */
2184 if (wcEnd != '\0')
2185 pwszSearchPath += cwcSkip + 1;
2186 else if (fSearchedCwd)
2187 break;
2188 else
2189 {
2190 fSearchedCwd = TRUE;
2191 wszPathFallback[0] = L'.';
2192 wszPathFallback[1] = L'\0';
2193 pwszSearchPath = wszPathFallback;
2194 }
2195 }
2196 }
2197
2198 /*
2199 * We need the shell. It will take care of finding/reporting missing
2200 * image files and such.
2201 */
2202 *pfNeedShell = TRUE;
2203 if (pszShell)
2204 {
2205 cwc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszShell, strlen(pszShell) + 1, wszPathBuf, MKWINCHILD_MAX_PATH);
2206 if (cwc > 0)
2207 return mkWinChildDuplicateUtf16String(wszPathBuf, cwc, ppwszImagePath);
2208 dwErr = GetLastError();
2209 MkWinChildError(pWorker, 1, _("MultiByteToWideChar failed to convert shell (%s): %u\n"), pszShell, dwErr);
2210 }
2211 else
2212 {
2213 MkWinChildError(pWorker, 1, "%s: not found!\n", pszArg0);
2214 dwErr = ERROR_FILE_NOT_FOUND;
2215 }
2216 }
2217 else
2218 {
2219 dwErr = GetLastError();
2220 MkWinChildError(pWorker, 1, _("MultiByteToWideChar failed to convert argv[0] (%s): %u\n"), pszArg0, dwErr);
2221 }
2222 return dwErr == ERROR_INSUFFICIENT_BUFFER ? ERROR_FILENAME_EXCED_RANGE : dwErr;
2223}
2224
2225/**
2226 * Creates the environment block.
2227 *
2228 * @returns 0 on success, windows error code on failure.
2229 * @param pWorker The childcare worker if on one, otherwise NULL.
2230 * @param papszEnv The environment vector to convert.
2231 * @param cbEnvStrings The size of the environment strings, iff they are
2232 * sequential in a block. Otherwise, zero.
2233 * @param ppwszEnv Where to return the pointer to the environment
2234 * block.
2235 * @param ppwszSearchPath Where to return the pointer to the path value
2236 * within the environment block. This will not be set
2237 * if cbEnvStrings is non-zero, more efficient to let
2238 * mkWinChildcareWorkerFindImage() search when needed.
2239 */
2240static int mkWinChildcareWorkerConvertEnvironment(PWINCHILDCAREWORKER pWorker, char **papszEnv, size_t cbEnvStrings,
2241 WCHAR **ppwszEnv, WCHAR const **ppwszSearchPath)
2242{
2243 DWORD dwErr;
2244 int cwcRc;
2245 int cwcDst;
2246 WCHAR *pwszzDst;
2247
2248 *ppwszSearchPath = NULL;
2249
2250 /*
2251 * We've got a little optimization here with help from mkWinChildCopyStringArray.
2252 */
2253 if (cbEnvStrings)
2254 {
2255 cwcDst = cbEnvStrings + 32;
2256 pwszzDst = (WCHAR *)xmalloc(cwcDst * sizeof(WCHAR));
2257 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, papszEnv[0], cbEnvStrings, pwszzDst, cwcDst);
2258 if (cwcRc != 0)
2259 {
2260 *ppwszEnv = pwszzDst;
2261 return 0;
2262 }
2263
2264 /* Resize the allocation and try again. */
2265 dwErr = GetLastError();
2266 if (dwErr == ERROR_INSUFFICIENT_BUFFER)
2267 {
2268 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, papszEnv[0], cbEnvStrings, NULL, 0);
2269 if (cwcRc > 0)
2270 cwcDst = cwcRc + 32;
2271 else
2272 cwcDst *= 2;
2273 pwszzDst = (WCHAR *)xrealloc(pwszzDst, cwcDst);
2274 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, papszEnv[0], cbEnvStrings, pwszzDst, cwcDst);
2275 if (cwcRc != 0)
2276 {
2277 *ppwszEnv = pwszzDst;
2278 return 0;
2279 }
2280 dwErr = GetLastError();
2281 }
2282 MkWinChildError(pWorker, 1, _("MultiByteToWideChar failed to convert environment block: %u\n"), dwErr);
2283 }
2284 /*
2285 * Need to convert it string by string.
2286 */
2287 else
2288 {
2289 size_t offPathValue = ~(size_t)0;
2290 size_t offDst;
2291
2292 /*
2293 * Estimate the size first.
2294 */
2295 size_t cEnvVars;
2296 size_t cwcDst = 32;
2297 size_t iVar = 0;
2298 const char *pszSrc;
2299 while ((pszSrc = papszEnv[iVar]) != NULL)
2300 {
2301 cwcDst += strlen(pszSrc) + 1;
2302 iVar++;
2303 }
2304 cEnvVars = iVar;
2305
2306 /* Allocate estimated WCHARs and convert the variables one by one, reallocating
2307 the block as needed. */
2308 pwszzDst = (WCHAR *)xmalloc(cwcDst * sizeof(WCHAR));
2309 cwcDst--; /* save one wchar for the terminating empty string. */
2310 offDst = 0;
2311 for (iVar = 0; iVar < cEnvVars; iVar++)
2312 {
2313 size_t cwcLeft = cwcDst - offDst;
2314 size_t const cbSrc = strlen(pszSrc = papszEnv[iVar]) + 1;
2315 assert(cwcDst >= offDst);
2316
2317
2318 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cbSrc, &pwszzDst[offDst], cwcLeft);
2319 if (cwcRc > 0)
2320 { /* likely */ }
2321 else
2322 {
2323 dwErr = GetLastError();
2324 if (dwErr == ERROR_INSUFFICIENT_BUFFER)
2325 {
2326 /* Need more space. So, calc exacly how much and resize the block accordingly. */
2327 size_t cbSrc2 = cbSrc;
2328 size_t iVar2 = iVar;
2329 cwcLeft = 1;
2330 for (;;)
2331 {
2332 size_t cwcRc2 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cbSrc, NULL, 0);
2333 if (cwcRc2 > 0)
2334 cwcLeft += cwcRc2;
2335 else
2336 cwcLeft += cbSrc * 4;
2337
2338 /* advance */
2339 iVar2++;
2340 if (iVar2 >= cEnvVars)
2341 break;
2342 pszSrc = papszEnv[iVar2];
2343 cbSrc2 = strlen(pszSrc) + 1;
2344 }
2345 pszSrc = papszEnv[iVar];
2346
2347 /* Grow the allocation and repeat the conversion. */
2348 if (offDst + cwcLeft > cwcDst + 1)
2349 {
2350 cwcDst = offDst + cwcLeft;
2351 pwszzDst = (WCHAR *)xrealloc(pwszzDst, cwcDst * sizeof(WCHAR));
2352 cwcDst--; /* save one wchar for the terminating empty string. */
2353 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cbSrc, &pwszzDst[offDst], cwcLeft - 1);
2354 if (cwcRc <= 0)
2355 dwErr = GetLastError();
2356 }
2357 }
2358 if (cwcRc <= 0)
2359 {
2360 MkWinChildError(pWorker, 1, _("MultiByteToWideChar failed to convert environment string #%u (%s): %u\n"),
2361 iVar, pszSrc, dwErr);
2362 free(pwszzDst);
2363 return dwErr;
2364 }
2365 }
2366
2367 /* Look for the PATH. */
2368 if ( offPathValue == ~(size_t)0
2369 && IS_PATH_ENV_VAR(cwcRc, &pwszzDst[offDst]) )
2370 offPathValue = offDst + 4 + 1;
2371
2372 /* Advance. */
2373 offDst += cwcRc;
2374 }
2375 pwszzDst[offDst++] = '\0';
2376
2377 if (offPathValue != ~(size_t)0)
2378 *ppwszSearchPath = &pwszzDst[offPathValue];
2379 *ppwszEnv = pwszzDst;
2380 return 0;
2381 }
2382 free(pwszzDst);
2383 return dwErr;
2384}
2385
2386/**
2387 * Childcare worker: handle regular process.
2388 *
2389 * @param pWorker The worker.
2390 * @param pChild The kSubmit child.
2391 */
2392static void mkWinChildcareWorkerThreadHandleProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
2393{
2394 WCHAR *pwszSearchPath = NULL;
2395 WCHAR *pwszzEnvironment = NULL;
2396 WCHAR *pwszCommandLine = NULL;
2397 WCHAR *pwszImageName = NULL;
2398 BOOL fNeedShell = FALSE;
2399 BOOL fProbableClExe = FALSE;
2400 int rc;
2401
2402 /*
2403 * First we convert the environment so we get the PATH we need to
2404 * search for the executable.
2405 */
2406 rc = mkWinChildcareWorkerConvertEnvironment(pWorker, pChild->u.Process.papszEnv ? pChild->u.Process.papszEnv : environ,
2407 pChild->u.Process.cbEnvStrings,
2408 &pwszzEnvironment, &pwszSearchPath);
2409 /*
2410 * Find the executable and maybe checking if it's a shell script, then
2411 * convert it to a command line.
2412 */
2413 if (rc == 0)
2414 rc = mkWinChildcareWorkerFindImage(pWorker, pChild->u.Process.papszArgs[0], pwszSearchPath, pwszzEnvironment,
2415 pChild->u.Process.pszShell, &pwszImageName, &fNeedShell, &pChild->fProbableClExe);
2416 if (rc == 0)
2417 {
2418 if (!fNeedShell)
2419 rc = mkWinChildcareWorkerConvertCommandline(pWorker, pChild->u.Process.papszArgs, 0 /*fFlags*/, &pwszCommandLine);
2420 else
2421 rc = mkWinChildcareWorkerConvertCommandlineWithShell(pWorker, pwszImageName, pChild->u.Process.papszArgs,
2422 &pwszCommandLine);
2423
2424 /*
2425 * Create the child process.
2426 */
2427 if (rc == 0)
2428 {
2429 BOOL afReplace[3] = { FALSE, pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE, pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE };
2430 HANDLE ahChild[3] = { INVALID_HANDLE_VALUE, pChild->u.Process.hStdOut, pChild->u.Process.hStdErr };
2431 rc = mkWinChildcareWorkerCreateProcess(pWorker, pwszImageName, pwszCommandLine, pwszzEnvironment,
2432 NULL /*pwszCwd*/, afReplace, ahChild, pChild->u.Process.fCatchOutput,
2433 &pChild->u.Process.hProcess);
2434 mkWinChildcareWorkerCloseStandardHandles(pChild);
2435 if (rc == 0)
2436 {
2437 /*
2438 * Wait for the child to complete.
2439 */
2440 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Process.hProcess, pwszImageName,
2441 pChild->u.Process.fCatchOutput);
2442 }
2443 else
2444 pChild->iExitCode = rc;
2445 }
2446 else
2447 pChild->iExitCode = rc;
2448 }
2449 else
2450 pChild->iExitCode = rc;
2451 free(pwszCommandLine);
2452 free(pwszImageName);
2453 free(pwszzEnvironment);
2454
2455 /* In case we failed, we must make sure the child end of pipes
2456 used by $(shell no_such_command.exe) are closed, otherwise
2457 the main thread will be stuck reading the parent end. */
2458 mkWinChildcareWorkerCloseStandardHandles(pChild);
2459}
2460
2461#ifdef KMK
2462
2463/**
2464 * Childcare worker: handle builtin command.
2465 *
2466 * @param pWorker The worker.
2467 * @param pChild The kSubmit child.
2468 */
2469static void mkWinChildcareWorkerThreadHandleBuiltIn(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
2470{
2471 PCKMKBUILTINENTRY pBuiltIn = pChild->u.BuiltIn.pBuiltIn;
2472 KMKBUILTINCTX Ctx =
2473 {
2474 pBuiltIn->uName.s.sz,
2475 pChild->pMkChild ? &pChild->pMkChild->output : NULL,
2476 pWorker,
2477 };
2478 if (pBuiltIn->uFnSignature == FN_SIG_MAIN)
2479 pChild->iExitCode = pBuiltIn->u.pfnMain(pChild->u.BuiltIn.cArgs, pChild->u.BuiltIn.papszArgs,
2480 pChild->u.BuiltIn.papszEnv, &Ctx);
2481 else if (pBuiltIn->uFnSignature == FN_SIG_MAIN_SPAWNS)
2482 pChild->iExitCode = pBuiltIn->u.pfnMainSpawns(pChild->u.BuiltIn.cArgs, pChild->u.BuiltIn.papszArgs,
2483 pChild->u.BuiltIn.papszEnv, &Ctx, pChild->pMkChild, NULL /*pPid*/);
2484 else
2485 {
2486 assert(0);
2487 pChild->iExitCode = 98;
2488 }
2489}
2490
2491/**
2492 * Childcare worker: handle append write-out.
2493 *
2494 * @param pWorker The worker.
2495 * @param pChild The kSubmit child.
2496 */
2497static void mkWinChildcareWorkerThreadHandleAppend(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
2498{
2499 int fd = open(pChild->u.Append.pszFilename,
2500 pChild->u.Append.fTruncate
2501 ? O_WRONLY | O_TRUNC | O_CREAT | _O_NOINHERIT | _O_BINARY
2502 : O_WRONLY | O_APPEND | O_CREAT | _O_NOINHERIT | _O_BINARY,
2503 0666);
2504 if (fd >= 0)
2505 {
2506 ssize_t cbWritten = write(fd, pChild->u.Append.pszAppend, pChild->u.Append.cbAppend);
2507 if (cbWritten == (ssize_t)pChild->u.Append.cbAppend)
2508 {
2509 if (close(fd) >= 0)
2510 {
2511 pChild->iExitCode = 0;
2512 return;
2513 }
2514 MkWinChildError(pWorker, 1, "kmk_builtin_append: close failed on '%s': %u (%s)\n",
2515 pChild->u.Append.pszFilename, errno, strerror(errno));
2516 }
2517 else
2518 MkWinChildError(pWorker, 1, "kmk_builtin_append: error writing %lu bytes to on '%s': %u (%s)\n",
2519 pChild->u.Append.cbAppend, pChild->u.Append.pszFilename, errno, strerror(errno));
2520 close(fd);
2521 }
2522 else
2523 MkWinChildError(pWorker, 1, "kmk_builtin_append: error opening '%s': %u (%s)\n",
2524 pChild->u.Append.pszFilename, errno, strerror(errno));
2525 pChild->iExitCode = 1;
2526}
2527
2528/**
2529 * Childcare worker: handle kSubmit job.
2530 *
2531 * @param pWorker The worker.
2532 * @param pChild The kSubmit child.
2533 */
2534static void mkWinChildcareWorkerThreadHandleSubmit(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
2535{
2536 void *pvSubmitWorker = pChild->u.Submit.pvSubmitWorker;
2537
2538 /*
2539 * Prep the wait handles.
2540 */
2541 HANDLE ahHandles[3] = { pChild->u.Submit.hEvent, NULL, NULL };
2542 DWORD cHandles = 1;
2543 if (pChild->u.Submit.pStdOut)
2544 {
2545 assert(pChild->u.Submit.pStdErr);
2546 pChild->u.Submit.pStdOut->fHaveWrittenOut = FALSE;
2547 ahHandles[cHandles++] = pChild->u.Submit.pStdOut->hEvent;
2548 pChild->u.Submit.pStdErr->fHaveWrittenOut = FALSE;
2549 ahHandles[cHandles++] = pChild->u.Submit.pStdErr->hEvent;
2550 }
2551
2552 /*
2553 * Wait loop.
2554 */
2555 for (;;)
2556 {
2557 int iExitCode = -42;
2558 int iSignal = -1;
2559 DWORD dwStatus;
2560 if (cHandles == 1)
2561 dwStatus = WaitForSingleObject(ahHandles[0], INFINITE);
2562 else
2563 {
2564 dwStatus = WaitForMultipleObjects(cHandles, ahHandles, FALSE /*fWaitAll*/, INFINITE);
2565 assert(dwStatus != WAIT_FAILED);
2566 if (dwStatus == WAIT_OBJECT_0 + 1)
2567 mkWinChildcareWorkerCatchOutput(pChild, pChild->u.Submit.pStdOut, FALSE /*fDraining*/);
2568 else if (dwStatus == WAIT_OBJECT_0 + 2)
2569 mkWinChildcareWorkerCatchOutput(pChild, pChild->u.Submit.pStdErr, FALSE /*fDraining*/);
2570 }
2571 if (kSubmitSubProcGetResult((intptr_t)pvSubmitWorker, dwStatus == WAIT_OBJECT_0 /*fBlock*/, &iExitCode, &iSignal) == 0)
2572 {
2573 if (pChild->u.Submit.pStdOut)
2574 MkWinChildcareWorkerDrainPipes(pChild, pChild->u.Submit.pStdOut, pChild->u.Submit.pStdErr);
2575
2576 pChild->iExitCode = iExitCode;
2577 pChild->iSignal = iSignal;
2578 /* Cleanup must be done on the main thread. */
2579 return;
2580 }
2581
2582 if (pChild->iSignal != 0)
2583 kSubmitSubProcKill((intptr_t)pvSubmitWorker, pChild->iSignal);
2584 }
2585}
2586
2587/**
2588 * Childcare worker: handle kmk_redirect process.
2589 *
2590 * @param pWorker The worker.
2591 * @param pChild The redirect child.
2592 */
2593static void mkWinChildcareWorkerThreadHandleRedirect(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
2594{
2595 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Redirect.hProcess, L"kmk_redirect", FALSE /*fCatchOutput*/);
2596}
2597
2598#endif /* KMK */
2599
2600/**
2601 * Childcare worker thread.
2602 *
2603 * @returns 0
2604 * @param pvUser The worker instance.
2605 */
2606static unsigned int __stdcall mkWinChildcareWorkerThread(void *pvUser)
2607{
2608 PWINCHILDCAREWORKER pWorker = (PWINCHILDCAREWORKER)pvUser;
2609 assert(pWorker->uMagic == WINCHILDCAREWORKER_MAGIC);
2610
2611#ifdef MKWINCHILD_DO_SET_PROCESSOR_GROUP
2612 /*
2613 * Adjust process group if necessary.
2614 *
2615 * Note! It seems that setting the mask to zero means that we select all
2616 * active processors. Couldn't find any alternative API for getting
2617 * the correct active processor mask.
2618 */
2619 if (g_cProcessorGroups > 1 && !g_pfnSetProcessDefaultCpuSetMasks)
2620 {
2621 BOOL fRet;
2622 GROUP_AFFINITY Affinity = { 0, pWorker->iProcessorGroup, { 0, 0, 0 } };
2623 if (g_pGroupInfo && pWorker->iProcessorGroup < g_pGroupInfo->ActiveGroupCount)
2624 Affinity.Mask = g_pGroupInfo->GroupInfo[pWorker->iProcessorGroup].ActiveProcessorMask;
2625
2626 fRet = g_pfnSetThreadGroupAffinity(GetCurrentThread(), &Affinity, NULL);
2627 assert(fRet); (void)fRet;
2628# ifndef NDEBUG
2629 {
2630 GROUP_AFFINITY ActualAffinity = { 0xbeefbeefU, 0xbeef, { 0xbeef, 0xbeef, 0xbeef } };
2631 fRet = GetThreadGroupAffinity(GetCurrentThread(), &ActualAffinity);
2632 assert(fRet); (void)fRet;
2633 assert(ActualAffinity.Group == pWorker->iProcessorGroup);
2634 }
2635# endif
2636 }
2637#endif
2638
2639 /*
2640 * Work loop.
2641 */
2642 while (!g_fShutdown)
2643 {
2644 /*
2645 * Try go idle.
2646 */
2647 PWINCHILD pChild = pWorker->pTailTodoChildren;
2648 if (!pChild)
2649 {
2650 _InterlockedExchange(&pWorker->fIdle, TRUE);
2651 pChild = pWorker->pTailTodoChildren;
2652 if (!pChild)
2653 {
2654 DWORD dwStatus;
2655
2656 _InterlockedIncrement((long *)&g_cIdleChildcareWorkers);
2657 _InterlockedExchange((long *)&g_idxLastChildcareWorker, pWorker->idxWorker);
2658 dwStatus = WaitForSingleObject(pWorker->hEvtIdle, INFINITE);
2659 _InterlockedExchange(&pWorker->fIdle, FALSE);
2660 _InterlockedDecrement((long *)&g_cIdleChildcareWorkers);
2661
2662 assert(dwStatus != WAIT_FAILED);
2663 if (dwStatus == WAIT_FAILED)
2664 Sleep(20);
2665
2666 pChild = pWorker->pTailTodoChildren;
2667 }
2668 else
2669 _InterlockedExchange(&pWorker->fIdle, FALSE);
2670 }
2671 if (pChild)
2672 {
2673 /*
2674 * We got work to do. First job is to deque the job.
2675 */
2676 pChild = mkWinChildDequeFromLifo(&pWorker->pTailTodoChildren, pChild);
2677#ifdef DEBUG_STDOUT_CLOSE_ISSUE
2678 my_check_stdout("mkWinChildcareWorkerThread/deque");
2679#endif
2680 assert(pChild);
2681 if (pChild)
2682 {
2683 PWINCHILD pTailExpect;
2684
2685 pChild->pWorker = pWorker;
2686 pWorker->pCurChild = pChild;
2687 switch (pChild->enmType)
2688 {
2689 case WINCHILDTYPE_PROCESS:
2690 mkWinChildcareWorkerThreadHandleProcess(pWorker, pChild);
2691#ifdef DEBUG_STDOUT_CLOSE_ISSUE
2692 my_check_stdout("mkWinChildcareWorkerThread/done-process");
2693#endif
2694 break;
2695#ifdef KMK
2696 case WINCHILDTYPE_BUILT_IN:
2697 mkWinChildcareWorkerThreadHandleBuiltIn(pWorker, pChild);
2698# ifdef DEBUG_STDOUT_CLOSE_ISSUE
2699 my_check_stdout("mkWinChildcareWorkerThread/done-built-in");
2700# endif
2701 break;
2702 case WINCHILDTYPE_APPEND:
2703 mkWinChildcareWorkerThreadHandleAppend(pWorker, pChild);
2704# ifdef DEBUG_STDOUT_CLOSE_ISSUE
2705 my_check_stdout("mkWinChildcareWorkerThread/done-append");
2706# endif
2707 break;
2708 case WINCHILDTYPE_SUBMIT:
2709 mkWinChildcareWorkerThreadHandleSubmit(pWorker, pChild);
2710# ifdef DEBUG_STDOUT_CLOSE_ISSUE
2711 my_check_stdout("mkWinChildcareWorkerThread/done-submit");
2712# endif
2713 break;
2714 case WINCHILDTYPE_REDIRECT:
2715 mkWinChildcareWorkerThreadHandleRedirect(pWorker, pChild);
2716# ifdef DEBUG_STDOUT_CLOSE_ISSUE
2717 my_check_stdout("mkWinChildcareWorkerThread/done-redirect");
2718# endif
2719 break;
2720#endif
2721 default:
2722 assert(0);
2723 }
2724 pWorker->pCurChild = NULL;
2725 pChild->pWorker = NULL;
2726
2727 /*
2728 * Move the child to the completed list.
2729 */
2730 pTailExpect = NULL;
2731 for (;;)
2732 {
2733 PWINCHILD pTailActual;
2734 pChild->pNext = pTailExpect;
2735 pTailActual = _InterlockedCompareExchangePointer(&g_pTailCompletedChildren, pChild, pTailExpect);
2736 if (pTailActual != pTailExpect)
2737 pTailExpect = pTailActual;
2738 else
2739 {
2740 _InterlockedDecrement(&g_cPendingChildren);
2741 if (pTailExpect)
2742 break;
2743 if (SetEvent(g_hEvtWaitChildren))
2744 break;
2745 MkWinChildError(pWorker, 1, "SetEvent(g_hEvtWaitChildren=%p) failed: %u\n",
2746 g_hEvtWaitChildren, GetLastError());
2747 break;
2748 }
2749 }
2750 }
2751 }
2752 }
2753
2754 _endthreadex(0);
2755 return 0;
2756}
2757
2758/**
2759 * Creates a pipe for catching child output.
2760 *
2761 * This is a custom CreatePipe implementation that allows for overlapped I/O on
2762 * our end of the pipe. Silly that they don't offer an API that does this.
2763 *
2764 * @returns The pipe that was created. NULL on failure.
2765 * @param pPipe The structure for the pipe.
2766 * @param iWhich Which standard descriptor this is a pipe for.
2767 * @param idxWorker The worker index.
2768 */
2769PWINCCWPIPE MkWinChildcareCreateWorkerPipe(unsigned iWhich, unsigned int idxWorker)
2770{
2771 /*
2772 * We try generate a reasonably unique name from the get go, so this retry
2773 * loop shouldn't really ever be needed. But you never know.
2774 */
2775 static unsigned s_iSeqNo = 0;
2776 DWORD const cMaxInstances = 1;
2777 DWORD const cbPipe = 4096;
2778 DWORD const cMsTimeout = 0;
2779 unsigned cTries = 256;
2780 while (cTries-- > 0)
2781 {
2782 /* Create the pipe (our end). */
2783 HANDLE hPipeRead;
2784 DWORD fOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE;
2785 DWORD fPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS;
2786 WCHAR wszName[MAX_PATH];
2787 s_iSeqNo++;
2788 _snwprintf(wszName, MAX_PATH, L"\\\\.\\pipe\\kmk-winchildren-%u-%u-%u-%s-%u-%u",
2789 GetCurrentProcessId(), GetCurrentThreadId(), idxWorker, iWhich == 1 ? L"out" : L"err", s_iSeqNo, GetTickCount());
2790 hPipeRead = CreateNamedPipeW(wszName, fOpenMode, fPipeMode, cMaxInstances, cbPipe, cbPipe, cMsTimeout, NULL /*pSecAttr*/);
2791 if (hPipeRead == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
2792 {
2793 fOpenMode &= ~FILE_FLAG_FIRST_PIPE_INSTANCE;
2794 fPipeMode &= ~PIPE_REJECT_REMOTE_CLIENTS;
2795 hPipeRead = CreateNamedPipeW(wszName, fOpenMode, fPipeMode, cMaxInstances, cbPipe, cbPipe, cMsTimeout, NULL /*pSecAttr*/);
2796 }
2797 if (hPipeRead != INVALID_HANDLE_VALUE)
2798 {
2799 /* Connect the other end. */
2800 HANDLE hPipeWrite = CreateFileW(wszName, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0 /*fShareMode*/, NULL /*pSecAttr*/,
2801 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
2802 if (hPipeWrite != INVALID_HANDLE_VALUE)
2803 {
2804 /*
2805 * Create the event object and we're done.
2806 *
2807 * It starts in signalled stated so we don't need special code
2808 * for handing when we start waiting.
2809 */
2810 HANDLE hEvent = CreateEventW(NULL /*pSecAttr*/, TRUE /*fManualReset*/, TRUE /*fInitialState*/, NULL /*pwszName*/);
2811 if (hEvent != NULL)
2812 {
2813 PWINCCWPIPE pPipe = (PWINCCWPIPE)xcalloc(sizeof(*pPipe));
2814 pPipe->hPipeMine = hPipeRead;
2815 pPipe->hPipeChild = hPipeWrite;
2816 pPipe->hEvent = hEvent;
2817 pPipe->iWhich = iWhich;
2818 pPipe->fReadPending = FALSE;
2819 pPipe->cbBuffer = cbPipe;
2820 pPipe->pbBuffer = xcalloc(cbPipe);
2821 return pPipe;
2822 }
2823
2824 CloseHandle(hPipeWrite);
2825 CloseHandle(hPipeRead);
2826 return NULL;
2827 }
2828 CloseHandle(hPipeRead);
2829 }
2830 }
2831 return NULL;
2832}
2833
2834/**
2835 * Destroys a childcare worker pipe.
2836 *
2837 * @param pPipe The pipe.
2838 */
2839void MkWinChildcareDeleteWorkerPipe(PWINCCWPIPE pPipe)
2840{
2841 if (pPipe->hPipeChild != NULL)
2842 {
2843 CloseHandle(pPipe->hPipeChild);
2844 pPipe->hPipeChild = NULL;
2845 }
2846
2847 if (pPipe->hPipeMine != NULL)
2848 {
2849 if (pPipe->fReadPending)
2850 if (!CancelIo(pPipe->hPipeMine))
2851 WaitForSingleObject(pPipe->hEvent, INFINITE);
2852 CloseHandle(pPipe->hPipeMine);
2853 pPipe->hPipeMine = NULL;
2854 }
2855
2856 if (pPipe->hEvent != NULL)
2857 {
2858 CloseHandle(pPipe->hEvent);
2859 pPipe->hEvent = NULL;
2860 }
2861
2862 if (pPipe->pbBuffer)
2863 {
2864 free(pPipe->pbBuffer);
2865 pPipe->pbBuffer = NULL;
2866 }
2867}
2868
2869/**
2870 * Initializes the processor group allocator.
2871 *
2872 * @param pState The allocator to initialize.
2873 */
2874void MkWinChildInitCpuGroupAllocator(PMKWINCHILDCPUGROUPALLOCSTATE pState)
2875{
2876 /* We shift the starting group with the make nesting level as part of
2877 our very simple distribution strategy. */
2878 pState->idxGroup = makelevel;
2879 pState->idxProcessorInGroup = 0;
2880}
2881
2882/**
2883 * Allocate CPU group for the next child process.
2884 *
2885 * @returns CPU group.
2886 * @param pState The allocator state. Must be initialized by
2887 * MkWinChildInitCpuGroupAllocator().
2888 */
2889unsigned int MkWinChildAllocateCpuGroup(PMKWINCHILDCPUGROUPALLOCSTATE pState)
2890{
2891 unsigned int iGroup = 0;
2892 if (g_cProcessorGroups > 1)
2893 {
2894 unsigned int cMaxInGroup;
2895 unsigned int cInGroup;
2896
2897 iGroup = pState->idxGroup % g_cProcessorGroups;
2898
2899 /* Advance. We employ a very simple strategy that does 50% in
2900 each group for each group cycle. Odd processor counts are
2901 caught in odd group cycles. The init function selects the
2902 starting group based on make nesting level to avoid stressing
2903 out the first group. */
2904 cInGroup = ++pState->idxProcessorInGroup;
2905 cMaxInGroup = g_pacProcessorsInGroup[iGroup];
2906 if ( !(cMaxInGroup & 1)
2907 || !((pState->idxGroup / g_cProcessorGroups) & 1))
2908 cMaxInGroup /= 2;
2909 else
2910 cMaxInGroup = cMaxInGroup / 2 + 1;
2911 if (cInGroup >= cMaxInGroup)
2912 {
2913 pState->idxProcessorInGroup = 0;
2914 pState->idxGroup++;
2915 }
2916 }
2917 return iGroup;
2918}
2919
2920/**
2921 * Creates another childcare worker.
2922 *
2923 * @returns The new worker, if we succeeded.
2924 */
2925static PWINCHILDCAREWORKER mkWinChildcareCreateWorker(void)
2926{
2927 PWINCHILDCAREWORKER pWorker = (PWINCHILDCAREWORKER)xcalloc(sizeof(*pWorker));
2928 pWorker->uMagic = WINCHILDCAREWORKER_MAGIC;
2929 pWorker->idxWorker = g_cChildCareworkers;
2930 pWorker->hEvtIdle = CreateEventW(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pwszName*/);
2931 if (pWorker->hEvtIdle)
2932 {
2933 pWorker->pStdOut = MkWinChildcareCreateWorkerPipe(1, pWorker->idxWorker);
2934 if (pWorker->pStdOut)
2935 {
2936 pWorker->pStdErr = MkWinChildcareCreateWorkerPipe(2, pWorker->idxWorker);
2937 if (pWorker->pStdErr)
2938 {
2939 /* Before we start the thread, assign it to a processor group. */
2940 pWorker->iProcessorGroup = MkWinChildAllocateCpuGroup(&g_ProcessorGroupAllocator);
2941
2942 /* Try start the thread. */
2943 pWorker->hThread = (HANDLE)_beginthreadex(NULL, 0 /*cbStack*/, mkWinChildcareWorkerThread, pWorker,
2944 0, &pWorker->tid);
2945 if (pWorker->hThread != NULL)
2946 {
2947 pWorker->idxWorker = g_cChildCareworkers++; /* paranoia */
2948 g_papChildCareworkers[pWorker->idxWorker] = pWorker;
2949 return pWorker;
2950 }
2951
2952 /* Bail out! */
2953 ONS (error, NILF, "_beginthreadex failed: %u (%s)\n", errno, strerror(errno));
2954 MkWinChildcareDeleteWorkerPipe(pWorker->pStdErr);
2955 }
2956 else
2957 ON (error, NILF, "Failed to create stderr pipe: %u\n", GetLastError());
2958 MkWinChildcareDeleteWorkerPipe(pWorker->pStdOut);
2959 }
2960 else
2961 ON (error, NILF, "Failed to create stdout pipe: %u\n", GetLastError());
2962 CloseHandle(pWorker->hEvtIdle);
2963 }
2964 else
2965 ON (error, NILF, "CreateEvent failed: %u\n", GetLastError());
2966 pWorker->uMagic = ~WINCHILDCAREWORKER_MAGIC;
2967 free(pWorker);
2968 return NULL;
2969}
2970
2971/**
2972 * Helper for copying argument and environment vectors.
2973 *
2974 * @returns Single alloc block copy.
2975 * @param papszSrc The source vector.
2976 * @param pcbStrings Where to return the size of the strings & terminator.
2977 */
2978static char **mkWinChildCopyStringArray(char **papszSrc, size_t *pcbStrings)
2979{
2980 const char *psz;
2981 char **papszDstArray;
2982 char *pszDstStr;
2983 size_t i;
2984
2985 /* Calc sizes first. */
2986 size_t cbStrings = 1; /* (one extra for terminator string) */
2987 size_t cStrings = 0;
2988 while ((psz = papszSrc[cStrings]) != NULL)
2989 {
2990 cbStrings += strlen(psz) + 1;
2991 cStrings++;
2992 }
2993 *pcbStrings = cbStrings;
2994
2995 /* Allocate destination. */
2996 papszDstArray = (char **)xmalloc(cbStrings + (cStrings + 1) * sizeof(papszDstArray[0]));
2997 pszDstStr = (char *)&papszDstArray[cStrings + 1];
2998
2999 /* Copy it. */
3000 for (i = 0; i < cStrings; i++)
3001 {
3002 const char *pszSource = papszSrc[i];
3003 size_t cchString = strlen(pszSource);
3004 papszDstArray[i] = pszDstStr;
3005 memcpy(pszDstStr, pszSource, cchString);
3006 pszDstStr += cchString;
3007 *pszDstStr++ = '\0';
3008 }
3009 *pszDstStr = '\0';
3010 assert(&pszDstStr[1] - papszDstArray[0] == cbStrings);
3011 papszDstArray[i] = NULL;
3012 return papszDstArray;
3013}
3014
3015/**
3016 * Allocate and init a WINCHILD.
3017 *
3018 * @returns The new windows child structure.
3019 * @param enmType The child type.
3020 */
3021static PWINCHILD mkWinChildNew(WINCHILDTYPE enmType)
3022{
3023 PWINCHILD pChild = xcalloc(sizeof(*pChild));
3024 pChild->enmType = enmType;
3025 pChild->fCoreDumped = 0;
3026 pChild->iSignal = 0;
3027 pChild->iExitCode = 222222;
3028 pChild->uMagic = WINCHILD_MAGIC;
3029 pChild->pid = (intptr_t)pChild;
3030 return pChild;
3031}
3032
3033/**
3034 * Destructor for WINCHILD.
3035 *
3036 * @param pChild The child structure to destroy.
3037 */
3038static void mkWinChildDelete(PWINCHILD pChild)
3039{
3040 assert(pChild->uMagic == WINCHILD_MAGIC);
3041 pChild->uMagic = ~WINCHILD_MAGIC;
3042
3043 switch (pChild->enmType)
3044 {
3045 case WINCHILDTYPE_PROCESS:
3046 {
3047 if (pChild->u.Process.papszArgs)
3048 {
3049 free(pChild->u.Process.papszArgs);
3050 pChild->u.Process.papszArgs = NULL;
3051 }
3052 if (pChild->u.Process.cbEnvStrings && pChild->u.Process.papszEnv)
3053 {
3054 free(pChild->u.Process.papszEnv);
3055 pChild->u.Process.papszEnv = NULL;
3056 }
3057 if (pChild->u.Process.pszShell)
3058 {
3059 free(pChild->u.Process.pszShell);
3060 pChild->u.Process.pszShell = NULL;
3061 }
3062 if (pChild->u.Process.hProcess)
3063 {
3064 CloseHandle(pChild->u.Process.hProcess);
3065 pChild->u.Process.hProcess = NULL;
3066 }
3067 mkWinChildcareWorkerCloseStandardHandles(pChild);
3068 break;
3069 }
3070
3071#ifdef KMK
3072 case WINCHILDTYPE_BUILT_IN:
3073 if (pChild->u.BuiltIn.papszArgs)
3074 {
3075 free(pChild->u.BuiltIn.papszArgs);
3076 pChild->u.BuiltIn.papszArgs = NULL;
3077 }
3078 if (pChild->u.BuiltIn.papszEnv)
3079 {
3080 free(pChild->u.BuiltIn.papszEnv);
3081 pChild->u.BuiltIn.papszEnv = NULL;
3082 }
3083 break;
3084
3085 case WINCHILDTYPE_APPEND:
3086 if (pChild->u.Append.pszFilename)
3087 {
3088 free(pChild->u.Append.pszFilename);
3089 pChild->u.Append.pszFilename = NULL;
3090 }
3091 if (pChild->u.Append.pszAppend)
3092 {
3093 free(pChild->u.Append.pszAppend);
3094 pChild->u.Append.pszAppend = NULL;
3095 }
3096 break;
3097
3098 case WINCHILDTYPE_SUBMIT:
3099 if (pChild->u.Submit.pvSubmitWorker)
3100 {
3101 kSubmitSubProcCleanup((intptr_t)pChild->u.Submit.pvSubmitWorker);
3102 pChild->u.Submit.pvSubmitWorker = NULL;
3103 }
3104 break;
3105
3106 case WINCHILDTYPE_REDIRECT:
3107 if (pChild->u.Redirect.hProcess)
3108 {
3109 CloseHandle(pChild->u.Redirect.hProcess);
3110 pChild->u.Redirect.hProcess = NULL;
3111 }
3112 break;
3113#endif /* KMK */
3114
3115 default:
3116 assert(0);
3117 }
3118
3119 free(pChild);
3120}
3121
3122/**
3123 * Queues the child with a worker, creating new workers if necessary.
3124 *
3125 * @returns 0 on success, windows error code on failure (child destroyed).
3126 * @param pChild The child.
3127 * @param pPid Where to return the PID (optional).
3128 */
3129static int mkWinChildPushToCareWorker(PWINCHILD pChild, pid_t *pPid)
3130{
3131 PWINCHILDCAREWORKER pWorker = NULL;
3132 PWINCHILD pOldChild;
3133 PWINCHILD pCurChild;
3134
3135 /*
3136 * There are usually idle workers around, except for at the start.
3137 */
3138 if (g_cIdleChildcareWorkers > 0)
3139 {
3140 /*
3141 * Try the idle hint first and move forward from it.
3142 */
3143 unsigned int const cWorkers = g_cChildCareworkers;
3144 unsigned int iHint = g_idxLastChildcareWorker;
3145 unsigned int i;
3146 for (i = iHint; i < cWorkers; i++)
3147 {
3148 PWINCHILDCAREWORKER pPossibleWorker = g_papChildCareworkers[i];
3149 if (pPossibleWorker->fIdle)
3150 {
3151 pWorker = pPossibleWorker;
3152 break;
3153 }
3154 }
3155 if (!pWorker)
3156 {
3157 /* Scan from the start. */
3158 if (iHint > cWorkers)
3159 iHint = cWorkers;
3160 for (i = 0; i < iHint; i++)
3161 {
3162 PWINCHILDCAREWORKER pPossibleWorker = g_papChildCareworkers[i];
3163 if (pPossibleWorker->fIdle)
3164 {
3165 pWorker = pPossibleWorker;
3166 break;
3167 }
3168 }
3169 }
3170 }
3171 if (!pWorker)
3172 {
3173 /*
3174 * Try create more workers if we haven't reached the max yet.
3175 */
3176 if (g_cChildCareworkers < g_cChildCareworkersMax)
3177 pWorker = mkWinChildcareCreateWorker();
3178
3179 /*
3180 * Queue it with an existing worker. Look for one without anthing extra scheduled.
3181 */
3182 if (!pWorker)
3183 {
3184 unsigned int i = g_cChildCareworkers;
3185 if (i == 0)
3186 fatal(NILF, 0, _("Failed to create worker threads for managing child processes!\n"));
3187 pWorker = g_papChildCareworkers[--i];
3188 if (pWorker->pTailTodoChildren)
3189 while (i-- > 0)
3190 {
3191 PWINCHILDCAREWORKER pPossibleWorker = g_papChildCareworkers[i];
3192 if (!pPossibleWorker->pTailTodoChildren)
3193 {
3194 pWorker = pPossibleWorker;
3195 break;
3196 }
3197 }
3198 }
3199 }
3200
3201 /*
3202 * Do the queueing.
3203 */
3204 pOldChild = NULL;
3205 for (;;)
3206 {
3207 pChild->pNext = pOldChild;
3208 pCurChild = _InterlockedCompareExchangePointer((void **)&pWorker->pTailTodoChildren, pChild, pOldChild);
3209 if (pCurChild == pOldChild)
3210 {
3211 DWORD volatile dwErr;
3212 _InterlockedIncrement(&g_cPendingChildren);
3213 if ( !pWorker->fIdle
3214 || SetEvent(pWorker->hEvtIdle))
3215 {
3216 *pPid = pChild->pid;
3217 return 0;
3218 }
3219
3220 _InterlockedDecrement(&g_cPendingChildren);
3221 dwErr = GetLastError();
3222 assert(0);
3223 mkWinChildDelete(pChild);
3224 return dwErr ? dwErr : -20;
3225 }
3226 pOldChild = pCurChild;
3227 }
3228}
3229
3230/**
3231 * Creates a regular child process (job.c).
3232 *
3233 * Will copy the information and push it to a childcare thread that does the
3234 * actual process creation.
3235 *
3236 * @returns 0 on success, windows status code on failure.
3237 * @param papszArgs The arguments.
3238 * @param papszEnv The environment (optional).
3239 * @param pszShell The SHELL variable value (optional).
3240 * @param pMkChild The make child structure (optional).
3241 * @param pPid Where to return the pid.
3242 */
3243int MkWinChildCreate(char **papszArgs, char **papszEnv, const char *pszShell, struct child *pMkChild, pid_t *pPid)
3244{
3245 PWINCHILD pChild = mkWinChildNew(WINCHILDTYPE_PROCESS);
3246 pChild->pMkChild = pMkChild;
3247
3248 pChild->u.Process.papszArgs = mkWinChildCopyStringArray(papszArgs, &pChild->u.Process.cbArgsStrings);
3249 if ( !papszEnv
3250 || !pMkChild
3251 || pMkChild->environment == papszEnv)
3252 {
3253 pChild->u.Process.cbEnvStrings = 0;
3254 pChild->u.Process.papszEnv = papszEnv;
3255 }
3256 else
3257 pChild->u.Process.papszEnv = mkWinChildCopyStringArray(papszEnv, &pChild->u.Process.cbEnvStrings);
3258 if (pszShell)
3259 pChild->u.Process.pszShell = xstrdup(pszShell);
3260 pChild->u.Process.hStdOut = INVALID_HANDLE_VALUE;
3261 pChild->u.Process.hStdErr = INVALID_HANDLE_VALUE;
3262
3263 /* We always catch the output in order to prevent character soups courtesy
3264 of the microsoft CRT and/or linkers writing character by character to the
3265 console. Always try write whole lines, even when --output-sync is none. */
3266 pChild->u.Process.fCatchOutput = TRUE;
3267
3268 return mkWinChildPushToCareWorker(pChild, pPid);
3269}
3270
3271/**
3272 * Creates a chile process with a pipe hooked up to stdout.
3273 *
3274 * @returns 0 on success, non-zero on failure.
3275 * @param papszArgs The argument vector.
3276 * @param papszEnv The environment vector (optional).
3277 * @param fdErr File descriptor to hook up to stderr.
3278 * @param pPid Where to return the pid.
3279 * @param pfdReadPipe Where to return the read end of the pipe.
3280 */
3281int MkWinChildCreateWithStdOutPipe(char **papszArgs, char **papszEnv, int fdErr, pid_t *pPid, int *pfdReadPipe)
3282{
3283 /*
3284 * Create the pipe.
3285 */
3286 HANDLE hReadPipe;
3287 HANDLE hWritePipe;
3288 if (CreatePipe(&hReadPipe, &hWritePipe, NULL, 0 /* default size */))
3289 {
3290 //if (SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT /* clear */ , HANDLE_FLAG_INHERIT /*set*/))
3291 {
3292 int fdReadPipe = _open_osfhandle((intptr_t)hReadPipe, O_RDONLY);
3293 if (fdReadPipe >= 0)
3294 {
3295 PWINCHILD pChild;
3296 int rc;
3297
3298 /*
3299 * Get a handle for fdErr. Ignore failure.
3300 */
3301 HANDLE hStdErr = INVALID_HANDLE_VALUE;
3302 if (fdErr >= 0)
3303 {
3304 HANDLE hNative = (HANDLE)_get_osfhandle(fdErr);
3305 if (!DuplicateHandle(GetCurrentProcess(), hNative, GetCurrentProcess(),
3306 &hStdErr, 0 /*DesiredAccess*/, TRUE /*fInherit*/, DUPLICATE_SAME_ACCESS))
3307 {
3308 ONN(error, NILF, _("DuplicateHandle failed on stderr descriptor (%u): %u\n"), fdErr, GetLastError());
3309 hStdErr = INVALID_HANDLE_VALUE;
3310 }
3311 }
3312
3313 /*
3314 * Push it off to the worker thread.
3315 */
3316 pChild = mkWinChildNew(WINCHILDTYPE_PROCESS);
3317 pChild->u.Process.papszArgs = mkWinChildCopyStringArray(papszArgs, &pChild->u.Process.cbArgsStrings);
3318 pChild->u.Process.papszEnv = mkWinChildCopyStringArray(papszEnv ? papszEnv : environ,
3319 &pChild->u.Process.cbEnvStrings);
3320 //if (pszShell)
3321 // pChild->u.Process.pszShell = xstrdup(pszShell);
3322 pChild->u.Process.hStdOut = hWritePipe;
3323 pChild->u.Process.hStdErr = hStdErr;
3324 pChild->u.Process.fCloseStdErr = TRUE;
3325 pChild->u.Process.fCloseStdOut = TRUE;
3326
3327 rc = mkWinChildPushToCareWorker(pChild, pPid);
3328 if (rc == 0)
3329 *pfdReadPipe = fdReadPipe;
3330 else
3331 {
3332 ON(error, NILF, _("mkWinChildPushToCareWorker failed on pipe: %d\n"), rc);
3333 close(fdReadPipe);
3334 *pfdReadPipe = -1;
3335 *pPid = -1;
3336 }
3337 return rc;
3338 }
3339
3340 ON(error, NILF, _("_open_osfhandle failed on pipe: %u\n"), errno);
3341 }
3342 //else
3343 // ON(error, NILF, _("SetHandleInformation failed on pipe: %u\n"), GetLastError());
3344 if (hReadPipe != INVALID_HANDLE_VALUE)
3345 CloseHandle(hReadPipe);
3346 CloseHandle(hWritePipe);
3347 }
3348 else
3349 ON(error, NILF, _("CreatePipe failed: %u\n"), GetLastError());
3350 *pfdReadPipe = -1;
3351 *pPid = -1;
3352 return -1;
3353}
3354
3355#ifdef KMK
3356
3357/**
3358 * Interface used by kmkbuiltin.c for executing builtin commands on threads.
3359 *
3360 * @returns 0 on success, windows status code on failure.
3361 * @param pBuiltIn The kmk built-in command entry.
3362 * @param cArgs The number of arguments in papszArgs.
3363 * @param papszArgs The argument vector.
3364 * @param papszEnv The environment vector, optional.
3365 * @param pMkChild The make child structure.
3366 * @param pPid Where to return the pid.
3367 */
3368int MkWinChildCreateBuiltIn(PCKMKBUILTINENTRY pBuiltIn, int cArgs, char **papszArgs, char **papszEnv,
3369 struct child *pMkChild, pid_t *pPid)
3370{
3371 size_t cbIgnored;
3372 PWINCHILD pChild = mkWinChildNew(WINCHILDTYPE_BUILT_IN);
3373 pChild->pMkChild = pMkChild;
3374 pChild->u.BuiltIn.pBuiltIn = pBuiltIn;
3375 pChild->u.BuiltIn.cArgs = cArgs;
3376 pChild->u.BuiltIn.papszArgs = mkWinChildCopyStringArray(papszArgs, &cbIgnored);
3377 pChild->u.BuiltIn.papszEnv = papszEnv ? mkWinChildCopyStringArray(papszEnv, &cbIgnored) : NULL;
3378 return mkWinChildPushToCareWorker(pChild, pPid);
3379}
3380
3381/**
3382 * Interface used by append.c for do the slow file system part.
3383 *
3384 * This will append the given buffer to the specified file and free the buffer.
3385 *
3386 * @returns 0 on success, windows status code on failure.
3387 *
3388 * @param pszFilename The name of the file to append to.
3389 * @param ppszAppend What to append. The pointer pointed to is set to
3390 * NULL once we've taken ownership of the buffer and
3391 * promise to free it.
3392 * @param cbAppend How much to append.
3393 * @param fTruncate Whether to truncate the file before appending to it.
3394 * @param pMkChild The make child structure.
3395 * @param pPid Where to return the pid.
3396 */
3397int MkWinChildCreateAppend(const char *pszFilename, char **ppszAppend, size_t cbAppend, int fTruncate,
3398 struct child *pMkChild, pid_t *pPid)
3399{
3400 size_t cbFilename = strlen(pszFilename) + 1;
3401 PWINCHILD pChild = mkWinChildNew(WINCHILDTYPE_APPEND);
3402 pChild->pMkChild = pMkChild;
3403 pChild->u.Append.fTruncate = fTruncate;
3404 pChild->u.Append.pszAppend = *ppszAppend;
3405 pChild->u.Append.cbAppend = cbAppend;
3406 pChild->u.Append.pszFilename = (char *)memcpy(xmalloc(cbFilename), pszFilename, cbFilename);
3407 *ppszAppend = NULL;
3408 return mkWinChildPushToCareWorker(pChild, pPid);
3409}
3410
3411/**
3412 * Interface used by kSubmit.c for registering stuff to wait on.
3413 *
3414 * @returns 0 on success, windows status code on failure.
3415 * @param hEvent The event object handle to wait on.
3416 * @param pvSubmitWorker The argument to pass back to kSubmit to clean up.
3417 * @param pStdOut Standard output pipe for the worker. Optional.
3418 * @param pStdErr Standard error pipe for the worker. Optional.
3419 * @param pMkChild The make child structure.
3420 * @param pPid Where to return the pid.
3421 */
3422int MkWinChildCreateSubmit(intptr_t hEvent, void *pvSubmitWorker, PWINCCWPIPE pStdOut, PWINCCWPIPE pStdErr,
3423 struct child *pMkChild, pid_t *pPid)
3424{
3425 PWINCHILD pChild = mkWinChildNew(WINCHILDTYPE_SUBMIT);
3426 pChild->pMkChild = pMkChild;
3427 pChild->u.Submit.hEvent = (HANDLE)hEvent;
3428 pChild->u.Submit.pvSubmitWorker = pvSubmitWorker;
3429 pChild->u.Submit.pStdOut = pStdOut;
3430 pChild->u.Submit.pStdErr = pStdErr;
3431 return mkWinChildPushToCareWorker(pChild, pPid);
3432}
3433
3434/**
3435 * Interface used by redirect.c for registering stuff to wait on.
3436 *
3437 * @returns 0 on success, windows status code on failure.
3438 * @param hProcess The process object to wait on.
3439 * @param pPid Where to return the pid.
3440 */
3441int MkWinChildCreateRedirect(intptr_t hProcess, pid_t *pPid)
3442{
3443 PWINCHILD pChild = mkWinChildNew(WINCHILDTYPE_REDIRECT);
3444 pChild->u.Redirect.hProcess = (HANDLE)hProcess;
3445 return mkWinChildPushToCareWorker(pChild, pPid);
3446}
3447
3448
3449/**
3450 * New interface used by redirect.c for spawning and waitin on a child.
3451 *
3452 * This interface is only used when kmk_builtin_redirect is already running on
3453 * a worker thread.
3454 *
3455 * @returns exit status.
3456 * @param pvWorker The worker instance.
3457 * @param pszExecutable The executable image to run.
3458 * @param papszArgs Argument vector.
3459 * @param fQuotedArgv Whether the argument vector is already quoted and
3460 * just need some space to be turned into a command
3461 * line.
3462 * @param papszEnvVars Environment vector.
3463 * @param pszCwd The working directory of the child. Optional.
3464 * @param pafReplace Which standard handles to replace. Maybe modified!
3465 * @param pahReplace The replacement handles. Maybe modified!
3466 *
3467 */
3468int MkWinChildBuiltInExecChild(void *pvWorker, const char *pszExecutable, char **papszArgs, BOOL fQuotedArgv,
3469 char **papszEnvVars, const char *pszCwd, BOOL pafReplace[3], HANDLE pahReplace[3])
3470{
3471 PWINCHILDCAREWORKER pWorker = (PWINCHILDCAREWORKER)pvWorker;
3472 WCHAR *pwszSearchPath = NULL;
3473 WCHAR *pwszzEnvironment = NULL;
3474 WCHAR *pwszCommandLine = NULL;
3475 WCHAR *pwszImageName = NULL;
3476 WCHAR *pwszCwd = NULL;
3477 BOOL fNeedShell = FALSE;
3478 PWINCHILD pChild;
3479 int rc;
3480 assert(pWorker->uMagic == WINCHILDCAREWORKER_MAGIC);
3481 pChild = pWorker->pCurChild;
3482 assert(pChild != NULL && pChild->uMagic == WINCHILD_MAGIC);
3483
3484 /*
3485 * Convert the CWD first since it's optional and we don't need to clean
3486 * up anything here if it fails.
3487 */
3488 if (pszCwd)
3489 {
3490 size_t cchCwd = strlen(pszCwd);
3491 int cwcCwd = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszCwd, cchCwd + 1, NULL, 0);
3492 pwszCwd = xmalloc((cwcCwd + 1) * sizeof(WCHAR)); /* (+1 in case cwcCwd is 0) */
3493 cwcCwd = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszCwd, cchCwd + 1, pwszCwd, cwcCwd + 1);
3494 if (!cwcCwd)
3495 {
3496 rc = GetLastError();
3497 MkWinChildError(pWorker, 1, _("MultiByteToWideChar failed to convert CWD (%s): %u\n"), pszCwd, (unsigned)rc);
3498 return rc;
3499 }
3500 }
3501
3502 /*
3503 * Before we search for the image, we convert the environment so we don't
3504 * have to traverse it twice to find the PATH.
3505 */
3506 rc = mkWinChildcareWorkerConvertEnvironment(pWorker, papszEnvVars ? papszEnvVars : environ, 0/*cbEnvStrings*/,
3507 &pwszzEnvironment, &pwszSearchPath);
3508 /*
3509 * Find the executable and maybe checking if it's a shell script, then
3510 * convert it to a command line.
3511 */
3512 if (rc == 0)
3513 rc = mkWinChildcareWorkerFindImage(pWorker, pszExecutable, pwszSearchPath, pwszzEnvironment, NULL /*pszShell*/,
3514 &pwszImageName, &fNeedShell, &pChild->fProbableClExe);
3515 if (rc == 0)
3516 {
3517 assert(!fNeedShell);
3518 if (!fQuotedArgv)
3519 rc = mkWinChildcareWorkerConvertCommandline(pWorker, papszArgs, 0 /*fFlags*/, &pwszCommandLine);
3520 else
3521 rc = mkWinChildcareWorkerConvertQuotedArgvToCommandline(pWorker, papszArgs, &pwszCommandLine);
3522
3523 /*
3524 * Create the child process.
3525 */
3526 if (rc == 0)
3527 {
3528 HANDLE hProcess;
3529 rc = mkWinChildcareWorkerCreateProcess(pWorker, pwszImageName, pwszCommandLine, pwszzEnvironment,
3530 pwszCwd, pafReplace, pahReplace, TRUE /*fCatchOutput*/, &hProcess);
3531 if (rc == 0)
3532 {
3533 /*
3534 * Wait for the child to complete.
3535 */
3536 rc = mkWinChildcareWorkerWaitForProcess(pWorker, pChild, hProcess, pwszImageName, TRUE /*fCatchOutput*/);
3537 CloseHandle(hProcess);
3538 }
3539 }
3540 }
3541
3542 free(pwszCwd);
3543 free(pwszCommandLine);
3544 free(pwszImageName);
3545 free(pwszzEnvironment);
3546
3547 return rc;
3548}
3549
3550#endif /* CONFIG_NEW_WIN_CHILDREN */
3551
3552/**
3553 * Interface used to kill process when processing Ctrl-C and fatal errors.
3554 *
3555 * @returns 0 on success, -1 & errno on error.
3556 * @param pid The process to kill (PWINCHILD).
3557 * @param iSignal What to kill it with.
3558 * @param pMkChild The make child structure for validation.
3559 */
3560int MkWinChildKill(pid_t pid, int iSignal, struct child *pMkChild)
3561{
3562 PWINCHILD pChild = (PWINCHILD)pid;
3563 if (pChild)
3564 {
3565 assert(pChild->uMagic == WINCHILD_MAGIC);
3566 if (pChild->uMagic == WINCHILD_MAGIC)
3567 {
3568 switch (pChild->enmType)
3569 {
3570 case WINCHILDTYPE_PROCESS:
3571 assert(pChild->pMkChild == pMkChild);
3572 TerminateProcess(pChild->u.Process.hProcess, DBG_TERMINATE_PROCESS);
3573 pChild->iSignal = iSignal;
3574 break;
3575#ifdef KMK
3576 case WINCHILDTYPE_SUBMIT:
3577 {
3578 pChild->iSignal = iSignal;
3579 SetEvent(pChild->u.Submit.hEvent);
3580 break;
3581 }
3582
3583 case WINCHILDTYPE_REDIRECT:
3584 TerminateProcess(pChild->u.Redirect.hProcess, DBG_TERMINATE_PROCESS);
3585 pChild->iSignal = iSignal;
3586 break;
3587
3588 case WINCHILDTYPE_BUILT_IN:
3589 break;
3590
3591#endif /* KMK */
3592 default:
3593 assert(0);
3594 }
3595 }
3596 }
3597 return -1;
3598}
3599
3600/**
3601 * Wait for a child process to complete
3602 *
3603 * @returns 0 on success, windows error code on failure.
3604 * @param fBlock Whether to block.
3605 * @param pPid Where to return the pid if a child process
3606 * completed. This is set to zero if none.
3607 * @param piExitCode Where to return the exit code.
3608 * @param piSignal Where to return the exit signal number.
3609 * @param pfCoreDumped Where to return the core dumped indicator.
3610 * @param ppMkChild Where to return the associated struct child pointer.
3611 */
3612int MkWinChildWait(int fBlock, pid_t *pPid, int *piExitCode, int *piSignal, int *pfCoreDumped, struct child **ppMkChild)
3613{
3614 PWINCHILD pChild;
3615#ifdef DEBUG_STDOUT_CLOSE_ISSUE
3616 my_check_stdout ("MkWinChildWait/entry");
3617#endif
3618
3619 *pPid = 0;
3620 *piExitCode = -222222;
3621 *pfCoreDumped = 0;
3622 *ppMkChild = NULL;
3623
3624 /*
3625 * Wait if necessary.
3626 */
3627 if (fBlock && !g_pTailCompletedChildren && g_cPendingChildren > 0)
3628 {
3629 DWORD dwStatus = WaitForSingleObject(g_hEvtWaitChildren, INFINITE);
3630#ifdef DEBUG_STDOUT_CLOSE_ISSUE
3631 my_check_stdout ("MkWinChildWait/wakeup");
3632#endif
3633 if (dwStatus == WAIT_FAILED)
3634 return (int)GetLastError();
3635 }
3636
3637 /*
3638 * Try unlink the last child in the LIFO.
3639 */
3640 pChild = g_pTailCompletedChildren;
3641 if (!pChild)
3642 return 0;
3643 pChild = mkWinChildDequeFromLifo(&g_pTailCompletedChildren, pChild);
3644 assert(pChild);
3645
3646 /*
3647 * Set return values and ditch the child structure.
3648 */
3649 *pPid = pChild->pid;
3650 *piExitCode = pChild->iExitCode;
3651 *pfCoreDumped = pChild->fCoreDumped;
3652 *ppMkChild = pChild->pMkChild;
3653 switch (pChild->enmType)
3654 {
3655 case WINCHILDTYPE_PROCESS:
3656 break;
3657#ifdef KMK
3658 case WINCHILDTYPE_BUILT_IN:
3659 case WINCHILDTYPE_APPEND:
3660 case WINCHILDTYPE_SUBMIT:
3661 case WINCHILDTYPE_REDIRECT:
3662 break;
3663#endif /* KMK */
3664 default:
3665 assert(0);
3666 }
3667 mkWinChildDelete(pChild);
3668
3669#ifdef KMK
3670 /* Flush the volatile directory cache. */
3671 dir_cache_invalid_after_job();
3672#endif
3673#ifdef DEBUG_STDOUT_CLOSE_ISSUE
3674 my_check_stdout ("MkWinChildWait/tail");
3675#endif
3676 return 0;
3677}
3678
3679/**
3680 * Get the child completed event handle.
3681 *
3682 * Needed when w32os.c is waiting for a job token to become available, given
3683 * that completed children is the typical source of these tokens (esp. for kmk).
3684 *
3685 * @returns Zero if no active children, event handle if waiting is required.
3686 */
3687intptr_t MkWinChildGetCompleteEventHandle(void)
3688{
3689 /* We don't return the handle if we've got completed children. This
3690 is a safe guard against being called twice in a row without any
3691 MkWinChildWait call inbetween. */
3692 if (!g_pTailCompletedChildren)
3693 return (intptr_t)g_hEvtWaitChildren;
3694 return 0;
3695}
3696
3697/**
3698 * Emulate execv() for restarting kmk after one or more makefiles has been made.
3699 *
3700 * Does not return.
3701 *
3702 * @param papszArgs The arguments.
3703 * @param papszEnv The environment.
3704 */
3705void MkWinChildReExecMake(char **papszArgs, char **papszEnv)
3706{
3707 PROCESS_INFORMATION ProcInfo;
3708 STARTUPINFOW StartupInfo;
3709 WCHAR *pwszCommandLine;
3710 WCHAR *pwszzEnvironment;
3711 WCHAR *pwszPathIgnored;
3712 int rc;
3713#ifdef DEBUG_STDOUT_CLOSE_ISSUE
3714 my_check_stdout("MkWinChildReExecMake/entry");
3715#endif
3716
3717 /*
3718 * Get the executable name.
3719 */
3720 WCHAR wszImageName[MKWINCHILD_MAX_PATH];
3721 DWORD cwcImageName = GetModuleFileNameW(GetModuleHandle(NULL), wszImageName, MKWINCHILD_MAX_PATH);
3722 if (cwcImageName == 0)
3723 ON(fatal, NILF, _("MkWinChildReExecMake: GetModuleFileName failed: %u\n"), GetLastError());
3724
3725 /*
3726 * Create the command line and environment.
3727 */
3728 rc = mkWinChildcareWorkerConvertCommandline(NULL, papszArgs, 0 /*fFlags*/, &pwszCommandLine);
3729 if (rc != 0)
3730 ON(fatal, NILF, _("MkWinChildReExecMake: mkWinChildcareWorkerConvertCommandline failed: %u\n"), rc);
3731
3732 rc = mkWinChildcareWorkerConvertEnvironment(NULL, papszEnv ? papszEnv : environ, 0 /*cbEnvStrings*/,
3733 &pwszzEnvironment, &pwszPathIgnored);
3734 if (rc != 0)
3735 ON(fatal, NILF, _("MkWinChildReExecMake: mkWinChildcareWorkerConvertEnvironment failed: %u\n"), rc);
3736
3737#ifdef KMK
3738 /*
3739 * Flush the file system cache to avoid messing up tools fetching
3740 * going on in the "exec'ed" make by keeping directories open.
3741 */
3742 dir_cache_invalid_all_and_close_dirs(1);
3743#endif
3744
3745 /*
3746 * Fill out the startup info and try create the process.
3747 */
3748 memset(&ProcInfo, 0, sizeof(ProcInfo));
3749 memset(&StartupInfo, 0, sizeof(StartupInfo));
3750 StartupInfo.cb = sizeof(StartupInfo);
3751 GetStartupInfoW(&StartupInfo);
3752 if (!CreateProcessW(wszImageName, pwszCommandLine, NULL /*pProcSecAttr*/, NULL /*pThreadSecAttr*/,
3753 TRUE /*fInheritHandles*/, CREATE_UNICODE_ENVIRONMENT, pwszzEnvironment, NULL /*pwsz*/,
3754 &StartupInfo, &ProcInfo))
3755 ON(fatal, NILF, _("MkWinChildReExecMake: CreateProcessW failed: %u\n"), GetLastError());
3756 CloseHandle(ProcInfo.hThread);
3757
3758 /*
3759 * Wait for it to complete and forward the status code to our parent.
3760 */
3761 for (;;)
3762 {
3763 DWORD dwExitCode = -2222;
3764 DWORD dwStatus = WaitForSingleObject(ProcInfo.hProcess, INFINITE);
3765 if ( dwStatus == WAIT_IO_COMPLETION
3766 || dwStatus == WAIT_TIMEOUT /* whatever */)
3767 continue; /* however unlikely, these aren't fatal. */
3768
3769 /* Get the status code and terminate. */
3770 if (dwStatus == WAIT_OBJECT_0)
3771 {
3772 if (!GetExitCodeProcess(ProcInfo.hProcess, &dwExitCode))
3773 {
3774 ON(fatal, NILF, _("MkWinChildReExecMake: GetExitCodeProcess failed: %u\n"), GetLastError());
3775 dwExitCode = -2222;
3776 }
3777 }
3778 else if (dwStatus)
3779 dwExitCode = dwStatus;
3780
3781 CloseHandle(ProcInfo.hProcess);
3782#ifdef DEBUG_STDOUT_CLOSE_ISSUE
3783 my_check_stdout("MkWinChildReExecMake/exit");
3784#endif
3785 for (;;)
3786 exit(dwExitCode);
3787 }
3788}
3789
3790#ifdef WITH_RW_LOCK
3791/** Serialization with kmkbuiltin_redirect. */
3792void MkWinChildExclusiveAcquire(void)
3793{
3794 AcquireSRWLockExclusive(&g_RWLock);
3795}
3796
3797/** Serialization with kmkbuiltin_redirect. */
3798void MkWinChildExclusiveRelease(void)
3799{
3800 ReleaseSRWLockExclusive(&g_RWLock);
3801}
3802#endif /* WITH_RW_LOCK */
3803
3804/**
3805 * Implementation of the CLOSE_ON_EXEC macro.
3806 *
3807 * @returns errno value.
3808 * @param fd The file descriptor to hide from children.
3809 */
3810int MkWinChildUnrelatedCloseOnExec(int fd)
3811{
3812 if (fd >= 0)
3813 {
3814 HANDLE hNative = (HANDLE)_get_osfhandle(fd);
3815 if (hNative != INVALID_HANDLE_VALUE && hNative != NULL)
3816 {
3817 if (SetHandleInformation(hNative, HANDLE_FLAG_INHERIT /*clear*/ , 0 /*set*/))
3818 return 0;
3819 }
3820 return errno;
3821 }
3822 return EINVAL;
3823}
3824
Note: See TracBrowser for help on using the repository browser.