VirtualBox

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

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

kmk/win: Reworking child process handling. This effort will hopefully handle processor groups better and allow executing internal function off the main thread.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 67.0 KB
Line 
1/* $Id: winchildren.c 3156 2018-03-18 20:10:03Z bird $ */
2/** @file
3 * Child process creation and management for kmk.
4 */
5
6/*
7 * Copyright (c) 2018 knut st. osmundsen <[email protected]>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/* No GNU coding style here atm, convert if upstreamed. */
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include "../makeint.h"
32#include "../job.h"
33#include "../debug.h"
34#include "../kmkbuiltin.h"
35#include "winchildren.h"
36
37#include <Windows.h>
38#include <assert.h>
39#include <process.h>
40
41
42/*********************************************************************************************************************************
43* Defined Constants And Macros *
44*********************************************************************************************************************************/
45#define MKWINCHILD_MAX_PATH 1024
46
47/** Checks the UTF-16 environment variable pointed to is the PATH. */
48#define IS_PATH_ENV_VAR(a_cwcVar, a_pwszVar) \
49 ( (a_cwcVar) >= 5 \
50 && (a_pwszVar)[4] == L'=' \
51 && ((a_pwszVar)[0] == L'P' || (a_pwszVar)[0] == L'p') \
52 && ((a_pwszVar)[1] == L'A' || (a_pwszVar)[1] == L'a') \
53 && ((a_pwszVar)[2] == L'T' || (a_pwszVar)[2] == L't') \
54 && ((a_pwszVar)[3] == L'H' || (a_pwszVar)[3] == L'h') )
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60/**
61 * Child process type.
62 */
63typedef enum WINCHILDTYPE
64{
65 WINCHILDTYPE_INVALID = 0,
66 /** Normal child process. */
67 WINCHILDTYPE_PROCESS,
68 /** kmkbuiltin command. */
69 WINCHILDTYPE_BUILTIN,
70 /** kSubmit job. */
71 WINCHILDTYPE_SUBMIT,
72 /** kmk_redirect job. */
73 WINCHILDTYPE_REDIRECT,
74 /** End of valid child types. */
75 WINCHILDTYPE_END
76} WINCHILDTYPE;
77
78
79/** Pointer to a windows child process. */
80typedef struct WINCHILD *PWINCHILD;
81/**
82 * Windows child process.
83 */
84typedef struct WINCHILD
85{
86 /** Magic / eyecatcher (WINCHILD_MAGIC). */
87 ULONG uMagic;
88 /** Child type. */
89 WINCHILDTYPE enmType;
90 /** Pointer to the next child process. */
91 PWINCHILD pNext;
92 /** The pid for this child. */
93 pid_t pid;
94 /** The make child structure associated with this child. */
95 struct child *pMkChild;
96
97 /** The process exit code. */
98 int iExitCode;
99 /** Kill signal, in case we or someone else killed it. */
100 int iSignal;
101 /** Set if core was dumped. */
102 int fCoreDumped;
103
104 /** Type specific data. */
105 union
106 {
107 /** Data for WINCHILDTYPE_PROCESS. */
108 struct
109 {
110 /** Argument vector (single allocation, strings following array). */
111 char **papszArgs;
112 /** Length of the argument strings. */
113 size_t cbArgsStrings;
114 /** Environment vector. Only a copy if fEnvIsCopy is set. */
115 char **papszEnv;
116 /** If we made a copy of the environment, this is the size of the
117 * strings and terminator string (not in array). This is done to
118 * speed up conversion, since MultiByteToWideChar can handle '\0'. */
119 size_t cbEnvStrings;
120 /** The make shell to use (copy). */
121 char *pszShell;
122 /** Child process handle. */
123 HANDLE hProcess;
124 } Process;
125
126 /** Data for WINCHILDTYPE_SUBMIT. */
127 struct
128 {
129 /** The event we're to wait on (hooked up to a pipe) */
130 HANDLE hEvent;
131 /** Parameter for the cleanup callback. */
132 void *pvSubmitWorker;
133 } Submit;
134
135 /** Data for WINCHILDTYPE_REDIRECT. */
136 struct
137 {
138 /** Child process handle. */
139 HANDLE hProcess;
140 } Redirect;
141 } u;
142
143} WINCHILD;
144/** WINCHILD::uMagic value. */
145#define WINCHILD_MAGIC 0xbabebabeU
146
147
148/**
149 * Data for a windows childcare worker thread.
150 *
151 * We use one worker thread per child, reusing the threads when possible.
152 *
153 * This setup helps avoid the 64-bit handle with the WaitForMultipleObject API.
154 *
155 * It also helps using all CPUs on systems with more than one CPU group
156 * (typically systems with more than 64 CPU threads or/and multiple sockets, or
157 * special configs).
158 *
159 * This helps facilitates using pipes for collecting output child rather
160 * than temporary files. Pipes doesn't involve NTFS and can easily be reused.
161 *
162 * Finally, kBuild specific, this allows running kmkbuiltin_xxxx commands in
163 * threads.
164 */
165typedef struct WINCHILDCAREWORKER
166{
167 /** Magic / eyecatcher (WINCHILDCAREWORKER_MAGIC). */
168 ULONG uMagic;
169 /** The processor group for this worker. */
170 unsigned int iProcessorGroup;
171 /** The thread ID. */
172 unsigned int tid;
173 /** The thread handle. */
174 HANDLE hThread;
175 /** The event the thread is idling on. */
176 HANDLE hEvtIdle;
177 /** Pointer to the current child. */
178 PWINCHILD volatile pCurChild;
179 /** List of children pending execution on this worker.
180 * This is updated atomitically just like g_pTailCompletedChildren. */
181 PWINCHILD volatile pTailTodoChildren;
182 /** TRUE if idle, FALSE if not. */
183 long volatile fIdle;
184} WINCHILDCAREWORKER;
185/** Pointer to a childcare worker thread. */
186typedef WINCHILDCAREWORKER *PWINCHILDCAREWORKER;
187/** WINCHILD::uMagic value. */
188#define WINCHILDCAREWORKER_MAGIC 0xdad0dad0U
189
190
191/*********************************************************************************************************************************
192* Global Variables *
193*********************************************************************************************************************************/
194/** Whether it's initialized or not. */
195static BOOL g_fInitialized = FALSE;
196/** Set when we're shutting down everything. */
197static BOOL volatile g_fShutdown = FALSE;
198/** Event used to wait for children. */
199static HANDLE g_hEvtWaitChildren = INVALID_HANDLE_VALUE;
200/** Number of childcare workers currently in g_papChildCareworkers. */
201static unsigned g_cChildCareworkers = 0;
202/** Maximum number of childcare workers in g_papChildCareworkers. */
203static unsigned g_cChildCareworkersMax = 0;
204/** Pointer to childcare workers. */
205static PWINCHILDCAREWORKER *g_papChildCareworkers = NULL;
206/** The group index for the worker allocator.
207 * This is ever increasing and must be modded by g_cProcessorGroups. */
208static unsigned g_idxProcessorGroupAllocator = 0;
209/** The processor in group index for the worker allocator. */
210static unsigned g_idxProcessorInGroupAllocator = 0;
211/** Number of processor groups in the system. */
212static unsigned g_cProcessorGroups = 1;
213/** Array detailing how many active processors there are in each group. */
214static unsigned const *g_pacProcessorsInGroup = &g_cProcessorGroups;
215/** Kernel32!GetActiveProcessorGroupCount */
216static WORD (WINAPI *g_pfnGetActiveProcessorGroupCount)(VOID);
217/** Kernel32!GetActiveProcessorCount */
218static DWORD (WINAPI *g_pfnGetActiveProcessorCount)(WORD);
219/** Kernel32!SetThreadGroupAffinity */
220static BOOL (WINAPI *g_pfnSetThreadGroupAffinity)(HANDLE, CONST GROUP_AFFINITY *, GROUP_AFFINITY *);
221/** Windows version info.
222 * @note Putting this before the volatile stuff, hoping to keep it in a
223 * different cache line than the static bits above. */
224static OSVERSIONINFOA g_VersionInfo = { sizeof(g_VersionInfo), 4, 0, 1381, VER_PLATFORM_WIN32_NT, {0} };
225
226/** Children that has been completed.
227 * This is updated atomically, pushing completed children in LIFO fashion
228 * (thus 'tail'), then hitting g_hEvtWaitChildren if head. */
229static PWINCHILD volatile g_pTailCompletedChildren = NULL;
230
231/** Number of idle pending children.
232 * This is updated before g_hEvtWaitChildren is signalled. */
233static unsigned volatile g_cPendingChildren = 0;
234
235/** Number of idle childcare worker threads. */
236static unsigned volatile g_cIdleChildcareWorkers = 0;
237/** Index of the last idle child careworker (just a hint). */
238static unsigned volatile g_idxLastChildcareWorker = 0;
239
240
241
242
243/**
244 * Initializes the windows child module.
245 *
246 * @param cJobSlots The number of job slots.
247 */
248void MkWinChildInit(unsigned int cJobSlots)
249{
250 /*
251 * Figure out how many childcare workers first.
252 */
253 static unsigned int const s_cMaxWorkers = 4096;
254 unsigned cWorkers;
255 if (cJobSlots >= 1 && cJobSlots < s_cMaxWorkers)
256 cWorkers = cJobSlots;
257 else
258 cWorkers = s_cMaxWorkers;
259
260 /*
261 * Allocate the array and the child completed event object.
262 */
263 g_papChildCareworkers = (PWINCHILDCAREWORKER *)xcalloc(cWorkers * sizeof(g_papChildCareworkers[0]));
264 g_cChildCareworkersMax = cWorkers;
265
266 g_hEvtWaitChildren = CreateEvent(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pszName*/);
267 if (!g_hEvtWaitChildren)
268 fatal(NILF, INTSTR_LENGTH, _("MkWinChildInit: CreateEvent failed: %u"), GetLastError());
269
270 /*
271 * Figure out how many processor groups there are.
272 * For that we need to first figure the windows version.
273 */
274 if (!GetVersionExA(&g_VersionInfo))
275 {
276 DWORD uRawVer = GetVersion();
277 g_VersionInfo.dwMajorVersion = uRawVer & 0xff;
278 g_VersionInfo.dwMinorVersion = (uRawVer >> 8) & 0xff;
279 g_VersionInfo.dwBuildNumber = (uRawVer >> 16) & 0x7fff;
280 }
281 if (g_VersionInfo.dwMajorVersion >= 6)
282 {
283 HMODULE hmod = GetModuleHandleA("KERNEL32.DLL");
284 *(FARPROC *)&g_pfnGetActiveProcessorGroupCount = GetProcAddress(hmod, "GetActiveProcessorGroupCount");
285 *(FARPROC *)&g_pfnGetActiveProcessorCount = GetProcAddress(hmod, "GetActiveProcessorCount");
286 *(FARPROC *)&g_pfnSetThreadGroupAffinity = GetProcAddress(hmod, "SetThreadGroupAffinity");
287 if ( g_pfnSetThreadGroupAffinity
288 && g_pfnGetActiveProcessorCount
289 && g_pfnGetActiveProcessorGroupCount)
290 {
291 unsigned int *pacProcessorsInGroup;
292 unsigned iGroup;
293 g_cProcessorGroups = g_pfnGetActiveProcessorGroupCount();
294 if (g_cProcessorGroups == 0)
295 g_cProcessorGroups = 1;
296
297 pacProcessorsInGroup = (unsigned int *)xmalloc(sizeof(g_pacProcessorsInGroup[0]) * g_cProcessorGroups);
298 g_pacProcessorsInGroup = pacProcessorsInGroup;
299 for (iGroup = 0; iGroup < g_cProcessorGroups; iGroup++)
300 pacProcessorsInGroup[iGroup] = g_pfnGetActiveProcessorCount(iGroup);
301
302 /* We shift the starting group with the make nesting level as part of
303 our very simple distribution strategy. */
304 g_idxProcessorGroupAllocator = makelevel;
305 }
306 else
307 {
308 g_pfnSetThreadGroupAffinity = NULL;
309 g_pfnGetActiveProcessorCount = NULL;
310 g_pfnGetActiveProcessorGroupCount = NULL;
311 }
312 }
313}
314
315
316/**
317 * Emulate execv() for restarting kmk after one ore more makefiles has been
318 * made.
319 *
320 * Does not return.
321 *
322 * @param papszArgs The arguments.
323 * @param papszEnv The environment.
324 */
325void MkWinChildReExecMake(char **papszArgs, char **papszEnv)
326{
327 fatal(NILF, 0, __FUNCTION__ "not implemented!\n");
328}
329
330/**
331 * Used by mkWinChildcareWorkerThread() and MkWinChildWait() to get the head
332 * child from a lifo (g_pTailCompletedChildren, pTailTodoChildren).
333 *
334 * @returns Head child.
335 * @param ppTail Pointer to the child variable.
336 * @param pChild Tail child.
337 */
338static PWINCHILD mkWinChildDequeFromLifo(PWINCHILD volatile *ppTail, PWINCHILD pChild)
339{
340 if (pChild->pNext)
341 {
342 PWINCHILD pPrev;
343 do
344 {
345 pPrev = pChild;
346 pChild = pChild->pNext;
347 } while (pChild->pNext);
348 pPrev->pNext = NULL;
349 }
350 else
351 {
352 PWINCHILD const pWantedChild = pChild;
353 pChild = _InterlockedCompareExchangePointer(ppTail, NULL, pWantedChild);
354 if (pChild != pWantedChild)
355 {
356 PWINCHILD pPrev;
357 do
358 {
359 pPrev = pChild;
360 pChild = pChild->pNext;
361 } while (pChild->pNext);
362 pPrev->pNext = NULL;
363 assert(pChild == pWantedChild);
364 }
365 }
366 return pChild;
367}
368
369/**
370 * Duplicates the given UTF-16 string.
371 *
372 * @returns 0
373 * @param pwszSrc The UTF-16 string to duplicate.
374 * @param cwcSrc Length, may include the terminator.
375 * @param ppwszDst Where to return the duplicate.
376 */
377static int mkWinChildDuplicateUtf16String(const WCHAR *pwszSrc, size_t cwcSrc, WCHAR **ppwszDst)
378{
379 size_t cb = sizeof(WCHAR) * cwcSrc;
380 if (cwcSrc > 0 && pwszSrc[cwcSrc - 1] == L'\0')
381 *ppwszDst = (WCHAR *)memcpy(xmalloc(cb), pwszSrc, cb);
382 else
383 {
384 WCHAR *pwszDst = (WCHAR *)xmalloc(cb + sizeof(WCHAR));
385 memcpy(pwszDst, pwszSrc, cb);
386 pwszDst[cwcSrc] = L'\0';
387 *ppwszDst = pwszDst;
388 }
389 return 0;
390}
391
392/**
393 * Commmon worker for waiting on a child process and retrieving the exit code.
394 *
395 * @param pWorker The worker.
396 * @param pChild The child.
397 * @param hProcess The process handle.
398 */
399static void mkWinChildcareWorkerWaitForProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild, HANDLE hProcess)
400{
401 for (;;)
402 {
403 DWORD dwExitCode = -42;
404 DWORD dwStatus = WaitForSingleObject(hProcess, INFINITE);
405 assert(dwStatus != WAIT_FAILED);
406 if (dwStatus == WAIT_OBJECT_0)
407 {
408 DWORD dwExitCode = -42;
409 if (GetExitCodeProcess(hProcess, &dwExitCode))
410 {
411 pChild->iExitCode = (int)dwExitCode;
412 return;
413 }
414 }
415 else if ( dwStatus == WAIT_IO_COMPLETION
416 || dwStatus == WAIT_TIMEOUT /* whatever */)
417 continue; /* however unlikely, these aren't fatal. */
418
419 /* Something failed. */
420 pChild->iExitCode = GetLastError();
421 if (pChild->iExitCode == 0)
422 pChild->iExitCode = -4242;
423 return;
424 }
425}
426
427#define MKWCCWCMD_F_CYGWIN_SHELL 1
428#define MKWCCWCMD_F_MKS_SHELL 2
429#define MKWCCWCMD_F_HAVE_SH 4
430#define MKWCCWCMD_F_HAVE_KASH_C 8 /**< kmk_ash -c "..." */
431
432static int mkWinChildcareWorkerConvertCommandline(char **papszArgs, unsigned fFlags, WCHAR **ppwszCommandLine)
433{
434 struct ARGINFO
435 {
436 size_t cchSrc;
437 size_t cwcDst; /**< converted size w/o terminator. */
438 size_t cwcDstExtra : 24; /**< Only set with fSlowly. */
439 size_t fSlowly : 1;
440 size_t fQuoteIt : 1;
441 size_t fEndSlashes : 1; /**< if escapes needed for trailing backslashes. */
442 size_t fExtraSpace : 1; /**< if kash -c "" needs an extra space before the quote. */
443 } *paArgInfo;
444 size_t cArgs;
445 size_t i;
446 size_t cwcNeeded;
447 WCHAR *pwszDst;
448 WCHAR *pwszCmdLine;
449
450 /*
451 * Count them first so we can allocate an info array of the stack.
452 */
453 cArgs = 0;
454 while (papszArgs[cArgs] != NULL)
455 cArgs++;
456 paArgInfo = (struct ARGINFO *)alloca(sizeof(paArgInfo[0]) * cArgs);
457
458 /*
459 * Preprocess them and calculate the exact command line length.
460 */
461 cwcNeeded = 1;
462 for (i = 0; i < cArgs; i++)
463 {
464 char *pszSrc = papszArgs[i];
465 size_t cchSrc = strlen(pszSrc);
466 paArgInfo[i].cchSrc = cchSrc;
467 if (cchSrc == 0)
468 {
469 /* empty needs quoting. */
470 paArgInfo[i].cwcDst = 2;
471 paArgInfo[i].cwcDstExtra = 0;
472 paArgInfo[i].fSlowly = 0;
473 paArgInfo[i].fQuoteIt = 1;
474 paArgInfo[i].fExtraSpace = 0;
475 paArgInfo[i].fEndSlashes = 0;
476 }
477 else
478 {
479 const char *pszSpace = memchr(pszSrc, ' ', cchSrc);
480 const char *pszTab = memchr(pszSrc, '\t', cchSrc);
481 const char *pszDQuote = memchr(pszSrc, '"', cchSrc);
482 const char *pszEscape = memchr(pszSrc, '\\', cchSrc);
483 int cwcDst = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cchSrc + 1, NULL, 0);
484 if (cwcDst >= 0)
485 --cwcDst;
486 else
487 {
488 DWORD dwErr = GetLastError();
489 fprintf(stderr, _("MultiByteToWideChar failed to convert argv[%u] (%s): %u\n"), i, pszSrc, dwErr);
490 return dwErr;
491 }
492#if 0
493 if (!pszSpace && !pszTab && !pszDQuote && !pszEscape)
494 {
495 /* no special handling needed. */
496 paArgInfo[i].cwcDst = cwcDst;
497 paArgInfo[i].cwcDstExtra = 0;
498 paArgInfo[i].fSlowly = 0;
499 paArgInfo[i].fQuoteIt = 0;
500 paArgInfo[i].fExtraSpace = 0;
501 paArgInfo[i].fEndSlashes = 0;
502 }
503 else if (!pszDQuote && !pszEscape)
504 {
505 /* Just double quote it. */
506 paArgInfo[i].cwcDst = cwcDst + 2;
507 paArgInfo[i].cwcDstExtra = 0;
508 paArgInfo[i].fSlowly = 0;
509 paArgInfo[i].fQuoteIt = 1;
510 paArgInfo[i].fExtraSpace = 0;
511 paArgInfo[i].fEndSlashes = 0;
512 }
513 else
514#endif
515 {
516 /* Complicated, need to scan the string to figure out what to do. */
517 size_t cwcDstExtra;
518 int cBackslashes;
519 char ch;
520
521 paArgInfo[i].fQuoteIt = 0;
522 paArgInfo[i].fSlowly = 1;
523 paArgInfo[i].fExtraSpace = 0;
524 paArgInfo[i].fEndSlashes = 0;
525
526 cwcDstExtra = 0;
527 cBackslashes = 0;
528 while ((ch = *pszSrc++) != '\0')
529 {
530 switch (ch)
531 {
532 default:
533 cBackslashes = 0;
534 break;
535
536 case '\\':
537 cBackslashes++;
538 break;
539
540 case '"':
541 if (fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_MKS_SHELL))
542 cwcDstExtra += 1;
543 else
544 cwcDstExtra += 1 + cBackslashes;
545 break;
546
547 case ' ':
548 case '\t':
549 if (!paArgInfo[i].fQuoteIt)
550 {
551 paArgInfo[i].fQuoteIt = 1;
552 cwcDstExtra += 2;
553 }
554 cBackslashes = 0;
555 break;
556 }
557 }
558
559 if ( cBackslashes > 0
560 && paArgInfo[i].fQuoteIt
561 && !(fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_MKS_SHELL)))
562 {
563 cwcDstExtra += cBackslashes;
564 paArgInfo[i].fEndSlashes = 1;
565 }
566
567 paArgInfo[i].cwcDst = cwcDst + cwcDstExtra;
568 paArgInfo[i].cwcDstExtra = cwcDstExtra;
569 }
570 }
571
572 if ( (fFlags & MKWCCWCMD_F_HAVE_KASH_C)
573 && paArgInfo[i].fQuoteIt)
574 {
575 paArgInfo[i].fExtraSpace = 1;
576 paArgInfo[i].cwcDst++;
577 paArgInfo[i].cwcDstExtra++;
578 }
579
580 cwcNeeded += (i != 0) + paArgInfo[i].cwcDst;
581 }
582
583 /*
584 * Allocate the result buffer and do the actual conversion.
585 */
586 pwszDst = pwszCmdLine = (WCHAR *)xmalloc(sizeof(WCHAR) * cwcNeeded);
587 for (i = 0; i < cArgs; i++)
588 {
589 char *pszSrc = papszArgs[i];
590 size_t cwcDst = paArgInfo[i].cwcDst;
591
592 if (i != 0)
593 *pwszDst++ = L' ';
594
595 if (paArgInfo[i].fQuoteIt)
596 {
597 *pwszDst++ = L'"';
598 cwcDst -= 2;
599 }
600
601 if (!paArgInfo[i].fSlowly)
602 {
603 int cwcDst2 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, paArgInfo[i].cchSrc, pwszDst, cwcDst + 1);
604 assert(cwcDst2 >= 0);
605 pwszDst += cwcDst;
606 }
607 else
608 {
609 /* Do the conversion into the end of the output buffer, then move
610 it up to where it should be char by char. */
611 size_t cBackslashes;
612 size_t cwcLeft = paArgInfo[i].cwcDst - paArgInfo[i].cwcDstExtra;
613 WCHAR volatile *pwchSlowSrc = pwszDst + paArgInfo[i].cwcDstExtra;
614 WCHAR volatile *pwchSlowDst = pwszDst;
615 int cwcDst2 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, paArgInfo[i].cchSrc,
616 (WCHAR *)pwchSlowSrc, cwcLeft + 1);
617 assert(cwcDst2 >= 0);
618
619 cBackslashes = 0;
620 while (cwcLeft-- > 0)
621 {
622 WCHAR wcSrc = *pwchSlowSrc++;
623 if (wcSrc != L'\\' && wcSrc != L'"')
624 cBackslashes = 0;
625 else if (wcSrc == L'\\')
626 cBackslashes++;
627 else if ( (fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_HAVE_SH))
628 == (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_HAVE_SH))
629 *pwchSlowDst++ = L'"'; /* cygwin: '"' instead of '\\', no escaped slashes. */
630 else
631 {
632 if (!(fFlags & (MKWCCWCMD_F_CYGWIN_SHELL | MKWCCWCMD_F_MKS_SHELL)))
633 cBackslashes = 1;
634 while (cBackslashes-- > 0)
635 *pwchSlowDst++ = L'\\';
636 }
637 *pwchSlowDst++ = wcSrc;
638 }
639
640 if (paArgInfo[i].fEndSlashes)
641 while (cBackslashes-- > 0)
642 *pwchSlowDst++ = L'\\';
643
644 pwszDst += cwcDst;
645 assert(pwszDst == (WCHAR *)pwchSlowDst);
646 }
647
648 if (paArgInfo[i].fExtraSpace)
649 *pwszDst++ = L' ';
650 if (paArgInfo[i].fQuoteIt)
651 *pwszDst++ = L'"';
652 }
653 *pwszDst = L'\0';
654 *ppwszCommandLine = pwszCmdLine;
655 return 0;
656}
657
658static int mkWinChildcareWorkerConvertCommandlineWithShell(const WCHAR *pwszShell, char **papszArgs, WCHAR **ppwszCommandLine)
659{
660 return -2;
661}
662
663/**
664 * Searches the environment block for the PATH variable.
665 *
666 * @returns Pointer to the path in the block or ".".
667 * @param pwszzEnv The UTF-16 environment block to search.
668 */
669static const WCHAR *mkWinChildcareWorkerFindPathValue(const WCHAR *pwszzEnv)
670{
671 while (*pwszzEnv)
672 {
673 size_t cwcVar = wcslen(pwszzEnv);
674 if (!IS_PATH_ENV_VAR(cwcVar, pwszzEnv))
675 pwszzEnv += cwcVar + 1;
676 else if (cwcVar > 5)
677 return &pwszzEnv[5];
678 else
679 break;
680 }
681 return L".";
682}
683
684/**
685 * Checks if we need to had this executable file to the shell.
686 *
687 * @returns TRUE if it's shell fooder, FALSE if we think windows can handle it.
688 * @param hFile Handle to the file in question
689 */
690static BOOL mkWinChildcareWorkerCheckIfNeedShell(HANDLE hFile)
691{
692 /*
693 * Read the first 512 bytes and check for an executable image header.
694 */
695 union
696 {
697 DWORD dwSignature;
698 WORD wSignature;
699 BYTE ab[128];
700 } uBuf;
701 DWORD cbRead;
702 uBuf.dwSignature = 0;
703 if ( ReadFile(hFile, &uBuf, sizeof(uBuf), &cbRead, NULL /*pOverlapped*/)
704 && cbRead == sizeof(uBuf))
705 {
706 if (uBuf.wSignature == IMAGE_DOS_SIGNATURE)
707 return FALSE;
708 if (uBuf.dwSignature == IMAGE_NT_SIGNATURE)
709 return FALSE;
710 if ( uBuf.wSignature == IMAGE_OS2_SIGNATURE /* NE */
711 || uBuf.wSignature == 0x5d4c /* LX */
712 || uBuf.wSignature == IMAGE_OS2_SIGNATURE_LE /* LE */)
713 return FALSE;
714 }
715 return TRUE;
716}
717
718
719/**
720 * Tries to locate the image file, searching the path and maybe falling back on
721 * the shell in case it knows more (think cygwin with its own view of the file
722 * system).
723 *
724 * This will also check for shell script, falling back on the shell too to
725 * handle those.
726 *
727 * @returns 0 on success, windows error code on failure.
728 * @param pszArg0 The first argument.
729 * @param pwszPath The path if mkWinChildcareWorkerConvertEnvironment
730 * found it.
731 * @param pwszzEnv The environment block, in case we need to look for
732 * the path.
733 * @param pszShell The shell.
734 * @param ppwszImagePath Where to return the pointer to the image path. This
735 * could be the shell.
736 * @param pfNeedShell Where to return shell vs direct execution indicator.
737 */
738static int mkWinChildcareWorkerFindImage(char const *pszArg0, WCHAR const *pwszPath, WCHAR const *pwszzEnv,
739 const char *pszShell, WCHAR **ppwszImagePath, BOOL *pfNeedShell)
740{
741 /** @todo Slap a cache on this code. We usually end up executing the same
742 * stuff over and over again (e.g. compilers, linkers, etc).
743 * Hitting the file system is slow on windows. */
744
745 /*
746 * Convert pszArg0 to unicode so we can work directly on that.
747 */
748 WCHAR wszArg0[MKWINCHILD_MAX_PATH + 4]; /* +4 for painless '.exe' appending */
749 DWORD dwErr;
750 size_t cbArg0 = strlen(pszArg0) + 1;
751 int const cwcArg0 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszArg0, cbArg0, wszArg0, MKWINCHILD_MAX_PATH);
752 if (cwcArg0 > 0)
753 {
754 HANDLE hFile = INVALID_HANDLE_VALUE;
755 WCHAR wszPathBuf[MKWINCHILD_MAX_PATH + 4]; /* +4 for painless '.exe' appending */
756 int cwc;
757
758 /*
759 * If there isn't an .exe suffix, we may have to add one.
760 * Also we ASSUME that .exe suffixes means no hash bang detection needed.
761 */
762 int const fHasExeSuffix = cwcArg0 > CSTRLEN(".exe")
763 && wszArg0[cwcArg0 - 4] == '.'
764 && (wszArg0[cwcArg0 - 3] == L'e' || wszArg0[cwcArg0 - 3] == L'E')
765 && (wszArg0[cwcArg0 - 2] == L'x' || wszArg0[cwcArg0 - 2] == L'X')
766 && (wszArg0[cwcArg0 - 1] == L'e' || wszArg0[cwcArg0 - 1] == L'E');
767
768 /*
769 * If there isn't any path specified, we need to search the PATH env.var.
770 */
771 int const fHasPath = wszArg0[1] == L':'
772 || wszArg0[0] == L'\\'
773 || wszArg0[0] == L'/'
774 || wmemchr(wszArg0, L'/', cwcArg0)
775 || wmemchr(wszArg0, L'\\', cwcArg0);
776
777 /* Before we do anything, flip UNIX slashes to DOS ones. */
778 WCHAR *pwc = wszArg0;
779 while ((pwc = wcschr(pwc, L'/')) != NULL)
780 *pwc++ = L'\\';
781
782 /* Don't need to set this all the time... */
783 *pfNeedShell = FALSE;
784
785 /*
786 * If any kind of path is specified in arg0, we will not search the
787 * PATH env.var and can limit ourselves to maybe slapping a .exe on to it.
788 */
789 if (fHasPath)
790 {
791 /*
792 * If relative to a CWD, turn it into an absolute one.
793 */
794 unsigned cwcPath = cwcArg0;
795 WCHAR *pwszPath = wszArg0;
796 if ( *pwszPath != L'\\'
797 && (pwszPath[1] != ':' || pwszPath[2] != L'\\') )
798 {
799 DWORD cwcAbsPath = GetFullPathNameW(wszArg0, MKWINCHILD_MAX_PATH, wszPathBuf, NULL);
800 if (cwcAbsPath > 0)
801 {
802 cwcAbsPath = cwcPath + 1; /* include terminator, like MultiByteToWideChar does. */
803 pwszPath = wszPathBuf;
804 }
805 }
806
807 /*
808 * If there is an exectuable path, we only need to check that it exists.
809 */
810 if (fHasExeSuffix)
811 {
812 DWORD dwAttribs = GetFileAttributesW(pwszPath);
813 if (dwAttribs != INVALID_FILE_ATTRIBUTES)
814 return mkWinChildDuplicateUtf16String(pwszPath, cwcPath + 4, ppwszImagePath);
815 }
816 else
817 {
818 /*
819 * No suffix, so try open it first to see if it's shell fooder.
820 * Otherwise, append a .exe suffix and check if it exists.
821 */
822 hFile = CreateFileW(pwszPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
823 NULL /*pSecAttr*/, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
824 if (hFile != INVALID_HANDLE_VALUE)
825 {
826 *pfNeedShell = mkWinChildcareWorkerCheckIfNeedShell(hFile);
827 CloseHandle(hFile);
828 if (!*pfNeedShell)
829 return mkWinChildDuplicateUtf16String(pwszPath, cwcPath, ppwszImagePath);
830 }
831 /* Append the .exe suffix and check if it exists. */
832 else
833 {
834 DWORD dwAttribs;
835 pwszPath[cwcPath - 1] = L'.';
836 pwszPath[cwcPath ] = L'e';
837 pwszPath[cwcPath + 1] = L'x';
838 pwszPath[cwcPath + 2] = L'e';
839 pwszPath[cwcPath + 3] = L'\0';
840 dwAttribs = GetFileAttributesW(pwszPath);
841 if (dwAttribs != INVALID_FILE_ATTRIBUTES)
842 return mkWinChildDuplicateUtf16String(pwszPath, cwcPath + 4, ppwszImagePath);
843 }
844 }
845 }
846 /*
847 * No path, need to search the PATH env.var. for the executable, maybe
848 * adding an .exe suffix while do so if that is missing.
849 */
850 else
851 {
852 BOOL fSearchedCwd = FALSE;
853 if (!pwszPath)
854 pwszPath = mkWinChildcareWorkerFindPathValue(pwszzEnv);
855 for (;;)
856 {
857 size_t cwcCombined;
858
859 /*
860 * Find the end of the current PATH component.
861 */
862 size_t cwcSkip;
863 WCHAR wcEnd;
864 size_t cwcComponent = 0;
865 WCHAR wc;
866 while ((wc = pwszPath[cwcComponent]) != L'\0')
867 {
868 if (wc != ';' && wc != ':')
869 { /* likely */ }
870 else if (wc == ';')
871 break;
872 else if (cwcComponent != pwszPath[cwcComponent] != L'"' ? 1 : 2)
873 break;
874 cwcComponent++;
875 }
876 wcEnd = wc;
877
878 /* Trim leading spaces and double quotes. */
879 while ( cwcComponent > 0
880 && ((wc = *pwszPath) == L'"' || wc == L' ' || wc == L'\t'))
881 {
882 pwszPath++;
883 cwcComponent--;
884 }
885 cwcSkip = cwcComponent;
886
887 /* Trim trailing spaces & double quotes. */
888 while ( cwcComponent > 0
889 && ((wc = pwszPath[cwcComponent - 1]) == L'"' || wc == L' ' || wc == L'\t'))
890 cwcComponent--;
891
892 /*
893 * Skip empty components. Join the component and the filename, making sure to
894 * resolve any CWD relative stuff first.
895 */
896 cwcCombined = cwcComponent + 1 + cwcArg0;
897 if (cwcComponent > 0 && cwcCombined <= MKWINCHILD_MAX_PATH)
898 {
899 DWORD dwAttribs;
900
901 /* Copy the component into wszPathBuf, maybe abspath'ing it. */
902 DWORD cwcAbsPath = 0;
903 if ( *pwszPath != L'\\'
904 && (pwszPath[1] != ':' || pwszPath[2] != L'\\') )
905 {
906 WCHAR const wcSaved = pwszPath[cwcCombined];
907 *(WCHAR *)&pwszPath[cwcCombined] = '\0'; /* Pointing to our converted buffer, so this is okay for now. */
908 cwcAbsPath = GetFullPathNameW(pwszPath, MKWINCHILD_MAX_PATH, wszPathBuf, NULL);
909 *(WCHAR *)&pwszPath[cwcCombined] = wcSaved;
910 if (cwcAbsPath > 0 && cwcAbsPath + 1 + cwcArg0 <= MKWINCHILD_MAX_PATH)
911 cwcCombined = cwcAbsPath + 1 + cwcArg0;
912 else
913 cwcAbsPath = 0;
914 }
915 if (cwcAbsPath == 0)
916 {
917 memcpy(wszPathBuf, pwszPath, cwcComponent);
918 cwcAbsPath = cwcComponent;
919 }
920
921 /* Append the filename. */
922 if ((wc = wszPathBuf[cwcAbsPath - 1]) == L'\\' || wc == L'/' || wc == L':')
923 {
924 memcpy(&wszPathBuf[cwcAbsPath], wszArg0, cwcArg0 * sizeof(WCHAR));
925 cwcCombined--;
926 }
927 else
928 {
929 wszPathBuf[cwcAbsPath] = L'\\';
930 memcpy(&wszPathBuf[cwcAbsPath + 1], wszArg0, cwcArg0 * sizeof(WCHAR));
931 }
932 assert(wszPathBuf[cwcCombined - 1] == L'\0');
933
934 /* DOS slash conversion */
935 pwc = wszPathBuf;
936 while ((pwc = wcschr(pwc, L'/')) != NULL)
937 *pwc++ = L'\\';
938
939 /*
940 * Search with exe suffix first.
941 */
942 if (!fHasExeSuffix)
943 {
944 wszPathBuf[cwcCombined - 1] = L'.';
945 wszPathBuf[cwcCombined ] = L'e';
946 wszPathBuf[cwcCombined + 1] = L'x';
947 wszPathBuf[cwcCombined + 2] = L'e';
948 wszPathBuf[cwcCombined + 3] = L'\0';
949 }
950 dwAttribs = GetFileAttributesW(wszPathBuf);
951 if ( dwAttribs != INVALID_FILE_ATTRIBUTES
952 && !(dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
953 return mkWinChildDuplicateUtf16String(wszPathBuf, cwcCombined + (fHasExeSuffix ? 0 : 4), ppwszImagePath);
954 if (!fHasExeSuffix)
955 {
956 wszPathBuf[cwcCombined - 1] = L'\0';
957
958 /*
959 * Check if the file exists w/o the added '.exe' suffix. If it does,
960 * we need to check if we can pass it to CreateProcess or need the shell.
961 */
962 hFile = CreateFileW(wszPathBuf, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
963 NULL /*pSecAttr*/, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
964 if (hFile != INVALID_HANDLE_VALUE)
965 {
966 *pfNeedShell = mkWinChildcareWorkerCheckIfNeedShell(hFile);
967 CloseHandle(hFile);
968 if (!*pfNeedShell)
969 return mkWinChildDuplicateUtf16String(wszPathBuf, cwcCombined, ppwszImagePath);
970 break;
971 }
972 }
973 }
974
975 /*
976 * Advance to the next component.
977 */
978 if (wcEnd != '\0')
979 pwszPath += cwcSkip + 1;
980 else if (fSearchedCwd)
981 break;
982 else
983 {
984 fSearchedCwd = TRUE;
985 pwszPath = L".";
986 }
987 }
988 }
989
990 /*
991 * We need the shell. It will take care of finding/reporting missing
992 * image files and such.
993 */
994 *pfNeedShell = TRUE;
995 cwc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszShell, strlen(pszShell), wszPathBuf, MKWINCHILD_MAX_PATH);
996 if (cwc > 0)
997 return mkWinChildDuplicateUtf16String(wszPathBuf, cwc, ppwszImagePath);
998 dwErr = GetLastError();
999 }
1000 else
1001 {
1002 dwErr = GetLastError();
1003 fprintf(stderr, _("MultiByteToWideChar failed to convert argv[0] (%s): %u\n"), pszArg0, dwErr);
1004 }
1005 return dwErr == ERROR_INSUFFICIENT_BUFFER ? ERROR_FILENAME_EXCED_RANGE : dwErr;
1006}
1007
1008/**
1009 * Creates the environment block.
1010 *
1011 * @returns 0 on success, windows error code on failure.
1012 * @param papszEnv The environment vector to convert.
1013 * @param cbEnvStrings The size of the environment strings, iff they are
1014 * sequential in a block. Otherwise, zero.
1015 * @param ppwszEnv Where to return the pointer to the environment
1016 * block.
1017 * @param ppwszPath Where to return the pointer to the path value within
1018 * the environment block. This will not be set if
1019 * cbEnvStrings is non-zero, more efficient to let
1020 * mkWinChildcareWorkerFindImage() search when needed.
1021 */
1022static int mkWinChildcareWorkerConvertEnvironment(char **papszEnv, size_t cbEnvStrings,
1023 WCHAR **ppwszEnv, WCHAR const **ppwszPath)
1024{
1025 DWORD dwErr;
1026 int cwcRc;
1027 int cwcDst;
1028 WCHAR *pwszzDst;
1029
1030 *ppwszPath = NULL;
1031
1032 /*
1033 * We've got a little optimization here with help from mkWinChildCopyStringArray.
1034 */
1035 if (cbEnvStrings)
1036 {
1037 cwcDst = cbEnvStrings + 32;
1038 pwszzDst = (WCHAR *)xmalloc(cwcDst);
1039 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, papszEnv[0], cbEnvStrings, pwszzDst, cwcDst);
1040 if (cwcRc != 0)
1041 {
1042 *ppwszEnv = pwszzDst;
1043 return 0;
1044 }
1045
1046 /* Resize the allocation and try again. */
1047 dwErr = GetLastError();
1048 if (dwErr == ERROR_INSUFFICIENT_BUFFER)
1049 {
1050 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, papszEnv[0], cbEnvStrings, NULL, 0);
1051 if (cwcRc > 0)
1052 cwcDst = cwcRc + 32;
1053 else
1054 cwcDst *= 2;
1055 pwszzDst = (WCHAR *)xrealloc(pwszzDst, cwcDst);
1056 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, papszEnv[0], cbEnvStrings, pwszzDst, cwcDst);
1057 if (cwcRc != 0)
1058 {
1059 *ppwszEnv = pwszzDst;
1060 return 0;
1061 }
1062 dwErr = GetLastError();
1063 }
1064 fprintf(stderr, _("MultiByteToWideChar failed to convert environment block: %u\n"), dwErr);
1065 }
1066 /*
1067 * Need to convert it string by string.
1068 */
1069 else
1070 {
1071 size_t offPathValue = ~(size_t)0;
1072 size_t offDst;
1073
1074 /*
1075 * Estimate the size first.
1076 */
1077 size_t cEnvVars;
1078 size_t cwcDst = 32;
1079 size_t iVar = 0;
1080 const char *pszSrc;
1081 while ((pszSrc = papszEnv[iVar]) != NULL)
1082 {
1083 cwcDst += strlen(pszSrc) + 1;
1084 iVar++;
1085 }
1086 cEnvVars = iVar;
1087
1088 /* Allocate estimated WCHARs and convert the variables one by one, reallocating
1089 the block as needed. */
1090 pwszzDst = (WCHAR *)xmalloc(cwcDst * sizeof(WCHAR));
1091 cwcDst--; /* save one wchar for the terminating empty string. */
1092 offDst = 0;
1093 for (iVar = 0; iVar < cEnvVars; iVar++)
1094 {
1095 size_t cwcLeft = cwcDst - offDst;
1096 size_t const cbSrc = strlen(pszSrc = papszEnv[iVar]) + 1;
1097 assert(cwcDst >= offDst);
1098
1099
1100 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cbSrc, &pwszzDst[offDst], cwcLeft);
1101 if (cwcRc > 0)
1102 { /* likely */ }
1103 else
1104 {
1105 dwErr = GetLastError();
1106 if (dwErr == ERROR_INSUFFICIENT_BUFFER)
1107 {
1108 /* Need more space. So, calc exacly how much and resize the block accordingly. */
1109 size_t cbSrc2 = cbSrc;
1110 size_t iVar2 = iVar;
1111 cwcLeft = 1;
1112 for (;;)
1113 {
1114 size_t cwcRc2 = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cbSrc, NULL, 0);
1115 if (cwcRc2 > 0)
1116 cwcLeft += cwcRc2;
1117 else
1118 cwcLeft += cbSrc * 4;
1119
1120 /* advance */
1121 iVar2++;
1122 if (iVar2 >= cEnvVars)
1123 break;
1124 pszSrc = papszEnv[iVar2];
1125 cbSrc2 = strlen(pszSrc) + 1;
1126 }
1127 pszSrc = papszEnv[iVar];
1128
1129 /* Grow the allocation and repeat the conversion. */
1130 if (offDst + cwcLeft > cwcDst + 1)
1131 {
1132 cwcDst = offDst + cwcLeft;
1133 pwszzDst = (WCHAR *)xrealloc(pwszzDst, cwcDst * sizeof(WCHAR));
1134 cwcDst--; /* save one wchar for the terminating empty string. */
1135 cwcRc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, pszSrc, cbSrc, &pwszzDst[offDst], cwcLeft - 1);
1136 if (cwcRc <= 0)
1137 dwErr = GetLastError();
1138 }
1139 }
1140 if (cwcRc <= 0)
1141 {
1142 fprintf(stderr, _("MultiByteToWideChar failed to convert environment string #%u (%s): %u\n"),
1143 iVar, pszSrc, dwErr);
1144 free(pwszzDst);
1145 return dwErr;
1146 }
1147 }
1148
1149 /* Look for the PATH. */
1150 if ( offPathValue == ~(size_t)0
1151 && IS_PATH_ENV_VAR(cwcRc, &pwszzDst[offDst]) )
1152 offPathValue = offDst + 4 + 1;
1153
1154 /* Advance. */
1155 offDst += cwcRc;
1156 }
1157 pwszzDst[offDst++] = '\0';
1158
1159 if (offPathValue != ~(size_t)0)
1160 *ppwszPath = &pwszzDst[offPathValue];
1161 *ppwszEnv = pwszzDst;
1162 return 0;
1163 }
1164 free(pwszzDst);
1165 return dwErr;
1166}
1167
1168/**
1169 * Childcare worker: handle regular process.
1170 *
1171 * @param pWorker The worker.
1172 * @param pChild The kSubmit child.
1173 */
1174static void mkWinChildcareWorkerThreadHandleProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
1175{
1176 PROCESS_INFORMATION ProcInfo;
1177 STARTUPINFOW StartupInfo;
1178 WCHAR const *pwszPath = NULL;
1179 WCHAR *pwszzEnvironment = NULL;
1180 WCHAR *pwszCommandLine = NULL;
1181 WCHAR *pwszImageName = NULL;
1182 BOOL fNeedShell = FALSE;
1183 DWORD fFlags = CREATE_UNICODE_ENVIRONMENT;
1184 BOOL fRet;
1185 int rc;
1186#ifdef KMK
1187 extern int process_priority;
1188#endif
1189
1190 /*
1191 * First we convert the environment so we get the PATH we need to
1192 * search for the executable.
1193 */
1194 rc = mkWinChildcareWorkerConvertEnvironment(pChild->u.Process.papszEnv ? pChild->u.Process.papszEnv : environ,
1195 pChild->u.Process.cbEnvStrings,
1196 &pwszzEnvironment, &pwszPath);
1197 /*
1198 * Find the executable and maybe checking if it's a shell script, then
1199 * convert it to a command line.
1200 */
1201 if (rc == 0)
1202 rc = mkWinChildcareWorkerFindImage(pChild->u.Process.papszArgs[0], pwszzEnvironment, pwszPath,
1203 pChild->u.Process.pszShell, &pwszImageName, &fNeedShell);
1204 if (rc == 0)
1205 {
1206 if (!fNeedShell)
1207 rc = mkWinChildcareWorkerConvertCommandline(pChild->u.Process.papszArgs, 0 /*fFlags*/, &pwszCommandLine);
1208 else
1209 rc = mkWinChildcareWorkerConvertCommandlineWithShell(pwszImageName, pChild->u.Process.papszArgs, &pwszCommandLine);
1210 }
1211 if (rc == 0)
1212 {
1213 /*
1214 * Populate startup info.
1215 */
1216 memset(&StartupInfo, 0, sizeof(StartupInfo));
1217 StartupInfo.cb = sizeof(StartupInfo);
1218 GetStartupInfoW(&StartupInfo);
1219
1220 /*
1221 * Flags.
1222 */
1223#ifdef KMK
1224 switch (process_priority)
1225 {
1226 case 1: fFlags |= CREATE_SUSPENDED | IDLE_PRIORITY_CLASS; break;
1227 case 2: fFlags |= CREATE_SUSPENDED | BELOW_NORMAL_PRIORITY_CLASS; break;
1228 case 3: fFlags |= CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS; break;
1229 case 4: fFlags |= CREATE_SUSPENDED | HIGH_PRIORITY_CLASS; break;
1230 case 5: fFlags |= CREATE_SUSPENDED | REALTIME_PRIORITY_CLASS; break;
1231 }
1232#endif
1233 if (g_cProcessorGroups > 1)
1234 fFlags |= CREATE_SUSPENDED;
1235
1236 /*
1237 * Try create the process.
1238 */
1239 DB(DB_JOBS, ("CreateProcessW(%ls, %ls,,, TRUE, %#x...)\n", pwszImageName, pwszCommandLine, fFlags));
1240 memset(&ProcInfo, 0, sizeof(ProcInfo));
1241 fRet = CreateProcessW(pwszImageName, pwszCommandLine, NULL /*pProcSecAttr*/, NULL /*pThreadSecAttr*/,
1242 TRUE /*fInheritHandles*/, fFlags, pwszzEnvironment, NULL /*pwsz*/, &StartupInfo, &ProcInfo);
1243 if (fRet)
1244 {
1245 /*
1246 * Make thread priority and affinity changes if necessary.
1247 */
1248 if (fFlags & CREATE_SUSPENDED)
1249 {
1250 BOOL fRet;
1251 if (g_cProcessorGroups > 1)
1252 {
1253 GROUP_AFFINITY Affinity = { ~(ULONG_PTR)0, pWorker->iProcessorGroup, { 0, 0, 0 } };
1254 fRet = g_pfnSetThreadGroupAffinity(ProcInfo.hThread, &Affinity, NULL);
1255 assert(fRet);
1256 }
1257#ifdef KMK
1258 switch (process_priority)
1259 {
1260 case 1: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_IDLE); break;
1261 case 2: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_BELOW_NORMAL); break;
1262 case 3: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_NORMAL); break;
1263 case 4: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_HIGHEST); break;
1264 case 5: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_TIME_CRITICAL); break;
1265 default: fRet = TRUE;
1266 }
1267 assert(fRet);
1268#endif
1269 fRet = ResumeThread(ProcInfo.hThread);
1270 assert(fRet);
1271 (void)fRet;
1272 }
1273 CloseHandle(ProcInfo.hThread);
1274 pChild->u.Process.hProcess = ProcInfo.hProcess;
1275
1276 /*
1277 * Wait for the child to complete.
1278 */
1279 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, ProcInfo.hProcess);
1280 }
1281 }
1282 else
1283 pChild->iExitCode = rc;
1284 free(pwszCommandLine);
1285 free(pwszImageName);
1286 free(pwszzEnvironment);
1287}
1288
1289/**
1290 * Childcare worker: handle builtin command.
1291 *
1292 * @param pWorker The worker.
1293 * @param pChild The kSubmit child.
1294 */
1295static void mkWinChildcareWorkerThreadHandleBuiltin(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
1296{
1297 /** @todo later. */
1298__debugbreak();
1299}
1300
1301/**
1302 * Childcare worker: handle kSubmit job.
1303 *
1304 * @param pWorker The worker.
1305 * @param pChild The kSubmit child.
1306 */
1307static void mkWinChildcareWorkerThreadHandleSubmit(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
1308{
1309 void *pvSubmitWorker = pChild->u.Submit.pvSubmitWorker;
1310 for (;;)
1311 {
1312 int iExitCode = -42;
1313 int iSignal = -1;
1314 DWORD dwStatus = WaitForSingleObject(pChild->u.Submit.hEvent, INFINITE);
1315 assert(dwStatus != WAIT_FAILED);
1316
1317 if (kSubmitSubProcGetResult((intptr_t)pvSubmitWorker, &iExitCode, &iSignal) == 0)
1318 {
1319 pChild->iExitCode = iExitCode;
1320 pChild->iSignal = iSignal;
1321 /* Cleanup must be done on the main thread. */
1322 return;
1323 }
1324
1325 if (pChild->iSignal != 0)
1326 kSubmitSubProcKill((intptr_t)pvSubmitWorker, pChild->iSignal);
1327 }
1328}
1329
1330/**
1331 * Childcare worker: handle kmk_redirect process.
1332 *
1333 * @param pWorker The worker.
1334 * @param pChild The redirect child.
1335 */
1336static void mkWinChildcareWorkerThreadHandleRedirect(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild)
1337{
1338 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Redirect.hProcess);
1339}
1340
1341/**
1342 * Childcare worker thread.
1343 *
1344 * @returns 0
1345 * @param pvUser The worker instance.
1346 */
1347static unsigned int __stdcall mkWinChildcareWorkerThread(void *pvUser)
1348{
1349 PWINCHILDCAREWORKER pWorker = (PWINCHILDCAREWORKER)pvUser;
1350 assert(pWorker->uMagic == WINCHILDCAREWORKER_MAGIC);
1351
1352 /*
1353 * Adjust process group if necessary.
1354 */
1355 if (g_cProcessorGroups > 1)
1356 {
1357 GROUP_AFFINITY Affinity = { ~(ULONG_PTR)0, pWorker->iProcessorGroup, { 0, 0, 0 } };
1358 BOOL fRet = g_pfnSetThreadGroupAffinity(GetCurrentThread(), &Affinity, NULL);
1359 assert(fRet); (void)fRet;
1360 }
1361
1362 /*
1363 * Work loop.
1364 */
1365 while (!g_fShutdown)
1366 {
1367 /*
1368 * Try go idle.
1369 */
1370 PWINCHILD pChild = pWorker->pTailTodoChildren;
1371 if (!pChild)
1372 {
1373 _InterlockedExchange(&pWorker->fIdle, TRUE);
1374 pChild = pWorker->pTailTodoChildren;
1375 if (!pChild)
1376 {
1377 DWORD dwStatus;
1378
1379 _InterlockedIncrement((long *)&g_cIdleChildcareWorkers);
1380 dwStatus = WaitForSingleObject(pWorker->hEvtIdle, INFINITE);
1381 _InterlockedExchange(&pWorker->fIdle, FALSE);
1382 _InterlockedDecrement((long *)&g_cIdleChildcareWorkers);
1383
1384 assert(dwStatus != WAIT_FAILED);
1385 if (dwStatus == WAIT_FAILED)
1386 Sleep(20);
1387
1388 pChild = pWorker->pTailTodoChildren;
1389 }
1390 else
1391 _InterlockedExchange(&pWorker->fIdle, FALSE);
1392 }
1393 if (pChild)
1394 {
1395 /*
1396 * We got work to do. First job is to deque the job.
1397 */
1398 pChild = mkWinChildDequeFromLifo(&pWorker->pTailTodoChildren, pChild);
1399 assert(pChild);
1400 if (pChild)
1401 {
1402 PWINCHILD pTailExpect;
1403
1404 switch (pChild->enmType)
1405 {
1406 case WINCHILDTYPE_PROCESS:
1407 mkWinChildcareWorkerThreadHandleProcess(pWorker, pChild);
1408 break;
1409 case WINCHILDTYPE_BUILTIN:
1410 mkWinChildcareWorkerThreadHandleBuiltin(pWorker, pChild);
1411 break;
1412 case WINCHILDTYPE_SUBMIT:
1413 mkWinChildcareWorkerThreadHandleSubmit(pWorker, pChild);
1414 break;
1415 case WINCHILDTYPE_REDIRECT:
1416 mkWinChildcareWorkerThreadHandleRedirect(pWorker, pChild);
1417 break;
1418 }
1419
1420 /*
1421 * Move the child to the completed list.
1422 */
1423 pTailExpect = NULL;
1424 for (;;)
1425 {
1426 PWINCHILD pTailActual;
1427 pChild->pNext = pTailExpect;
1428 pTailActual = _InterlockedCompareExchangePointer(&g_pTailCompletedChildren, pChild, pTailExpect);
1429 if (pTailActual == pTailExpect)
1430 {
1431 _InterlockedDecrement(&g_cPendingChildren);
1432 if (pTailExpect)
1433 break;
1434 if (SetEvent(g_hEvtWaitChildren))
1435 break;
1436 fprintf(stderr, "SetEvent(g_hEvtWaitChildren=%p) failed: %u\n", g_hEvtWaitChildren, GetLastError());
1437 break;
1438 }
1439 }
1440 }
1441 }
1442 }
1443
1444 _endthreadex(0);
1445 return 0;
1446}
1447
1448/**
1449 * Creates another childcare worker.
1450 *
1451 * @returns The new worker, if we succeeded.
1452 */
1453static PWINCHILDCAREWORKER mkWinChildcareCreateWorker(void)
1454{
1455 PWINCHILDCAREWORKER pWorker = (PWINCHILDCAREWORKER)xcalloc(sizeof(*pWorker));
1456 pWorker->uMagic = WINCHILDCAREWORKER_MAGIC;
1457 pWorker->hEvtIdle = CreateEvent(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pszName*/);
1458 if (pWorker->hEvtIdle)
1459 {
1460 /* Before we start the thread, assign it to a processor group. */
1461 if (g_cProcessorGroups > 1)
1462 {
1463 unsigned int cMaxInGroup;
1464 unsigned int cInGroup;
1465 unsigned int iGroup = g_idxProcessorGroupAllocator % g_cProcessorGroups;
1466 pWorker->iProcessorGroup = iGroup;
1467
1468 /* Advance. We employ a very simple strategy that does 50% in
1469 each group for each group cycle. Odd processor counts are
1470 caught in odd group cycles. The init function selects the
1471 starting group based on make nesting level to avoid stressing
1472 out the first group. */
1473 cInGroup = ++g_idxProcessorInGroupAllocator;
1474 cMaxInGroup = g_pacProcessorsInGroup[iGroup];
1475 if ( !(cMaxInGroup & 1)
1476 || !((g_idxProcessorGroupAllocator / g_cProcessorGroups) & 1))
1477 cMaxInGroup /= 2;
1478 else
1479 cMaxInGroup = cMaxInGroup / 2 + 1;
1480 if (cInGroup >= cMaxInGroup)
1481 {
1482 g_idxProcessorInGroupAllocator = 0;
1483 g_idxProcessorGroupAllocator++;
1484 }
1485 }
1486
1487 /* Try start the thread. */
1488 pWorker->hThread = (HANDLE)_beginthreadex(NULL, 0 /*cbStack*/, mkWinChildcareWorkerThread, pWorker,
1489 0, &pWorker->tid);
1490 if (pWorker->hThread != NULL)
1491 {
1492 g_papChildCareworkers[g_cChildCareworkers++] = pWorker;
1493 return pWorker;
1494 }
1495 CloseHandle(pWorker->hEvtIdle);
1496 }
1497 pWorker->uMagic = ~WINCHILDCAREWORKER_MAGIC;
1498 free(pWorker);
1499 return NULL;
1500}
1501
1502/**
1503 * Helper for copying argument and environment vectors.
1504 *
1505 * @returns Single alloc block copy.
1506 * @param papszSrc The source vector.
1507 * @param pcbStrings Where to return the size of the strings & terminator.
1508 */
1509static char **mkWinChildCopyStringArray(char **papszSrc, size_t *pcbStrings)
1510{
1511 const char *psz;
1512 char **papszDstArray;
1513 char *pszDstStr;
1514 size_t i;
1515
1516 /* Calc sizes first. */
1517 size_t cbStrings = 1; /* (one extra for terminator string) */
1518 size_t cStrings = 0;
1519 while ((psz = papszSrc[cStrings]) != NULL)
1520 {
1521 cbStrings += strlen(psz) + 1;
1522 cStrings++;
1523 }
1524 *pcbStrings = cbStrings;
1525
1526 /* Allocate destination. */
1527 papszDstArray = (char **)xmalloc(cbStrings + (cStrings + 1) * sizeof(papszDstArray[0]));
1528 pszDstStr = (char *)&papszDstArray[cStrings + 1];
1529
1530 /* Copy it. */
1531 for (i = 0; i < cStrings; i++)
1532 {
1533 size_t cbString = strlen(papszSrc[i]) + 1;
1534 papszDstArray[i] = (char *)memcpy(pszDstStr, papszSrc[i], cbString);
1535 pszDstStr += cbString;
1536 }
1537 *pszDstStr = '\0';
1538 assert(&pszDstStr[1] - papszDstArray[0] == cbStrings);
1539 papszDstArray[i] = NULL;
1540 return papszDstArray;
1541}
1542
1543/**
1544 * Allocate and init a WINCHILD.
1545 *
1546 * @returns The new windows child structure.
1547 * @param enmType The child type.
1548 */
1549static PWINCHILD mkWinChildNew(WINCHILDTYPE enmType)
1550{
1551 PWINCHILD pChild = xcalloc(sizeof(*pChild));
1552 pChild->enmType = enmType;
1553 pChild->fCoreDumped = 0;
1554 pChild->iSignal = 0;
1555 pChild->iExitCode = 222222;
1556 pChild->uMagic = WINCHILD_MAGIC;
1557 pChild->pid = (intptr_t)pChild;
1558 return pChild;
1559}
1560
1561/**
1562 * Destructor for WINCHILD.
1563 *
1564 * @param pChild The child structure to destroy.
1565 */
1566static void mkWinChildDelete(PWINCHILD pChild)
1567{
1568 assert(pChild->uMagic == WINCHILD_MAGIC);
1569 pChild->uMagic = ~WINCHILD_MAGIC;
1570
1571 switch (pChild->enmType)
1572 {
1573 case WINCHILDTYPE_PROCESS:
1574 {
1575 if (pChild->u.Process.papszArgs)
1576 {
1577 free(pChild->u.Process.papszArgs);
1578 pChild->u.Process.papszArgs = NULL;
1579 }
1580 if (pChild->u.Process.cbEnvStrings && pChild->u.Process.papszEnv)
1581 {
1582 free(pChild->u.Process.papszEnv);
1583 pChild->u.Process.papszEnv = NULL;
1584 }
1585 if (pChild->u.Process.pszShell)
1586 {
1587 free(pChild->u.Process.pszShell);
1588 pChild->u.Process.pszShell = NULL;
1589 }
1590 if (pChild->u.Process.hProcess)
1591 {
1592 CloseHandle(pChild->u.Process.hProcess);
1593 pChild->u.Process.hProcess = NULL;
1594 }
1595 break;
1596 }
1597
1598 case WINCHILDTYPE_BUILTIN:
1599 assert(0);
1600 break;
1601
1602 case WINCHILDTYPE_SUBMIT:
1603 if (pChild->u.Submit.pvSubmitWorker)
1604 {
1605 kSubmitSubProcCleanup((intptr_t)pChild->u.Submit.pvSubmitWorker);
1606 pChild->u.Submit.pvSubmitWorker = NULL;
1607 }
1608 break;
1609
1610 case WINCHILDTYPE_REDIRECT:
1611 if (pChild->u.Redirect.hProcess)
1612 {
1613 CloseHandle(pChild->u.Redirect.hProcess);
1614 pChild->u.Redirect.hProcess = NULL;
1615 }
1616 break;
1617 }
1618
1619 free(pChild);
1620}
1621
1622/**
1623 * Queues the child with a worker, creating new workers if necessary.
1624 *
1625 * @returns 0 on success, windows error code on failure (child destroyed).
1626 * @param pChild The child.
1627 * @param pPid Where to return the PID (optional).
1628 */
1629static int mkWinChildPushToCareWorker(PWINCHILD pChild, pid_t *pPid)
1630{
1631 PWINCHILDCAREWORKER pWorker = NULL;
1632 PWINCHILD pOldChild;
1633 PWINCHILD pCurChild;
1634
1635 /*
1636 * There are usually idle workers around, except for at the start.
1637 */
1638 if (g_cIdleChildcareWorkers > 0)
1639 {
1640 /*
1641 * Try the idle hint first and move forward from it.
1642 */
1643 unsigned int const cWorkers = g_cChildCareworkers;
1644 unsigned int iHint = g_idxLastChildcareWorker;
1645 unsigned int i;
1646 for (i = iHint; i < cWorkers; i++)
1647 {
1648 PWINCHILDCAREWORKER pPossibleWorker = g_papChildCareworkers[i];
1649 if (pPossibleWorker->fIdle)
1650 {
1651 pWorker = pPossibleWorker;
1652 break;
1653 }
1654 }
1655 if (!pWorker)
1656 {
1657 /* Scan from the start. */
1658 if (iHint > cWorkers)
1659 iHint = cWorkers;
1660 for (i = 0; i < iHint; i++)
1661 {
1662 PWINCHILDCAREWORKER pPossibleWorker = g_papChildCareworkers[i];
1663 if (pPossibleWorker->fIdle)
1664 {
1665 pWorker = pPossibleWorker;
1666 break;
1667 }
1668 }
1669 }
1670 }
1671 if (!pWorker)
1672 {
1673 /*
1674 * Try create more workers if we haven't reached the max yet.
1675 */
1676 if (g_cChildCareworkers < g_cChildCareworkersMax)
1677 pWorker = mkWinChildcareCreateWorker();
1678
1679 /*
1680 * Queue it with an existing worker. Look for one without anthing extra scheduled.
1681 */
1682 if (!pWorker)
1683 {
1684 unsigned int i = g_cChildCareworkers;
1685 if (i == 0)
1686 fatal(NILF, 0, _("Failed to create worker threads for managing child processes!\n"));
1687 pWorker = g_papChildCareworkers[--i];
1688 if (pWorker->pTailTodoChildren)
1689 while (i-- > 0)
1690 {
1691 PWINCHILDCAREWORKER pPossibleWorker = g_papChildCareworkers[i];
1692 if (!pPossibleWorker->pTailTodoChildren)
1693 {
1694 pWorker = pPossibleWorker;
1695 break;
1696 }
1697 }
1698 }
1699 }
1700
1701 /*
1702 * Do the queueing.
1703 */
1704 pOldChild = NULL;
1705 for (;;)
1706 {
1707 pChild->pNext = pOldChild;
1708 pCurChild = _InterlockedCompareExchangePointer((void **)&pWorker->pTailTodoChildren, pChild, pOldChild);
1709 if (pCurChild == pOldChild)
1710 {
1711 DWORD volatile dwErr;
1712 _InterlockedIncrement(&g_cPendingChildren);
1713 if ( !pWorker->fIdle
1714 || SetEvent(pWorker->hEvtIdle))
1715 {
1716 *pPid = pChild->pid;
1717 return 0;
1718 }
1719
1720 _InterlockedDecrement(&g_cPendingChildren);
1721 dwErr = GetLastError();
1722 assert(0);
1723 mkWinChildDelete(pChild);
1724 return dwErr ? dwErr : -1;
1725 }
1726 pOldChild = pCurChild;
1727 }
1728}
1729
1730/**
1731 * Creates a regular child process (job.c).
1732 *
1733 * Will copy the information and push it to a childcare thread that does the
1734 * actual process creation.
1735 *
1736 * @returns 0 on success, windows status code on failure.
1737 * @param papszArgs The arguments.
1738 * @param papszEnv The environment (optional).
1739 * @param pszShell The SHELL variable value (optional).
1740 * @param pMkChild The make child structure (optional).
1741 * @param pPid Where to return the pid.
1742 */
1743int MkWinChildCreate(char **papszArgs, char **papszEnv, const char *pszShell, struct child *pMkChild, pid_t *pPid)
1744{
1745 PWINCHILD pChild = mkWinChildNew(WINCHILDTYPE_PROCESS);
1746 pChild->pMkChild = pMkChild;
1747
1748 pChild->u.Process.papszArgs = mkWinChildCopyStringArray(papszArgs, &pChild->u.Process.cbArgsStrings);
1749 if ( !papszEnv
1750 || !pMkChild
1751 || pMkChild->environment == papszEnv)
1752 {
1753 pChild->u.Process.cbEnvStrings = 0;
1754 pChild->u.Process.papszEnv = papszEnv;
1755 }
1756 else
1757 pChild->u.Process.papszEnv = mkWinChildCopyStringArray(papszEnv, &pChild->u.Process.cbEnvStrings);
1758 if (pszShell)
1759 pChild->u.Process.pszShell = xstrdup(pszShell);
1760
1761 return mkWinChildPushToCareWorker(pChild, pPid);
1762}
1763
1764int MkWinChildCreateWithStdOutPipe(char **papszArgs, char **papszEnv, int fdErr, pid_t *pPid, int *pfdReadPipe)
1765{
1766 fatal(NILF, 0, __FUNCTION__ "not implemented!\n");
1767 return -1;
1768}
1769
1770/**
1771 * Interface used by kSubmit.c for registering stuff to wait on.
1772 *
1773 * @returns 0 on success, windows status code on failure.
1774 * @param hEvent The event object handle to wait on.
1775 * @param pvSubmitWorker The argument to pass back to kSubmit to clean up.
1776 * @param pPid Where to return the pid.
1777 */
1778int MkWinChildCreateSubmit(intptr_t hEvent, void *pvSubmitWorker, pid_t *pPid)
1779{
1780 PWINCHILD pChild = mkWinChildNew(WINCHILDTYPE_SUBMIT);
1781 pChild->u.Submit.hEvent = (HANDLE)hEvent;
1782 pChild->u.Submit.pvSubmitWorker = pvSubmitWorker;
1783 return mkWinChildPushToCareWorker(pChild, pPid);
1784}
1785
1786/**
1787 * Interface used by redirect.c for registering stuff to wait on.
1788 *
1789 * @returns 0 on success, windows status code on failure.
1790 * @param hProcess The process object to wait on.
1791 * @param pPid Where to return the pid.
1792 */
1793int MkWinChildCreateRedirect(intptr_t hProcess, pid_t *pPid)
1794{
1795 PWINCHILD pChild = mkWinChildNew(WINCHILDTYPE_REDIRECT);
1796 pChild->u.Redirect.hProcess = (HANDLE)hProcess;
1797 return mkWinChildPushToCareWorker(pChild, pPid);
1798}
1799
1800/**
1801 * Interface used to kill process when processing Ctrl-C and fatal errors.
1802 *
1803 * @returns 0 on success, -1+errno on error.
1804 * @param pid The process to kill (PWINCHILD).
1805 * @param iSignal What to kill it with.
1806 * @param pMkChild The make child structure for validation.
1807 */
1808int MkWinChildKill(pid_t pid, int iSignal, struct child *pMkChild)
1809{
1810 PWINCHILD pChild = (PWINCHILD)pid;
1811 if (pChild)
1812 {
1813 assert(pChild->uMagic == WINCHILD_MAGIC);
1814 if (pChild->uMagic == WINCHILD_MAGIC)
1815 {
1816 switch (pChild->enmType)
1817 {
1818 case WINCHILDTYPE_PROCESS:
1819 assert(pChild->pMkChild == pMkChild);
1820 TerminateProcess(pChild->u.Process.hProcess, DBG_TERMINATE_PROCESS);
1821 pChild->iSignal = iSignal;
1822 break;
1823
1824 case WINCHILDTYPE_SUBMIT:
1825 {
1826 pChild->iSignal = iSignal;
1827 SetEvent(pChild->u.Submit.hEvent);
1828 break;
1829 }
1830
1831 case WINCHILDTYPE_REDIRECT:
1832 TerminateProcess(pChild->u.Redirect.hProcess, DBG_TERMINATE_PROCESS);
1833 pChild->iSignal = iSignal;
1834 break;
1835
1836 case WINCHILDTYPE_BUILTIN:
1837 break;
1838 }
1839 }
1840 }
1841 return -1;
1842}
1843
1844int MkWinChildWait(int fBlock, pid_t *pPid, int *piExitCode, int *piSignal, int *pfCoreDumped, struct child **ppMkChild)
1845{
1846 PWINCHILD pChild;
1847
1848 *pPid = 0;
1849 *piExitCode = 0;
1850 *pfCoreDumped = 0;
1851 *ppMkChild = NULL;
1852
1853 /*
1854 * Wait if necessary.
1855 */
1856 if (fBlock && !g_pTailCompletedChildren && g_cPendingChildren > 0)
1857 {
1858 DWORD dwStatus = WaitForSingleObject(g_hEvtWaitChildren, INFINITE);
1859 if (dwStatus == WAIT_FAILED)
1860 return (int)GetLastError();
1861 }
1862
1863 /*
1864 * Try unlink the last child in the LIFO.
1865 */
1866 pChild = g_pTailCompletedChildren;
1867 if (!pChild)
1868 return 0;
1869 pChild = mkWinChildDequeFromLifo(&g_pTailCompletedChildren, pChild);
1870 assert(pChild);
1871
1872 /*
1873 * Set return values and ditch the child structure.
1874 */
1875 *pPid = pChild->pid;
1876 *piExitCode = pChild->iExitCode;
1877 *pfCoreDumped = pChild->fCoreDumped;
1878 *ppMkChild = pChild->pMkChild;
1879 switch (pChild->enmType)
1880 {
1881 case WINCHILDTYPE_PROCESS:
1882 break;
1883 case WINCHILDTYPE_BUILTIN:
1884 break;
1885 case WINCHILDTYPE_SUBMIT:
1886 break;
1887 case WINCHILDTYPE_REDIRECT:
1888 break;
1889 default:
1890 assert(0);
1891 }
1892 mkWinChildDelete(pChild);
1893 return 0;
1894}
1895
1896
1897intptr_t MkWinChildGetCompleteEventHandle(void)
1898{
1899 return (intptr_t)g_hEvtWaitChildren;
1900}
1901
1902int MkWinChildUnrelatedCloseOnExec(int fd)
1903{
1904 if (fd >= 0)
1905 {
1906 HANDLE hNative = (HANDLE)_get_osfhandle(fd);
1907 if (hNative != INVALID_HANDLE_VALUE && hNative != NULL)
1908 {
1909 if (SetHandleInformation(hNative, HANDLE_FLAG_INHERIT /*clear*/ , 0 /*set*/))
1910 return 0;
1911 }
1912 }
1913 return EINVAL;
1914}
1915
Note: See TracBrowser for help on using the repository browser.

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