VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/kSubmit.c@ 3355

Last change on this file since 3355 was 3355, checked in by bird, 5 years ago

kWorker,kmk: Correcting a few SetThreadGroupAffinity calls.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 73.7 KB
Line 
1/* $Id: kSubmit.c 3355 2020-06-05 02:06:42Z bird $ */
2/** @file
3 * kMk Builtin command - submit job to a kWorker.
4 */
5
6/*
7 * Copyright (c) 2007-2016 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/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#ifdef __APPLE__
30# define _POSIX_C_SOURCE 1 /* 10.4 sdk and unsetenv */
31#endif
32#include "makeint.h"
33#include "job.h"
34#include "variable.h"
35#include "pathstuff.h"
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <errno.h>
40#include <assert.h>
41#ifdef HAVE_ALLOCA_H
42# include <alloca.h>
43#endif
44#if defined(_MSC_VER)
45# include <ctype.h>
46# include <io.h>
47# include <direct.h>
48# include <process.h>
49#else
50# include <unistd.h>
51#endif
52#ifdef KBUILD_OS_WINDOWS
53# ifndef CONFIG_NEW_WIN_CHILDREN
54# include "sub_proc.h"
55# else
56# include "../w32/winchildren.h"
57# endif
58# include "nt/nt_child_inject_standard_handles.h"
59#endif
60
61#include "kbuild.h"
62#include "kmkbuiltin.h"
63#include "err.h"
64
65
66/*********************************************************************************************************************************
67* Defined Constants And Macros *
68*********************************************************************************************************************************/
69/** Hashes a pid. */
70#define KWORKER_PID_HASH(a_pid) ((size_t)(a_pid) % 61)
71
72#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
73
74
75/*********************************************************************************************************************************
76* Structures and Typedefs *
77*********************************************************************************************************************************/
78typedef struct WORKERINSTANCE *PWORKERINSTANCE;
79typedef struct WORKERINSTANCE
80{
81 /** Pointer to the next worker instance. */
82 PWORKERINSTANCE pNext;
83 /** Pointer to the previous worker instance. */
84 PWORKERINSTANCE pPrev;
85 /** Pointer to the next worker with the same pid hash slot. */
86 PWORKERINSTANCE pNextPidHash;
87 /** 32 or 64. */
88 unsigned cBits;
89 /** The process ID of the kWorker process. */
90 pid_t pid;
91 union
92 {
93 struct
94 {
95 /** The exit code. */
96 int32_t rcExit;
97 /** Set to 1 if the worker is exiting. */
98 uint8_t bWorkerExiting;
99 uint8_t abUnused[3];
100 } s;
101 uint8_t ab[8];
102 } Result;
103 /** Number of result bytes read alread. */
104 size_t cbResultRead;
105
106#ifdef KBUILD_OS_WINDOWS
107 /** The process handle. */
108 HANDLE hProcess;
109 /** The bi-directional pipe we use to talk to the kWorker process. */
110 HANDLE hPipe;
111 /** For overlapped read (have valid event semaphore). */
112 OVERLAPPED OverlappedRead;
113# ifdef CONFIG_NEW_WIN_CHILDREN
114 /** Standard output catcher (reused). */
115 PWINCCWPIPE pStdOut;
116 /** Standard error catcher (reused). */
117 PWINCCWPIPE pStdErr;
118# endif
119#else
120 /** The socket descriptor we use to talk to the kWorker process. */
121 int fdSocket;
122#endif
123
124 /** Current history index (must mod with aHistory element count). */
125 unsigned iHistory;
126 /** History. */
127 struct
128 {
129 /** Pointer to the message, NULL if none. */
130 void *pvMsg;
131 /** The message size, zero if not present. */
132 size_t cbMsg;
133 } aHistory[4];
134
135 /** What it's busy with. NULL if idle. */
136 struct child *pBusyWith;
137} WORKERINSTANCE;
138
139
140typedef struct WORKERLIST
141{
142 /** The head of the list. NULL if empty. */
143 PWORKERINSTANCE pHead;
144 /** The tail of the list. NULL if empty. */
145 PWORKERINSTANCE pTail;
146 /** Number of list entries. */
147 size_t cEntries;
148} WORKERLIST;
149typedef WORKERLIST *PWORKERLIST;
150
151
152/*********************************************************************************************************************************
153* Global Variables *
154*********************************************************************************************************************************/
155/** List of idle worker.*/
156static WORKERLIST g_IdleList;
157/** List of busy workers. */
158static WORKERLIST g_BusyList;
159/** PID hash table for the workers.
160 * @sa KWORKER_PID_HASH() */
161static PWORKERINSTANCE g_apPidHash[61];
162
163#ifdef KBUILD_OS_WINDOWS
164/** For naming the pipes.
165 * Also indicates how many worker instances we've spawned. */
166static unsigned g_uWorkerSeqNo = 0;
167#endif
168/** Set if we've registred the atexit handler already. */
169static int g_fAtExitRegistered = 0;
170
171/** @var g_cArchBits
172 * The bit count of the architecture this binary is compiled for. */
173/** @var g_szArch
174 * The name of the architecture this binary is compiled for. */
175/** @var g_cArchBits
176 * The bit count of the alternative architecture. */
177/** @var g_szAltArch
178 * The name of the alternative architecture. */
179#if defined(KBUILD_ARCH_AMD64)
180static unsigned g_cArchBits = 64;
181static char const g_szArch[] = "amd64";
182static unsigned g_cAltArchBits = 32;
183static char const g_szAltArch[] = "x86";
184#elif defined(KBUILD_ARCH_X86)
185static unsigned g_cArchBits = 32;
186static char const g_szArch[] = "x86";
187static unsigned g_cAltArchBits = 64;
188static char const g_szAltArch[] = "amd64";
189#else
190# error "Port me!"
191#endif
192
193#ifdef KBUILD_OS_WINDOWS
194/** The processor group allocator state. */
195static MKWINCHILDCPUGROUPALLOCSTATE g_SubmitProcessorGroupAllocator;
196# if K_ARCH_BITS == 64
197/** The processor group allocator state for 32-bit processes. */
198static MKWINCHILDCPUGROUPALLOCSTATE g_SubmitProcessorGroupAllocator32;
199# endif
200#endif
201
202#ifdef KBUILD_OS_WINDOWS
203/** Pointer to kernel32!SetThreadGroupAffinity. */
204static BOOL (WINAPI *g_pfnSetThreadGroupAffinity)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
205#endif
206
207
208
209/**
210 * Unlinks a worker instance from a list.
211 *
212 * @param pList The list.
213 * @param pWorker The worker.
214 */
215static void kSubmitListUnlink(PWORKERLIST pList, PWORKERINSTANCE pWorker)
216{
217 PWORKERINSTANCE pNext = pWorker->pNext;
218 PWORKERINSTANCE pPrev = pWorker->pPrev;
219
220 if (pNext)
221 {
222 assert(pNext->pPrev == pWorker);
223 pNext->pPrev = pPrev;
224 }
225 else
226 {
227 assert(pList->pTail == pWorker);
228 pList->pTail = pPrev;
229 }
230
231 if (pPrev)
232 {
233 assert(pPrev->pNext == pWorker);
234 pPrev->pNext = pNext;
235 }
236 else
237 {
238 assert(pList->pHead == pWorker);
239 pList->pHead = pNext;
240 }
241
242 assert(!pList->pHead || pList->pHead->pPrev == NULL);
243 assert(!pList->pTail || pList->pTail->pNext == NULL);
244
245 assert(pList->cEntries > 0);
246 pList->cEntries--;
247
248 pWorker->pNext = NULL;
249 pWorker->pPrev = NULL;
250}
251
252
253/**
254 * Appends a worker instance to the tail of a list.
255 *
256 * @param pList The list.
257 * @param pWorker The worker.
258 */
259static void kSubmitListAppend(PWORKERLIST pList, PWORKERINSTANCE pWorker)
260{
261 PWORKERINSTANCE pTail = pList->pTail;
262
263 assert(pTail != pWorker);
264 assert(pList->pHead != pWorker);
265
266 pWorker->pNext = NULL;
267 pWorker->pPrev = pTail;
268 if (pTail != NULL)
269 {
270 assert(pTail->pNext == NULL);
271 pTail->pNext = pWorker;
272 }
273 else
274 {
275 assert(pList->pHead == NULL);
276 pList->pHead = pWorker;
277 }
278 pList->pTail = pWorker;
279
280 assert(pList->pHead->pPrev == NULL);
281 assert(pList->pTail->pNext == NULL);
282
283 pList->cEntries++;
284}
285
286
287/**
288 * Remove worker from the process ID hash table.
289 *
290 * @param pWorker The worker.
291 */
292static void kSubmitPidHashRemove(PWORKERINSTANCE pWorker)
293{
294 size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
295 if (g_apPidHash[idxHash] == pWorker)
296 g_apPidHash[idxHash] = pWorker->pNext;
297 else
298 {
299 PWORKERINSTANCE pPrev = g_apPidHash[idxHash];
300 while (pPrev && pPrev->pNext != pWorker)
301 pPrev = pPrev->pNext;
302 assert(pPrev != NULL);
303 if (pPrev)
304 pPrev->pNext = pWorker->pNext;
305 }
306 pWorker->pid = -1;
307}
308
309
310/**
311 * Looks up a worker by its process ID.
312 *
313 * @returns Pointer to the worker instance if found. NULL if not.
314 * @param pid The process ID of the worker.
315 */
316static PWORKERINSTANCE kSubmitFindWorkerByPid(pid_t pid)
317{
318 PWORKERINSTANCE pWorker = g_apPidHash[KWORKER_PID_HASH(pid)];
319 while (pWorker && pWorker->pid != pid)
320 pWorker = pWorker->pNextPidHash;
321 return pWorker;
322}
323
324
325/**
326 * Calcs the path to the kWorker binary for the worker.
327 *
328 * @returns
329 * @param pCtx The command execution context.
330 * @param pWorker The worker (for its bitcount).
331 * @param pszExecutable The output buffer.
332 * @param cbExecutable The output buffer size.
333 */
334static int kSubmitCalcExecutablePath(PKMKBUILTINCTX pCtx, PWORKERINSTANCE pWorker, char *pszExecutable, size_t cbExecutable)
335{
336#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
337 static const char s_szWorkerName[] = "kWorker.exe";
338#else
339 static const char s_szWorkerName[] = "kWorker";
340#endif
341 const char *pszBinPath = get_kbuild_bin_path();
342 size_t const cchBinPath = strlen(pszBinPath);
343 size_t cchExecutable;
344 if ( pWorker->cBits == g_cArchBits
345 ? cchBinPath + 1 + sizeof(s_szWorkerName) <= cbExecutable
346 : cchBinPath + 1 - sizeof(g_szArch) + sizeof(g_szAltArch) + sizeof(s_szWorkerName) <= cbExecutable )
347 {
348 memcpy(pszExecutable, pszBinPath, cchBinPath);
349 cchExecutable = cchBinPath;
350
351 /* Replace the arch bin directory extension with the alternative one if requested. */
352 if (pWorker->cBits != g_cArchBits)
353 {
354 if ( cchBinPath < sizeof(g_szArch)
355 || memcmp(&pszExecutable[cchBinPath - sizeof(g_szArch) + 1], g_szArch, sizeof(g_szArch) - 1) != 0)
356 return errx(pCtx, 1, "KBUILD_BIN_PATH does not end with main architecture (%s) as expected: %s",
357 pszBinPath, g_szArch);
358 cchExecutable -= sizeof(g_szArch) - 1;
359 memcpy(&pszExecutable[cchExecutable], g_szAltArch, sizeof(g_szAltArch) - 1);
360 cchExecutable += sizeof(g_szAltArch) - 1;
361 }
362
363 /* Append a slash and the worker name. */
364 pszExecutable[cchExecutable++] = '/';
365 memcpy(&pszExecutable[cchExecutable], s_szWorkerName, sizeof(s_szWorkerName));
366 return 0;
367 }
368 return errx(pCtx, 1, "KBUILD_BIN_PATH is too long");
369}
370
371
372#ifdef KBUILD_OS_WINDOWS
373/**
374 * Calcs the UTF-16 path to the kWorker binary for the worker.
375 *
376 * @returns
377 * @param pCtx The command execution context.
378 * @param pWorker The worker (for its bitcount).
379 * @param pwszExecutable The output buffer.
380 * @param cwcExecutable The output buffer size.
381 */
382static int kSubmitCalcExecutablePathW(PKMKBUILTINCTX pCtx, PWORKERINSTANCE pWorker, wchar_t *pwszExecutable, size_t cwcExecutable)
383{
384 char szExecutable[MAX_PATH];
385 int rc = kSubmitCalcExecutablePath(pCtx, pWorker, szExecutable, sizeof(szExecutable));
386 if (rc == 0)
387 {
388 int cwc = MultiByteToWideChar(CP_ACP, 0 /*fFlags*/, szExecutable, strlen(szExecutable) + 1,
389 pwszExecutable, cwcExecutable);
390 if (cwc > 0)
391 return 0;
392 return errx(pCtx, 1, "MultiByteToWideChar failed on '%s': %u", szExecutable, GetLastError());
393 }
394 return rc;
395}
396#endif
397
398
399/**
400 * Creates a new worker process.
401 *
402 * @returns 0 on success, non-zero value on failure.
403 * @param pCtx The command execution context.
404 * @param pWorker The worker structure. Caller does the linking
405 * (as we might be reusing an existing worker
406 * instance because a worker shut itself down due
407 * to high resource leak level).
408 * @param cVerbosity The verbosity level.
409 */
410static int kSubmitSpawnWorker(PKMKBUILTINCTX pCtx, PWORKERINSTANCE pWorker, int cVerbosity)
411{
412 int rc;
413#ifdef KBUILD_OS_WINDOWS
414 wchar_t wszExecutable[MAX_PATH];
415#else
416 PATH_VAR(szExecutable);
417#endif
418
419 /*
420 * Get the output path so it can be passed on as a volatile.
421 */
422 const char *pszVarVolatile;
423 struct variable *pVarVolatile = lookup_variable(TUPLE("PATH_OUT"));
424 if (pVarVolatile)
425 pszVarVolatile = "PATH_OUT";
426 else
427 {
428 pVarVolatile = lookup_variable(TUPLE("PATH_OUT_BASE"));
429 if (pVarVolatile)
430 pszVarVolatile = "PATH_OUT_BASE";
431 else
432 warn(pCtx, "Neither PATH_OUT_BASE nor PATH_OUT was found.");
433 }
434 if (pVarVolatile && strchr(pVarVolatile->value, '"'))
435 return errx(pCtx, -1, "%s contains double quotes.", pszVarVolatile);
436 if (pVarVolatile && strlen(pVarVolatile->value) >= GET_PATH_MAX)
437 return errx(pCtx, -1, "%s is too long (max %u)", pszVarVolatile, GET_PATH_MAX);
438
439 /*
440 * Construct the executable path.
441 */
442#ifdef KBUILD_OS_WINDOWS
443 rc = kSubmitCalcExecutablePathW(pCtx, pWorker, wszExecutable, K_ELEMENTS(wszExecutable));
444#else
445 rc = kSubmitCalcExecutablePath(pCtx, pWorker, szExecutable, GET_PATH_MAX);
446#endif
447 if (rc == 0)
448 {
449#ifdef KBUILD_OS_WINDOWS
450 static DWORD s_fDenyRemoteClients = ~(DWORD)0;
451 wchar_t wszPipeName[128];
452 HANDLE hWorkerPipe;
453 int iProcessorGroup;
454
455# if K_ARCH_BITS == 64
456 /** @todo make it return -1 if not applicable (e.g only one group). */
457 if (pWorker->cBits != 32)
458 iProcessorGroup = MkWinChildAllocateCpuGroup(&g_SubmitProcessorGroupAllocator);
459 else
460 iProcessorGroup = MkWinChildAllocateCpuGroup(&g_SubmitProcessorGroupAllocator32);
461# else
462 iProcessorGroup = MkWinChildAllocateCpuGroup(&g_SubmitProcessorGroupAllocator);
463# endif
464
465 /*
466 * Create the bi-directional pipe with overlapping I/O enabled.
467 */
468 if (s_fDenyRemoteClients == ~(DWORD)0)
469 s_fDenyRemoteClients = GetVersion() >= 0x60000 ? PIPE_REJECT_REMOTE_CLIENTS : 0;
470 _snwprintf(wszPipeName, sizeof(wszPipeName), L"\\\\.\\pipe\\kmk-%u-kWorker-%u-%u",
471 GetCurrentProcessId(), g_uWorkerSeqNo++, GetTickCount());
472 hWorkerPipe = CreateNamedPipeW(wszPipeName,
473 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE /* win2k sp2+ */,
474 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | s_fDenyRemoteClients,
475 1 /* cMaxInstances */,
476 64 /*cbOutBuffer*/,
477 65536 /*cbInBuffer*/,
478 0 /*cMsDefaultTimeout -> 50ms*/,
479 NULL /* pSecAttr - no inherit */);
480 if (hWorkerPipe != INVALID_HANDLE_VALUE)
481 {
482 pWorker->hPipe = CreateFileW(wszPipeName,
483 GENERIC_READ | GENERIC_WRITE,
484 0 /* dwShareMode - no sharing */,
485 NULL /*pSecAttr - no inherit */,
486 OPEN_EXISTING,
487 FILE_FLAG_OVERLAPPED,
488 NULL /*hTemplate*/);
489 if (pWorker->hPipe != INVALID_HANDLE_VALUE)
490 {
491 pWorker->OverlappedRead.hEvent = CreateEventW(NULL /*pSecAttrs - no inherit*/, TRUE /*bManualReset*/,
492 TRUE /*bInitialState*/, NULL /*pwszName*/);
493 if (pWorker->OverlappedRead.hEvent != NULL)
494 {
495 extern int process_priority; /* main.c */
496 wchar_t wszCommandLine[MAX_PATH * 3 + 32];
497 wchar_t *pwszDst = wszCommandLine;
498 size_t cwcDst = K_ELEMENTS(wszCommandLine);
499 int cwc;
500 DWORD fFlags;
501 STARTUPINFOW StartupInfo;
502 PROCESS_INFORMATION ProcInfo = { NULL, NULL, 0, 0 };
503
504 /*
505 * Compose the command line.
506 */
507 cwc = _snwprintf(pwszDst, cwcDst, L"\"%s\" ", wszExecutable);
508 assert(cwc > 0 && cwc < cwcDst);
509 pwszDst += cwc;
510 cwcDst -= cwc;
511 if (pVarVolatile && *pVarVolatile->value)
512 {
513 char chEnd = strchr(pVarVolatile->value, '\0')[-1];
514 if (chEnd == '\\')
515 cwc = _snwprintf(pwszDst, cwcDst, L" --volatile \"%S.\"", pVarVolatile->value);
516 else
517 cwc = _snwprintf(pwszDst, cwcDst, L" --volatile \"%S\"", pVarVolatile->value);
518 assert(cwc > 0 && cwc < cwcDst);
519 pwszDst += cwc;
520 cwcDst -= cwc;
521 }
522 if (iProcessorGroup >= 0)
523 {
524 cwc = _snwprintf(pwszDst, cwcDst, L" --group %d", iProcessorGroup);
525 assert(cwc > 0 && cwc < cwcDst);
526 pwszDst += cwc;
527 cwcDst -= cwc;
528 }
529 *pwszDst = '\0';
530
531 /*
532 * Fill in the startup information.
533 */
534 memset(&StartupInfo, 0, sizeof(StartupInfo));
535 StartupInfo.cb = sizeof(StartupInfo);
536 GetStartupInfoW(&StartupInfo);
537 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
538 StartupInfo.lpReserved2 = NULL;
539 StartupInfo.cbReserved2 = 0;
540
541 /*
542 * Flags and such.
543 */
544 fFlags = CREATE_SUSPENDED;
545 switch (process_priority)
546 {
547 case 1: fFlags |= CREATE_SUSPENDED | IDLE_PRIORITY_CLASS; break;
548 case 2: fFlags |= CREATE_SUSPENDED | BELOW_NORMAL_PRIORITY_CLASS; break;
549 case 3: fFlags |= CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS; break;
550 case 4: fFlags |= CREATE_SUSPENDED | HIGH_PRIORITY_CLASS; break;
551 case 5: fFlags |= CREATE_SUSPENDED | REALTIME_PRIORITY_CLASS; break;
552 }
553
554 /*
555 * Create the worker process.
556 */
557 if (CreateProcessW(wszExecutable, wszCommandLine, NULL /*pProcSecAttr*/, NULL /*pThreadSecAttr*/,
558 FALSE /*fInheritHandles*/, fFlags, NULL /*pwszzEnvironment*/,
559 NULL /*pwszCwd*/, &StartupInfo, &ProcInfo))
560 {
561 char szErrMsg[256];
562 BOOL afReplace[3] = { TRUE, FALSE, FALSE };
563 HANDLE ahReplace[3] = { hWorkerPipe, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
564 if (pWorker->pStdOut)
565 {
566 afReplace[1] = TRUE;
567 afReplace[2] = TRUE;
568 ahReplace[1] = pWorker->pStdOut->hPipeChild;
569 ahReplace[2] = pWorker->pStdErr->hPipeChild;
570 }
571
572 rc = nt_child_inject_standard_handles(ProcInfo.hProcess, afReplace, ahReplace, szErrMsg, sizeof(szErrMsg));
573 if (rc == 0)
574 {
575 BOOL fRet;
576 switch (process_priority)
577 {
578 case 1: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_IDLE); break;
579 case 2: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_BELOW_NORMAL); break;
580 case 3: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_NORMAL); break;
581 case 4: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_HIGHEST); break;
582 case 5: fRet = SetThreadPriority(ProcInfo.hThread, THREAD_PRIORITY_TIME_CRITICAL); break;
583 default: fRet = TRUE;
584 }
585 if (!fRet)
586 warnx(pCtx, "warning: failed to set kWorker thread priority: %u\n", GetLastError());
587
588 if (iProcessorGroup >= 0 && g_pfnSetThreadGroupAffinity)
589 {
590 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
591 GROUP_AFFINITY NewAff = { 0 /* == all active apparently */, (WORD)iProcessorGroup, 0, 0, 0 };
592 if (!g_pfnSetThreadGroupAffinity(ProcInfo.hThread, &NewAff, &OldAff))
593 warnx(pCtx, "warning: Failed to set processor group to %d: %u\n",
594 iProcessorGroup, GetLastError());
595 }
596
597 /*
598 * Now, we just need to resume the thread.
599 */
600 if (ResumeThread(ProcInfo.hThread))
601 {
602 CloseHandle(hWorkerPipe);
603 CloseHandle(ProcInfo.hThread);
604 pWorker->pid = ProcInfo.dwProcessId;
605 pWorker->hProcess = ProcInfo.hProcess;
606 if (cVerbosity > 0)
607 warnx(pCtx, "created %d bit worker %d\n", pWorker->cBits, pWorker->pid);
608 return 0;
609 }
610
611 /*
612 * Failed, bail out.
613 */
614 rc = errx(pCtx, -3, "ResumeThread failed: %u", GetLastError());
615 }
616 else
617 rc = errx(pCtx, -3, "%s", szErrMsg);
618 TerminateProcess(ProcInfo.hProcess, 1234);
619 CloseHandle(ProcInfo.hThread);
620 CloseHandle(ProcInfo.hProcess);
621 }
622 else
623 rc = errx(pCtx, -2, "CreateProcessW failed: %u (exe=%S cmdline=%S)",
624 GetLastError(), wszExecutable, wszCommandLine);
625 CloseHandle(pWorker->OverlappedRead.hEvent);
626 pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE;
627 }
628 else
629 rc = errx(pCtx, -1, "CreateEventW failed: %u", GetLastError());
630 CloseHandle(pWorker->hPipe);
631 pWorker->hPipe = INVALID_HANDLE_VALUE;
632 }
633 else
634 rc = errx(pCtx, -1, "Opening named pipe failed: %u", GetLastError());
635 CloseHandle(hWorkerPipe);
636 }
637 else
638 rc = errx(pCtx, -1, "CreateNamedPipeW failed: %u", GetLastError());
639
640#else
641 /*
642 * Create a socket pair.
643 */
644 int aiPair[2] = { -1, -1 };
645 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, aiPair) == 0)
646 {
647 pWorker->fdSocket = aiPair[1];
648
649 rc = -1;
650 }
651 else
652 rc = err(pCtx, -1, "socketpair");
653#endif
654 }
655 else
656 rc = errx(pCtx, -1, "KBUILD_BIN_PATH is too long");
657 return rc;
658}
659
660
661/**
662 * Selects an idle worker or spawns a new one.
663 *
664 * @returns Pointer to the selected worker instance. NULL on error.
665 * @param pCtx The command execution context.
666 * @param pWorker The idle worker instance to respawn.
667 * On failure this will be freed!
668 * @param cBitsWorker The worker bitness - 64 or 32.
669 */
670static int kSubmitRespawnWorker(PKMKBUILTINCTX pCtx, PWORKERINSTANCE pWorker, int cVerbosity)
671{
672 /*
673 * Clean up after the old worker.
674 */
675#ifdef KBUILD_OS_WINDOWS
676 DWORD rcWait;
677
678 /* Close the pipe handle first, breaking the pipe in case it's not already
679 busted up. Close the event semaphore too before waiting for the process. */
680 if (pWorker->hPipe != INVALID_HANDLE_VALUE)
681 {
682 if (!CloseHandle(pWorker->hPipe))
683 warnx(pCtx, "CloseHandle(pWorker->hPipe): %u", GetLastError());
684 pWorker->hPipe = INVALID_HANDLE_VALUE;
685 }
686
687 if (!CloseHandle(pWorker->OverlappedRead.hEvent))
688 warnx(pCtx, "CloseHandle(pWorker->OverlappedRead.hEvent): %u", GetLastError());
689 pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE;
690
691 if (pWorker->pStdOut)
692 MkWinChildcareWorkerDrainPipes(NULL, pWorker->pStdOut, pWorker->pStdErr);
693
694 /* It's probably shutdown already, if not give it 10 milliseconds before
695 we terminate it forcefully. */
696 rcWait = WaitForSingleObject(pWorker->hProcess, 10);
697 if (rcWait != WAIT_OBJECT_0)
698 {
699 BOOL fRc = TerminateProcess(pWorker->hProcess, 127);
700
701 if (pWorker->pStdOut)
702 MkWinChildcareWorkerDrainPipes(NULL, pWorker->pStdOut, pWorker->pStdErr);
703
704 rcWait = WaitForSingleObject(pWorker->hProcess, 100);
705 if (rcWait != WAIT_OBJECT_0)
706 warnx(pCtx, "WaitForSingleObject returns %u (and TerminateProcess %d)", rcWait, fRc);
707 }
708
709 if (pWorker->pStdOut)
710 MkWinChildcareWorkerDrainPipes(NULL, pWorker->pStdOut, pWorker->pStdErr);
711
712 if (!CloseHandle(pWorker->hProcess))
713 warnx(pCtx, "CloseHandle(pWorker->hProcess): %u", GetLastError());
714 pWorker->hProcess = INVALID_HANDLE_VALUE;
715
716#else
717 pid_t pidWait;
718 int rc;
719
720 if (pWorker->fdSocket != -1)
721 {
722 if (close(pWorker->fdSocket) != 0)
723 warn(pCtx, "close(pWorker->fdSocket)");
724 pWorker->fdSocket = -1;
725 }
726
727 kill(pWorker->pid, SIGTERM);
728 pidWait = waitpid(pWorker->pid, &rc, 0);
729 if (pidWait != pWorker->pid)
730 warn(pCtx, "waitpid(pWorker->pid,,0)");
731#endif
732
733 /*
734 * Unlink it from the hash table.
735 */
736 kSubmitPidHashRemove(pWorker);
737
738 /*
739 * Respawn it.
740 */
741 if (kSubmitSpawnWorker(pCtx, pWorker, cVerbosity) == 0)
742 {
743 /*
744 * Insert it into the process ID hash table and idle list.
745 */
746 size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
747 pWorker->pNextPidHash = g_apPidHash[idxHash];
748 g_apPidHash[idxHash] = pWorker;
749 return 0;
750 }
751
752 kSubmitListUnlink(&g_IdleList, pWorker);
753 free(pWorker);
754 return -1;
755}
756
757
758/**
759 * Selects an idle worker or spawns a new one.
760 *
761 * @returns Pointer to the selected worker instance. NULL on error.
762 * @param cBitsWorker The worker bitness - 64 or 32.
763 */
764static PWORKERINSTANCE kSubmitSelectWorkSpawnNewIfNecessary(PKMKBUILTINCTX pCtx, unsigned cBitsWorker, int cVerbosity)
765{
766 /*
767 * Lookup up an idle worker.
768 */
769 PWORKERINSTANCE pWorker = g_IdleList.pHead;
770 while (pWorker)
771 {
772 if (pWorker->cBits == cBitsWorker)
773 return pWorker;
774 pWorker = pWorker->pNext;
775 }
776
777 /*
778 * Create a new worker instance.
779 */
780 pWorker = (PWORKERINSTANCE)xcalloc(sizeof(*pWorker));
781 pWorker->cBits = cBitsWorker;
782#if defined(CONFIG_NEW_WIN_CHILDREN) && defined(KBUILD_OS_WINDOWS)
783 if (output_sync != OUTPUT_SYNC_NONE)
784 {
785 pWorker->pStdOut = MkWinChildcareCreateWorkerPipe(1, g_uWorkerSeqNo << 1);
786 pWorker->pStdErr = MkWinChildcareCreateWorkerPipe(2, g_uWorkerSeqNo << 1);
787 }
788 if ( output_sync == OUTPUT_SYNC_NONE
789 || ( pWorker->pStdOut != NULL
790 && pWorker->pStdErr != NULL))
791#endif
792 {
793 if (kSubmitSpawnWorker(pCtx, pWorker, cVerbosity) == 0)
794 {
795 /*
796 * Insert it into the process ID hash table and idle list.
797 */
798 size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
799 pWorker->pNextPidHash = g_apPidHash[idxHash];
800 g_apPidHash[idxHash] = pWorker;
801
802 kSubmitListAppend(&g_IdleList, pWorker);
803 return pWorker;
804 }
805 }
806#if defined(CONFIG_NEW_WIN_CHILDREN) && defined(KBUILD_OS_WINDOWS)
807 if (pWorker->pStdErr)
808 MkWinChildcareDeleteWorkerPipe(pWorker->pStdErr);
809 if (pWorker->pStdOut)
810 MkWinChildcareDeleteWorkerPipe(pWorker->pStdOut);
811#endif
812
813 free(pWorker);
814 return NULL;
815}
816
817
818/**
819 * Composes a JOB mesage for a worker.
820 *
821 * @returns Pointer to the message.
822 * @param pszExecutable The executable to run.
823 * @param papszArgs The argument vector.
824 * @param papszEnvVars The environment vector.
825 * @param pszCwd The current directory.
826 * @param fWatcomBrainDamage The wcc/wcc386 workaround.
827 * @param fNoPchCaching Whether to disable precompiled header caching.
828 * @param pszSpecialEnv Environment variable (name=value) subject to
829 * special expansion in kWorker. NULL if none.
830 * @param papszPostCmdArgs The post command and it's arguments.
831 * @param cPostCmdArgs Number of post command argument, including the
832 * command. Zero if no post command scheduled.
833 * @param pcbMsg Where to return the message length.
834 */
835static void *kSubmitComposeJobMessage(const char *pszExecutable, char **papszArgs, char **papszEnvVars,
836 const char *pszCwd, int fWatcomBrainDamage, int fNoPchCaching, const char *pszSpecialEnv,
837 char **papszPostCmdArgs, uint32_t cPostCmdArgs, uint32_t *pcbMsg)
838{
839 size_t cbTmp;
840 size_t cbSpecialEnv;
841 uint32_t i;
842 uint32_t cbMsg;
843 uint32_t cArgs;
844 uint32_t cEnvVars;
845 uint8_t *pbMsg;
846 uint8_t *pbCursor;
847
848 /*
849 * Adjust input.
850 */
851 if (!pszExecutable)
852 pszExecutable = papszArgs[0];
853
854 /*
855 * Calculate the message length first.
856 */
857 cbMsg = sizeof(cbMsg);
858 cbMsg += sizeof("JOB");
859 cbMsg += strlen(pszExecutable) + 1;
860 cbMsg += strlen(pszCwd) + 1;
861
862 cbMsg += sizeof(cArgs);
863 for (i = 0; papszArgs[i] != NULL; i++)
864 cbMsg += 1 + strlen(papszArgs[i]) + 1;
865 cArgs = i;
866
867 cbMsg += sizeof(cArgs);
868 for (i = 0; papszEnvVars[i] != NULL; i++)
869 cbMsg += strlen(papszEnvVars[i]) + 1;
870 cEnvVars = i;
871
872 cbMsg += 1; /* fWatcomBrainDamage */
873 cbMsg += 1; /* fNoPchCaching */
874
875 cbSpecialEnv = pszSpecialEnv ? strchr(pszSpecialEnv, '=') - pszSpecialEnv : 0;
876 cbMsg += cbSpecialEnv + 1;
877
878 cbMsg += sizeof(cPostCmdArgs);
879 for (i = 0; i < cPostCmdArgs; i++)
880 cbMsg += strlen(papszPostCmdArgs[i]) + 1;
881
882 /*
883 * Compose the message.
884 */
885 pbMsg = pbCursor = xmalloc(cbMsg);
886
887 /* header */
888 memcpy(pbCursor, &cbMsg, sizeof(cbMsg));
889 pbCursor += sizeof(cbMsg);
890 memcpy(pbCursor, "JOB", sizeof("JOB"));
891 pbCursor += sizeof("JOB");
892
893 /* executable. */
894 cbTmp = strlen(pszExecutable) + 1;
895 memcpy(pbCursor, pszExecutable, cbTmp);
896 pbCursor += cbTmp;
897
898 /* cwd */
899 cbTmp = strlen(pszCwd) + 1;
900 memcpy(pbCursor, pszCwd, cbTmp);
901 pbCursor += cbTmp;
902
903 /* arguments */
904 memcpy(pbCursor, &cArgs, sizeof(cArgs));
905 pbCursor += sizeof(cArgs);
906 for (i = 0; papszArgs[i] != NULL; i++)
907 {
908 *pbCursor++ = 0; /* Argument expansion flags (MSC, EMX). */
909 cbTmp = strlen(papszArgs[i]) + 1;
910 memcpy(pbCursor, papszArgs[i], cbTmp);
911 pbCursor += cbTmp;
912 }
913 assert(i == cArgs);
914
915 /* environment */
916 memcpy(pbCursor, &cEnvVars, sizeof(cEnvVars));
917 pbCursor += sizeof(cEnvVars);
918 for (i = 0; papszEnvVars[i] != NULL; i++)
919 {
920 cbTmp = strlen(papszEnvVars[i]) + 1;
921 memcpy(pbCursor, papszEnvVars[i], cbTmp);
922 pbCursor += cbTmp;
923 }
924 assert(i == cEnvVars);
925
926 /* flags */
927 *pbCursor++ = fWatcomBrainDamage != 0;
928 *pbCursor++ = fNoPchCaching != 0;
929
930 /* Special environment variable name. */
931 memcpy(pbCursor, pszSpecialEnv, cbSpecialEnv);
932 pbCursor += cbSpecialEnv;
933 *pbCursor++ = '\0';
934
935 /* post command */
936 memcpy(pbCursor, &cPostCmdArgs, sizeof(cPostCmdArgs));
937 pbCursor += sizeof(cPostCmdArgs);
938 for (i = 0; i < cPostCmdArgs; i++)
939 {
940 cbTmp = strlen(papszPostCmdArgs[i]) + 1;
941 memcpy(pbCursor, papszPostCmdArgs[i], cbTmp);
942 pbCursor += cbTmp;
943 }
944 assert(i == cPostCmdArgs);
945
946 assert(pbCursor - pbMsg == (size_t)cbMsg);
947
948 /*
949 * Done.
950 */
951 *pcbMsg = cbMsg;
952 return pbMsg;
953}
954
955
956/**
957 * Sends the job message to the given worker, respawning the worker if
958 * necessary.
959 *
960 * @returns 0 on success, non-zero on failure.
961 *
962 * @param pCtx The command execution context.
963 * @param pWorker The work to send the request to. The worker is
964 * on the idle list.
965 * @param pvMsg The message to send.
966 * @param cbMsg The size of the message.
967 * @param fNoRespawning Set if
968 * @param cVerbosity The verbosity level.
969 */
970static int kSubmitSendJobMessage(PKMKBUILTINCTX pCtx, PWORKERINSTANCE pWorker, void const *pvMsg, uint32_t cbMsg,
971 int fNoRespawning, int cVerbosity)
972{
973 int cRetries;
974
975 /*
976 * Respawn the worker if it stopped by itself and we closed the pipe already.
977 */
978#ifdef KBUILD_OS_WINDOWS
979 if (pWorker->hPipe == INVALID_HANDLE_VALUE)
980#else
981 if (pWorker->fdSocket == -1)
982#endif
983 {
984 if (!fNoRespawning)
985 {
986 if (cVerbosity > 0)
987 warnx(pCtx, "Respawning worker (#1)...\n");
988 if (kSubmitRespawnWorker(pCtx, pWorker, cVerbosity) != 0)
989 return 2;
990 }
991
992 }
993
994 /*
995 * Restart-on-broken-pipe loop. Necessary?
996 */
997 for (cRetries = !fNoRespawning ? 1 : 0; ; cRetries--)
998 {
999 /*
1000 * Try write the message.
1001 */
1002 uint32_t cbLeft = cbMsg;
1003 uint8_t const *pbLeft = (uint8_t const *)pvMsg;
1004#ifdef KBUILD_OS_WINDOWS
1005 DWORD dwErr;
1006 DWORD cbWritten;
1007 while (WriteFile(pWorker->hPipe, pbLeft, cbLeft, &cbWritten, NULL /*pOverlapped*/))
1008 {
1009 assert(cbWritten <= cbLeft);
1010 cbLeft -= cbWritten;
1011 if (!cbLeft)
1012 return 0;
1013
1014 /* This scenario shouldn't really ever happen. But just in case... */
1015 pbLeft += cbWritten;
1016 }
1017 dwErr = GetLastError();
1018 if ( ( dwErr != ERROR_BROKEN_PIPE
1019 && dwErr != ERROR_NO_DATA)
1020 || cRetries <= 0)
1021 return errx(pCtx, 1, "Error writing to worker: %u", dwErr);
1022#else
1023 ssize_t cbWritten
1024 while ((cbWritten = write(pWorker->fdSocket, pbLeft, cbLeft)) >= 0)
1025 {
1026 assert(cbWritten <= cbLeft);
1027 cbLeft -= cbWritten;
1028 if (!cbLeft)
1029 return 0;
1030
1031 pbLeft += cbWritten;
1032 }
1033 if ( ( errno != EPIPE
1034 && errno != ENOTCONN
1035 && errno != ECONNRESET))
1036 || cRetries <= 0)
1037 return err(pCtx, 1, "Error writing to worker");
1038# error "later"
1039#endif
1040
1041 /*
1042 * Broken connection. Try respawn the worker.
1043 */
1044 if (cVerbosity > 0)
1045 warnx(pCtx, "Respawning worker (#2)...\n");
1046 if (kSubmitRespawnWorker(pCtx, pWorker, cVerbosity) != 0)
1047 return 2;
1048 }
1049}
1050
1051
1052/**
1053 * Closes the connection on a worker that said it is going to exit now.
1054 *
1055 * This is a way of dealing with imperfect resource management in the worker, it
1056 * will monitor it a little and trigger a respawn when it looks bad.
1057 *
1058 * This function just closes the pipe / socket connection to the worker. The
1059 * kSubmitSendJobMessage function will see this a trigger a respawn the next
1060 * time the worker is engaged. This will usually mean there's a little delay in
1061 * which the process can terminate without us having to actively wait for it.
1062 *
1063 * @param pCtx The command execution context.
1064 * @param pWorker The worker instance.
1065 */
1066static void kSubmitCloseConnectOnExitingWorker(PKMKBUILTINCTX pCtx, PWORKERINSTANCE pWorker)
1067{
1068#ifdef KBUILD_OS_WINDOWS
1069 if (!CloseHandle(pWorker->hPipe))
1070 warnx(pCtx, "CloseHandle(pWorker->hPipe): %u", GetLastError());
1071 pWorker->hPipe = INVALID_HANDLE_VALUE;
1072#else
1073 if (close(pWorker->fdSocket) != 0)
1074 warn(pCtx, "close(pWorker->fdSocket)");
1075 pWorker->fdSocket = -1;
1076#endif
1077}
1078
1079
1080#ifdef KBUILD_OS_WINDOWS
1081
1082/**
1083 * Handles read failure.
1084 *
1085 * @returns Exit code.
1086 * @param pCtx The command execution context.
1087 * @param pWorker The worker instance.
1088 * @param dwErr The error code.
1089 * @param pszWhere Where it failed.
1090 */
1091static int kSubmitWinReadFailed(PKMKBUILTINCTX pCtx, PWORKERINSTANCE pWorker, DWORD dwErr, const char *pszWhere)
1092{
1093 DWORD dwExitCode;
1094
1095 if (pWorker->cbResultRead == 0)
1096 errx(pCtx, 1, "%s/ReadFile failed: %u", pszWhere, dwErr);
1097 else
1098 errx(pCtx, 1, "%s/ReadFile failed: %u (read %u bytes)", pszWhere, dwErr, pWorker->cbResultRead);
1099 assert(dwErr != 0);
1100
1101 /* Complete the result. */
1102 pWorker->Result.s.rcExit = 127;
1103 pWorker->Result.s.bWorkerExiting = 1;
1104 pWorker->cbResultRead = sizeof(pWorker->Result);
1105
1106 if (GetExitCodeProcess(pWorker->hProcess, &dwExitCode))
1107 {
1108 if (dwExitCode != 0)
1109 pWorker->Result.s.rcExit = dwExitCode;
1110 }
1111
1112 return dwErr != 0 ? (int)(dwErr & 0x7fffffff) : 0x7fffffff;
1113
1114}
1115
1116
1117/**
1118 * Used by
1119 * @returns 0 if we got the whole result, -1 if I/O is pending, and windows last
1120 * error on ReadFile failure.
1121 * @param pCtx The command execution context.
1122 * @param pWorker The worker instance.
1123 */
1124static int kSubmitReadMoreResultWin(PKMKBUILTINCTX pCtx, PWORKERINSTANCE pWorker, const char *pszWhere)
1125{
1126 /*
1127 * Set up the result read, telling the sub_proc.c unit about it.
1128 */
1129 while (pWorker->cbResultRead < sizeof(pWorker->Result))
1130 {
1131 DWORD cbRead = 0;
1132
1133 BOOL fRc = ResetEvent(pWorker->OverlappedRead.hEvent);
1134 assert(fRc); (void)fRc;
1135
1136 pWorker->OverlappedRead.Offset = 0;
1137 pWorker->OverlappedRead.OffsetHigh = 0;
1138
1139 if (!ReadFile(pWorker->hPipe, &pWorker->Result.ab[pWorker->cbResultRead],
1140 sizeof(pWorker->Result) - pWorker->cbResultRead,
1141 &cbRead,
1142 &pWorker->OverlappedRead))
1143 {
1144 DWORD dwErr = GetLastError();
1145 if (dwErr == ERROR_IO_PENDING)
1146 return -1;
1147 return kSubmitWinReadFailed(pCtx, pWorker, dwErr, pszWhere);
1148 }
1149
1150 pWorker->cbResultRead += cbRead;
1151 assert(pWorker->cbResultRead <= sizeof(pWorker->Result));
1152 }
1153 return 0;
1154}
1155
1156#endif /* KBUILD_OS_WINDOWS */
1157
1158
1159/**
1160 * Adds the given message to the history.
1161 *
1162 * @returns Pointer to old message, or NULL if no old msg to free.
1163 * @param pWorker The worker instance.
1164 * @param pvMsg The message.
1165 * @param cbMsg The message size.
1166 */
1167static void *kSubmitUpdateHistory(PWORKERINSTANCE pWorker, void *pvMsg, size_t cbMsg)
1168{
1169 unsigned iHistory = pWorker->iHistory % K_ELEMENTS(pWorker->aHistory);
1170 void *pvRet;
1171 pWorker->iHistory++;
1172 pvRet = pWorker->aHistory[iHistory].pvMsg;
1173 pWorker->aHistory[iHistory].pvMsg = pvMsg;
1174 pWorker->aHistory[iHistory].cbMsg = cbMsg;
1175 return pvRet;
1176}
1177
1178typedef struct HISTORYDUMPBUF
1179{
1180 char *pszBuf;
1181 size_t cbBuf;
1182 size_t off;
1183 PKMKBUILTINCTX pCtx;
1184} HISTORYDUMPBUF;
1185
1186
1187static void kSubmitDumpHistoryWrite(HISTORYDUMPBUF *pBuf, const char *pch, size_t cch)
1188{
1189 if (pBuf->off + cch >= pBuf->cbBuf)
1190 {
1191 size_t cbNew = pBuf->cbBuf ? pBuf->cbBuf * 2 : 65536;
1192 while (pBuf->off + cch >= cbNew)
1193 cbNew *= 2;
1194 pBuf->pszBuf = (char *)xrealloc(pBuf->pszBuf, cbNew);
1195 pBuf->cbBuf = cbNew;
1196 }
1197
1198 memcpy(&pBuf->pszBuf[pBuf->off], pch, cch);
1199 pBuf->off += cch;
1200 pBuf->pszBuf[pBuf->off] = '\0';
1201}
1202
1203static void kSubmitDumpHistoryPrintf(HISTORYDUMPBUF *pBuf, const char *pszFormat, ...)
1204{
1205 char szTmp[32];
1206 va_list va;
1207 va_start(va, pszFormat);
1208 for (;;)
1209 {
1210 const char *pszPct = strchr(pszFormat, '%');
1211 if (!pszPct)
1212 {
1213 kSubmitDumpHistoryWrite(pBuf, pszFormat, strlen(pszFormat));
1214 return;
1215 }
1216 if (pszPct != pszFormat)
1217 {
1218 kSubmitDumpHistoryWrite(pBuf, pszFormat, pszPct - pszFormat);
1219 pszFormat = pszPct;
1220 }
1221 pszFormat++;
1222 switch (*pszFormat++)
1223 {
1224 case 's':
1225 {
1226 const char * const psz = va_arg(va, const char *);
1227 size_t const cch = strlen(psz);
1228 if (memchr(psz, '\'', cch))
1229 {
1230 kSubmitDumpHistoryWrite(pBuf, TUPLE("\"")); /** @todo what if there are '"' in the string? */
1231 kSubmitDumpHistoryWrite(pBuf, psz, cch);
1232 kSubmitDumpHistoryWrite(pBuf, TUPLE("\""));
1233 }
1234 else if ( !memchr(psz, ' ', cch)
1235 && !memchr(psz, '\t', cch)
1236 && !memchr(psz, '\n', cch)
1237 && !memchr(psz, '\r', cch)
1238 && !memchr(psz, '&', cch)
1239 && !memchr(psz, ';', cch)
1240 && !memchr(psz, '|', cch))
1241 kSubmitDumpHistoryWrite(pBuf, psz, cch);
1242 else
1243 {
1244 kSubmitDumpHistoryWrite(pBuf, TUPLE("'"));
1245 kSubmitDumpHistoryWrite(pBuf, psz, strlen(psz));
1246 kSubmitDumpHistoryWrite(pBuf, TUPLE("'"));
1247 }
1248 break;
1249 }
1250
1251 case 'd':
1252 {
1253 int iValue = va_arg(va, int);
1254 kSubmitDumpHistoryWrite(pBuf, szTmp, snprintf(szTmp, sizeof(szTmp), "%d", iValue));
1255 break;
1256 }
1257
1258 case 'u':
1259 {
1260 unsigned uValue = va_arg(va, unsigned);
1261 kSubmitDumpHistoryWrite(pBuf, szTmp, snprintf(szTmp, sizeof(szTmp), "%u", uValue));
1262 break;
1263 }
1264
1265 case '%':
1266 kSubmitDumpHistoryWrite(pBuf, "%s", 1);
1267 break;
1268
1269 default:
1270 assert(0);
1271 }
1272 }
1273 va_end(va);
1274}
1275
1276static void kSubmitDumpHistoryFlush(HISTORYDUMPBUF *pBuf)
1277{
1278 if (pBuf->off > 0)
1279 output_write_text(pBuf->pCtx->pOut, 1, pBuf->pszBuf, pBuf->off);
1280 pBuf->off = 0;
1281}
1282
1283/**
1284 * Dumps the history for this worker to stderr in the given context.
1285 *
1286 * @param pCtx The command execution context. (Typically not a
1287 * real context.)
1288 * @param pWorker The worker instance.
1289 */
1290static void kSubmitDumpHistory(PKMKBUILTINCTX pCtx, PWORKERINSTANCE pWorker)
1291{
1292 HISTORYDUMPBUF Buf = { NULL, 0, 0, pCtx };
1293 int iHistory = pWorker->iHistory;
1294 unsigned cDumped = 0;
1295
1296 while (cDumped < K_ELEMENTS(pWorker->aHistory) && iHistory > 0)
1297 {
1298 unsigned const idx = (unsigned)--iHistory % K_ELEMENTS(pWorker->aHistory);
1299 const char *pszMsg = (const char *)pWorker->aHistory[idx].pvMsg;
1300 ssize_t cbMsg = pWorker->aHistory[idx].cbMsg;
1301 const char *pszExe;
1302 const char *pszCwd;
1303 uint32_t i;
1304 uint32_t cArgs;
1305 const char *pszArgs;
1306 size_t cbArgs;
1307 uint32_t cEnvVars;
1308 const char *pszEnvVars;
1309 size_t cbEnvVars;
1310 const char *pszSpecialEnv;
1311 char fNoPchCaching;
1312 char fWatcomBrainDamage;
1313 uint32_t cPostArgs;
1314 const char *pszPostArgs;
1315 size_t cbPostArgs;
1316
1317 cDumped++;
1318 if (!pszMsg || !cbMsg)
1319 break;
1320
1321#define SKIP_BYTES(a_cbSkip) do { pszMsg += (a_cbSkip); cbMsg -= (a_cbSkip); } while (0)
1322#define SKIP_STR() do { size_t const cbToSkip = strlen(pszMsg) + 1; SKIP_BYTES(cbToSkip); } while (0)
1323#define SKIP_STRING_ARRAY(a_cStrings, a_cbPreable) do { \
1324 for (i = 0; i < (a_cStrings) && cbMsg > 0; i++) { \
1325 size_t const cbToSkip = (a_cbPreable) + strlen(pszMsg + (a_cbPreable)) + 1; \
1326 SKIP_BYTES(cbToSkip); \
1327 } } while (0)
1328
1329 /* Decode it: */
1330 SKIP_BYTES(sizeof(uint32_t) + sizeof("JOB"));
1331 pszExe = pszMsg;
1332 SKIP_STR();
1333 pszCwd = pszMsg;
1334 SKIP_STR();
1335
1336 cArgs = *(uint32_t *)pszMsg;
1337 SKIP_BYTES(sizeof(uint32_t));
1338 pszArgs = pszMsg;
1339 SKIP_STRING_ARRAY(cArgs, 1 /*fbFlags*/);
1340 cbArgs = pszMsg - pszArgs;
1341
1342 cEnvVars = *(uint32_t *)pszMsg;
1343 SKIP_BYTES(sizeof(uint32_t));
1344 pszEnvVars = pszMsg;
1345 SKIP_STRING_ARRAY(cEnvVars, 0);
1346 cbEnvVars = pszMsg - pszEnvVars;
1347
1348 fWatcomBrainDamage = pszMsg[0] != '\0';
1349 fNoPchCaching = pszMsg[1] != '\0';
1350 SKIP_BYTES(2);
1351
1352 pszSpecialEnv = pszMsg;
1353 SKIP_STR();
1354
1355 cPostArgs = *(uint32_t *)pszMsg;
1356 SKIP_BYTES(sizeof(uint32_t));
1357 pszPostArgs = pszMsg;
1358 SKIP_STRING_ARRAY(cPostArgs, 0);
1359 cbPostArgs = pszMsg - pszPostArgs;
1360
1361 /* Produce parseable output: */
1362 kSubmitDumpHistoryPrintf(&Buf, "kWorker %u/%u:\n\tkSubmit", (long)pWorker->pid, iHistory, pszExe);
1363 if (fNoPchCaching)
1364 kSubmitDumpHistoryWrite(&Buf, TUPLE(" --no-pch-caching"));
1365 if (fWatcomBrainDamage)
1366 kSubmitDumpHistoryWrite(&Buf, TUPLE(" --watcom-brain-damage"));
1367 if (pszSpecialEnv)
1368 kSubmitDumpHistoryPrintf(&Buf, " --special-env %s", pszSpecialEnv);
1369 kSubmitDumpHistoryPrintf(&Buf, " --chdir %s \\\n", pszCwd);
1370
1371 pszMsg = pszEnvVars;
1372 cbMsg = cbEnvVars;
1373 for (i = 0; i < cEnvVars && cbMsg > 0; i++)
1374 {
1375 kSubmitDumpHistoryPrintf(&Buf, "\t--putenv %s \\\n", pszMsg);
1376 SKIP_STR();
1377 }
1378
1379 if (cPostArgs > 0)
1380 {
1381 kSubmitDumpHistoryWrite(&Buf, TUPLE("\t--post-cmd "));
1382 pszMsg = pszPostArgs;
1383 cbMsg = cbPostArgs;
1384 for (i = 0; i < cPostArgs && cbMsg > 0; i++)
1385 {
1386 kSubmitDumpHistoryPrintf(&Buf, " %s", pszMsg);
1387 SKIP_STR();
1388 }
1389 kSubmitDumpHistoryWrite(&Buf, TUPLE(" \\\n"));
1390 }
1391 kSubmitDumpHistoryWrite(&Buf, TUPLE("\t-- \\\n"));
1392
1393 pszMsg = pszArgs;
1394 cbMsg = cbArgs;
1395 for (i = 0; i < cArgs && cbMsg > 0; i++)
1396 {
1397 SKIP_BYTES(1);
1398 kSubmitDumpHistoryPrintf(&Buf, i + 1 < cArgs ? "\t%s \\\n" : "\t%s\n", pszMsg);
1399 SKIP_STR();
1400 }
1401
1402#undef SKIP_BYTES
1403#undef SKIP_STR
1404#undef SKIP_STRING_ARRAY
1405 }
1406
1407 kSubmitDumpHistoryFlush(&Buf);
1408 free(Buf.pszBuf);
1409}
1410
1411
1412/**
1413 * Marks the worker active.
1414 *
1415 * On windows this involves setting up the async result read and telling
1416 * sub_proc.c about the process.
1417 *
1418 * @returns Exit code.
1419 * @param pCtx The command execution context.
1420 * @param pWorker The worker instance to mark as active.
1421 * @param cVerbosity The verbosity level.
1422 * @param pChild The kmk child to associate the job with.
1423 * @param pPidSpawned If @a *pPidSpawned is non-zero if the child is
1424 * running, otherwise the worker is already done
1425 * and we've returned the exit code of the job.
1426 */
1427static int kSubmitMarkActive(PKMKBUILTINCTX pCtx, PWORKERINSTANCE pWorker, int cVerbosity, struct child *pChild, pid_t *pPidSpawned)
1428{
1429#ifdef KBUILD_OS_WINDOWS
1430 int rc;
1431#endif
1432
1433 pWorker->cbResultRead = 0;
1434
1435#ifdef KBUILD_OS_WINDOWS
1436 /*
1437 * Setup the async result read on windows. If we're slow and the worker
1438 * very fast, this may actually get the result immediately.
1439 */
1440l_again:
1441 rc = kSubmitReadMoreResultWin(pCtx, pWorker, "kSubmitMarkActive");
1442 if (rc == -1)
1443 {
1444# ifndef CONFIG_NEW_WIN_CHILDREN
1445 if (process_kmk_register_submit(pWorker->OverlappedRead.hEvent, (intptr_t)pWorker, pPidSpawned) == 0)
1446 { /* likely */ }
1447 else
1448 {
1449 /* We need to do the waiting here because sub_proc.c has too much to do. */
1450 warnx(pCtx, "Too many processes for sub_proc.c to handle!");
1451 WaitForSingleObject(pWorker->OverlappedRead.hEvent, INFINITE);
1452 goto l_again;
1453 }
1454# else
1455 if (MkWinChildCreateSubmit((intptr_t)pWorker->OverlappedRead.hEvent, pWorker,
1456 pWorker->pStdOut, pWorker->pStdErr, pChild, pPidSpawned) == 0)
1457 { /* likely */ }
1458 else
1459 {
1460 /* We need to do the waiting here because sub_proc.c has too much to do. */
1461 warnx(pCtx, "MkWinChildCreateSubmit failed!");
1462 WaitForSingleObject(pWorker->OverlappedRead.hEvent, INFINITE);
1463 goto l_again;
1464 }
1465# endif
1466 }
1467 else
1468 {
1469 assert(rc == 0 || pWorker->Result.s.rcExit != 0);
1470 if (pWorker->Result.s.bWorkerExiting)
1471 kSubmitCloseConnectOnExitingWorker(pCtx, pWorker);
1472 if (pWorker->Result.s.rcExit && 1)
1473 kSubmitDumpHistory(pCtx, pWorker);
1474 *pPidSpawned = 0;
1475 return pWorker->Result.s.rcExit;
1476 }
1477#endif
1478
1479 /*
1480 * Mark it busy and move it to the active instance.
1481 */
1482 pWorker->pBusyWith = pChild;
1483#ifndef KBUILD_OS_WINDOWS
1484 *pPidSpawned = pWorker->pid;
1485#endif
1486
1487 kSubmitListUnlink(&g_IdleList, pWorker);
1488 kSubmitListAppend(&g_BusyList, pWorker);
1489 return 0;
1490}
1491
1492
1493#ifdef KBUILD_OS_WINDOWS
1494
1495/**
1496 * Retrieve the worker child result.
1497 *
1498 * If incomplete, we restart the ReadFile operation like kSubmitMarkActive does.
1499 *
1500 * @returns 0 on success, -1 if ReadFile was restarted.
1501 * @param pvUser The worker instance.
1502 * @param fBlock if we're to block waiting for the result or not.
1503 * @param prcExit Where to return the exit code.
1504 * @param piSigNo Where to return the signal number.
1505 */
1506int kSubmitSubProcGetResult(intptr_t pvUser, int fBlock, int *prcExit, int *piSigNo)
1507{
1508 PWORKERINSTANCE pWorker = (PWORKERINSTANCE)pvUser;
1509 KMKBUILTINCTX FakeCtx = { "kSubmit/GetResult", NULL };
1510 PKMKBUILTINCTX pCtx = &FakeCtx;
1511
1512 /*
1513 * Get the overlapped result. There should be one since we're here
1514 * because of a satisfied WaitForMultipleObject.
1515 */
1516 DWORD cbRead = 0;
1517 if (GetOverlappedResult(pWorker->hPipe, &pWorker->OverlappedRead, &cbRead, fBlock ? TRUE : FALSE))
1518 {
1519 pWorker->cbResultRead += cbRead;
1520 assert(pWorker->cbResultRead <= sizeof(pWorker->Result));
1521
1522 /* More to be read? */
1523 while (pWorker->cbResultRead < sizeof(pWorker->Result))
1524 {
1525 int rc = kSubmitReadMoreResultWin(pCtx, pWorker, "kSubmitSubProcGetResult/more");
1526 if (rc == -1)
1527 return -1;
1528 assert(rc == 0 || pWorker->Result.s.rcExit != 0);
1529 }
1530 assert(pWorker->cbResultRead == sizeof(pWorker->Result));
1531 }
1532 else
1533 {
1534 DWORD dwErr = GetLastError();
1535 if (dwErr == ERROR_IO_INCOMPLETE && !fBlock)
1536 return -1;
1537 kSubmitWinReadFailed(pCtx, pWorker, dwErr, "kSubmitSubProcGetResult/result");
1538 }
1539
1540 /*
1541 * Okay, we've got a result.
1542 */
1543 *prcExit = pWorker->Result.s.rcExit;
1544 switch (pWorker->Result.s.rcExit)
1545 {
1546 default: *piSigNo = 0; break;
1547 case CONTROL_C_EXIT: *piSigNo = SIGINT; break;
1548 case STATUS_INTEGER_DIVIDE_BY_ZERO: *piSigNo = SIGFPE; break;
1549 case STATUS_ACCESS_VIOLATION: *piSigNo = SIGSEGV; break;
1550 case STATUS_PRIVILEGED_INSTRUCTION:
1551 case STATUS_ILLEGAL_INSTRUCTION: *piSigNo = SIGILL; break;
1552 }
1553 if (pWorker->Result.s.rcExit && 1)
1554 kSubmitDumpHistory(pCtx, pWorker);
1555 if (pWorker->Result.s.bWorkerExiting)
1556 kSubmitCloseConnectOnExitingWorker(pCtx, pWorker);
1557
1558 return 0;
1559}
1560
1561
1562int kSubmitSubProcKill(intptr_t pvUser, int iSignal)
1563{
1564 return -1;
1565}
1566
1567
1568/**
1569 * Called by process_cleanup when it's done with the worker.
1570 *
1571 * @param pvUser The worker instance.
1572 */
1573void kSubmitSubProcCleanup(intptr_t pvUser)
1574{
1575 PWORKERINSTANCE pWorker = (PWORKERINSTANCE)pvUser;
1576 kSubmitListUnlink(&g_BusyList, pWorker);
1577 kSubmitListAppend(&g_IdleList, pWorker);
1578}
1579
1580#endif /* KBUILD_OS_WINDOWS */
1581
1582
1583/**
1584 * atexit callback that trigger worker termination.
1585 */
1586static void kSubmitAtExitCallback(void)
1587{
1588 PWORKERINSTANCE pWorker;
1589 DWORD msStartTick;
1590 DWORD cKillRaids = 0;
1591 KMKBUILTINCTX FakeCtx = { "kSubmit/atexit", NULL };
1592 PKMKBUILTINCTX pCtx = &FakeCtx;
1593
1594 /*
1595 * Tell all the workers to exit by breaking the connection.
1596 */
1597 for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1598 kSubmitCloseConnectOnExitingWorker(pCtx, pWorker);
1599 for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1600 kSubmitCloseConnectOnExitingWorker(pCtx, pWorker);
1601
1602 /*
1603 * Wait a little while for them to stop.
1604 */
1605 Sleep(0);
1606 msStartTick = GetTickCount();
1607 for (;;)
1608 {
1609 /*
1610 * Collect handles of running processes.
1611 */
1612 PWORKERINSTANCE apWorkers[MAXIMUM_WAIT_OBJECTS];
1613 HANDLE ahHandles[MAXIMUM_WAIT_OBJECTS];
1614 DWORD cHandles = 0;
1615
1616 for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1617 if (pWorker->hProcess != INVALID_HANDLE_VALUE)
1618 {
1619 if (cHandles < MAXIMUM_WAIT_OBJECTS)
1620 {
1621 apWorkers[cHandles] = pWorker;
1622 ahHandles[cHandles] = pWorker->hProcess;
1623 }
1624 cHandles++;
1625 }
1626 for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1627 if (pWorker->hProcess != INVALID_HANDLE_VALUE)
1628 {
1629 if (cHandles < MAXIMUM_WAIT_OBJECTS)
1630 {
1631 apWorkers[cHandles] = pWorker;
1632 ahHandles[cHandles] = pWorker->hProcess;
1633 }
1634 cHandles++;
1635 }
1636 if (cHandles == 0)
1637 return;
1638
1639 /*
1640 * Wait for the processes.
1641 */
1642 for (;;)
1643 {
1644 DWORD cMsElapsed = GetTickCount() - msStartTick;
1645 DWORD dwWait = WaitForMultipleObjects(cHandles <= MAXIMUM_WAIT_OBJECTS ? cHandles : MAXIMUM_WAIT_OBJECTS,
1646 ahHandles, FALSE /*bWaitAll*/,
1647 cMsElapsed < 5000 ? 5000 - cMsElapsed + 16 : 16);
1648 if ( dwWait >= WAIT_OBJECT_0
1649 && dwWait <= WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS)
1650 {
1651 size_t idx = dwWait - WAIT_OBJECT_0;
1652 CloseHandle(apWorkers[idx]->hProcess);
1653 apWorkers[idx]->hProcess = INVALID_HANDLE_VALUE;
1654
1655 if (cHandles <= MAXIMUM_WAIT_OBJECTS)
1656 {
1657 /* Restart the wait with the worker removed, or quit if it was the last worker. */
1658 cHandles--;
1659 if (!cHandles)
1660 return;
1661 if (idx != cHandles)
1662 {
1663 apWorkers[idx] = apWorkers[cHandles];
1664 ahHandles[idx] = ahHandles[cHandles];
1665 }
1666 continue;
1667 }
1668 /* else: Reconstruct the wait array so we get maximum coverage. */
1669 }
1670 else if (dwWait == WAIT_TIMEOUT)
1671 {
1672 /* Terminate the whole bunch. */
1673 cKillRaids++;
1674 if (cKillRaids == 1 && getenv("KMK_KSUBMIT_NO_KILL") == NULL)
1675 {
1676 warnx(pCtx, "Killing %u lingering worker processe(s)!\n", cHandles);
1677 for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1678 if (pWorker->hProcess != INVALID_HANDLE_VALUE)
1679 TerminateProcess(pWorker->hProcess, WAIT_TIMEOUT);
1680 for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
1681 if (pWorker->hProcess != INVALID_HANDLE_VALUE)
1682 TerminateProcess(pWorker->hProcess, WAIT_TIMEOUT);
1683 }
1684 else
1685 {
1686 warnx(pCtx, "Giving up on the last %u worker processe(s). :-(\n", cHandles);
1687 return;
1688 }
1689 }
1690 else
1691 {
1692 /* Some kind of wait error. Could be a bad handle, check each and remove
1693 bad ones as well as completed ones. */
1694 size_t idx;
1695 warnx(pCtx, "WaitForMultipleObjects unexpectedly returned %#u (err=%u)\n",
1696 dwWait, GetLastError());
1697 for (idx = 0; idx < cHandles; idx++)
1698 {
1699 dwWait = WaitForSingleObject(ahHandles[idx], 0 /*ms*/);
1700 if (dwWait != WAIT_TIMEOUT)
1701 {
1702 CloseHandle(apWorkers[idx]->hProcess);
1703 apWorkers[idx]->hProcess = INVALID_HANDLE_VALUE;
1704 }
1705 }
1706 }
1707 break;
1708 } /* wait loop */
1709 } /* outer wait loop */
1710}
1711
1712
1713static int kmk_builtin_kSubmit_usage(PKMKBUILTINCTX pCtx, int fIsErr)
1714{
1715 kmk_builtin_ctx_printf(pCtx, fIsErr,
1716 "usage: %s [-Z|--zap-env] [-E|--set <var=val>] [-U|--unset <var=val>]\n"
1717 " [-A|--append <var=val>] [-D|--prepend <var=val>]\n"
1718 " [-s|--special-env <var=val>] [-C|--chdir <dir>]\n"
1719 " [--wcc-brain-damage] [--no-pch-caching]\n"
1720 " [-3|--32-bit] [-6|--64-bit] [-v] [--debug-dump-history]\n"
1721 " [-P|--post-cmd <cmd> [args]] -- <program> [args]\n"
1722 " or: %s --help\n"
1723 " or: %s --version\n"
1724 "\n"
1725 "Options:\n"
1726 " -Z, --zap-env, -i, --ignore-environment\n"
1727 " Zaps the environment. Position dependent.\n"
1728 " -E, --set <var>=[value]\n"
1729 " Sets an environment variable putenv fashion. Position dependent.\n"
1730 " -U, --unset <var>\n"
1731 " Removes an environment variable. Position dependent.\n"
1732 " -A, --append <var>=<value>\n"
1733 " Appends the given value to the environment variable.\n"
1734 " -D,--prepend <var>=<value>\n"
1735 " Prepends the given value to the environment variable.\n"
1736 " -s,--special-env <var>=<value>\n"
1737 " Same as --set, but flags the variable for further expansion\n"
1738 " within kWorker. Replacements:\n"
1739 " @@PROCESSOR_GROUP@@ - The processor group number.\n"
1740 " @@AUTHENTICATION_ID@@ - The authentication ID from the process token.\n"
1741 " @@PID@@ - The kWorker process ID.\n"
1742 " @@@@ - Escaped \"@@\".\n"
1743 " @@DEBUG_COUNTER@@ - An ever increasing counter (starts at zero).\n"
1744 " -C, --chdir <dir>\n"
1745 " Specifies the current directory for the program. Relative paths\n"
1746 " are relative to the previous -C option. Default is getcwd value.\n"
1747 " -3, --32-bit\n"
1748 " Selects a 32-bit kWorker process. Default: kmk bit count\n"
1749 " -6, --64-bit\n"
1750 " Selects a 64-bit kWorker process. Default: kmk bit count\n"
1751 " --wcc-brain-damage\n"
1752 " Works around wcc and wcc386 (Open Watcom) not following normal\n"
1753 " quoting conventions on Windows, OS/2, and DOS.\n"
1754 " --no-pch-caching\n"
1755 " Do not cache precompiled header files because they're being created.\n"
1756 " -v,--verbose\n"
1757 " More verbose execution.\n"
1758 " --debug-dump-history\n"
1759 " Dump the history as of the submitted command. Handy for debuging\n"
1760 " trouble caused by a previous job.\n"
1761 " -P|--post-cmd <cmd> ...\n"
1762 " For running a built-in command on the output, specifying the command\n"
1763 " and all it's parameters. Currently supported commands:\n"
1764 " kDepObj\n"
1765 " -V,--version\n"
1766 " Show the version number.\n"
1767 " -h,--help\n"
1768 " Show this usage information.\n"
1769 "\n"
1770 ,
1771 pCtx->pszProgName, pCtx->pszProgName, pCtx->pszProgName);
1772 return 2;
1773}
1774
1775
1776int kmk_builtin_kSubmit(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx, struct child *pChild, pid_t *pPidSpawned)
1777{
1778#ifdef KBUILD_OS_WINDOWS
1779 static int s_fInitialized = 0;
1780#endif
1781 int rcExit = 0;
1782 int iArg;
1783 unsigned cAllocatedEnvVars;
1784 unsigned cEnvVars;
1785 char **papszEnvVars;
1786 const char *pszExecutable = NULL;
1787 const char *pszSpecialEnv = NULL;
1788 int iPostCmd = argc;
1789 int cPostCmdArgs = 0;
1790 unsigned cBitsWorker = g_cArchBits;
1791 int fWatcomBrainDamage = 0;
1792 int fNoPchCaching = 0;
1793 int fDebugDumpHistory = 0;
1794 int cVerbosity = 0;
1795 size_t const cbCwdBuf = GET_PATH_MAX;
1796 PATH_VAR(szCwd);
1797
1798#ifdef KBUILD_OS_WINDOWS
1799 /*
1800 * First time thru we must perform some initializations.
1801 */
1802 if (s_fInitialized)
1803 { }
1804 else
1805 {
1806 MkWinChildInitCpuGroupAllocator(&g_SubmitProcessorGroupAllocator);
1807# if K_ARCH_BITS == 64
1808 MkWinChildInitCpuGroupAllocator(&g_SubmitProcessorGroupAllocator32);
1809# endif
1810 *(FARPROC *)&g_pfnSetThreadGroupAffinity = GetProcAddress(GetModuleHandleW(L"KERNEL32.DLL"), "SetThreadGroupAffinity");
1811 s_fInitialized = 1;
1812 }
1813#endif
1814
1815 /*
1816 * Create default program environment.
1817 *
1818 * Note! We only clean up the environment on successful return, assuming
1819 * make will stop after that.
1820 */
1821 if (getcwd_fs(szCwd, cbCwdBuf) != NULL)
1822 { /* likely */ }
1823 else
1824 return err(pCtx, 1, "getcwd_fs failed\n");
1825
1826 /* The environment starts out in read-only mode and will be duplicated if modified. */
1827 cAllocatedEnvVars = 0;
1828 papszEnvVars = envp;
1829 cEnvVars = 0;
1830 while (papszEnvVars[cEnvVars] != NULL)
1831 cEnvVars++;
1832
1833 /*
1834 * Parse the command line.
1835 */
1836 for (iArg = 1; iArg < argc; iArg++)
1837 {
1838 const char *pszArg = argv[iArg];
1839 if (*pszArg == '-')
1840 {
1841 char chOpt = *++pszArg;
1842 pszArg++;
1843 if (chOpt != '-')
1844 {
1845 if (chOpt != '\0')
1846 { /* likely */ }
1847 else
1848 {
1849 errx(pCtx, 1, "Incomplete option: '-'");
1850 return kmk_builtin_kSubmit_usage(pCtx, 1);
1851 }
1852 }
1853 else
1854 {
1855 /* '--' indicates where the bits to execute start. */
1856 if (*pszArg == '\0')
1857 {
1858 iArg++;
1859 break;
1860 }
1861
1862 if ( strcmp(pszArg, "wcc-brain-damage") == 0
1863 || strcmp(pszArg, "watcom-brain-damage") == 0)
1864 {
1865 fWatcomBrainDamage = 1;
1866 continue;
1867 }
1868
1869 if (strcmp(pszArg, "no-pch-caching") == 0)
1870 {
1871 fNoPchCaching = 1;
1872 continue;
1873 }
1874
1875 if (strcmp(pszArg, "debug-dump-history") == 0)
1876 {
1877 fDebugDumpHistory = 1;
1878 continue;
1879 }
1880
1881
1882 /* convert to short. */
1883 if (strcmp(pszArg, "help") == 0)
1884 chOpt = 'h';
1885 else if (strcmp(pszArg, "version") == 0)
1886 chOpt = 'V';
1887 else if (strcmp(pszArg, "set") == 0)
1888 chOpt = 'E';
1889 else if (strcmp(pszArg, "append") == 0)
1890 chOpt = 'A';
1891 else if (strcmp(pszArg, "prepend") == 0)
1892 chOpt = 'D';
1893 else if (strcmp(pszArg, "unset") == 0)
1894 chOpt = 'U';
1895 else if ( strcmp(pszArg, "zap-env") == 0
1896 || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ )
1897 chOpt = 'Z';
1898 else if (strcmp(pszArg, "chdir") == 0)
1899 chOpt = 'C';
1900 else if (strcmp(pszArg, "set-special") == 0)
1901 chOpt = 's';
1902 else if (strcmp(pszArg, "post-cmd") == 0)
1903 chOpt = 'P';
1904 else if (strcmp(pszArg, "32-bit") == 0)
1905 chOpt = '3';
1906 else if (strcmp(pszArg, "64-bit") == 0)
1907 chOpt = '6';
1908 else if (strcmp(pszArg, "verbose") == 0)
1909 chOpt = 'v';
1910 else if (strcmp(pszArg, "executable") == 0)
1911 chOpt = 'e';
1912 else
1913 {
1914 errx(pCtx, 2, "Unknown option: '%s'", pszArg - 2);
1915 return kmk_builtin_kSubmit_usage(pCtx, 1);
1916 }
1917 pszArg = "";
1918 }
1919
1920 do
1921 {
1922 /* Get option value first, if the option takes one. */
1923 const char *pszValue = NULL;
1924 switch (chOpt)
1925 {
1926 case 'A':
1927 case 'C':
1928 case 'E':
1929 case 'U':
1930 case 'D':
1931 case 'e':
1932 case 's':
1933 if (*pszArg != '\0')
1934 pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
1935 else if (++iArg < argc)
1936 pszValue = argv[iArg];
1937 else
1938 {
1939 errx(pCtx, 1, "Option -%c requires a value!", chOpt);
1940 return kmk_builtin_kSubmit_usage(pCtx, 1);
1941 }
1942 break;
1943 }
1944
1945 switch (chOpt)
1946 {
1947 case 'Z':
1948 case 'i': /* GNU env compatibility. */
1949 rcExit = kBuiltinOptEnvZap(pCtx, &papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity);
1950 if (rcExit == 0)
1951 break;
1952 return rcExit;
1953
1954 case 'E':
1955 rcExit = kBuiltinOptEnvSet(pCtx, &papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1956 if (rcExit == 0)
1957 break;
1958 return rcExit;
1959
1960 case 'A':
1961 rcExit = kBuiltinOptEnvAppend(pCtx, &papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1962 if (rcExit == 0)
1963 break;
1964 return rcExit;
1965
1966 case 'D':
1967 rcExit = kBuiltinOptEnvPrepend(pCtx, &papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1968 if (rcExit == 0)
1969 break;
1970 return rcExit;
1971
1972 case 'U':
1973 rcExit = kBuiltinOptEnvUnset(pCtx, &papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1974 if (rcExit == 0)
1975 break;
1976 return rcExit;
1977
1978 case 'C':
1979 rcExit = kBuiltinOptChDir(pCtx, szCwd, cbCwdBuf, pszValue);
1980 if (rcExit == 0)
1981 break;
1982 return rcExit;
1983
1984 case 's':
1985 if (pszSpecialEnv)
1986 return errx(pCtx, 1, "The -s option can only be used once!");
1987 pszSpecialEnv = pszValue;
1988 rcExit = kBuiltinOptEnvSet(pCtx, &papszEnvVars, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
1989 if (rcExit == 0)
1990 break;
1991 return rcExit;
1992
1993 case 'P':
1994 if (cPostCmdArgs > 0)
1995 return errx(pCtx, 1, "The -P option can only be used once!");
1996 if (*pszArg != '\0')
1997 return errx(pCtx, 1, "The cmd part of the -P needs to be a separate argument!");
1998 iPostCmd = ++iArg;
1999 if (iArg >= argc)
2000 return errx(pCtx, 1, "The -P option requires a command following it!");
2001 while (iArg < argc && strcmp(argv[iArg], "--") != 0)
2002 iArg++;
2003 cPostCmdArgs = iArg - iPostCmd;
2004 iArg--;
2005 break;
2006
2007 case '3':
2008 cBitsWorker = 32;
2009 break;
2010
2011 case '6':
2012 cBitsWorker = 64;
2013 break;
2014
2015 case 'e':
2016 pszExecutable = pszValue;
2017 break;
2018
2019 case 'v':
2020 cVerbosity++;
2021 break;
2022
2023 case 'h':
2024 kmk_builtin_kSubmit_usage(pCtx, 0);
2025 kBuiltinOptEnvCleanup(&papszEnvVars, cEnvVars, &cAllocatedEnvVars);
2026 return 0;
2027
2028 case 'V':
2029 kBuiltinOptEnvCleanup(&papszEnvVars, cEnvVars, &cAllocatedEnvVars);
2030 return kbuild_version(argv[0]);
2031 }
2032 } while ((chOpt = *pszArg++) != '\0');
2033 }
2034 else
2035 {
2036 errx(pCtx, 1, "Unknown argument: '%s'", pszArg);
2037 return kmk_builtin_kSubmit_usage(pCtx, 1);
2038 }
2039 }
2040
2041 /*
2042 * Check that we've got something to execute.
2043 */
2044 if (iArg < argc)
2045 {
2046 uint32_t cbMsg;
2047 void *pvMsg = kSubmitComposeJobMessage(pszExecutable, &argv[iArg], papszEnvVars, szCwd,
2048 fWatcomBrainDamage, fNoPchCaching, pszSpecialEnv,
2049 &argv[iPostCmd], cPostCmdArgs, &cbMsg);
2050 PWORKERINSTANCE pWorker = kSubmitSelectWorkSpawnNewIfNecessary(pCtx, cBitsWorker, cVerbosity);
2051 if (pWorker)
2052 {
2053 /* Before we send off the job, we should dump pending output, since
2054 the kWorker process currently does not coordinate its output with
2055 the output.c mechanics. */
2056#ifdef CONFIG_NEW_WIN_CHILDREN
2057 if (pCtx->pOut && !pWorker->pStdOut)
2058#else
2059 if (pCtx->pOut)
2060#endif
2061 output_dump(pCtx->pOut);
2062 rcExit = kSubmitSendJobMessage(pCtx, pWorker, pvMsg, cbMsg, 0 /*fNoRespawning*/, cVerbosity);
2063 if (rcExit == 0)
2064 {
2065 pvMsg = kSubmitUpdateHistory(pWorker, pvMsg, cbMsg);
2066 if (fDebugDumpHistory)
2067 kSubmitDumpHistory(pCtx, pWorker);
2068 rcExit = kSubmitMarkActive(pCtx, pWorker, cVerbosity, pChild, pPidSpawned);
2069 }
2070
2071 if (!g_fAtExitRegistered)
2072 if (atexit(kSubmitAtExitCallback) == 0)
2073 g_fAtExitRegistered = 1;
2074 }
2075 else
2076 rcExit = 1;
2077 free(pvMsg);
2078 }
2079 else
2080 {
2081 errx(pCtx, 1, "Nothing to executed!");
2082 rcExit = kmk_builtin_kSubmit_usage(pCtx, 1);
2083 }
2084
2085 kBuiltinOptEnvCleanup(&papszEnvVars, cEnvVars, &cAllocatedEnvVars);
2086 return rcExit;
2087}
2088
Note: See TracBrowser for help on using the repository browser.

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