source: trunk/src/kmk/kmkbuiltin/kSubmit.c@ 3281

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

kmk: Fixed a couple of bugs in mkWinChildcareWorkerThreadHandleSubmit that would cause it to lock up.

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