Changeset 2841
- Timestamp:
- Aug 26, 2016, 10:04:50 PM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kmk/kmkbuiltin/submit.c
r2840 r2841 38 38 #include <string.h> 39 39 #include <errno.h> 40 #include <assert.h> 40 41 #ifdef HAVE_ALLOCA_H 41 42 # include <alloca.h> … … 50 51 #endif 51 52 53 #include "kbuild.h" 52 54 #include "kmkbuiltin.h" 53 55 #include "err.h" 54 56 55 #ifdef __OS2__ 56 # define INCL_BASE 57 # include <os2.h> 58 # ifndef LIBPATHSTRICT 59 # define LIBPATHSTRICT 3 60 # endif61 #endif 57 58 /********************************************************************************************************************************* 59 * Defined Constants And Macros * 60 *********************************************************************************************************************************/ 61 /** Hashes a pid. */ 62 #define KWORKER_PID_HASH(a_pid) ((size_t)(a_pid) % 61) 63 62 64 63 65 /********************************************************************************************************************************* … … 71 73 /** Pointer to the previous worker instance. */ 72 74 PWORKERINSTANCE pPrev; 75 /** Pointer to the next worker with the same pid hash slot. */ 76 PWORKERINSTANCE pNextPidHash; 73 77 /** 32 or 64. */ 74 78 unsigned cBits; 79 /** The process ID of the kWorker process. */ 80 pid_t pid; 81 #ifdef KBUILD_OS_WINDOWS 75 82 /** The process handle. */ 76 83 HANDLE hProcess; 77 84 /** The bi-directional pipe we use to talk to the kWorker process. */ 85 HANDLE hPipe; 86 /** For overlapped read (have valid event semaphore). */ 87 OVERLAPPED OverlappedRead; 88 /** The 32-bit exit code read bufffer. */ 89 uint32_t u32ReadResult; 90 #else 91 /** The socket descriptor we use to talk to the kWorker process. */ 92 int fdSocket; 93 #endif 94 95 /** What it's busy with. NULL if idle. */ 96 struct child *pBusyWith; 78 97 } WORKERINSTANCE; 98 99 100 typedef struct WORKERLIST 101 { 102 /** The head of the list. NULL if empty. */ 103 PWORKERINSTANCE pHead; 104 /** The tail of the list. NULL if empty. */ 105 PWORKERINSTANCE pTail; 106 /** Number of list entries. */ 107 size_t cEntries; 108 } WORKERLIST; 109 typedef WORKERLIST *PWORKERLIST; 79 110 80 111 … … 82 113 * Global Variables * 83 114 *********************************************************************************************************************************/ 84 static PWORKERINSTANCE g_pIdleHead; 85 static PWORKERINSTANCE g_pIdleTail; 86 115 /** List of idle worker.*/ 116 static WORKERLIST g_IdleList; 117 /** List of busy workers. */ 118 static WORKERLIST g_BusyList; 119 /** PID hash table for the workers. 120 * @sa KWORKER_PID_HASH() */ 121 static PWORKERINSTANCE g_apPidHash[61]; 122 123 #ifdef KBUILD_OS_WINDOWS 124 /** For naming the pipes. 125 * Also indicates how many worker instances we've spawned. */ 126 static unsigned g_uWorkerSeqNo = 0; 127 #endif 128 129 /** @var g_cArchBits 130 * The bit count of the architecture this binary is compiled for. */ 131 /** @var g_szArch 132 * The name of the architecture this binary is compiled for. */ 133 /** @var g_cArchBits 134 * The bit count of the alternative architecture. */ 135 /** @var g_szAltArch 136 * The name of the alternative architecture. */ 137 #if defined(KBUILD_ARCH_AMD64) 138 static unsigned g_cArchBits = 64; 139 static char const g_szArch[] = "amd64"; 140 static unsigned g_cAltArchBits = 32; 141 static char const g_szAltArch[] = "x86"; 142 #elif defined(KBUILD_ARCH_X86) 143 static unsigned g_cArchBits = 32; 144 static char const g_szArch[] = "x86"; 145 static unsigned g_cAltArchBits = 64; 146 static char const g_szAltArch[] = "amd64"; 147 #else 148 # error "Port me!" 149 #endif 150 151 152 /** 153 * Unlinks a worker instance from a list. 154 * 155 * @param pList The list. 156 * @param pWorker The worker. 157 */ 158 static void kSubmitListUnlink(PWORKERLIST pList, PWORKERINSTANCE pWorker) 159 { 160 PWORKERINSTANCE pNext = pWorker->pNext; 161 PWORKERINSTANCE pPrev = pWorker->pPrev; 162 163 if (pNext) 164 { 165 assert(pNext->pPrev == pWorker); 166 pNext->pPrev = pPrev; 167 } 168 else 169 { 170 assert(pList->pHead == pWorker); 171 pList->pHead = pPrev; 172 } 173 174 if (pPrev) 175 { 176 assert(pPrev->pNext == pWorker); 177 pPrev->pNext = pNext; 178 } 179 else 180 { 181 assert(pList->pTail == pWorker); 182 pList->pTail = pNext; 183 } 184 185 assert(pList->cEntries > 0); 186 pList->cEntries--; 187 188 pWorker->pNext = NULL; 189 pWorker->pPrev = NULL; 190 } 191 192 193 /** 194 * Appends a worker instance to the tail of a list. 195 * 196 * @param pList The list. 197 * @param pWorker The worker. 198 */ 199 static void kSubmitListAppend(PWORKERLIST pList, PWORKERINSTANCE pWorker) 200 { 201 PWORKERINSTANCE pTail = pList->pTail; 202 203 assert(pTail != pWorker); 204 assert(pList->pHead != pWorker); 205 206 pWorker->pNext = NULL; 207 pWorker->pPrev = pTail; 208 if (pTail != NULL) 209 { 210 assert(pTail->pNext == NULL); 211 pTail->pNext = pWorker; 212 } 213 else 214 { 215 assert(pList->pHead == NULL); 216 pList->pHead = pWorker; 217 pList->pTail = pWorker; 218 } 219 220 pList->cEntries++; 221 } 222 223 224 /** 225 * Looks up a worker by its process ID. 226 * 227 * @returns Pointer to the worker instance if found. NULL if not. 228 * @param pid The process ID of the worker. 229 */ 230 static PWORKERINSTANCE kSubmitFindWorkerByPid(pid_t pid) 231 { 232 PWORKERINSTANCE pWorker = g_apPidHash[KWORKER_PID_HASH(pid)]; 233 while (pWorker && pWorker->pid != pid) 234 pWorker = pWorker->pNextPidHash; 235 return pWorker; 236 } 237 238 239 /** 240 * Creates a new worker process. 241 * 242 * @returns 0 on success, non-zero value on failure. 243 * @param pWorker The worker structure. Caller does the linking 244 * (as we might be reusing an existing worker 245 * instance because a worker shut itself down due 246 * to high resource leak level). 247 * @param cVerbosity The verbosity level. 248 */ 249 static int kSubmitSpawnWorker(PWORKERINSTANCE pWorker, int cVerbosity) 250 { 251 #if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2) 252 static const char s_szWorkerName[] = "kWorker.exe"; 253 #else 254 static const char s_szWorkerName[] = "kWorker"; 255 #endif 256 const char *pszBinPath = get_kbuild_bin_path(); 257 size_t const cchBinPath = strlen(pszBinPath); 258 size_t cchExectuable; 259 size_t const cbExecutableBuf = GET_PATH_MAX; 260 PATH_VAR(szExecutable); 261 262 /* 263 * Construct the executable path. 264 */ 265 if ( pWorker->cBits == g_cArchBits 266 ? cchBinPath + 1 + sizeof(s_szWorkerName) <= cbExecutableBuf 267 : cchBinPath + 1 - sizeof(g_szArch) + sizeof(g_szAltArch) + sizeof(s_szWorkerName) <= cbExecutableBuf ) 268 { 269 #ifdef KBUILD_OS_WINDOWS 270 static DWORD s_fDenyRemoteClients = ~(DWORD)0; 271 wchar_t wszPipeName[64]; 272 HANDLE hWorkerPipe; 273 SECURITY_ATTRIBUTES SecAttrs = { /*nLength:*/ sizeof(SecAttrs), /*pAttrs:*/ NULL, /*bInheritHandle:*/ TRUE }; 274 #else 275 int aiPair[2] = { -1, -1 }; 276 #endif 277 278 memcpy(szExecutable, pszBinPath, cchBinPath); 279 cchExectuable = cchBinPath; 280 281 /* Replace the arch bin directory extension with the alternative one if requested. */ 282 if (pWorker->cBits != g_cArchBits) 283 { 284 if ( cchBinPath < sizeof(g_szArch) 285 || memcmp(&szExecutable[cchBinPath - sizeof(g_szArch) + 1], g_szArch, sizeof(g_szArch) - 1) != 0) 286 return errx(1, "KBUILD_BIN_PATH does not end with main architecture (%s) as expected: %s", pszBinPath, g_szArch); 287 cchExectuable -= sizeof(g_szArch) - 1; 288 memcpy(&szExecutable[cchExectuable], g_szAltArch, sizeof(g_szAltArch) - 1); 289 cchExectuable += sizeof(g_szAltArch) - 1; 290 } 291 292 /* Append a slash and the worker name. */ 293 szExecutable[cchExectuable++] = '/'; 294 memcpy(&szExecutable[cchExectuable], s_szWorkerName, sizeof(s_szWorkerName)); 295 296 #ifdef KBUILD_OS_WINDOWS 297 /* 298 * Create the bi-directional pipe. Worker end is marked inheritable, our end is not. 299 */ 300 if (s_fDenyRemoteClients == ~(DWORD)0) 301 s_fDenyRemoteClients = GetVersion() >= 0x60000 ? PIPE_REJECT_REMOTE_CLIENTS : 0; 302 _snwprintf(wszPipeName, sizeof(wszPipeName), L"\\\\.\\pipe\\kmk-%u-kWorker-%u", getpid(), g_uWorkerSeqNo++); 303 hWorkerPipe = CreateNamedPipeW(wszPipeName, 304 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE /* win2k sp2+ */, 305 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | s_fDenyRemoteClients, 306 1 /* cMaxInstances */, 307 64 /*cbOutBuffer*/, 308 65536 /*cbInBuffer*/, 309 0 /*cMsDefaultTimeout -> 50ms*/, 310 &SecAttrs /* inherit */); 311 if (hWorkerPipe != INVALID_HANDLE_VALUE) 312 { 313 pWorker->hPipe = CreateFileW(wszPipeName, 314 GENERIC_READ | GENERIC_WRITE, 315 0 /* dwShareMode - no sharing */, 316 NULL /*pSecAttr - no inherit */, 317 OPEN_EXISTING, 318 FILE_FLAG_OVERLAPPED, 319 NULL /*hTemplate*/); 320 if (pWorker->hPipe != INVALID_HANDLE_VALUE) 321 { 322 pWorker->OverlappedRead.hEvent = CreateEventW(NULL /*pSecAttrs - no inherit*/, TRUE /*bManualReset*/, 323 TRUE /*bInitialState*/, NULL /*pwszName*/); 324 if (pWorker->OverlappedRead.hEvent != NULL) 325 { 326 char szHandleArg[16]; 327 const char *apszArgs[4] = { szExecutable, "--pipe", szHandleArg, NULL }; 328 _snprintf(szHandleArg, sizeof(szHandleArg), "%p", hWorkerPipe); 329 330 /* 331 * Create the worker process. 332 */ 333 pWorker->hProcess = (HANDLE) _spawnve(_P_NOWAIT, szExecutable, apszArgs, environ); 334 if ((intptr_t)pWorker->hProcess != -1) 335 { 336 CloseHandle(hWorkerPipe); 337 pWorker->pid = GetProcessId(pWorker->hProcess); 338 if (cVerbosity > 0) 339 fprintf(stderr, "kSubmit: created %d bit worker %d\n", pWorker->cBits, pWorker->pid); 340 return 0; 341 } 342 err(1, "_spawnve(,%s,,)", szExecutable); 343 CloseHandle(pWorker->OverlappedRead.hEvent); 344 pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE; 345 } 346 else 347 errx(1, "CreateEventW failed: %u", GetLastError()); 348 CloseHandle(pWorker->hPipe); 349 pWorker->hPipe = INVALID_HANDLE_VALUE; 350 } 351 else 352 errx(1, "Opening named pipe failed: %u", GetLastError()); 353 CloseHandle(hWorkerPipe); 354 } 355 else 356 errx(1, "CreateNamedPipeW failed: %u", GetLastError()); 357 358 #else 359 /* 360 * Create a socket pair. 361 */ 362 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, aiPair) == 0) 363 { 364 pWorker->fdSocket = aiPair[1]; 365 } 366 else 367 err(1, "socketpair"); 368 #endif 369 } 370 else 371 errx(1, "KBUILD_BIN_PATH is too long"); 372 return -1; 373 } 374 375 376 /** 377 * Selects an idle worker or spawns a new one. 378 * 379 * @returns Pointer to the selected worker instance. NULL on error. 380 * @param pWorker The idle worker instance to respawn. 381 * On failure this will be freed! 382 * @param cBitsWorker The worker bitness - 64 or 32. 383 */ 384 static int kSubmitRespawnWorker(PWORKERINSTANCE pWorker, int cVerbosity) 385 { 386 size_t idxHash; 387 388 /* 389 * Clean up after the old worker. 390 */ 391 #ifdef KBUILD_OS_WINDOWS 392 DWORD rcWait; 393 394 /* Close the pipe handle first, breaking the pipe in case it's not already 395 busted up. Close the event semaphore too before waiting for the process. */ 396 if (!CloseHandle(pWorker->hPipe)) 397 warnx("CloseHandle(pWorker->hPipe): %u", GetLastError()); 398 pWorker->hPipe = INVALID_HANDLE_VALUE; 399 400 if (!CloseHandle(pWorker->OverlappedRead.hEvent)) 401 warnx("CloseHandle(pWorker->OverlappedRead.hEvent): %u", GetLastError()); 402 pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE; 403 404 /* It's probably shutdown already, if not give it 10 milliseconds before 405 we terminate it forcefully. */ 406 rcWait = WaitForSingleObject(pWorker->hProcess, 10); 407 if (rcWait != WAIT_OBJECT_0) 408 { 409 BOOL fRc = TerminateProcess(pWorker->hProcess, 127); 410 rcWait = WaitForSingleObject(pWorker->hProcess, 100); 411 if (rcWait != WAIT_OBJECT_0) 412 warnx("WaitForSingleObject returns %u (and TerminateProcess %d)", rcWait, fRc); 413 } 414 415 if (!CloseHandle(pWorker->hProcess)) 416 warnx("CloseHandle(pWorker->hProcess): %u", GetLastError()); 417 pWorker->hProcess = INVALID_HANDLE_VALUE; 418 419 #else 420 pid_t pidWait; 421 int rc; 422 423 if (close(pWorker->fdSocket) != 0) 424 warn("close(pWorker->fdSocket)"); 425 pWorker->fdSocket = -1; 426 427 kill(pWorker->pid, SIGTERM); 428 pidWait = waitpid(pWorker->pid, &rc, 0); 429 if (pidWait != pWorker->pid) 430 warn("waitpid(pWorker->pid,,0)"); 431 #endif 432 433 /* 434 * Unlink it from the hash table. 435 */ 436 idxHash = KWORKER_PID_HASH(pWorker->pid); 437 if (g_apPidHash[idxHash] == pWorker) 438 g_apPidHash[idxHash] = pWorker->pNext; 439 else 440 { 441 PWORKERINSTANCE pPrev = g_apPidHash[idxHash]; 442 while (pPrev && pPrev->pNext != pWorker) 443 pPrev = pPrev->pNext; 444 assert(pPrev != NULL); 445 if (pPrev) 446 pPrev->pNext = pWorker->pNext; 447 } 448 pWorker->pid = -1; 449 450 /* 451 * Respawn it. 452 */ 453 if (kSubmitSpawnWorker(pWorker, cVerbosity) == 0) 454 { 455 /* 456 * Insert it into the process ID hash table and idle list. 457 */ 458 size_t idxHash = KWORKER_PID_HASH(pWorker->pid); 459 pWorker->pNextPidHash = g_apPidHash[idxHash]; 460 g_apPidHash[idxHash] = pWorker; 461 return 0; 462 } 463 464 kSubmitListUnlink(&g_IdleList, pWorker); 465 free(pWorker); 466 return -1; 467 } 468 469 470 /** 471 * Selects an idle worker or spawns a new one. 472 * 473 * @returns Pointer to the selected worker instance. NULL on error. 474 * @param cBitsWorker The worker bitness - 64 or 32. 475 */ 476 static PWORKERINSTANCE kSubmitSelectWorkSpawnNewIfNecessary(unsigned cBitsWorker, int cVerbosity) 477 { 478 /* 479 * Lookup up an idle worker. 480 */ 481 PWORKERINSTANCE pWorker = g_IdleList.pHead; 482 while (pWorker) 483 { 484 if (pWorker->cBits == cBitsWorker) 485 return pWorker; 486 pWorker = pWorker->pNext; 487 } 488 489 /* 490 * Create a new worker instance. 491 */ 492 pWorker = (PWORKERINSTANCE)xcalloc(sizeof(*pWorker)); 493 pWorker->cBits = cBitsWorker; 494 if (kSubmitSpawnWorker(pWorker, cVerbosity) == 0) 495 { 496 /* 497 * Insert it into the process ID hash table and idle list. 498 */ 499 size_t idxHash = KWORKER_PID_HASH(pWorker->pid); 500 pWorker->pNextPidHash = g_apPidHash[idxHash]; 501 g_apPidHash[idxHash] = pWorker; 502 503 kSubmitListAppend(&g_IdleList, pWorker); 504 return pWorker; 505 } 506 507 free(pWorker); 508 return NULL; 509 } 510 511 512 /** 513 * Composes a JOB mesage for a worker. 514 * 515 * @returns Pointer to the message. 516 * @param pszExecutable The executable to run. 517 * @param papszArgs The argument vector. 518 * @param papszEnvVars The environment vector. 519 * @param pszCwd The current directory. 520 * @param pcbMsg Where to return the message length. 521 */ 522 static void *kSubmitComposeJobMessage(const char *pszExecutable, char **papszArgs, char **papszEnvVars, 523 const char *pszCwd, uint32_t *pcbMsg) 524 { 525 size_t i; 526 size_t cbTmp; 527 uint32_t cbMsg; 528 uint8_t *pbMsg; 529 uint8_t *pbCursor; 530 531 /* 532 * Calculate the message length first. 533 */ 534 cbMsg = sizeof(cbMsg); 535 cbMsg += sizeof("JOB"); 536 cbMsg += strlen(pszExecutable) + 1; 537 538 for (i = 0; papszArgs[i] != NULL; i++) 539 cbMsg += strlen(papszArgs[i]) + 1; 540 cbMsg += 1; 541 542 for (i = 0; papszEnvVars[i] != NULL; i++) 543 cbMsg += strlen(papszEnvVars[i]) + 1; 544 cbMsg += 1; 545 546 cbMsg += strlen(pszCwd) + 1; 547 548 /* 549 * Compose the message. 550 */ 551 pbMsg = pbCursor = xmalloc(cbMsg); 552 553 memcpy(pbCursor, &cbMsg, sizeof(cbMsg)); 554 pbCursor += sizeof(cbMsg); 555 memcpy(pbCursor, "JOB", sizeof("JOB")); 556 pbCursor += sizeof("JOB"); 557 558 cbTmp = strlen(pszExecutable) + 1; 559 memcpy(pbCursor, pszExecutable, cbTmp); 560 pbCursor += cbTmp; 561 562 for (i = 0; papszArgs[i] != NULL; i++) 563 { 564 cbTmp = strlen(papszArgs[i]) + 1; 565 memcpy(pbCursor, papszArgs[i], cbTmp); 566 pbCursor += cbTmp; 567 } 568 *pbCursor++ = '\0'; 569 570 for (i = 0; papszEnvVars[i] != NULL; i++) 571 { 572 cbTmp = strlen(papszEnvVars[i]) + 1; 573 memcpy(pbCursor, papszEnvVars[i], cbTmp); 574 pbCursor += cbTmp; 575 } 576 *pbCursor++ = '\0'; 577 578 cbTmp = strlen(pszCwd) + 1; 579 memcpy(pbCursor, pszCwd, cbTmp); 580 pbCursor += cbTmp; 581 582 assert(pbCursor - pbMsg == (size_t)cbMsg); 583 584 /* done */ 585 *pcbMsg = cbMsg; 586 return pbMsg; 587 } 588 589 590 /** 591 * Sends the job message to the given worker, respawning the worker if 592 * necessary. 593 * 594 * @returns 0 on success, non-zero on failure. 595 * 596 * @param pWorker The work to send the request to. The worker is 597 * on the idle list. 598 * @param pvMsg The message to send. 599 * @param cbMsg The size of the message. 600 * @param cVerbosity The verbosity level. 601 */ 602 static int kSubmitSendJobMessage(PWORKERINSTANCE pWorker, void const *pvMsg, uint32_t cbMsg, int cVerbosity) 603 { 604 int cRetries = 1; 605 for (;; cRetries--) 606 { 607 /* 608 * Try write the message. 609 */ 610 uint32_t cbLeft = cbMsg; 611 uint8_t const *pbLeft = (uint8_t const *)pvMsg; 612 #ifdef KBUILD_OS_WINDOWS 613 DWORD dwErr; 614 DWORD cbWritten; 615 while (WriteFile(pWorker->hPipe, pbLeft, cbLeft, &cbWritten, NULL /*pOverlapped*/)) 616 { 617 assert(cbWritten <= cbLeft); 618 cbLeft -= cbWritten; 619 if (!cbLeft) 620 return 0; 621 622 /* This scenario shouldn't really ever happen. But just in case... */ 623 pbLeft += cbWritten; 624 } 625 dwErr = GetLastError(); 626 if ( ( dwErr != ERROR_BROKEN_PIPE 627 && dwErr != ERROR_NO_DATA) 628 || cRetries <= 0) 629 return errx(1, "Error writing to worker: %u", dwErr); 630 #else 631 ssize_t cbWritten 632 while ((cbWritten = write(pWorker->fdSocket, pbLeft, cbLeft)) >= 0) 633 { 634 assert(cbWritten <= cbLeft); 635 cbLeft -= cbWritten; 636 if (!cbLeft) 637 return 0; 638 639 pbLeft += cbWritten; 640 } 641 if ( ( errno != EPIPE 642 && errno != ENOTCONN 643 && errno != ECONNRESET)) 644 || cRetries <= 0) 645 return err(1, "Error writing to worker"); 646 # error "later" 647 #endif 648 649 /* 650 * Broken connection. Try respawn the worker. 651 */ 652 if (kSubmitRespawnWorker(pWorker, cVerbosity) != 0) 653 return 2; 654 } 655 } 656 657 658 /** 659 * Handles the --set var=value option. 660 * 661 * @returns 0 on success, non-zero exit code on error. 662 * @param papszEnv The environment vector. 663 * @param pcEnvVars Pointer to the variable holding the number of 664 * environment variables held by @a papszEnv. 665 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the 666 * environment vector. 667 * @param cVerbosity The verbosity level. 668 * @param pszValue The var=value string to apply. 669 */ 670 static int kSubmitOptEnvSet(char **papszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, 671 int cVerbosity, const char *pszValue) 672 { 673 const char *pszEqual = strchr(pszValue, '='); 674 if (pszEqual) 675 { 676 unsigned iEnvVar; 677 unsigned cEnvVars = *pcEnvVars; 678 size_t const cchVar = pszValue - pszEqual; 679 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++) 680 if ( strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0 681 && papszEnv[iEnvVar][cchVar] == '=') 682 { 683 if (cVerbosity > 0) 684 fprintf(stderr, "kSubmit: replacing '%s' with '%s'\n", papszEnv[iEnvVar], pszValue); 685 free(papszEnv[iEnvVar]); 686 papszEnv[iEnvVar] = xstrdup(pszValue); 687 break; 688 } 689 if (iEnvVar == cEnvVars) 690 { 691 /* Append new variable. We probably need to resize the vector. */ 692 if ((cEnvVars + 2) > *pcAllocatedEnvVars) 693 { 694 *pcAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf; 695 papszEnv = (char **)xrealloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0])); 696 } 697 papszEnv[cEnvVars++] = xstrdup(pszValue); 698 papszEnv[cEnvVars] = NULL; 699 *pcEnvVars = cEnvVars; 700 if (cVerbosity > 0) 701 fprintf(stderr, "kSubmit: added '%s'\n", papszEnv[iEnvVar]); 702 } 703 else 704 { 705 /* Check for duplicates. */ 706 for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++) 707 if ( strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0 708 && papszEnv[iEnvVar][cchVar] == '=') 709 { 710 if (cVerbosity > 0) 711 fprintf(stderr, "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]); 712 free(papszEnv[iEnvVar]); 713 cEnvVars--; 714 if (iEnvVar != cEnvVars) 715 papszEnv[iEnvVar] = papszEnv[cEnvVars]; 716 papszEnv[cEnvVars] = NULL; 717 iEnvVar--; 718 } 719 } 720 } 721 else 722 return errx(1, "Missing '=': -E %s", pszValue); 723 724 return 0; 725 } 726 727 728 /** 729 * Handles the --unset var option. 730 * 731 * @returns 0 on success, non-zero exit code on error. 732 * @param papszEnv The environment vector. 733 * @param pcEnvVars Pointer to the variable holding the number of 734 * environment variables held by @a papszEnv. 735 * @param cVerbosity The verbosity level. 736 * @param pszVarToRemove The name of the variable to remove. 737 */ 738 static int kSubmitOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove) 739 { 740 if (strchr(pszVarToRemove, '=') == NULL) 741 { 742 unsigned cRemoved = 0; 743 size_t const cchVar = strlen(pszVarToRemove); 744 unsigned cEnvVars = *pcEnvVars; 745 unsigned iEnvVar; 746 747 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++) 748 if ( strncmp(papszEnv[iEnvVar], pszVarToRemove, cchVar) == 0 749 && papszEnv[iEnvVar][cchVar] == '=') 750 { 751 if (cVerbosity > 0) 752 fprintf(stderr, !cRemoved ? "kSubmit: removing '%s'\n" 753 : "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]); 754 free(papszEnv[iEnvVar]); 755 cEnvVars--; 756 if (iEnvVar != cEnvVars) 757 papszEnv[iEnvVar] = papszEnv[cEnvVars]; 758 papszEnv[cEnvVars] = NULL; 759 cRemoved++; 760 iEnvVar--; 761 } 762 *pcEnvVars = cEnvVars; 763 764 if (cVerbosity > 0 && !cRemoved) 765 fprintf(stderr, "kSubmit: not found '%s'\n", pszVarToRemove); 766 } 767 else 768 return errx(1, "Found invalid variable name character '=' in: -U %s", pszVarToRemove); 769 return 0; 770 } 771 772 773 774 /** 775 * Handles the --chdir dir option. 776 * 777 * @returns 0 on success, non-zero exit code on error. 778 * @param pszCwd The CWD buffer. Contains current CWD on input, 779 * modified by @a pszValue on output. 780 * @param cbCwdBuf The size of the CWD buffer. 781 * @param pszValue The --chdir value to apply. 782 */ 783 static int kSubmitOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue) 784 { 785 size_t cchNewCwd = strlen(pszValue); 786 size_t offDst; 787 if (cchNewCwd) 788 { 789 #ifdef HAVE_DOS_PATHS 790 if (*pszValue == '/' || *pszValue == '\\') 791 { 792 if (pszValue[1] == '/' || pszValue[1] == '\\') 793 offDst = 0; /* UNC */ 794 else if (pszCwd[1] == ':' && isalpha(pszCwd[0])) 795 offDst = 2; /* Take drive letter from CWD. */ 796 else 797 return errx(1, "UNC relative CWD not implemented: cur='%s' new='%s'", pszCwd, pszValue); 798 } 799 else if ( pszValue[1] == ':' 800 && isalpha(pszValue[0])) 801 { 802 if (pszValue[2] == '/'|| pszValue[2] == '\\') 803 offDst = 0; /* DOS style absolute path. */ 804 else if ( pszCwd[1] == ':' 805 && tolower(pszCwd[0]) == tolower(pszValue[0]) ) 806 { 807 pszValue += 2; /* Same drive as CWD, append drive relative path from value. */ 808 cchNewCwd -= 2; 809 offDst = strlen(pszCwd); 810 } 811 else 812 { 813 /* Get current CWD on the specified drive and append value. */ 814 int iDrive = tolower(pszValue[0]) - 'a' + 1; 815 if (!_getdcwd(iDrive, pszCwd, cbCwdBuf)) 816 return err(1, "_getdcwd(%d,,) failed", iDrive); 817 pszValue += 2; 818 cchNewCwd -= 2; 819 } 820 } 821 #else 822 if (*pszValue == '/') 823 offDst = 0; 824 #endif 825 else 826 offDst = strlen(pszCwd); /* Relative path, append to the existing CWD value. */ 827 828 /* Do the copying. */ 829 #ifdef HAVE_DOS_PATHS 830 if (offDst > 0 && pszCwd[offDst - 1] != '/' && pszCwd[offDst - 1] != '\\') 831 #else 832 if (offDst > 0 && pszCwd[offDst - 1] != '/') 833 #endif 834 pszCwd[offDst++] = '/'; 835 if (offDst + cchNewCwd >= cbCwdBuf) 836 return errx(1, "Too long CWD: %*.*s%s", offDst, offDst, pszCwd, pszValue); 837 memcpy(&pszCwd[offDst], pszValue, cchNewCwd + 1); 838 } 839 /* else: relative, no change - quitely ignore. */ 840 return 0; 841 } 87 842 88 843 … … 133 888 unsigned iEnvVar; 134 889 unsigned cEnvVars; 135 char **papszEnv = NULL; 136 const char *pszCwd = NULL; 137 unsigned cBitsWorker = 0; 138 int fWatcomBrainDamage = 0; 139 int cVerbosity = 0; 140 size_t const cbCwdBuf = GET_PATH_MAX; 890 char **papszEnv = NULL; 891 const char *pszExecutable = NULL; 892 const char *pszCwd = NULL; 893 unsigned cBitsWorker = g_cArchBits; 894 int fWatcomBrainDamage = 0; 895 int cVerbosity = 0; 896 size_t const cbCwdBuf = GET_PATH_MAX; 141 897 PATH_VAR(szCwd); 142 898 … … 215 971 else if (strcmp(pszArg, "verbose") == 0) 216 972 chOpt = 'v'; 973 else if (strcmp(pszArg, "executable") == 0) 974 chOpt = 'e'; 217 975 else 218 976 { … … 232 990 case 'U': 233 991 case 'C': 992 case 'e': 234 993 if (*pszArg != '\0') 235 994 pszValue = pszArg + (*pszArg == ':' || *pszArg == '='); … … 255 1014 256 1015 case 'E': 257 { 258 const char *pszEqual = strchr(pszValue, '='); 259 if (pszEqual) 260 { 261 size_t const cchVar = pszValue - pszEqual; 262 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++) 263 if ( strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0 264 && papszEnv[iEnvVar][cchVar] == '=') 265 { 266 if (cVerbosity > 0) 267 fprintf(stderr, "kSubmit: replacing '%s' with '%s'\n", papszEnv[iEnvVar], pszValue); 268 free(papszEnv[iEnvVar]); 269 papszEnv[iEnvVar] = xstrdup(pszValue); 270 break; 271 } 272 if (iEnvVar == cEnvVars) 273 { 274 /* Append new variable. We probably need to resize the vector. */ 275 if ((cEnvVars + 2) > cAllocatedEnvVars) 276 { 277 cAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf; 278 pChild->environment = papszEnv = (char **)xrealloc(papszEnv, 279 cAllocatedEnvVars * sizeof(papszEnv[0])); 280 } 281 papszEnv[cEnvVars++] = xstrdup(pszValue); 282 papszEnv[cEnvVars] = NULL; 283 if (cVerbosity > 0) 284 fprintf(stderr, "kSubmit: added '%s'\n", papszEnv[iEnvVar]); 285 } 286 else 287 { 288 /* Check for duplicates. */ 289 for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++) 290 if ( strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0 291 && papszEnv[iEnvVar][cchVar] == '=') 292 { 293 if (cVerbosity > 0) 294 fprintf(stderr, "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]); 295 free(papszEnv[iEnvVar]); 296 cEnvVars--; 297 if (iEnvVar != cEnvVars) 298 papszEnv[iEnvVar] = papszEnv[cEnvVars]; 299 papszEnv[cEnvVars] = NULL; 300 iEnvVar--; 301 } 302 } 303 } 304 else 305 return errx(1, "Missing '=': -E %s", pszValue); 306 break; 307 } 1016 rcExit = kSubmitOptEnvSet(papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue); 1017 pChild->environment = papszEnv; 1018 if (rcExit == 0) 1019 break; 1020 return rcExit; 308 1021 309 1022 case 'U': 310 { 311 if (strchr(pszValue, '=') == NULL) 312 { 313 unsigned cRemoved = 0; 314 size_t const cchVar = strlen(pszValue); 315 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++) 316 if ( strncmp(papszEnv[iEnvVar], pszValue, cchVar) == 0 317 && papszEnv[iEnvVar][cchVar] == '=') 318 { 319 if (cVerbosity > 0) 320 fprintf(stderr, !cRemoved ? "kSubmit: removing '%s'\n" 321 : "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]); 322 free(papszEnv[iEnvVar]); 323 cEnvVars--; 324 if (iEnvVar != cEnvVars) 325 papszEnv[iEnvVar] = papszEnv[cEnvVars]; 326 papszEnv[cEnvVars] = NULL; 327 cRemoved++; 328 iEnvVar--; 329 } 330 if (cVerbosity > 0 && !cRemoved) 331 fprintf(stderr, "kSubmit: not found '%s'\n", pszValue); 332 } 333 else 334 return errx(1, "Found invalid variable name character '=' in: -U %s", pszValue); 335 break; 336 } 1023 rcExit = kSubmitOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue); 1024 if (rcExit == 0) 1025 break; 1026 return rcExit; 337 1027 338 1028 case 'C': 339 { 340 size_t cchNewCwd = strlen(pszValue); 341 size_t offDst; 342 if (cchNewCwd) 343 { 344 #ifdef HAVE_DOS_PATHS 345 if (*pszValue == '/' || *pszValue == '\\') 346 { 347 if (pszValue[1] == '/' || pszValue[1] == '\\') 348 offDst = 0; /* UNC */ 349 else if (szCwd[1] == ':' && isalpha(szCwd[0])) 350 offDst = 2; /* Take drive letter from CWD. */ 351 else 352 return errx(1, "UNC relative CWD not implemented: cur='%s' new='%s'", szCwd, pszValue); 353 } 354 else if ( pszValue[1] == ':' 355 && isalpha(pszValue[0])) 356 { 357 if (pszValue[2] == '/'|| pszValue[2] == '\\') 358 offDst = 0; /* DOS style absolute path. */ 359 else if ( szCwd[1] == ':' 360 && tolower(szCwd[0]) == tolower(pszValue[0]) ) 361 { 362 pszValue += 2; /* Same drive as CWD, append drive relative path from value. */ 363 cchNewCwd -= 2; 364 offDst = strlen(szCwd); 365 } 366 else 367 { 368 /* Get current CWD on the specified drive and append value. */ 369 int iDrive = tolower(pszValue[0]) - 'a' + 1; 370 if (!_getdcwd(iDrive, szCwd, cbCwdBuf)) 371 return err(1, "_getdcwd(%d,,) failed", iDrive); 372 pszValue += 2; 373 cchNewCwd -= 2; 374 } 375 } 376 #else 377 if (*pszValue == '/') 378 offDst = 0; 379 #endif 380 else 381 offDst = strlen(szCwd); /* Relative path, append to the existing CWD value. */ 382 383 /* Do the copying. */ 384 #ifdef HAVE_DOS_PATHS 385 if (offDst > 0 && szCwd[offDst - 1] != '/' && szCwd[offDst - 1] != '\\') 386 #else 387 if (offDst > 0 && szCwd[offDst - 1] != '/') 388 #endif 389 szCwd[offDst++] = '/'; 390 if (offDst + cchNewCwd >= cbCwdBuf) 391 return errx(1, "Too long CWD: %*.*s%s", offDst, offDst, szCwd, pszValue); 392 memcpy(&szCwd[offDst], pszValue, cchNewCwd + 1); 393 } 394 /* else: relative, no change - quitely ignore. */ 395 break; 396 } 1029 rcExit = kSubmitOptChDir(szCwd, cbCwdBuf, pszValue); 1030 if (rcExit == 0) 1031 break; 1032 return rcExit; 397 1033 398 1034 case '3': … … 402 1038 case '6': 403 1039 cBitsWorker = 64; 1040 break; 1041 1042 case 'e': 1043 pszExecutable = pszValue; 404 1044 break; 405 1045 … … 433 1073 if (iArg < argc) 434 1074 { 435 1075 uint32_t cbMsg; 1076 void *pvMsg = kSubmitComposeJobMessage(pszExecutable, &argv[iArg], papszEnv, szCwd, &cbMsg); 1077 PWORKERINSTANCE pWorker = kSubmitSelectWorkSpawnNewIfNecessary(cBitsWorker, cVerbosity); 1078 if (pWorker) 1079 { 1080 rcExit = kSubmitSendJobMessage(pWorker, pvMsg, cbMsg, cVerbosity); 1081 if (rcExit == 0) 1082 { 1083 pWorker->pBusyWith = pChild; 1084 /** @todo integrate with sub_proc.c / whatever. */ 1085 } 1086 } 1087 else 1088 rcExit = 1; 1089 free(pvMsg); 436 1090 } 437 1091 else
Note:
See TracChangeset
for help on using the changeset viewer.