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

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

kmk/kSubmit,kWorker: Pass the pipe handle via RTL_USER_PROCESS_PARAMETERS::StandardInput instead of command line & inheritance. Replaces _spawnve with CreateProcessW and no automatic handle inherting, using nt_child_inject_standard_handles to do the job. The priority parameter is also obsolete now as kSubmit can set it.

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