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

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

updates

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