VirtualBox

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

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

kmk,kWorker: Catch output from kWorker processes when --output-sync isn't 'none'.

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