VirtualBox

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

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

kmk/kSubmit: Only dump history when asked to.

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