VirtualBox

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

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

kmk: Merged in changes from GNU make 4.2.1 (2e55f5e4abdc0e38c1d64be703b446695e70b3b6 / https://git.savannah.gnu.org/git/make.git).

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