VirtualBox

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

Last change on this file since 3387 was 3363, checked in by bird, 4 years ago

kSubmit: Quoting fixes for the history dump.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use