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

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

Included kDepObj in kWorker as a post execution option.

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