Changeset 2912 for trunk/src/kmk/kmkbuiltin/common-env-and-cwd-opt.c
- Timestamp:
- Sep 14, 2016, 3:36:15 PM (9 years ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kmk/kmkbuiltin/common-env-and-cwd-opt.c
r2899 r2912 1 1 /* $Id$ */ 2 2 /** @file 3 * kMk Builtin command - submit job to a kWorker.3 * kMk Builtin command - Commmon environment and CWD option handling code. 4 4 */ 5 5 … … 27 27 * Header Files * 28 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" 29 #include "config.h" 36 30 #include <stdio.h> 37 31 #include <stdlib.h> 38 32 #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" 33 57 34 #include "kmkbuiltin.h" 58 35 #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 *********************************************************************************************************************************/71 typedef struct WORKERINSTANCE *PWORKERINSTANCE;72 typedef struct WORKERINSTANCE73 {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 union85 {86 struct87 {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_WINDOWS100 /** 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 #else107 /** The socket descriptor we use to talk to the kWorker process. */108 int fdSocket;109 #endif110 111 /** What it's busy with. NULL if idle. */112 struct child *pBusyWith;113 } WORKERINSTANCE;114 115 116 typedef struct WORKERLIST117 {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;125 typedef WORKERLIST *PWORKERLIST;126 127 128 /*********************************************************************************************************************************129 * Global Variables *130 *********************************************************************************************************************************/131 /** List of idle worker.*/132 static WORKERLIST g_IdleList;133 /** List of busy workers. */134 static WORKERLIST g_BusyList;135 /** PID hash table for the workers.136 * @sa KWORKER_PID_HASH() */137 static PWORKERINSTANCE g_apPidHash[61];138 139 #ifdef KBUILD_OS_WINDOWS140 /** For naming the pipes.141 * Also indicates how many worker instances we've spawned. */142 static unsigned g_uWorkerSeqNo = 0;143 #endif144 /** Set if we've registred the atexit handler already. */145 static int g_fAtExitRegistered = 0;146 147 /** @var g_cArchBits148 * The bit count of the architecture this binary is compiled for. */149 /** @var g_szArch150 * The name of the architecture this binary is compiled for. */151 /** @var g_cArchBits152 * The bit count of the alternative architecture. */153 /** @var g_szAltArch154 * The name of the alternative architecture. */155 #if defined(KBUILD_ARCH_AMD64)156 static unsigned g_cArchBits = 64;157 static char const g_szArch[] = "amd64";158 static unsigned g_cAltArchBits = 32;159 static char const g_szAltArch[] = "x86";160 #elif defined(KBUILD_ARCH_X86)161 static unsigned g_cArchBits = 32;162 static char const g_szArch[] = "x86";163 static unsigned g_cAltArchBits = 64;164 static char const g_szAltArch[] = "amd64";165 #else166 # error "Port me!"167 #endif168 169 170 171 /**172 * Unlinks a worker instance from a list.173 *174 * @param pList The list.175 * @param pWorker The worker.176 */177 static 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 else188 {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 else199 {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 */221 static 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 else236 {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 */254 static 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 else260 {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 */278 static 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 linking292 * (as we might be reusing an existing worker293 * instance because a worker shut itself down due294 * to high resource leak level).295 * @param cVerbosity The verbosity level.296 */297 static 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 #else302 static const char s_szWorkerName[] = "kWorker";303 #endif304 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) - 1310 struct variable *pVarVolatile = lookup_variable(TUPLE("PATH_OUT"));311 if (pVarVolatile)312 { /* likely */ }313 else314 {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_cArchBits324 ? cchBinPath + 1 + sizeof(s_szWorkerName) <= cbExecutableBuf325 : cchBinPath + 1 - sizeof(g_szArch) + sizeof(g_szAltArch) + sizeof(s_szWorkerName) <= cbExecutableBuf )326 {327 #ifdef KBUILD_OS_WINDOWS328 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 #else333 int aiPair[2] = { -1, -1 };334 #endif335 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_WINDOWS355 /*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 NULL390 };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 else410 errx(1, "CreateEventW failed: %u", GetLastError());411 CloseHandle(pWorker->hPipe);412 pWorker->hPipe = INVALID_HANDLE_VALUE;413 }414 else415 errx(1, "Opening named pipe failed: %u", GetLastError());416 CloseHandle(hWorkerPipe);417 }418 else419 errx(1, "CreateNamedPipeW failed: %u", GetLastError());420 421 #else422 /*423 * Create a socket pair.424 */425 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, aiPair) == 0)426 {427 pWorker->fdSocket = aiPair[1];428 }429 else430 err(1, "socketpair");431 #endif432 }433 else434 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 */447 static int kSubmitRespawnWorker(PWORKERINSTANCE pWorker, int cVerbosity)448 {449 /*450 * Clean up after the old worker.451 */452 #ifdef KBUILD_OS_WINDOWS453 DWORD rcWait;454 455 /* Close the pipe handle first, breaking the pipe in case it's not already456 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 before469 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 #else484 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 #endif499 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 */531 static 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 the578 * command. Zero if no post command scheduled.579 * @param pcbMsg Where to return the message length.580 */581 static 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 if693 * necessary.694 *695 * @returns 0 on success, non-zero on failure.696 *697 * @param pWorker The work to send the request to. The worker is698 * on the idle list.699 * @param pvMsg The message to send.700 * @param cbMsg The size of the message.701 * @param fNoRespawning Set if702 * @param cVerbosity The verbosity level.703 */704 static 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_WINDOWS712 if (pWorker->hPipe == INVALID_HANDLE_VALUE)713 #else714 if (pWorker->fdSocket == -1)715 #endif716 {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_WINDOWS738 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_PIPE752 && dwErr != ERROR_NO_DATA)753 || cRetries <= 0)754 return errx(1, "Error writing to worker: %u", dwErr);755 #else756 ssize_t cbWritten757 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 != EPIPE767 && errno != ENOTCONN768 && errno != ECONNRESET))769 || cRetries <= 0)770 return err(1, "Error writing to worker");771 # error "later"772 #endif773 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, it789 * 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. The792 * kSubmitSendJobMessage function will see this a trigger a respawn the next793 * time the worker is engaged. This will usually mean there's a little delay in794 * which the process can terminate without us having to actively wait for it.795 *796 * @param pWorker The worker instance.797 */798 static void kSubmitCloseConnectOnExitingWorker(PWORKERINSTANCE pWorker)799 {800 #ifdef KBUILD_OS_WINDOWS801 if (!CloseHandle(pWorker->hPipe))802 warnx("CloseHandle(pWorker->hPipe): %u", GetLastError());803 pWorker->hPipe = INVALID_HANDLE_VALUE;804 #else805 if (close(pWorker->fdSocket) != 0)806 warn("close(pWorker->fdSocket)");807 pWorker->fdSocket = -1;808 #endif809 }810 811 812 #ifdef KBUILD_OS_WINDOWS813 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 */822 static 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 else829 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 by850 * @returns 0 if we got the whole result, -1 if I/O is pending, and windows last851 * error on ReadFile failure.852 * @param pWorker The worker instance.853 */854 static 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 telling892 * 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 is899 * running, otherwise the worker is already done900 * and we've returned the exit code of the job.901 */902 static int kSubmitMarkActive(PWORKERINSTANCE pWorker, int cVerbosity, struct child *pChild, pid_t *pPidSpawned)903 {904 #ifdef KBUILD_OS_WINDOWS905 int rc;906 #endif907 908 pWorker->cbResultRead = 0;909 910 #ifdef KBUILD_OS_WINDOWS911 /*912 * Setup the async result read on windows. If we're slow and the worker913 * very fast, this may actually get the result immediately.914 */915 l_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 else922 {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 else930 {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 #endif938 939 /*940 * Mark it busy and move it to the active instance.941 */942 pWorker->pBusyWith = pChild;943 #ifndef KBUILD_OS_WINDOWS944 *pPidSpawned = pWorker->pid;945 #endif946 947 kSubmitListUnlink(&g_IdleList, pWorker);948 kSubmitListAppend(&g_BusyList, pWorker);949 return 0;950 }951 952 953 #ifdef KBUILD_OS_WINDOWS954 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 */965 int 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 here971 * 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 else990 {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 1015 int 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 */1026 void 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 */1039 static 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_01100 && 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 else1136 {1137 fprintf(stderr, "kmk/kSubmit: Giving up on the last %u worker processe(s). :-(\n", cHandles);1138 break;1139 }1140 }1141 else1142 {1143 /* Some kind of wait error. Could be a bad handle, check each and remove1144 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 36 1163 37 … … 1183 57 * @param pszValue The var=value string to apply. 1184 58 */ 1185 static int kSubmitOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, 1186 int cVerbosity, const char *pszValue) 59 int kBuiltinOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, int cVerbosity, const char *pszValue) 1187 60 { 1188 61 const char *pszEqual = strchr(pszValue, '='); … … 1200 73 { 1201 74 if (cVerbosity > 0) 1202 fprintf(stderr, "kSubmit: replacing '%s' with '%s'\n", papszEnv[iEnvVar], pszValue);75 warnx("replacing '%s' with '%s'", papszEnv[iEnvVar], pszValue); 1203 76 free(papszEnv[iEnvVar]); 1204 papszEnv[iEnvVar] = xstrdup(pszValue); 77 papszEnv[iEnvVar] = strdup(pszValue); 78 if (!papszEnv[iEnvVar]) 79 return errx(1, "out of memory!"); 1205 80 break; 1206 81 } … … 1212 87 { 1213 88 *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; 89 papszEnv = (char **)realloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0])); 90 if (!papszEnv) 91 return errx(1, "out of memory!"); 92 *ppapszEnv = papszEnv; 93 } 94 papszEnv[cEnvVars] = strdup(pszValue); 95 if (!papszEnv[cEnvVars]) 96 return errx(1, "out of memory!"); 97 papszEnv[++cEnvVars] = NULL; 1218 98 *pcEnvVars = cEnvVars; 1219 99 if (cVerbosity > 0) 1220 fprintf(stderr, "kSubmit: added '%s'\n", papszEnv[iEnvVar]);100 warnx("added '%s'", papszEnv[iEnvVar]); 1221 101 } 1222 102 else … … 1228 108 { 1229 109 if (cVerbosity > 0) 1230 fprintf(stderr, "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);110 warnx("removing duplicate '%s'", papszEnv[iEnvVar]); 1231 111 free(papszEnv[iEnvVar]); 1232 112 cEnvVars--; … … 1255 135 * @param pszVarToRemove The name of the variable to remove. 1256 136 */ 1257 static int kSubmitOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove)137 int kBuiltinOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove) 1258 138 { 1259 139 if (strchr(pszVarToRemove, '=') == NULL) … … 1269 149 { 1270 150 if (cVerbosity > 0) 1271 fprintf(stderr, !cRemoved ? "kSubmit: removing '%s'\n" 1272 : "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]); 151 warnx(!cRemoved ? "removing '%s'" : "removing duplicate '%s'", papszEnv[iEnvVar]); 1273 152 free(papszEnv[iEnvVar]); 1274 153 cEnvVars--; … … 1282 161 1283 162 if (cVerbosity > 0 && !cRemoved) 1284 fprintf(stderr, "kSubmit: not found '%s'\n", pszVarToRemove);163 warnx("not found '%s'", pszVarToRemove); 1285 164 } 1286 165 else … … 1300 179 * @param pszValue The --chdir value to apply. 1301 180 */ 1302 static int kSubmitOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue)181 int kBuiltinOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue) 1303 182 { 1304 183 size_t cchNewCwd = strlen(pszValue); … … 1360 239 } 1361 240 1362 1363 static 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 1407 int 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 else1433 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 else1458 {1459 errx(1, "Incomplete option: '-'");1460 return usage(stderr, argv[0]);1461 }1462 }1463 else1464 {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") == 01473 || 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") == 01489 || 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 else1504 {1505 errx(1, "Unknown option: '%s'", pszArg - 2);1506 return usage(stderr, argv[0]);1507 }1508 pszArg = "";1509 }1510 1511 do1512 {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 else1526 {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 else1602 {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 else1631 rcExit = 1;1632 free(pvMsg);1633 }1634 else1635 {1636 errx(1, "Nothing to executed!");1637 rcExit = usage(stderr, argv[0]);1638 }1639 1640 return rcExit;1641 }1642 1643 1644
Note:
See TracChangeset
for help on using the changeset viewer.