VirtualBox

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

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

kmk/win: Windows kmk now defaults to --output-sync=target. Fixed output sync on windows in nested make processes that got busted by winchildren and it's no inheritance policy.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette