VirtualBox

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

Last change on this file since 2856 was 2846, checked in by bird, 9 years ago

fixes

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