VirtualBox

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

Last change on this file since 3642 was 3642, checked in by bird, 6 months ago

kmk/kSubmit: win.arm64 build fix.

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