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

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

kmk/win: Catch output from processes spawned by kmk_redirect. Made imagecase threadsafe and made winchildren use it.

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