Changeset 2912 in kBuild
- Timestamp:
- Sep 14, 2016 1:36:15 PM (9 years ago)
- Location:
- trunk
- Files:
-
- 19 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/kBuild/footer-pass2-fetches.kmk
r2726 r2912 362 362 ,$$(call MSG_REFETCH,$(target)),$$(call MSG_FETCH,$(target))),$$(call MSG_UNFETCH,$(target))) 363 363 $$(QUIET)$(TEST_EXT) -f $(out).lst -- $$(MAKE) -f $(MAKEFILE) --no-print-directory $(out)_unfetched 364 if $(KBUILD_KMK_REVISION) > 2911 365 $$(QUIET)kmk_builtin_dircache deleted "$(dir $(out))" 366 endif 364 367 $$(QUIET)$$(if $$(_TARGET_$(target)_DIGEST),$$(MAKE) -f $(MAKEFILE) --no-print-directory $(out).lst,$$(RMDIR) -p --ignore-fail-on-non-empty --ignore-fail-on-not-exist -- $$(dir $$@)) 365 368 $$(QUIET2)$$(if $$(_TARGET_$(target)_DIGEST),$$(APPEND) $$@ "_TARGET_$(target)_DIGEST_PREV := $(_TARGET_$(target)_DIGEST)") -
trunk/kBuild/header.kmk
r2895 r2912 702 702 703 703 REDIRECT_EXT:= $(KBUILD_BIN_PATH)/kmk_redirect$(HOSTSUFF_EXE) 704 if $(KBUILD_KMK_REVISION) > 2911 705 REDIRECT_INT:= kmk_builtin_redirect 706 else 704 707 REDIRECT_INT:= $(REDIRECT_EXT) 708 endif 705 709 REDIRECT := $(REDIRECT_INT) 706 710 -
trunk/src/kWorker/kWorker.c
r2906 r2912 2638 2638 static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc) 2639 2639 { 2640 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)2640 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK) 2641 2641 { 2642 2642 PKWEXITCALLACK pCallback; … … 2663 2663 static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void)) 2664 2664 { 2665 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)2665 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK) 2666 2666 { 2667 2667 PKWEXITCALLACK pCallback; … … 7492 7492 PKWEXITCALLACK pExitCallback; 7493 7493 7494 7495 /* 7496 * First stuff that may cause code to run. 7497 */ 7498 7494 7499 /* Do exit callback first. */ 7495 7500 pExitCallback = g_Sandbox.pExitCallbackHead; … … 7514 7519 } 7515 7520 7521 /* Free left behind FlsAlloc leaks. */ 7522 pLocalStorage = g_Sandbox.pFlsAllocHead; 7523 g_Sandbox.pFlsAllocHead = NULL; 7524 while (pLocalStorage) 7525 { 7526 PKWLOCALSTORAGE pNext = pLocalStorage->pNext; 7527 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx)); 7528 FlsFree(pLocalStorage->idx); 7529 kHlpFree(pLocalStorage); 7530 pLocalStorage = pNext; 7531 } 7532 7533 /* Free left behind TlsAlloc leaks. */ 7534 pLocalStorage = g_Sandbox.pTlsAllocHead; 7535 g_Sandbox.pTlsAllocHead = NULL; 7536 while (pLocalStorage) 7537 { 7538 PKWLOCALSTORAGE pNext = pLocalStorage->pNext; 7539 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx)); 7540 TlsFree(pLocalStorage->idx); 7541 kHlpFree(pLocalStorage); 7542 pLocalStorage = pNext; 7543 } 7544 7545 7546 /* 7547 * Then free resources associated with the sandbox run. 7548 */ 7516 7549 7517 7550 #ifdef WITH_TEMP_MEMORY_FILES … … 7533 7566 #endif 7534 7567 7568 /* Free left behind HeapCreate leaks. */ 7569 pHeap = g_Sandbox.pHeapHead; 7570 g_Sandbox.pHeapHead = NULL; 7571 while (pHeap != NULL) 7572 { 7573 PKWHEAP pNext = pHeap->pNext; 7574 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap)); 7575 HeapDestroy(pHeap->hHeap); 7576 pHeap = pNext; 7577 } 7578 7535 7579 /* Free left behind VirtualAlloc leaks. */ 7536 7580 pTracker = g_Sandbox.pVirtualAllocHead; … … 7544 7588 pTracker = pNext; 7545 7589 } 7546 7547 /* Free left behind HeapCreate leaks. */7548 pHeap = g_Sandbox.pHeapHead;7549 g_Sandbox.pHeapHead = NULL;7550 while (pHeap != NULL)7551 {7552 PKWHEAP pNext = pHeap->pNext;7553 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));7554 HeapDestroy(pHeap->hHeap);7555 pHeap = pNext;7556 }7557 7558 /* Free left behind FlsAlloc leaks. */7559 pLocalStorage = g_Sandbox.pFlsAllocHead;7560 g_Sandbox.pFlsAllocHead = NULL;7561 while (pLocalStorage)7562 {7563 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;7564 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));7565 FlsFree(pLocalStorage->idx);7566 kHlpFree(pLocalStorage);7567 pLocalStorage = pNext;7568 }7569 7570 /* Free left behind TlsAlloc leaks. */7571 pLocalStorage = g_Sandbox.pTlsAllocHead;7572 g_Sandbox.pTlsAllocHead = NULL;7573 while (pLocalStorage)7574 {7575 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;7576 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));7577 TlsFree(pLocalStorage->idx);7578 kHlpFree(pLocalStorage);7579 pLocalStorage = pNext;7580 }7581 7582 7590 7583 7591 /* Free the environment. */ … … 8184 8192 /* Optional directory change. */ 8185 8193 if ( i < argc 8186 && strcmp(argv[i], "--chdir") == 0) 8194 && ( strcmp(argv[i], "--chdir") == 0 8195 || strcmp(argv[i], "-C") == 0 ) ) 8187 8196 { 8188 8197 i++; -
trunk/src/kmk/Makefile.kmk
r2907 r2912 89 89 kmkbuiltin/strmode.c \ 90 90 kmkbuiltin/kbuild_protection.c \ 91 kmkbuiltin/common-env-and-cwd-opt.c \ 91 92 getopt.c \ 92 93 getopt1.c \ … … 280 281 kmkbuiltin/ln.c \ 281 282 kmkbuiltin/printf.c \ 283 kmkbuiltin/redirect.c \ 282 284 kmkbuiltin/rm.c \ 283 285 kmkbuiltin/rmdir.c \ … … 395 397 396 398 kmk_redirect_TEMPLATE = BIN-KMK 397 kmk_redirect_DEFS = kmk_builtin_redirect=main398 399 kmk_redirect_SOURCES = \ 399 400 kmkbuiltin/redirect.c -
trunk/src/kmk/dir-nt-bird.c
r2886 r2912 121 121 else 122 122 { 123 PKFSOBJ pNameObj = kFsCacheLookupRelativeToDirA(g_pFsCache, (PKFSDIR)pDirObj, 124 pszName, strlen(pszName), &enmError, NULL);123 PKFSOBJ pNameObj = kFsCacheLookupRelativeToDirA(g_pFsCache, (PKFSDIR)pDirObj, pszName, strlen(pszName), 124 0/*fFlags*/, &enmError, NULL); 125 125 if (pNameObj) 126 126 { … … 586 586 } 587 587 588 /** 589 * Invalidates a deleted directory so the cache can close handles to it. 590 * 591 * Used by kmk_builtin_rm and kmk_builtin_rmdir. 592 * 593 * @returns 0 on success, -1 on failure. 594 * @param pszDir The directory to invalidate as deleted. 595 */ 596 int dir_cache_deleted_directory(const char *pszDir) 597 { 598 if (kFsCacheInvalidateDeletedDirectoryA(g_pFsCache, pszDir)) 599 return 0; 600 return -1; 601 } 602 603 604 int kmk_builtin_dircache(int argc, char **argv, char **envp) 605 { 606 if (argc >= 2) 607 { 608 const char *pszCmd = argv[1]; 609 if (strcmp(pszCmd, "invalidate") == 0) 610 { 611 if (argc == 2) 612 { 613 dir_cache_invalid_all(); 614 return 0; 615 } 616 fprintf(stderr, "kmk_builtin_dircache: the 'invalidate' command takes no arguments!\n"); 617 } 618 else if (strcmp(pszCmd, "invalidate-missing") == 0) 619 { 620 if (argc == 2) 621 { 622 dir_cache_invalid_missing (); 623 return 0; 624 } 625 fprintf(stderr, "kmk_builtin_dircache: the 'invalidate-missing' command takes no arguments!\n"); 626 } 627 else if (strcmp(pszCmd, "volatile") == 0) 628 { 629 int i; 630 for (i = 2; i < argc; i++) 631 dir_cache_volatile_dir(argv[i]); 632 return 0; 633 } 634 else if (strcmp(pszCmd, "deleted") == 0) 635 { 636 int i; 637 for (i = 2; i < argc; i++) 638 dir_cache_deleted_directory(argv[i]); 639 return 0; 640 } 641 else 642 fprintf(stderr, "kmk_builtin_dircache: Invalid command '%s'!\n", pszCmd); 643 } 644 else 645 fprintf(stderr, "kmk_builtin_dircache: No command given!\n"); 646 647 K_NOREF(envp); 648 return 2; 649 } 650 -
trunk/src/kmk/function.c
r2886 r2912 5491 5491 } 5492 5492 } 5493 else if (strcmp (cmd, "deleted") == 0) 5494 { 5495 size_t i; 5496 for (i = 1; argv[i] != NULL; i++) 5497 { 5498 const char *dir = argv[i]; 5499 while (isblank ((unsigned char)*dir)) 5500 dir++; 5501 if (*dir) 5502 dir_cache_deleted_directory (dir); 5503 } 5504 } 5493 5505 else 5494 5506 error (reading_file, "Unknown $(dircache-ctl ) command: '%s'", cmd); -
trunk/src/kmk/kmkbuiltin.c
r2843 r2912 206 206 else if (!strcmp(pszCmd, "mv")) 207 207 rc = kmk_builtin_mv(argc, argv, environ); 208 /*else if (!strcmp(pszCmd, "redirect"))209 rc = kmk_builtin_redirect(argc, argv, environ, p PidSpawned);*/208 else if (!strcmp(pszCmd, "redirect")) 209 rc = kmk_builtin_redirect(argc, argv, environ, pChild, pPidSpawned); 210 210 else if (!strcmp(pszCmd, "rm")) 211 211 rc = kmk_builtin_rm(argc, argv, environ); … … 233 233 else if (!strcmp(pszCmd, "sleep")) 234 234 rc = kmk_builtin_sleep(argc, argv, environ); 235 else if (!strcmp(pszCmd, "dircache")) 236 #ifdef KBUILD_OS_WINDOWS 237 rc = kmk_builtin_dircache(argc, argv, environ); 238 #else 239 rc = 0; 240 #endif 235 241 else 236 242 { -
trunk/src/kmk/kmkbuiltin.h
r2899 r2912 46 46 extern int kmk_builtin_chmod(int argc, char **argv, char **envp); 47 47 extern int kmk_builtin_cmp(int argc, char **argv, char **envp); 48 extern int kmk_builtin_dircache(int argc, char **argv, char **envp); 48 49 extern int kmk_builtin_echo(int argc, char **argv, char **envp); 49 50 extern int kmk_builtin_expr(int argc, char **argv, char **envp); … … 54 55 extern int kmk_builtin_mv(int argc, char **argv, char **envp); 55 56 extern int kmk_builtin_printf(int argc, char **argv, char **envp); 57 extern int kmk_builtin_redirect(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned); 56 58 extern int kmk_builtin_rm(int argc, char **argv, char **envp); 57 59 extern int kmk_builtin_rmdir(int argc, char **argv, char **envp); … … 73 75 extern char *kmk_builtin_func_printf(char *o, char **argv, const char *funcname); 74 76 77 /* common-env-and-cwd-opt.c: */ 78 extern int kBuiltinOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, 79 int cVerbosity, const char *pszValue); 80 extern int kBuiltinOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove); 81 extern int kBuiltinOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue); 82 75 83 #endif 76 84 -
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 -
trunk/src/kmk/kmkbuiltin/kSubmit.c
r2894 r2912 1162 1162 1163 1163 1164 /** The environment variable compare function.1165 * We must use case insensitive compare on windows (Path vs PATH). */1166 #ifdef KBUILD_OS_WINDOWS1167 # define KSUBMIT_ENV_NCMP _strnicmp1168 #else1169 # define KSUBMIT_ENV_NCMP strncmp1170 #endif1171 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 of1179 * environment variables held by @a papszEnv.1180 * @param pcAllocatedEnvVars Pointer to the variable holding max size of the1181 * environment vector.1182 * @param cVerbosity The verbosity level.1183 * @param pszValue The var=value string to apply.1184 */1185 static 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) == 01199 && 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 else1223 {1224 /* Check for duplicates. */1225 for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++)1226 if ( KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszValue, cchVar) == 01227 && 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 else1241 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 of1253 * environment variables held by @a papszEnv.1254 * @param cVerbosity The verbosity level.1255 * @param pszVarToRemove The name of the variable to remove.1256 */1257 static 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) == 01268 && 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 else1287 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 */1302 static 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_PATHS1309 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 else1316 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 else1331 {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 #else1341 if (*pszValue == '/')1342 offDst = 0;1343 #endif1344 else1345 offDst = strlen(pszCwd); /* Relative path, append to the existing CWD value. */1346 1347 /* Do the copying. */1348 #ifdef HAVE_DOS_PATHS1349 if (offDst > 0 && pszCwd[offDst - 1] != '/' && pszCwd[offDst - 1] != '\\')1350 #else1351 if (offDst > 0 && pszCwd[offDst - 1] != '/')1352 #endif1353 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 1363 1164 static int usage(FILE *pOut, const char *argv0) 1364 1165 { … … 1401 1202 , 1402 1203 argv0, argv0, argv0); 1403 return 1;1204 return 2; 1404 1205 } 1405 1206 … … 1414 1215 char **papszEnv = NULL; 1415 1216 const char *pszExecutable = NULL; 1416 const char *pszCwd = NULL;1417 1217 int iPostCmd = argc; 1418 1218 int cPostCmdArgs = 0; … … 1503 1303 else 1504 1304 { 1505 errx( 1, "Unknown option: '%s'", pszArg - 2);1305 errx(2, "Unknown option: '%s'", pszArg - 2); 1506 1306 return usage(stderr, argv[0]); 1507 1307 } … … 1542 1342 1543 1343 case 'E': 1544 rcExit = k SubmitOptEnvSet(&papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);1344 rcExit = kBuiltinOptEnvSet(&papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue); 1545 1345 pChild->environment = papszEnv; 1546 1346 if (rcExit == 0) … … 1549 1349 1550 1350 case 'U': 1551 rcExit = k SubmitOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue);1351 rcExit = kBuiltinOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue); 1552 1352 if (rcExit == 0) 1553 1353 break; … … 1555 1355 1556 1356 case 'C': 1557 rcExit = k SubmitOptChDir(szCwd, cbCwdBuf, pszValue);1357 rcExit = kBuiltinOptChDir(szCwd, cbCwdBuf, pszValue); 1558 1358 if (rcExit == 0) 1559 1359 break; -
trunk/src/kmk/kmkbuiltin/redirect.c
r2839 r2912 30 30 # define _POSIX_C_SOURCE 1 /* 10.4 sdk and unsetenv */ 31 31 #endif 32 #include "config.h" 32 #include "make.h" 33 #include <assert.h> 33 34 #include <stdio.h> 34 35 #include <stdlib.h> … … 36 37 #include <errno.h> 37 38 #include <fcntl.h> 39 #if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2) 40 # include <process.h> 41 #endif 38 42 #if defined(_MSC_VER) 39 43 # include <ctype.h> 40 44 # include <io.h> 41 # include <direct.h>42 # include <process.h>43 45 # include "quote_argv.h" 44 46 #else 45 47 # include <unistd.h> 48 # include <spawn.h> 49 #endif 50 51 #include <k/kDefs.h> 52 #include <k/kTypes.h> 53 #include "err.h" 54 #include "kbuild_version.h" 55 #include "kmkbuiltin.h" 56 #ifdef KMK 57 # ifdef KBUILD_OS_WINDOWS 58 # include "sub_proc.h" 59 # include "pathstuff.h" 60 # endif 61 # include "job.h" 62 # include "variable.h" 46 63 #endif 47 64 … … 54 71 #endif 55 72 56 57 /********************************************************************************************************************************* 58 * Global Variables * 59 *********************************************************************************************************************************/ 60 /** Number of times the '-v' switch was seen. */ 61 static unsigned g_cVerbosity = 0; 73 #if !defined(KBUILD_OS_WINDOWS) && !defined(KBUILD_OS_OS2) 74 # define USE_POSIX_SPAWN 75 #endif 76 77 78 /* String + strlen tuple. */ 79 #define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1 62 80 63 81 … … 107 125 static int usage(FILE *pOut, const char *argv0) 108 126 { 127 argv0 = name(argv0); 109 128 fprintf(pOut, 110 "usage: %s [-[rwa+tb]<fd> <file>] [-c<fd>] [-Z] [-E <var=val>] [-C <dir>] [--wcc-brain-damage] [-v] -- <program> [args]\n" 129 "usage: %s [-[rwa+tb]<fd> <file>] [-d<fd>=<src-fd>] [-c<fd>]\n" 130 " [-Z] [-E <var=val>] [-C <dir>] [--wcc-brain-damage]\n" 131 " [-v] -- <program> [args]\n" 111 132 " or: %s --help\n" 112 133 " or: %s --version\n" … … 117 138 " o = stdout\n" 118 139 " e = stderr\n" 140 "\n" 141 "The -d switch duplicate the right hand file descriptor (src-fd) to the left\n" 142 "hand side one (fd).\n" 119 143 "\n" 120 144 "The -c switch will close the specified file descriptor.\n" … … 139 163 , 140 164 argv0, argv0, argv0); 141 return 1;165 return 2; 142 166 } 143 167 144 168 169 /** 170 * Decoded file descriptor operations. 171 */ 172 typedef struct REDIRECTORDERS 173 { 174 enum { 175 kRedirectOrder_Invalid = 0, 176 kRedirectOrder_Close, 177 kRedirectOrder_Open, 178 kRedirectOrder_Dup, 179 } enmOrder; 180 /** The target file handle. */ 181 int fdTarget; 182 /** The source file name, -1 on close only. 183 * This is an opened file if pszFilename is set. */ 184 int fdSource; 185 /** Whether to remove the file on failure cleanup. */ 186 int fRemoveOnFailure; 187 /** The open flags (for O_TEXT/O_BINARY) on windows. */ 188 int fOpen; 189 /** The filename - NULL if close only. */ 190 const char *pszFilename; 191 #ifndef USE_POSIX_SPAWN 192 /** Saved file descriptor. */ 193 int fdSaved; 194 /** Saved flags. */ 195 int fSaved; 196 #endif 197 } REDIRECTORDERS; 198 199 200 #ifdef _MSC_VER 201 202 /** 203 * Safe way of getting the OS handle of a file descriptor without triggering 204 * invalid parameter handling. 205 * 206 * @returns The handle value if open, INVALID_HANDLE_VALUE if not. 207 * @param fd The file descriptor in question. 208 */ 209 static HANDLE mscGetOsHandle(int fd) 210 { 211 intptr_t hHandle; 212 _invalid_parameter_handler pfnOld = _get_invalid_parameter_handler(); 213 _set_invalid_parameter_handler(ignore_invalid_parameter); 214 hHandle = _get_osfhandle(fd); 215 _set_invalid_parameter_handler(pfnOld); 216 return hHandle != -1 ? (HANDLE)hHandle : INVALID_HANDLE_VALUE; 217 } 218 219 /** 220 * Checks if the specified file descriptor is open. 221 * 222 * @returns K_TRUE if open, K_FALSE if not. 223 * @param fd The file descriptor in question. 224 */ 225 static KBOOL mscIsOpenFile(int fd) 226 { 227 return mscGetOsHandle(fd) != INVALID_HANDLE_VALUE; 228 } 229 230 /** 231 * Checks if the native handle is inheritable. 232 * 233 * @returns K_TRUE if it is, K_FALSE if it isn't or isn't a valid handle. 234 * @param hHandle The native handle. 235 */ 236 static KBOOL mscIsNativeHandleInheritable(HANDLE hHandle) 237 { 238 DWORD fFlags = 0; 239 if (GetHandleInformation(hHandle, &fFlags)) 240 return (fFlags & HANDLE_FLAG_INHERIT) != 0; 241 return K_FALSE; 242 } 243 244 /** 245 * Checks if the file descriptor is inheritable or not. 246 * 247 * @returns K_TRUE if it is, K_FALSE if it isn't or isn't a valid descriptor. 248 * @param fd The file descriptor in question. 249 */ 250 static KBOOL mscIsInheritable(int fd) 251 { 252 HANDLE hHandle = mscGetOsHandle(fd); 253 if (hHandle != INVALID_HANDLE_VALUE) 254 return mscIsNativeHandleInheritable(hHandle); 255 return K_FALSE; 256 } 257 258 /** 259 * A dup3 like function. 260 * 261 * @returns fdNew on success, -1 on failure w/ error details written to pStdErr. 262 * @param fdSource The source descriptor. 263 * @param fdNew The new descriptor. 264 * @param fFlags The inherit and text/binary mode flag. 265 * @param pStdErr Working stderr to write error details to. 266 */ 267 static int mscDup3(int fdSource, int fdNew, int fFlags, FILE *pStdErr) 268 { 269 if (!fFlags & _O_NOINHERIT) 270 { 271 /* ASSUMES fFlags doesn't include any changing _O_TEXT/_O_BINARY. */ 272 int fdDup = _dup2(fdSource, fdNew); 273 if (fdDup != -1) 274 return fdDup; 275 fprintf(pStdErr, "%s: _dup2(%d,%d) failed: %s\n", g_progname, fdSource, fdNew, strerror(errno)); 276 } 277 else 278 { 279 HANDLE hSource = mscGetOsHandle(fdSource); 280 unsigned cTries = 0; 281 int aFdTries[48]; 282 283 if (hSource != INVALID_HANDLE_VALUE) 284 { 285 HANDLE hCurProc = GetCurrentProcess(); 286 BOOL fInherit = !(fFlags & _O_NOINHERIT); 287 288 /* 289 * Make sure the old descriptor is closed and can be used again. 290 */ 291 _invalid_parameter_handler pfnOld = _get_invalid_parameter_handler(); 292 _set_invalid_parameter_handler(ignore_invalid_parameter); 293 close(fdNew); 294 _set_invalid_parameter_handler(pfnOld); 295 296 /* 297 * Duplicate the source handle till we've got a match. 298 */ 299 for (;;) 300 { 301 HANDLE hDup = INVALID_HANDLE_VALUE; 302 if (DuplicateHandle(hCurProc, hSource, hCurProc, &hDup, 0 /* DesiredAccess */, 303 fInherit, DUPLICATE_SAME_ACCESS)) 304 { 305 int fdDup = _open_osfhandle((intptr_t)hDup, fFlags); 306 if (fdDup != -1) 307 { 308 if (fdDup == fdNew) 309 { 310 while (cTries-- > 0) 311 close(aFdTries[cTries]); 312 return fdDup; 313 } 314 315 aFdTries[cTries++] = fdDup; 316 if ( fdDup < fdNew 317 && cTries < K_ELEMENTS(aFdTries)) 318 continue; 319 fprintf(pStdErr, "%s: mscDup3(%d,%d): giving up! (last fdDup=%d)\n", 320 g_progname, fdSource, fdNew, fdDup); 321 } 322 else 323 { 324 fprintf(pStdErr, "%s: _open_osfhandle(%#x) failed: %u\n", g_progname, hDup, strerror(errno)); 325 CloseHandle(hDup); 326 } 327 } 328 else 329 fprintf(pStdErr, "%s: DuplicateHandle(%#x) failed: %u\n", g_progname, hSource, GetLastError()); 330 break; 331 } 332 333 while (cTries-- > 0) 334 close(aFdTries[cTries]); 335 } 336 else 337 fprintf(pStdErr, "%s: mscDup3(%d,%d): source descriptor is invalid!\n", g_progname, fdSource, fdNew); 338 } 339 return -1; 340 } 341 342 #endif /* _MSC_VER */ 343 344 static KBOOL kRedirectHasConflict(int fd, unsigned cOrders, REDIRECTORDERS *paOrders) 345 { 346 while (cOrders-- > 0) 347 if (paOrders[cOrders].fdTarget == fd) 348 return K_TRUE; 349 return K_FALSE; 350 } 351 352 353 /** 354 * Creates a file descriptor for @a pszFilename that does not conflict with any 355 * previous orders. 356 * 357 * We need to be careful that there isn't a close or dup targetting the 358 * temporary file descriptor we return. Also, we need to take care with the 359 * descriptor's inheritability. It should only be inheritable if the returned 360 * descriptor matches the target descriptor (@a fdTarget). 361 * 362 * @returns File descriptor on success, -1 & err/errx on failure. 363 * 364 * The returned file descriptor is not inherited (i.e. close-on-exec), 365 * unless it matches @a fdTarget 366 * 367 * @param pszFilename The filename to open. 368 * @param fOpen The open flags. 369 * @param fMode The file creation mode (if applicable). 370 * @param cOrders The number of orders. 371 * @param paOrders The order array. 372 * @param fRemoveOnFailure Whether to remove the file on failure. 373 * @param fdTarget The target descriptor. 374 */ 375 static int kRedirectOpenWithoutConflict(const char *pszFilename, int fOpen, mode_t fMode, 376 unsigned cOrders, REDIRECTORDERS *paOrders, int fRemoveOnFailure, int fdTarget) 377 { 378 #ifdef _O_NOINHERIT 379 int const fNoInherit = _O_NOINHERIT; 380 #elif defined(O_NOINHERIT) 381 int const fNoInherit = O_NOINHERIT; 382 #elif defined(O_CLOEXEC) 383 int const fNoInherit = O_CLOEXEC; 384 #else 385 # error "port me" 386 #endif 387 int aFdTries[32]; 388 int cTries; 389 390 /* Open it first. */ 391 int fdOpened = open(pszFilename, fOpen | fNoInherit, fMode); 392 if (fdOpened < 0) 393 return err(9, "open(%s,%#x,) failed", pszFilename, fOpen); 394 395 /* Check for conflicts. */ 396 if (!kRedirectHasConflict(fdOpened, cOrders, paOrders)) 397 { 398 if (fdOpened != fdTarget) 399 return fdOpened; 400 #ifndef _MSC_VER /* Stupid, stupid MSVCRT! No friggin way of making a handle inheritable (or not). */ 401 if (fcntl(fdOpened, F_SETFD, FD_CLOEXEC) != -1) 402 return fdOpened; 403 #endif 404 } 405 406 /* 407 * Do conflict resolving. 408 */ 409 cTries = 1; 410 aFdTries[cTries++] = fdOpened; 411 while (cTries < K_ELEMENTS(aFdTries)) 412 { 413 fdOpened = open(pszFilename, fOpen | fNoInherit, fMode); 414 if (fdOpened >= 0) 415 { 416 if ( !kRedirectHasConflict(fdOpened, cOrders, paOrders) 417 #ifdef _MSC_VER 418 && fdOpened != fdTarget 419 #endif 420 ) 421 { 422 #ifndef _MSC_VER 423 if ( fdOpened != fdTarget 424 || fcntl(fdOpened, F_SETFD, FD_CLOEXEC) != -1) 425 #endif 426 { 427 while (cTries-- > 0) 428 close(aFdTries[cTries]); 429 return fdOpened; 430 } 431 } 432 433 } 434 else 435 { 436 err(9, "open(%s,%#x,) #%u failed", pszFilename, cTries + 1, fOpen); 437 break; 438 } 439 aFdTries[cTries++] = fdOpened; 440 } 441 442 /* 443 * Give up. 444 */ 445 if (fdOpened >= 0) 446 errx(9, "failed to find a conflict free file descriptor for '%s'!", pszFilename); 447 448 while (cTries-- > 0) 449 close(aFdTries[cTries]); 450 return -1; 451 } 452 453 #ifndef USE_POSIX_SPAWN 454 455 /** 456 * Saves a file handle to one which isn't inherited and isn't affected by the 457 * file orders. 458 * 459 * @returns 0 on success, non-zero exit code on failure. 460 * @param pToSave Pointer to the file order to save the target 461 * descriptor of. 462 * @param cOrders Number of file orders. 463 * @param paOrders The array of file orders. 464 * @param ppWorkingStdErr Pointer to a pointer to a working stderr. This will 465 * get replaced if we're saving stderr, so that we'll 466 * keep having a working one to report failures to. 467 */ 468 static int kRedirectSaveHandle(REDIRECTORDERS *pToSave, unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr) 469 { 470 int fdToSave = pToSave->fdTarget; 471 int rcRet = 10; 472 473 /* 474 * First, check if there's actually handle here that needs saving. 475 */ 476 # ifdef KBUILD_OS_WINDOWS 477 HANDLE hToSave = mscGetOsHandle(fdToSave); 478 if (hToSave != INVALID_HANDLE_VALUE) 479 { 480 pToSave->fSaved = _setmode(fdToSave, _O_BINARY); 481 if (pToSave->fSaved != _O_BINARY) 482 _setmode(fdToSave, pToSave->fSaved); 483 if (!mscIsNativeHandleInheritable(hToSave)) 484 pToSave->fSaved |= _O_NOINHERIT; 485 } 486 if (hToSave != INVALID_HANDLE_VALUE) 487 # else 488 pToSave->fSaved = fcntl(pToSave->fdTarget, F_GETFD, 0); 489 if (pToSave->fSaved != -1) 490 # endif 491 { 492 /* 493 * Try up to 32 times to get a duplicate descriptor that doesn't conflict. 494 */ 495 # ifdef KBUILD_OS_WINDOWS 496 HANDLE hCurProc = GetCurrentProcess(); 497 # endif 498 int aFdTries[32]; 499 int cTries = 0; 500 do 501 { 502 /* Duplicate the handle (windows makes this complicated). */ 503 int fdDup; 504 # ifdef KBUILD_OS_WINDOWS 505 HANDLE hDup = INVALID_HANDLE_VALUE; 506 if (!DuplicateHandle(hCurProc, hToSave, hCurProc, &hDup, 0 /* DesiredAccess */, 507 FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS)) 508 { 509 fprintf(*ppWorkingStdErr, "%s: DuplicateHandle(%#x) failed: %u\n", g_progname, hToSave, GetLastError()); 510 break; 511 } 512 fdDup = _open_osfhandle((intptr_t)hDup, pToSave->fSaved | _O_NOINHERIT); 513 if (fdDup == -1) 514 { 515 fprintf(*ppWorkingStdErr, "%s: _open_osfhandle(%#x) failed: %u\n", g_progname, hDup, strerror(errno)); 516 CloseHandle(hDup); 517 break; 518 } 519 # else 520 fdDup = dup(fdToSave); 521 if (fdDup == -1) 522 { 523 fprintf(*ppWorkingStdErr, "%s: dup(%#x) failed: %u\n", g_progname, fdToSave, strerror(errno)); 524 break; 525 } 526 #endif 527 /* Is the duplicate usable? */ 528 if (!kRedirectHasConflict(fdDup, cOrders, paOrders)) 529 { 530 pToSave->fdSaved = fdDup; 531 if ( *ppWorkingStdErr == stderr 532 && fdToSave == fileno(*ppWorkingStdErr)) 533 { 534 *ppWorkingStdErr = fdopen(fdDup, "wt"); 535 if (*ppWorkingStdErr == NULL) 536 { 537 fprintf(stderr, "%s: fdopen(%d,\"wt\") failed: %s\n", g_progname, fdDup, strerror(errno)); 538 *ppWorkingStdErr = stderr; 539 close(fdDup); 540 break; 541 } 542 } 543 rcRet = 0; 544 break; 545 } 546 547 /* Not usuable, stash it and try again. */ 548 aFdTries[cTries++] = fdDup; 549 } while (cTries < K_ELEMENTS(aFdTries)); 550 551 /* 552 * Clean up unused duplicates. 553 */ 554 while (cTries-- > 0) 555 close(aFdTries[cTries]); 556 } 557 else 558 { 559 /* 560 * Nothing to save. 561 */ 562 pToSave->fdSaved = -1; 563 rcRet = 0; 564 } 565 return rcRet; 566 } 567 568 569 /** 570 * Cleans up the file operation orders. 571 * 572 * This does not restore stuff, just closes handles we've opened for the guest. 573 * 574 * @param cOrders Number of file operation orders. 575 * @param paOrders The file operation orders. 576 * @param fFailed Set if it's a failure. 577 */ 578 static void kRedirectCleanupFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, KBOOL fFailure) 579 { 580 unsigned i = cOrders; 581 while (i-- > 0) 582 { 583 if ( paOrders[i].enmOrder == kRedirectOrder_Open 584 && paOrders[i].fdSource != -1) 585 { 586 close(paOrders[i].fdSource); 587 paOrders[i].fdSource = -1; 588 if ( fFailure 589 && paOrders[i].fRemoveOnFailure 590 && paOrders[i].pszFilename) 591 remove(paOrders[i].pszFilename); 592 } 593 } 594 } 595 596 597 /** 598 * Restores the target file descriptors affected by the file operation orders. 599 * 600 * @param cOrders Number of file operation orders. 601 * @param paOrders The file operation orders. 602 * @param ppWorkingStdErr Pointer to a pointer to the working stderr. If this 603 * is one of the saved file descriptors, we'll restore 604 * it to stderr. 605 */ 606 static void kRedirectRestoreFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr) 607 { 608 int iSavedErrno = errno; 609 unsigned i = cOrders; 610 while (i-- > 0) 611 { 612 if (paOrders[i].fdSaved != -1) 613 { 614 KBOOL fRestoreStdErr = *ppWorkingStdErr != stderr 615 && paOrders[i].fdSaved == fileno(*ppWorkingStdErr); 616 617 #ifdef KBUILD_OS_WINDOWS 618 if (mscDup3(paOrders[i].fdSaved, paOrders[i].fdTarget, paOrders[i].fSaved, *ppWorkingStdErr) != -1) 619 #else 620 if (dup2(paOrders[i].fdSaved, paOrders[i].fdTarget) != -1) 621 #endif 622 { 623 close(paOrders[i].fdSaved); 624 paOrders[i].fdSaved = -1; 625 626 if (fRestoreStdErr) 627 { 628 *ppWorkingStdErr = stderr; 629 assert(fileno(stderr) == paOrders[i].fdTarget); 630 } 631 } 632 #ifndef KBUILD_OS_WINDOWS 633 else 634 fprintf(*ppWorkingStdErr, "%s: dup2(%d,%d) failed: %s", 635 g_progname, paOrders[i].fdSaved, paOrders[i].fdTarget, strerror(errno)); 636 #endif 637 } 638 639 #ifndef KBUILD_OS_WINDOWS 640 if (paOrders[i].fSaved != -1) 641 { 642 if (fcntl(paOrders[i].fdTarget, F_SETFD, paOrders[i].fSaved & FD_CLOEXEC) == -1) 643 paOrders[i].fSaved = -1; 644 else 645 fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_SETFD,%s) failed: %s", 646 g_progname, paOrders[i].fdTarget, paOrders[i].fSaved & FD_CLOEXEC ? "FD_CLOEXEC" : "0", strerror(errno)); 647 } 648 #endif 649 } 650 errno = iSavedErrno; 651 } 652 653 654 /** 655 * Executes the file operation orders. 656 * 657 * @returns 0 on success, exit code on failure. 658 * @param cOrders Number of file operation orders. 659 * @param paOrders File operation orders to execute. 660 * @param ppWorkingStdErr Where to return a working stderr (mainly for 661 * kRedirectRestoreFdOrders). 662 */ 663 static int kRedirectExecFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr) 664 { 665 unsigned i; 666 667 *ppWorkingStdErr = stderr; 668 for (i = 0; i < cOrders; i++) 669 { 670 int rcExit = 10; 671 switch (paOrders[i].enmOrder) 672 { 673 case kRedirectOrder_Close: 674 { 675 /* If the handle isn't used by any of the following operation, 676 just mark it as non-inheritable if necessary. */ 677 int const fdTarget = paOrders[i].fdTarget; 678 unsigned j; 679 for (j = i + 1; j < cOrders; j++) 680 if (paOrders[j].fdTarget == fdTarget) 681 break; 682 # ifdef _MSC_VER 683 if (j >= cOrders && !mscIsInheritable(fdTarget)) 684 rcExit = 0; 685 # else 686 if (j >= cOrders) 687 { 688 paOrders[j].fSaved = fcntl(fdTarget, F_GETFD, 0); 689 if (paOrders[j].fSaved != -1) 690 { 691 if (paOrders[j].fSaved & FD_CLOEXEC) 692 rcExit = 0; 693 else if ( fcntl(fdTarget, F_SETFD, FD_CLOEXEC) != -1 694 || errno == EBADF) 695 rcExit = 0; 696 else 697 fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_SETFD,FD_CLOEXEC) failed: %s", 698 g_progname, fdTarget, strerror(errno)); 699 } 700 else if (errno == EBADF) 701 rcExit = 0; 702 else 703 fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_GETFD,0) failed: %s", g_progname, fdTarget, strerror(errno)); 704 } 705 # endif 706 else 707 rcExit = kRedirectSaveHandle(&paOrders[i], cOrders, paOrders, ppWorkingStdErr); 708 break; 709 } 710 711 case kRedirectOrder_Dup: 712 case kRedirectOrder_Open: 713 rcExit = kRedirectSaveHandle(&paOrders[i], cOrders, paOrders, ppWorkingStdErr); 714 if (rcExit == 0) 715 { 716 if (dup2(paOrders[i].fdSource, paOrders[i].fdTarget) != -1) 717 rcExit = 0; 718 else 719 { 720 if (paOrders[i].enmOrder == kRedirectOrder_Open) 721 fprintf(*ppWorkingStdErr, "%s: dup2(%d [%s],%d) failed: %s", g_progname, paOrders[i].fdSource, 722 paOrders[i].pszFilename, paOrders[i].fdTarget, strerror(errno)); 723 else 724 fprintf(*ppWorkingStdErr, "%s: dup2(%d,%d) failed: %s", 725 g_progname, paOrders[i].fdSource, paOrders[i].fdTarget, strerror(errno)); 726 rcExit = 10; 727 } 728 } 729 break; 730 731 default: 732 fprintf(*ppWorkingStdErr, "%s: error! invalid enmOrder=%d\n", g_progname, paOrders[i].enmOrder); 733 rcExit = 99; 734 break; 735 } 736 737 if (rcExit != 0) 738 { 739 kRedirectRestoreFdOrders(i, paOrders, ppWorkingStdErr); 740 return rcExit; 741 } 742 } 743 744 return 0; 745 } 746 747 #endif /* !USE_POSIX_SPAWN */ 748 749 750 /** 751 * Does the child spawning . 752 * 753 * @returns Exit code. 754 * @param pszExecutable The child process executable. 755 * @param cArgs Number of arguments. 756 * @param papszArgs The child argument vector. 757 * @param fWatcomBrainDamage Whether MSC need to do quoting according to 758 * weird Watcom WCC rules. 759 * @param papszEnv The child environment vector. 760 * @param pszCwd The current working directory of the child. 761 * @param pszSavedCwd The saved current working directory. This is 762 * NULL if the CWD doesn't need changing. 763 * @param cOrders Number of file operation orders. 764 * @param paOrders The file operation orders. 765 * @param pFileActions The posix_spawn file actions. 766 * @param cVerbosity The verbosity level. 767 * @param pPidSpawned Where to return the PID of the spawned child 768 * when we're inside KMK and we're return without 769 * waiting. 770 * @param pfIsChildExitCode Where to indicate whether the return exit code 771 * is from the child or from our setup efforts. 772 */ 773 static int kRedirectDoSpawn(const char *pszExecutable, int cArgs, char **papszArgs, int fWatcomBrainDamage, char **papszEnv, 774 const char *pszCwd, const char *pszSavedCwd, unsigned cOrders, REDIRECTORDERS *paOrders, 775 #ifdef USE_POSIX_SPAWN 776 posix_spawn_file_actions_t *pFileActions, 777 #endif 778 unsigned cVerbosity, 779 #ifdef KMK 780 pid_t *pPidSpawned, 781 #endif 782 KBOOL *pfIsChildExitCode) 783 { 784 int rcExit; 785 int i; 786 #ifdef _MSC_VER 787 char **papszArgsOriginal = papszArgs; 788 #endif 789 *pfIsChildExitCode = K_FALSE; 790 791 #ifdef _MSC_VER 792 /* 793 * Do MSC parameter quoting. 794 */ 795 papszArgs = malloc((cArgs + 1) * sizeof(papszArgs[0])); 796 if (papszArgs) 797 memcpy(papszArgs, papszArgsOriginal, (cArgs + 1) * sizeof(papszArgs[0])); 798 else 799 return errx(9, "out of memory!"); 800 801 rcExit = quote_argv(cArgs, papszArgs, fWatcomBrainDamage, 0 /*fFreeOrLeak*/); 802 if (rcExit == 0) 803 #endif 804 { 805 /* 806 * Display what we're about to execute if we're in verbose mode. 807 */ 808 if (cVerbosity > 0) 809 { 810 for (i = 0; i < cArgs; i++) 811 warnx("debug: argv[%i]=%s<eos>", i, papszArgs[i]); 812 for (i = 0; i < (int)cOrders; i++) 813 switch (paOrders[i].enmOrder) 814 { 815 case kRedirectOrder_Close: 816 warnx("debug: close %d\n", paOrders[i].fdTarget); 817 break; 818 case kRedirectOrder_Dup: 819 warnx("debug: dup %d to %d\n", paOrders[i].fdSource, paOrders[i].fdTarget); 820 break; 821 case kRedirectOrder_Open: 822 warnx("debug: open '%s' (%#x) as [%d ->] %d\n", 823 paOrders[i].pszFilename, paOrders[i].fOpen, paOrders[i].fdSource, paOrders[i].fdTarget); 824 break; 825 default: 826 warnx("error! invalid enmOrder=%d", paOrders[i].enmOrder); 827 assert(0); 828 break; 829 } 830 if (pszSavedCwd) 831 warnx("debug: chdir %s\n", pszCwd); 832 } 833 834 /* 835 * Change working directory if so requested. 836 */ 837 if (pszSavedCwd) 838 { 839 if (chdir(pszCwd) < 0) 840 rcExit = errx(10, "Failed to change directory to '%s'", pszCwd); 841 } 842 if (rcExit == 0) 843 { 844 #ifndef USE_POSIX_SPAWN 845 /* 846 * Execute the file orders. 847 */ 848 FILE *pWorkingStdErr = NULL; 849 rcExit = kRedirectExecFdOrders(cOrders, paOrders, &pWorkingStdErr); 850 if (rcExit == 0) 851 #endif 852 { 853 #ifdef KMK 854 /* 855 * We're spawning from within kmk. 856 */ 857 #if defined(KBUILD_OS_WINDOWS) 858 /* Windows is slightly complicated due to handles and sub_proc.c. */ 859 HANDLE hProcess = (HANDLE)_spawnvpe(_P_NOWAIT, pszExecutable, papszArgs, papszEnv); 860 kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr); 861 if ((intptr_t)hProcess != -1) 862 { 863 if (process_kmk_register_redirect(hProcess, pPidSpawned) == 0) 864 { 865 if (cVerbosity > 0) 866 warnx("debug: spawned %d", *pPidSpawned); 867 } 868 else 869 { 870 DWORD dwTmp; 871 warn("sub_proc is out of slots, waiting for child..."); 872 dwTmp = WaitForSingleObject(hProcess, INFINITE); 873 if (dwTmp != WAIT_OBJECT_0) 874 warn("WaitForSingleObject failed: %#x\n", dwTmp); 875 876 if (GetExitCodeProcess(hProcess, &dwTmp)) 877 rcExit = (int)dwTmp; 878 else 879 { 880 warn("GetExitCodeProcess failed: %u\n", GetLastError()); 881 TerminateProcess(hProcess, 127); 882 rcExit = 127; 883 } 884 885 CloseHandle(hProcess); 886 *pPidSpawned = 0; 887 *pfIsChildExitCode = K_TRUE; 888 } 889 } 890 else 891 rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable); 892 893 # elif defined(KBUILD_OS_OS2) 894 *pPidSpawned = _spawnve(_P_NOWAIT, pszExecutable, papszArgs, papszEnv); 895 kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr); 896 if (*pPidSpawned != -1) 897 { 898 if (cVerbosity > 0) 899 warnx("debug: spawned %d", *pPidSpawned); 900 } 901 else 902 { 903 rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable); 904 *pPidSpawned = 0; 905 } 906 #else 907 rcExit = posix_spawnp(pPidSpawned, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars); 908 if (rcExit == 0) 909 { 910 if (cVerbosity > 0) 911 warnx("debug: spawned %d", *pPidSpawned); 912 } 913 else 914 { 915 rcExit = errx(10, "posix_spawnp(%s) failed: %s", pszExecutable, strerror(rcExit)); 916 *pPidSpawned = 0; 917 } 918 #endif 919 920 #else /* !KMK */ 921 /* 922 * Spawning from inside the kmk_redirect executable. 923 */ 924 # if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2) 925 errno = 0; 926 rcExit = (int)_spawnvpe(_P_WAIT, pszExecutable, papszArgs, papszEnv); 927 kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr); 928 if (rcExit != -1 || errno == 0) 929 { 930 *pfIsChildExitCode = K_TRUE; 931 if (cVerbosity > 0) 932 warnx("debug: exit code: %d", rcExit); 933 } 934 else 935 rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable); 936 937 # else 938 pid_t pidChild = 0; 939 rcExit = posix_spawnp(&pidChild, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars); 940 if (rcExit == 0) 941 { 942 *pfIsChildExitCode = K_TRUE; 943 if (cVerbosity > 0) 944 warnx("debug: spawned %d", pidChild); 945 946 /* Wait for the child. */ 947 for (;;) 948 { 949 pid_t pid = waitpid(pidChild, &rcExit, 0 /*block*/); 950 if (pid == pidChild) 951 { 952 if (cVerbosity > 0) 953 warnx("debug: %d exit code: %d", pidChild, rcExit); 954 break; 955 } 956 if ( errno != EINTR 957 # ifdef ERESTART 958 && errno != ERESTART 959 # endif 960 ) 961 { 962 rcExit = err(11, "waitpid failed"); 963 kill(pidChild, SIGKILL); 964 break; 965 } 966 } 967 } 968 else 969 rcExit = errx(10, "posix_spawnp(%s) failed: %s", pszExecutable, strerror(rcExit)); 970 971 # endif 972 #endif /* !KMK */ 973 } 974 } 975 976 /* 977 * Restore the current directory. 978 */ 979 if (pszSavedCwd) 980 { 981 if (chdir(pszSavedCwd) < 0) 982 warn("Failed to restore directory to '%s'", pszSavedCwd); 983 } 984 } 985 #ifdef _MSC_VER 986 else 987 rcExit = errx(9, "quite_argv failed: %u", rcExit); 988 989 /* Restore the original argv strings, freeing the quote_argv replacements. */ 990 i = cArgs; 991 while (i-- > 0) 992 if (papszArgs[i] != papszArgsOriginal[i]) 993 free(papszArgs[i]); 994 free(papszArgs); 995 #endif 996 return rcExit; 997 } 998 999 1000 /** 1001 * The function that does almost everything here... ugly. 1002 */ 1003 #ifdef KMK 1004 int kmk_builtin_redirect(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned) 1005 #else 145 1006 int main(int argc, char **argv, char **envp) 146 { 147 int i; 148 int j; 149 #if defined(_MSC_VER) 150 intptr_t rc; 151 #endif 152 FILE *pStdErr = stderr; 153 FILE *pStdOut = stdout; 154 int fWatcomBrainDamage = 0; 1007 #endif 1008 { 1009 int rcExit = 0; 1010 KBOOL fChildExitCode = K_FALSE; 1011 #ifdef USE_POSIX_SPAWN 1012 posix_spawn_file_actions_t FileActions; 1013 #endif 1014 unsigned cOrders = 0; 1015 REDIRECTORDERS aOrders[32]; 1016 1017 int iArg; 1018 const char *pszExecutable = NULL; 1019 char **papszEnv = NULL; 1020 unsigned cAllocatedEnvVars; 1021 unsigned iEnvVar; 1022 unsigned cEnvVars; 1023 int fWatcomBrainDamage = 0; 1024 int cVerbosity = 0; 1025 char *pszSavedCwd = NULL; 1026 size_t const cbCwdBuf = GET_PATH_MAX; 1027 PATH_VAR(szCwd); 1028 #ifdef KBUILD_OS_OS2 1029 ULONG ulLibPath; 1030 char *apszSavedLibPaths[LIBPATHSTRICT + 1] = { NULL, NULL, NULL, NULL }; 1031 #endif 1032 1033 1034 g_progname = argv[0]; 1035 1036 if (argc <= 1) 1037 return usage(stderr, argv[0]); 1038 1039 /* 1040 * Create default program environment. 1041 */ 1042 #if defined(KMK) && defined(KBUILD_OS_WINDOWS) 1043 if (getcwd_fs(szCwd, cbCwdBuf) != NULL) 1044 #else 1045 if (getcwd(szCwd, cbCwdBuf) != NULL) 1046 #endif 1047 { /* likely */ } 1048 else 1049 return err(9, "getcwd failed"); 1050 1051 #if defined(KMK) 1052 /* We get it from kmk and just count it: */ 1053 papszEnv = pChild->environment; 1054 if (!papszEnv) 1055 pChild->environment = papszEnv = target_environment(pChild->file); 1056 cEnvVars = 0; 1057 while (papszEnv[cEnvVars] != NULL) 1058 cEnvVars++; 1059 cAllocatedEnvVars = cEnvVars; 1060 #else 1061 /* We make a copy and we manage ourselves: */ 1062 cEnvVars = 0; 1063 while (envp[cEnvVars] != NULL) 1064 cEnvVars++; 1065 1066 cAllocatedEnvVars = cEnvVars + 4; 1067 papszEnv = malloc((cAllocatedEnvVars + 1) * sizeof(papszEnv)); 1068 if (!papszEnv) 1069 return errx(9, "out of memory!"); 1070 1071 iEnvVar = cEnvVars; 1072 papszEnv[iEnvVar] = NULL; 1073 while (iEnvVar-- > 0) 1074 { 1075 papszEnv[iEnvVar] = strdup(envp[iEnvVar]); 1076 if (!papszEnv[iEnvVar]) 1077 { 1078 while (iEnvVar-- > 0) 1079 free(papszEnv[iEnvVar]); 1080 free(papszEnv); 1081 return errx(9, "out of memory!"); 1082 } 1083 } 1084 #endif 1085 1086 #ifdef USE_POSIX_SPAWN 1087 /* 1088 * Init posix attributes. 1089 */ 1090 rcExit = posix_spawn_file_actions_init(&FileActions); 1091 if (rcExit != 0) 1092 rcExit = errx(9, "posix_spawn_file_actions_init failed: %s", strerror(rcExit)); 1093 #endif 155 1094 156 1095 /* 157 1096 * Parse arguments. 158 1097 */ 159 if (argc <= 1) 160 return usage(pStdErr, name(argv[0])); 161 for (i = 1; i < argc; i++) 1098 for (iArg = 1; rcExit == 0 && iArg < argc; iArg++) 162 1099 { 163 if (argv[i][0] == '-') 1100 char *pszArg = argv[iArg]; 1101 if (*pszArg == '-') 164 1102 { 165 int fd; 166 int fdOpened; 167 int fOpen; 168 char *psz = &argv[i][1]; 169 if (*psz == '-') 170 { 171 /* '--' ? */ 172 if (!psz[1]) 173 { 174 i++; 1103 int fd; 1104 char chOpt; 1105 const char *pszValue; 1106 1107 chOpt = *++pszArg; 1108 pszArg++; 1109 if (chOpt == '-') 1110 { 1111 /* '--' indicates where the bits to execute start. */ 1112 if (*pszArg == '\0') 1113 { 1114 iArg++; 175 1115 break; 176 1116 } 177 1117 178 /* convert to short. */ 179 if (!strcmp(psz, "-help")) 180 psz = "h"; 181 else if (!strcmp(psz, "-version")) 182 psz = "V"; 183 else if (!strcmp(psz, "-env")) 184 psz = "E"; 185 else if (!strcmp(psz, "-chdir")) 186 psz = "C"; 187 else if (!strcmp(psz, "-zap-env")) 188 psz = "Z"; 189 else if (!strcmp(psz, "-close")) 190 psz = "c"; 191 else if (!strcmp(psz, "-wcc-brain-damage")) 1118 if ( strcmp(pszArg, "wcc-brain-damage") == 0 1119 || strcmp(pszArg, "watcom-brain-damage") == 0) 192 1120 { 193 1121 fWatcomBrainDamage = 1; 194 1122 continue; 195 1123 } 1124 1125 /* convert to short. */ 1126 if (strcmp(pszArg, "help") == 0) 1127 chOpt = 'h'; 1128 else if (strcmp(pszArg, "version") == 0) 1129 chOpt = 'V'; 1130 else if ( strcmp(pszArg, "set") == 0 1131 || strcmp(pszArg, "env") == 0) 1132 chOpt = 'E'; 1133 else if (strcmp(pszArg, "unset") == 0) 1134 chOpt = 'U'; 1135 else if ( strcmp(pszArg, "zap-env") == 0 1136 || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ ) 1137 chOpt = 'Z'; 1138 else if (strcmp(pszArg, "chdir") == 0) 1139 chOpt = 'C'; 1140 else if (strcmp(pszArg, "close") == 0) 1141 chOpt = 'c'; 1142 else if (strcmp(pszArg, "verbose") == 0) 1143 chOpt = 'v'; 1144 else 1145 { 1146 errx(2, "Unknown option: '%s'", pszArg - 2); 1147 rcExit = usage(stderr, argv[0]); 1148 break; 1149 } 1150 pszArg = ""; 196 1151 } 197 1152 198 1153 /* 199 * Deal with the obligatory help and version switches first .1154 * Deal with the obligatory help and version switches first to get them out of the way. 200 1155 */ 201 if (*psz == 'h') 202 { 203 usage(pStdOut, name(argv[0])); 204 return 0; 205 } 206 if (*psz == 'V') 207 { 208 printf("kmk_redirect - kBuild version %d.%d.%d (r%u)\n" 209 "Copyright (C) 2007-2012 knut st. osmundsen\n", 210 KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH, 211 KBUILD_SVN_REV); 212 return 0; 213 } 1156 if (chOpt == 'h') 1157 { 1158 usage(stdout, argv[0]); 1159 rcExit = -1; 1160 break; 1161 } 1162 if (chOpt == 'V') 1163 { 1164 kbuild_version(argv[0]); 1165 rcExit = -1; 1166 break; 1167 } 1168 1169 /* 1170 * Get option value first, if the option takes one. 1171 */ 1172 if ( chOpt == 'E' 1173 || chOpt == 'U' 1174 || chOpt == 'C' 1175 || chOpt == 'c' 1176 || chOpt == 'd' 1177 || chOpt == 'e') 1178 { 1179 if (*pszArg != '\0') 1180 pszValue = pszArg + (*pszArg == ':' || *pszArg == '='); 1181 else if (++iArg < argc) 1182 pszValue = argv[iArg]; 1183 else 1184 { 1185 errx(2, "syntax error: Option -%c requires a value!", chOpt); 1186 rcExit = usage(stderr, argv[0]); 1187 break; 1188 } 1189 } 1190 else 1191 pszValue = NULL; 214 1192 215 1193 /* 216 1194 * Environment switch? 217 1195 */ 218 if (*psz == 'E') 219 { 220 psz++; 221 if (*psz == ':' || *psz == '=') 222 psz++; 223 else 224 { 225 if (i + 1 >= argc) 1196 if (chOpt == 'E') 1197 { 1198 const char *pchEqual = strchr(pszValue, '='); 1199 #ifdef KBUILD_OS_OS2 1200 if ( strncmp(pszValue, TUPLE("BEGINLIBPATH=")) == 0 1201 || strncmp(pszValue, TUPLE("ENDLIBPATH=")) == 0 1202 || strncmp(pszValue, TUPLE("LIBPATHSTRICT=")) == 0) 1203 { 1204 ULONG ulVar = *pszValue == 'B' ? BEGIN_LIBPATH 1205 : *pszValue == 'E' ? END_LIBPATH 1206 : LIBPATHSTRICT; 1207 APIRET rc; 1208 if (apszSavedLibPaths[ulVar] == NULL) 226 1209 { 227 fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]); 228 return 1; 1210 /* The max length is supposed to be 1024 bytes. */ 1211 apszSavedLibPaths[ulVar] = calloc(1024 * 2); 1212 if (apszSavedLibPaths[ulVar]) 1213 { 1214 rc = DosQueryExtLIBPATH(apszSavedLibPaths[ulVar], ulVar); 1215 if (rc) 1216 { 1217 rcExit = errx(9, "DosQueryExtLIBPATH(,%u) failed: %lu", ulVar, rc); 1218 free(apszSavedLibPaths[ulVar]); 1219 apszSavedLibPaths[ulVar] = NULL; 1220 } 1221 } 1222 else 1223 rcExit = errx(9, "out of memory!"); 229 1224 } 230 psz = argv[++i]; 231 } 232 #ifdef __OS2__ 233 if ( !strncmp(psz, "BEGINLIBPATH=", sizeof("BEGINLIBPATH=") - 1) 234 || !strncmp(psz, "ENDLIBPATH=", sizeof("ENDLIBPATH=") - 1) 235 || !strncmp(psz, "LIBPATHSTRICT=", sizeof("LIBPATHSTRICT=") - 1)) 236 { 237 ULONG ulVar = *psz == 'B' ? BEGIN_LIBPATH 238 : *psz == 'E' ? END_LIBPATH 239 : LIBPATHSTRICT; 240 const char *pszVal = strchr(psz, '=') + 1; 241 APIRET rc = DosSetExtLIBPATH(pszVal, ulVar); 242 if (rc) 1225 if (rcExit == 0) 243 1226 { 244 fprintf(pStdErr, "%s: error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu\n", 245 name(argv[0]), pszVal, pszVal - psz - 1, psz, ulVar, rc); 246 return 1; 1227 rc = DosSetExtLIBPATH(pchEqual + 1, ulVar); 1228 if (rc) 1229 rcExit = errx(9, "error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu", 1230 pchEqual, pchEqual - pszValue, pchEqual + 1, ulVar, rc); 247 1231 } 248 } 249 else 250 #endif /* __OS2__ */ 251 { 252 const char *pchEqual = strchr(psz, '='); 253 if (pchEqual && pchEqual[1] != '\0') 1232 continue; 1233 } 1234 #endif /* KBUILD_OS_OS2 */ 1235 1236 /* We differ from kSubmit here and use putenv sematics. */ 1237 if (pchEqual) 1238 { 1239 if (pchEqual[1] != '\0') 254 1240 { 255 if (putenv(psz)) 256 { 257 fprintf(pStdErr, "%s: error: putenv(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno)); 258 return 1; 259 } 1241 rcExit = kBuiltinOptEnvSet(&papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue); 1242 #ifdef KMK 1243 pChild->environment = papszEnv; 1244 #endif 260 1245 } 261 1246 else 262 1247 { 263 size_t cchVar = pchEqual ? (size_t)(pchEqual - psz) : strlen(psz); 264 char *pszCopy = (char *)malloc(cchVar + 2); 265 memcpy(pszCopy, psz, cchVar); 266 267 #if defined(_MSC_VER) || defined(__OS2__) 268 pszCopy[cchVar] = '='; 269 pszCopy[cchVar + 1] = '\0'; 270 if (putenv(pszCopy)) 1248 char *pszCopy = strdup(pszValue); 1249 if (pszCopy) 271 1250 { 272 fprintf(pStdErr, "%s: error: putenv(\"%s\"): %s\n", name(argv[0]), pszCopy, strerror(errno)); 273 return 1; 1251 pszCopy[pchEqual - pszValue] = '\0'; 1252 rcExit = kBuiltinOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszCopy); 1253 free(pszCopy); 274 1254 } 275 #else 276 pszCopy[cchVar] = '\0'; 277 if (unsetenv(pszCopy)) 278 { 279 fprintf(pStdErr, "%s: error: unsetenv(\"%s\"): %s\n", name(argv[0]), pszCopy, strerror(errno)); 280 return 1; 281 } 282 #endif 283 free(pszCopy); 1255 else 1256 rcExit = errx(1, "out of memory!"); 284 1257 } 285 } 1258 continue; 1259 } 1260 /* Simple unset. */ 1261 chOpt = 'U'; 1262 } 1263 1264 /* 1265 * Unset environment variable. 1266 */ 1267 if (chOpt == 'U') 1268 { 1269 #ifdef KBUILD_OS_OS2 1270 if ( strcmp(pszValue, "BEGINLIBPATH") == 0 1271 || strcmp(pszValue, "ENDLIBPATH") == 0 1272 || strcmp(pszValue, "LIBPATHSTRICT") == 0) 1273 rcExit = errx(2, "error: '%s' cannot be unset, only set to an empty value using -E/--set.", pszValue); 1274 else 1275 #endif 1276 rcExit = kBuiltinOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue); 1277 continue; 1278 } 1279 1280 /* 1281 * Zap environment switch? 1282 */ 1283 if ( chOpt == 'Z' 1284 || chOpt == 'i' /* GNU env compatibility. */ ) 1285 { 1286 for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++) 1287 free(papszEnv[iEnvVar]); 1288 papszEnv[0] = NULL; 1289 cEnvVars = 0; 286 1290 continue; 287 1291 } … … 290 1294 * Change directory switch? 291 1295 */ 292 if (*psz == 'C') 293 { 294 psz++; 295 if (*psz == ':' || *psz == '=') 296 psz++; 1296 if (chOpt == 'C') 1297 { 1298 if (pszSavedCwd == NULL) 1299 pszSavedCwd = strdup(szCwd); 1300 if (pszSavedCwd) 1301 rcExit = kBuiltinOptChDir(szCwd, cbCwdBuf, pszValue); 297 1302 else 298 { 299 if (i + 1 >= argc) 300 { 301 fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]); 302 return 1; 303 } 304 psz = argv[++i]; 305 } 306 if (!chdir(psz)) 307 continue; 308 #ifdef _MSC_VER 309 { 310 /* drop trailing slash if any. */ 311 size_t cch = strlen(psz); 312 if ( cch > 2 313 && (psz[cch - 1] == '/' || psz[cch - 1] == '\\') 314 && psz[cch - 1] != ':') 315 { 316 int rc2; 317 char *pszCopy = strdup(psz); 318 do pszCopy[--cch] = '\0'; 319 while ( cch > 2 320 && (pszCopy[cch - 1] == '/' || pszCopy[cch - 1] == '\\') 321 && pszCopy[cch - 1] != ':'); 322 rc2 = chdir(pszCopy); 323 free(pszCopy); 324 if (!rc2) 325 continue; 326 } 327 } 328 #endif 329 fprintf(pStdErr, "%s: error: chdir(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno)); 330 return 1; 331 } 332 333 /* 334 * Zap environment switch? 335 * This is a bit of a hack. 336 */ 337 if (*psz == 'Z') 338 { 339 unsigned j = 0; 340 while (envp[j] != NULL) 341 j++; 342 while (j-- > 0) 343 { 344 char *pszEqual = strchr(envp[j], '='); 345 char *pszCopy; 346 347 if (pszEqual) 348 *pszEqual = '\0'; 349 pszCopy = strdup(envp[j]); 350 if (pszEqual) 351 *pszEqual = '='; 352 353 #if defined(_MSC_VER) || defined(__OS2__) 354 putenv(pszCopy); 355 #else 356 unsetenv(pszCopy); 357 #endif 358 free(pszCopy); 359 } 1303 rcExit = err(9, "out of memory!"); 360 1304 continue; 361 1305 } 1306 362 1307 363 1308 /* 364 1309 * Verbose operation switch? 365 1310 */ 366 if ( *psz== 'v')367 { 368 g_cVerbosity++;1311 if (chOpt == 'v') 1312 { 1313 cVerbosity++; 369 1314 continue; 370 1315 } 371 1316 372 1317 /* 373 * Close the specified file descriptor (no stderr/out/in aliases).1318 * Executable image other than the first argument following '--'. 374 1319 */ 375 if (*psz == 'c') 376 { 377 psz++; 378 if (!*psz) 379 { 380 i++; 381 if (i >= argc) 1320 if (chOpt == 'e') 1321 { 1322 pszExecutable = pszValue; 1323 continue; 1324 } 1325 1326 /* 1327 * Okay, it is some file descriptor opearation. Make sure we've got room for it. 1328 */ 1329 if (cOrders + 1 < K_ELEMENTS(aOrders)) 1330 { 1331 aOrders[cOrders].fdTarget = -1; 1332 aOrders[cOrders].fdSource = -1; 1333 aOrders[cOrders].fOpen = 0; 1334 aOrders[cOrders].fRemoveOnFailure = 0; 1335 aOrders[cOrders].pszFilename = NULL; 1336 #ifndef USE_POSIX_SPAWN 1337 aOrders[cOrders].fdSaved = -1; 1338 #endif 1339 } 1340 else 1341 { 1342 rcExit = errx(2, "error: too many file actions (max: %d)", K_ELEMENTS(aOrders)); 1343 break; 1344 } 1345 1346 if (chOpt == 'c') 1347 { 1348 /* 1349 * Close the specified file descriptor (no stderr/out/in aliases). 1350 */ 1351 char *pszTmp; 1352 fd = (int)strtol(pszValue, &pszTmp, 0); 1353 if (pszTmp == pszValue || *pszTmp != '\0') 1354 rcExit = errx(2, "error: failed to convert '%s' to a number", pszValue); 1355 else if (fd < 0) 1356 rcExit = errx(2, "error: negative fd %d (%s)", fd, pszValue); 1357 else 1358 { 1359 aOrders[cOrders].enmOrder = kRedirectOrder_Close; 1360 aOrders[cOrders].fdTarget = fd; 1361 cOrders++; 1362 #ifdef USE_POSIX_SPAWN 1363 rcExit = posix_spawn_file_actions_addclose(&FileActions, fd); 1364 if (rcExit != 0) 1365 rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit)); 1366 #endif 1367 } 1368 } 1369 else if (chOpt == 'd') 1370 { 1371 /* 1372 * Duplicate file handle. Value is fdTarget=fdSource 1373 */ 1374 char *pszEqual; 1375 fd = (int)strtol(pszValue, &pszEqual, 0); 1376 if (pszEqual == pszValue) 1377 rcExit = errx(2, "error: failed to convert target descriptor of '-d %s' to a number", pszValue); 1378 else if (fd < 0) 1379 rcExit = errx(2, "error: negative target descriptor %d ('-d %s')", fd, pszValue); 1380 else if (*pszEqual != '=') 1381 rcExit = errx(2, "syntax error: expected '=' to follow target descriptor: '-d %s'", pszValue); 1382 else 1383 { 1384 char *pszEnd; 1385 int fdSource = (int)strtol(++pszEqual, &pszEnd, 0); 1386 if (pszEnd == pszEqual || *pszEnd != '\0') 1387 rcExit = errx(2, "error: failed to convert source descriptor of '-d %s' to a number", pszValue); 1388 else if (fdSource < 0) 1389 rcExit = errx(2, "error: negative source descriptor %d ('-d %s')", fdSource, pszValue); 1390 else 382 1391 { 383 fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0])); 384 return 1; 1392 aOrders[cOrders].enmOrder = kRedirectOrder_Dup; 1393 aOrders[cOrders].fdTarget = fd; 1394 aOrders[cOrders].fdSource = fdSource; 1395 cOrders++; 1396 #ifdef USE_POSIX_SPAWN 1397 rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdSource, fd); 1398 if (rcExit != 0) 1399 rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit)); 1400 #endif 385 1401 } 386 psz = argv[i]; 387 } 388 389 fd = (int)strtol(psz, &psz, 0); 390 if (!fd || *psz) 391 { 392 fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]); 393 return 1; 394 395 } 396 if (fd < 0) 397 { 398 fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]); 399 return 1; 400 } 401 /** @todo deal with stderr */ 402 safeCloseFd(fd); 403 continue; 404 } 405 406 /* 407 * Parse a file descriptor argument. 408 */ 409 410 /* mode */ 411 switch (*psz) 412 { 413 case 'r': 414 psz++; 415 if (*psz == '+') 1402 } 1403 } 1404 else 1405 { 1406 /* 1407 * Open file as a given file descriptor. 1408 */ 1409 int fdOpened; 1410 int fOpen; 1411 1412 /* mode */ 1413 switch (chOpt) 1414 { 1415 case 'r': 1416 chOpt = *pszArg++; 1417 if (chOpt == '+') 1418 { 1419 fOpen = O_RDWR; 1420 chOpt = *pszArg++; 1421 } 1422 else 1423 fOpen = O_RDONLY; 1424 break; 1425 1426 case 'w': 1427 chOpt = *pszArg++; 1428 if (chOpt == '+') 1429 { 1430 fOpen = O_RDWR | O_CREAT | O_TRUNC; 1431 chOpt = *pszArg++; 1432 } 1433 else 1434 fOpen = O_WRONLY | O_CREAT | O_TRUNC; 1435 aOrders[cOrders].fRemoveOnFailure = 1; 1436 break; 1437 1438 case 'a': 1439 chOpt = *pszArg++; 1440 if (chOpt == '+') 1441 { 1442 fOpen = O_RDWR | O_CREAT | O_APPEND; 1443 chOpt = *pszArg++; 1444 } 1445 else 1446 fOpen = O_WRONLY | O_CREAT | O_APPEND; 1447 break; 1448 1449 case 'i': /* make sure stdin is read-only. */ 1450 fOpen = O_RDONLY; 1451 break; 1452 1453 case '+': 1454 rcExit = errx(2, "syntax error: Unexpected '+' in '%s'", argv[iArg]); 1455 continue; 1456 1457 default: 1458 fOpen = O_RDWR | O_CREAT | O_TRUNC; 1459 aOrders[cOrders].fRemoveOnFailure = 1; 1460 break; 1461 } 1462 1463 /* binary / text modifiers */ 1464 switch (chOpt) 1465 { 1466 case 'b': 1467 chOpt = *pszArg++; 1468 default: 1469 #ifdef O_BINARY 1470 fOpen |= O_BINARY; 1471 #elif defined(_O_BINARY) 1472 fOpen |= _O_BINARY; 1473 #endif 1474 break; 1475 1476 case 't': 1477 #ifdef O_TEXT 1478 fOpen |= O_TEXT; 1479 #elif defined(_O_TEXT) 1480 fOpen |= _O_TEXT; 1481 #endif 1482 chOpt = *pszArg++; 1483 break; 1484 1485 } 1486 1487 /* convert to file descriptor number */ 1488 switch (chOpt) 1489 { 1490 case 'i': 1491 fd = 0; 1492 break; 1493 1494 case 'o': 1495 fd = 1; 1496 break; 1497 1498 case 'e': 1499 fd = 2; 1500 break; 1501 1502 case '0': 1503 if (*pszArg == '\0') 1504 { 1505 fd = 0; 1506 break; 1507 } 1508 case '1': 1509 case '2': 1510 case '3': 1511 case '4': 1512 case '5': 1513 case '6': 1514 case '7': 1515 case '8': 1516 case '9': 1517 pszValue = pszArg - 1; 1518 fd = (int)strtol(pszValue, &pszArg, 0); 1519 if (pszArg == pszValue) 1520 rcExit = errx(2, "error: failed to convert '%s' to a number", argv[iArg]); 1521 else if (fd < 0) 1522 rcExit = errx(2, "error: negative fd %d (%s)", fd, argv[iArg]); 1523 else 1524 break; 1525 continue; 1526 1527 /* 1528 * Invalid argument. 1529 */ 1530 default: 1531 rcExit = errx(2, "error: failed to convert '%s' ('%s') to a file descriptor", pszArg, argv[iArg]); 1532 continue; 1533 } 1534 1535 /* 1536 * Check for the filename. 1537 */ 1538 if (*pszArg != '\0') 1539 { 1540 if (*pszArg != ':' && *pszArg != '=') 416 1541 { 417 fOpen = O_RDWR; 418 psz++; 419 } 420 else 421 fOpen = O_RDONLY; 422 break; 423 424 case 'w': 425 psz++; 426 if (*psz == '+') 427 { 428 psz++; 429 fOpen = O_RDWR | O_CREAT | O_TRUNC; 430 } 431 else 432 fOpen = O_WRONLY | O_CREAT | O_TRUNC; 433 break; 434 435 case 'a': 436 psz++; 437 if (*psz == '+') 438 { 439 psz++; 440 fOpen = O_RDWR | O_CREAT | O_APPEND; 441 } 442 else 443 fOpen = O_WRONLY | O_CREAT | O_APPEND; 444 break; 445 446 case 'i': /* make sure stdin is read-only. */ 447 fOpen = O_RDONLY; 448 break; 449 450 case '+': 451 fprintf(pStdErr, "%s: syntax error: Unexpected '+' in '%s'\n", name(argv[0]), argv[i]); 452 return 1; 453 454 default: 455 fOpen = O_RDWR | O_CREAT | O_TRUNC; 456 break; 457 } 458 459 /* binary / text modifiers */ 460 switch (*psz) 461 { 462 case 'b': 463 #ifdef O_BINARY 464 fOpen |= O_BINARY; 465 #endif 466 psz++; 467 break; 468 469 case 't': 470 #ifdef O_TEXT 471 fOpen |= O_TEXT; 472 #endif 473 psz++; 474 break; 475 476 default: 477 #ifdef O_BINARY 478 fOpen |= O_BINARY; 479 #endif 480 break; 481 482 } 483 484 /* convert to file descriptor number */ 485 switch (*psz) 486 { 487 case 'i': 488 fd = 0; 489 psz++; 490 break; 491 492 case 'o': 493 fd = 1; 494 psz++; 495 break; 496 497 case 'e': 498 fd = 2; 499 psz++; 500 break; 501 502 case '0': 503 if (!psz[1]) 504 { 505 fd = 0; 506 psz++; 1542 rcExit = errx(2, "syntax error: characters following the file descriptor: '%s' ('%s')", 1543 pszArg, argv[iArg]); 507 1544 break; 508 1545 } 509 case '1': 510 case '2': 511 case '3': 512 case '4': 513 case '5': 514 case '6': 515 case '7': 516 case '8': 517 case '9': 518 fd = (int)strtol(psz, &psz, 0); 519 if (!fd) 1546 pszArg++; 1547 } 1548 else if (++iArg < argc) 1549 pszArg = argv[iArg]; 1550 else 1551 { 1552 rcExit = errx(2, "syntax error: missing filename argument."); 1553 break; 1554 } 1555 1556 /* 1557 * Open the file. We could've used posix_spawn_file_actions_addopen here, 1558 * but that means complicated error reporting. So, since we need to do 1559 * this for windows anyway, just do it the same way everywhere. 1560 */ 1561 fdOpened = kRedirectOpenWithoutConflict(pszArg, fOpen, 0666, cOrders, aOrders, 1562 aOrders[cOrders].fRemoveOnFailure, fd); 1563 if (fdOpened >= 0) 1564 { 1565 aOrders[cOrders].enmOrder = kRedirectOrder_Open; 1566 aOrders[cOrders].fdTarget = fd; 1567 aOrders[cOrders].fdSource = fdOpened; 1568 aOrders[cOrders].fOpen = fOpen; 1569 aOrders[cOrders].pszFilename = pszArg; 1570 cOrders++; 1571 1572 #ifdef USE_POSIX_SPAWN 1573 if (fdOpened != fdSource) 520 1574 { 521 fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]); 522 return 1; 523 1575 rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdOpened, fd); 1576 if (rcExit != 0) 1577 rcExit = err(9, "posix_spawn_file_actions_adddup2(,%d [%s], %d) failed: %s", 1578 fdOpened, fd, pszArg, strerror(rcExit)); 524 1579 } 525 if (fd < 0) 526 { 527 fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]); 528 return 1; 529 } 530 break; 531 532 /* 533 * Invalid argument. 534 */ 535 default: 536 fprintf(pStdErr, "%s: error: failed to convert '%s' ('%s') to a file descriptor\n", name(argv[0]), psz, argv[i]); 537 return 1; 538 } 539 540 /* 541 * Check for the filename. 542 */ 543 if (*psz) 544 { 545 if (*psz != ':' && *psz != '=') 546 { 547 fprintf(pStdErr, "%s: syntax error: characters following the file descriptor: '%s' ('%s')\n", name(argv[0]), psz, argv[i]); 548 return 1; 549 } 550 psz++; 551 } 552 else 553 { 554 i++; 555 if (i >= argc) 556 { 557 fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0])); 558 return 1; 559 } 560 psz = argv[i]; 561 } 562 563 /* 564 * Setup the redirection. 565 */ 566 if (fd == fileno(pStdErr)) 567 { 568 /* 569 * Move stderr to a new location, making it close on exec. 570 * If pStdOut has already teamed up with pStdErr, update it too. 571 */ 572 FILE *pNew; 573 fdOpened = dup(fileno(pStdErr)); 574 if (fdOpened == -1) 575 { 576 fprintf(pStdErr, "%s: error: failed to dup stderr (%d): %s\n", name(argv[0]), fileno(pStdErr), strerror(errno)); 577 return 1; 578 } 579 #ifdef _MSC_VER 580 /** @todo figure out how to make the handle close-on-exec. We'll simply close it for now. 581 * SetHandleInformation + set FNOINHERIT in CRT. 582 */ 583 #else 584 if (fcntl(fdOpened, F_SETFD, FD_CLOEXEC) == -1) 585 { 586 fprintf(pStdErr, "%s: error: failed to make stderr (%d) close-on-exec: %s\n", name(argv[0]), fdOpened, strerror(errno)); 587 return 1; 588 } 589 #endif 590 591 pNew = fdopen(fdOpened, "w"); 592 if (!pNew) 593 { 594 fprintf(pStdErr, "%s: error: failed to fdopen the new stderr (%d): %s\n", name(argv[0]), fdOpened, strerror(errno)); 595 return 1; 596 } 597 if (pStdOut == pStdErr) 598 pStdOut = pNew; 599 pStdErr = pNew; 600 } 601 else if (fd == 1 && pStdOut != pStdErr) 602 pStdOut = pStdErr; 603 604 /* 605 * Close and open the new file descriptor. 606 */ 607 safeCloseFd(fd); 608 #if defined(_MSC_VER) 609 if (!strcmp(psz, "/dev/null")) 610 psz = (char *)"nul"; 611 #endif 612 fdOpened = open(psz, fOpen, 0666); 613 if (fdOpened == -1) 614 { 615 fprintf(pStdErr, "%s: error: failed to open '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno)); 616 return 1; 617 } 618 if (fdOpened != fd) 619 { 620 /* move it (dup2 returns 0 on MSC). */ 621 if (dup2(fdOpened, fd) == -1) 622 { 623 fprintf(pStdErr, "%s: error: failed to dup '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno)); 624 return 1; 625 } 626 close(fdOpened); 1580 #endif 1581 } 627 1582 } 628 1583 } 629 1584 else 630 1585 { 631 fprintf(pStdErr, "%s: syntax error: Invalid argument '%s'.\n", name(argv[0]), argv[i]);632 r eturn usage(pStdErr, name(argv[0]));1586 errx(2, "syntax error: Invalid argument '%s'.", argv[iArg]); 1587 rcExit = usage(stderr, argv[0]); 633 1588 } 634 1589 } 1590 if (!pszExecutable) 1591 pszExecutable = argv[iArg]; 635 1592 636 1593 /* 637 1594 * Make sure there's something to execute. 638 1595 */ 639 if ( i >=argc)1596 if (rcExit == 0 && iArg < argc) 640 1597 { 641 fprintf(pStdErr, "%s: syntax error: nothing to execute!\n", name(argv[0])); 642 return usage(pStdErr, name(argv[0])); 1598 /* 1599 * Do the spawning in a separate function (main is far to large as it is by now). 1600 */ 1601 rcExit = kRedirectDoSpawn(pszExecutable, argc - iArg, &argv[iArg], fWatcomBrainDamage, papszEnv, szCwd, pszSavedCwd, 1602 #ifdef USE_POSIX_SPAWN 1603 cOrders, aOrders, &FileActions, cVerbosity, 1604 #else 1605 cOrders, aOrders, cVerbosity, 1606 #endif 1607 #ifdef KMK 1608 pPidSpawned, 1609 #endif 1610 &fChildExitCode); 643 1611 } 644 645 #if defined(_MSC_VER) 646 if (fileno(pStdErr) != 2) /* no close-on-exec flag on windows */ 1612 else if (rcExit == 0) 647 1613 { 648 fclose(pStdErr);649 pStdErr = NULL;1614 errx(2, "syntax error: nothing to execute!"); 1615 rcExit = usage(stderr, argv[0]); 650 1616 } 651 652 /* MSC is a PITA since it refuses to quote the arguments... */ 653 quote_argv(argc - i, &argv[i], fWatcomBrainDamage, 0 /*fFreeOrLeak*/); 654 if (g_cVerbosity > 0) 655 for (j = i; j < argc; j++) 656 fprintf(pStdErr, "kmk_redirect: debug: argv[%i]=%s<eos>\n", j - i, argv[j]); 657 rc = _spawnvp(_P_WAIT, argv[i], &argv[i]); 658 if (rc == -1 && pStdErr) 659 { 660 fprintf(pStdErr, "%s: error: _spawnvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno)); 661 rc = 1; 662 } 663 return rc; 664 #else 665 if (g_cVerbosity > 0) 666 for (j = i; j < argc; j++) 667 fprintf(pStdErr, "kmk_redirect: debug: argv[%i]=%s<eos>\n", j - i, argv[j]); 668 execvp(argv[i], &argv[i]); 669 fprintf(pStdErr, "%s: error: _execvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno)); 670 return 1; 671 #endif 1617 /* Help and version sets rcExit to -1. Change it to zero. */ 1618 else if (rcExit == -1) 1619 rcExit = 0; 1620 1621 /* 1622 * Cleanup. 1623 */ 1624 if (pszSavedCwd) 1625 free(pszSavedCwd); 1626 kRedirectCleanupFdOrders(cOrders, aOrders, rcExit != 0 && !fChildExitCode); 1627 #ifdef USE_POSIX_SPAWN 1628 posix_spawn_file_actions_destroy(&FileActions); 1629 #endif 1630 #ifndef KMK 1631 iEnvVar = cEnvVars; 1632 while (iEnvVar-- > 0) 1633 free(papszEnv[iEnvVar]); 1634 free(papszEnv); 1635 #endif 1636 #ifdef KBUILD_OS_OS2 1637 for (ulLibPath = 0; ulLibPath < K_ELEMENTS(apszSavedLibPaths); ulLibPath++) 1638 if (apszSavedLibPaths[ulLibPath] != NULL) 1639 { 1640 APIRET rc = DosSetExtLIBPATH(apszSavedLibPaths[ulLibPath], ulLibPath); 1641 if (rc != 0) 1642 warnx("DosSetExtLIBPATH('%s',%u) failed with %u when restoring the original values!", 1643 apszSavedLibPaths[ulLibPath], ulLibPath, rc); 1644 free(apszSavedLibPaths[ulLibPath]) 1645 } 1646 #endif 1647 1648 return rcExit; 672 1649 } 673 1650 -
trunk/src/kmk/kmkbuiltin/rm.c
r2713 r2912 394 394 (void)printf("%s\n", 395 395 p->fts_path); 396 #if defined(KMK) && defined(KBUILD_OS_WINDOWS) 397 if (rval == 0) { 398 extern int dir_cache_deleted_directory(const char *pszDir); 399 dir_cache_deleted_directory(p->fts_accpath); 400 } 401 #endif 396 402 continue; 397 403 } 398 operation = " mkdir";404 operation = "rmdir"; 399 405 break; 400 406 -
trunk/src/kmk/kmkbuiltin/rmdir.c
r2466 r2912 60 60 # include "mscfakes.h" 61 61 #endif 62 #if defined(KMK) && defined(KBUILD_OS_WINDOWS) 63 extern int dir_cache_deleted_directory(const char *pszDir); 64 #endif 62 65 63 66 static int rm_path(char *); … … 135 138 continue; 136 139 /* (only ignored doesn't exist errors fall thru) */ 137 } else if (vflag) { 138 printf("%s\n", *argv); 140 } else { 141 #if defined(KMK) && defined(KBUILD_OS_WINDOWS) 142 dir_cache_deleted_directory(*argv); 143 #endif 144 if (vflag) { 145 printf("%s\n", *argv); 146 } 139 147 } 140 148 if (pflag) … … 178 186 179 187 if (rmdir(path) < 0) { 180 if (ignore_fail_on_non_empty && (errno == ENOTEMPTY || errno == EPERM || errno == EACCES || errno == EINVAL || errno == EEXIST)) 188 if ( ignore_fail_on_non_empty 189 && ( errno == ENOTEMPTY || errno == EPERM || errno == EACCES || errno == EINVAL || errno == EEXIST)) 181 190 break; 182 191 if (!ignore_fail_on_not_exist || errno != ENOENT) { … … 185 194 } 186 195 } 196 #if defined(KMK) && defined(KBUILD_OS_WINDOWS) 197 else { 198 dir_cache_deleted_directory(path); 199 } 200 #endif 187 201 if (vflag) 188 202 printf("%s\n", path); -
trunk/src/kmk/make.h
r2886 r2912 985 985 extern void dir_cache_invalid_missing (void); 986 986 extern int dir_cache_volatile_dir (const char *dir); 987 extern int dir_cache_deleted_directory(const char *pszDir); 987 988 # endif 988 989 #endif -
trunk/src/kmk/w32/include/sub_proc.h
r2844 r2912 50 50 #ifdef KMK 51 51 EXTERN_DECL(int process_kmk_register_submit, (HANDLE hEvent, intptr_t clue, pid_t *pPid)); 52 EXTERN_DECL(int process_kmk_register_redirect, (HANDLE hProcess, pid_t *pPid)); 52 53 #endif 53 54 -
trunk/src/kmk/w32/subproc/sub_proc.c
r2886 r2912 199 199 200 200 #ifdef KMK 201 201 202 /** 202 203 * Interface used by kmkbuiltin/kSubmit.c to register stuff going down in a … … 223 224 return -1; 224 225 } 225 #endif 226 227 /** 228 * Interface used by kmkbuiltin/kRedirect.c to register a spawned process. 229 * 230 * @returns 0 on success, -1 if there are too many sub-processes already. 231 * @param hProcess The process handle. 232 * @param pPid Where to return the pid that job.c expects. 233 */ 234 int 235 process_kmk_register_redirect(HANDLE hProcess, pid_t *pPid) 236 { 237 if (proc_index < MAXIMUM_WAIT_OBJECTS) { 238 sub_process *pSubProc = (sub_process *)xcalloc(sizeof(*pSubProc)); 239 pSubProc->enmType = kRegular; 240 pSubProc->pid = (intptr_t)hProcess; 241 242 proc_array[proc_index++] = pSubProc; 243 *pPid = (intptr_t)pSubProc; 244 return 0; 245 } 246 return -1; 247 } 248 249 #endif /* KMK */ 226 250 227 251 /* -
trunk/src/lib/nt/kFsCache.c
r2879 r2912 1916 1916 * @param pCache The cache. 1917 1917 * @param chLetter The uppercased drive letter. 1918 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX. 1918 1919 * @param penmError Where to return details as to why the lookup 1919 1920 * failed. 1920 1921 */ 1921 static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, K FSLOOKUPERROR *penmError)1922 static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KU32 fFlags, KFSLOOKUPERROR *penmError) 1922 1923 { 1923 1924 KU32 const uHash = chLetter - 'A'; … … 1952 1953 return pCur; 1953 1954 kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING); 1954 if (kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError)) 1955 if ( (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH) 1956 || kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError)) 1955 1957 return pCur; 1956 1958 return NULL; 1957 1959 } 1960 } 1961 1962 if (fFlags & KFSCACHE_LOOKUP_F_NO_INSERT) 1963 { 1964 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; /* close enough */ 1965 return NULL; 1958 1966 } 1959 1967 … … 2199 2207 * @param pCache The cache. 2200 2208 * @param pszPath The path. 2209 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX. 2201 2210 * @param poff Where to return the root dire. 2202 2211 * @param penmError Where to return details as to why the lookup 2203 2212 * failed. 2204 2213 */ 2205 static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 *poff, KFSLOOKUPERROR *penmError) 2214 static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 fFlags, 2215 KU32 *poff, KFSLOOKUPERROR *penmError) 2206 2216 { 2207 2217 #if 0 /* later */ … … 2250 2260 * @param pCache The cache. 2251 2261 * @param pwszPath The path. 2262 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX. 2252 2263 * @param poff Where to return the root dire. 2253 2264 * @param penmError Where to return details as to why the lookup 2254 2265 * failed. 2255 2266 */ 2256 static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 *poff, KFSLOOKUPERROR *penmError) 2267 static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 fFlags, 2268 KU32 *poff, KFSLOOKUPERROR *penmError) 2257 2269 { 2258 2270 #if 0 /* later */ … … 2304 2316 * @param pszPath The path to walk. 2305 2317 * @param cchPath The length of the path. 2318 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX. 2306 2319 * @param penmError Where to return details as to why the lookup 2307 2320 * failed. … … 2310 2323 * found problem. Optional. 2311 2324 */ 2312 PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, 2325 PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, KU32 fFlags, 2313 2326 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor) 2314 2327 { … … 2345 2358 * Do we need to populate or refresh this directory first? 2346 2359 */ 2347 if ( pParent->fPopulated 2360 if ( !pParent->fNeedRePopulating 2361 && pParent->fPopulated 2348 2362 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2349 2363 || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) ) 2350 2364 { /* likely */ } 2351 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError)) 2365 else if ( (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)) 2366 || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError)) 2352 2367 { /* likely */ } 2353 2368 else … … 2365 2380 else 2366 2381 { 2367 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS) 2382 if ( (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS) 2383 && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT)) 2368 2384 pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError); 2369 2385 if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath) … … 2388 2404 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2389 2405 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN] 2406 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH) 2390 2407 || kFsCacheRefreshMissing(pCache, pChild, penmError) ) 2391 2408 { /* likely */ } … … 2409 2426 } 2410 2427 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2411 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) 2428 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN] 2429 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)) 2412 2430 { 2413 2431 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; … … 2444 2462 * @param pszPath The path to walk. No dot-dot bits allowed! 2445 2463 * @param cchPath The length of the path. 2464 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX. 2446 2465 * @param penmError Where to return details as to why the lookup 2447 2466 * failed. … … 2450 2469 * found problem. Optional. 2451 2470 */ 2452 PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, 2471 PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags, 2453 2472 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor) 2454 2473 { … … 2485 2504 * Do we need to populate or refresh this directory first? 2486 2505 */ 2487 if ( pParent->fPopulated 2506 if ( !pParent->fNeedRePopulating 2507 && pParent->fPopulated 2488 2508 && ( pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2489 2509 || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) ) 2490 2510 { /* likely */ } 2491 else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError)) 2511 else if ( (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)) 2512 || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError)) 2492 2513 { /* likely */ } 2493 2514 else … … 2505 2526 else 2506 2527 { 2507 if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS) 2528 if ( (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS) 2529 && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT)) 2508 2530 pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError); 2509 2531 if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath) … … 2528 2550 || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2529 2551 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN] 2552 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH) 2530 2553 || kFsCacheRefreshMissing(pCache, pChild, penmError) ) 2531 2554 { /* likely */ } … … 2549 2572 } 2550 2573 else if ( pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE 2551 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) 2574 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN] 2575 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH) ) 2576 2552 2577 { 2553 2578 *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; … … 2583 2608 * @param pszPath The path to walk. No dot-dot bits allowed! 2584 2609 * @param cchPath The length of the path. 2610 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX. 2585 2611 * @param penmError Where to return details as to why the lookup 2586 2612 * failed. … … 2589 2615 * found problem. Optional. 2590 2616 */ 2591 static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, 2617 static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags, 2592 2618 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor) 2593 2619 { … … 2609 2635 offEnd = 2; 2610 2636 kHlpAssert(IS_SLASH(pszPath[2])); 2611 pRoot = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), penmError);2637 pRoot = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), fFlags, penmError); 2612 2638 } 2613 2639 else if ( IS_SLASH(pszPath[0]) 2614 2640 && IS_SLASH(pszPath[1]) ) 2615 pRoot = kFswCacheLookupUncShareA(pCache, pszPath, &offEnd, penmError);2641 pRoot = kFswCacheLookupUncShareA(pCache, pszPath, fFlags, &offEnd, penmError); 2616 2642 else 2617 2643 { … … 2640 2666 ? pCache->auGenerations[ pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN] 2641 2667 : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) 2668 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH) 2642 2669 || kFsCacheRefreshObj(pCache, pRoot, penmError)) 2643 2670 return kFsCacheObjRetainInternal(pRoot); … … 2661 2688 */ 2662 2689 return kFsCacheLookupRelativeToDirA(pCache, (PKFSDIR)pRoot, &pszPath[offEnd + cchSlashes], 2663 cchPath - offEnd - cchSlashes, penmError, ppLastAncestor);2690 cchPath - offEnd - cchSlashes, fFlags, penmError, ppLastAncestor); 2664 2691 } 2665 2692 … … 2677 2704 * @param pwszPath The path to walk. 2678 2705 * @param cwcPath The length of the path (in wchar_t's). 2706 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX. 2679 2707 * @param penmError Where to return details as to why the lookup 2680 2708 * failed. … … 2683 2711 * found problem. Optional. 2684 2712 */ 2685 static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, 2713 static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags, 2686 2714 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor) 2687 2715 { … … 2706 2734 offEnd = 2; 2707 2735 kHlpAssert(IS_SLASH(pwszPath[2])); 2708 pRoot = kFswCacheLookupDrive(pCache, toupper(pwszPath[0]), penmError);2736 pRoot = kFswCacheLookupDrive(pCache, toupper(pwszPath[0]), fFlags, penmError); 2709 2737 } 2710 2738 else if ( IS_SLASH(pwszPath[0]) 2711 2739 && IS_SLASH(pwszPath[1]) ) 2712 pRoot = kFswCacheLookupUncShareW(pCache, pwszPath, &offEnd, penmError);2740 pRoot = kFswCacheLookupUncShareW(pCache, pwszPath, fFlags, &offEnd, penmError); 2713 2741 else 2714 2742 { … … 2737 2765 ? pCache->auGenerations[ pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN] 2738 2766 : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) 2767 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH) 2739 2768 || kFsCacheRefreshObj(pCache, pRoot, penmError)) 2740 2769 return kFsCacheObjRetainInternal(pRoot); … … 2758 2787 */ 2759 2788 return kFsCacheLookupRelativeToDirW(pCache, (PKFSDIR)pRoot, &pwszPath[offEnd + cwcSlashes], 2760 cwcPath - offEnd - cwcSlashes, penmError, ppLastAncestor);2789 cwcPath - offEnd - cwcSlashes, fFlags, penmError, ppLastAncestor); 2761 2790 } 2762 2791 … … 2772 2801 * @param pszPath The path. 2773 2802 * @param cchPath The length of the path. 2803 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX. 2774 2804 * @param penmError Where to return details as to why the lookup 2775 2805 * failed. … … 2778 2808 * found problem. Optional. 2779 2809 */ 2780 static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, 2810 static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags, 2781 2811 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor) 2782 2812 { … … 2790 2820 && cchFull < sizeof(szFull)) 2791 2821 { 2792 PKFSOBJ pFsObj;2793 2822 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%s)\n", pszPath)); 2794 pFsObj = kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, penmError, ppLastAncestor); 2795 2796 #if 0 /* No need to do this until it's actually queried. */ 2797 /* Cache the resulting path. */ 2798 if ( pFsObj 2799 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS) 2800 || *penmError == KFSLOOKUPERROR_UNSUPPORTED) 2801 { 2802 KU32 uHashPath = kFsCacheStrHash(szFull); 2803 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath, 2804 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError); 2805 } 2806 #endif 2807 return pFsObj; 2823 return kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, fFlags, penmError, ppLastAncestor); 2808 2824 } 2809 2825 … … 2825 2841 * @param pwszPath The path. 2826 2842 * @param cwcPath The length of the path (in wchar_t's). 2843 * @param fFlags Lookup flags, KFSCACHE_LOOKUP_F_XXX. 2827 2844 * @param penmError Where to return details as to why the lookup 2828 2845 * failed. … … 2831 2848 * found problem. Optional. 2832 2849 */ 2833 static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, 2850 static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, KU32 fFlags, 2834 2851 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor) 2835 2852 { … … 2843 2860 && cwcFull < KFSCACHE_CFG_MAX_PATH) 2844 2861 { 2845 PKFSOBJ pFsObj;2846 2862 KFSCACHE_LOG2(("kFsCacheLookupSlowA(%ls)\n", pwszPath)); 2847 pFsObj = kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, penmError, ppLastAncestor); 2848 2849 #if 0 /* No need to do this until it's actually queried. */ 2850 /* Cache the resulting path. */ 2851 if ( pFsObj 2852 || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS) 2853 || *penmError == KFSLOOKUPERROR_UNSUPPORTED) 2854 { 2855 KU32 uHashPath = kFsCacheStrHash(szFull); 2856 kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath, 2857 uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError); 2858 } 2859 #endif 2860 return pFsObj; 2863 return kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, fFlags, penmError, ppLastAncestor); 2861 2864 } 2862 2865 … … 2882 2885 { 2883 2886 if (pHashEntry->fAbsolute) 2884 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 2887 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/, 2885 2888 &pHashEntry->enmError, &pLastAncestor); 2886 2889 else 2887 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 2890 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/, 2888 2891 &pHashEntry->enmError, &pLastAncestor); 2889 2892 } … … 2900 2903 kFsCacheObjRelease(pCache, pHashEntry->pFsObj); 2901 2904 if (pHashEntry->fAbsolute) 2902 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 2905 pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/, 2903 2906 &pHashEntry->enmError, &pLastAncestor); 2904 2907 else 2905 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 2908 pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/, 2906 2909 &pHashEntry->enmError, &pLastAncestor); 2907 2910 } … … 2943 2946 { 2944 2947 if (pHashEntry->fAbsolute) 2945 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 2948 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/, 2946 2949 &pHashEntry->enmError, &pLastAncestor); 2947 2950 else 2948 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 2951 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/, 2949 2952 &pHashEntry->enmError, &pLastAncestor); 2950 2953 } … … 2961 2964 kFsCacheObjRelease(pCache, pHashEntry->pFsObj); 2962 2965 if (pHashEntry->fAbsolute) 2963 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 2966 pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/, 2964 2967 &pHashEntry->enmError, &pLastAncestor); 2965 2968 else 2966 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 2969 pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/, 2967 2970 &pHashEntry->enmError, &pLastAncestor); 2968 2971 } … … 3069 3072 && !kFsCacheHasDotDotA(pchPath, cchPath) ) 3070 3073 { 3071 pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, penmError, &pLastAncestor);3074 pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor); 3072 3075 fAbsolute = K_TRUE; 3073 3076 } 3074 3077 else 3075 3078 { 3076 pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, penmError, &pLastAncestor);3079 pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor); 3077 3080 fAbsolute = K_FALSE; 3078 3081 } … … 3177 3180 && !kFsCacheHasDotDotW(pwcPath, cwcPath) ) 3178 3181 { 3179 pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, penmError, &pLastAncestor);3182 pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor); 3180 3183 fAbsolute = K_TRUE; 3181 3184 } 3182 3185 else 3183 3186 { 3184 pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, penmError, &pLastAncestor);3187 pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor); 3185 3188 fAbsolute = K_FALSE; 3186 3189 } … … 3980 3983 return K_TRUE; 3981 3984 } 3985 return K_FALSE; 3986 } 3987 3988 3989 /** 3990 * Invalidates a deleted directory, ANSI version. 3991 * 3992 * @returns K_TRUE if found and is a non-root directory. Otherwise K_FALSE. 3993 * @param pCache The cache. 3994 * @param pszDir The directory. 3995 */ 3996 KBOOL kFsCacheInvalidateDeletedDirectoryA(PKFSCACHE pCache, const char *pszDir) 3997 { 3998 KU32 cchDir = (KU32)kHlpStrLen(pszDir); 3999 KFSLOOKUPERROR enmError; 4000 PKFSOBJ pFsObj; 4001 4002 /* Is absolute without any '..' bits? */ 4003 if ( cchDir >= 3 4004 && ( ( pszDir[1] == ':' /* Drive letter */ 4005 && IS_SLASH(pszDir[2]) 4006 && IS_ALPHA(pszDir[0]) ) 4007 || ( IS_SLASH(pszDir[0]) /* UNC */ 4008 && IS_SLASH(pszDir[1]) ) ) 4009 && !kFsCacheHasDotDotA(pszDir, cchDir) ) 4010 pFsObj = kFsCacheLookupAbsoluteA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH, 4011 &enmError, NULL); 4012 else 4013 pFsObj = kFsCacheLookupSlowA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH, 4014 &enmError, NULL); 4015 if (pFsObj) 4016 { 4017 /* Is directory? */ 4018 if (pFsObj->bObjType == KFSOBJ_TYPE_DIR) 4019 { 4020 if (pFsObj->pParent != &pCache->RootDir) 4021 { 4022 PKFSDIR pDir = (PKFSDIR)pFsObj; 4023 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: %s hDir=%p\n", pszDir, pDir->hDir)); 4024 if (pDir->hDir != INVALID_HANDLE_VALUE) 4025 { 4026 g_pfnNtClose(pDir->hDir); 4027 pDir->hDir = INVALID_HANDLE_VALUE; 4028 } 4029 pDir->fNeedRePopulating = K_TRUE; 4030 pDir->Obj.uCacheGen = pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN] - 1; 4031 kFsCacheObjRetainInternal(&pDir->Obj); 4032 return K_TRUE; 4033 } 4034 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a root directory was deleted! %s\n", pszDir)); 4035 } 4036 else 4037 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a non-directory: bObjType=%d %s\n", 4038 pFsObj->bObjType, pszDir)); 4039 kFsCacheObjRetainInternal(pFsObj); 4040 } 4041 else 4042 KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: '%s' was not found\n", pszDir)); 3982 4043 return K_FALSE; 3983 4044 } -
trunk/src/lib/nt/kFsCache.h
r2868 r2912 457 457 PKFSOBJ kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError); 458 458 PKFSOBJ kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError); 459 PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, 459 PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, KU32 fFlags, 460 460 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor); 461 PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, 461 PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags, 462 462 KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor); 463 463 PKFSOBJ kFsCacheLookupWithLengthA(PKFSCACHE pCache, const char *pchPath, KSIZE cchPath, KFSLOOKUPERROR *penmError); … … 466 466 PKFSOBJ kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError); 467 467 468 /** @name KFSCACHE_LOOKUP_F_XXX - lookup flags 469 * @{ */ 470 /** No inserting new cache entries. 471 * This effectively prevent directories from being repopulated too. */ 472 #define KFSCACHE_LOOKUP_F_NO_INSERT KU32_C(1) 473 /** No refreshing cache entries. */ 474 #define KFSCACHE_LOOKUP_F_NO_REFRESH KU32_C(2) 475 /** @} */ 468 476 469 477 KU32 kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj); … … 485 493 void kFsCacheInvalidateCustomBoth(PKFSCACHE pCache); 486 494 KBOOL kFsCacheSetupCustomRevisionForTree(PKFSCACHE pCache, PKFSOBJ pRoot); 487 488 #endif 495 KBOOL kFsCacheInvalidateDeletedDirectoryA(PKFSCACHE pCache, const char *pszDir); 496 497 #endif -
trunk/src/lib/quote_argv.c
r2894 r2912 80 80 * Arguments": http://msdn.microsoft.com/en-us/library/a1y7w461.aspx 81 81 * 82 * @returns 0 on success, -1 if out of memory. 82 83 * @param argc The argument count. 83 84 * @param argv The argument vector. … … 90 91 * Suggest doing the latter if it's main()'s argv. 91 92 */ 92 voidquote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak)93 int quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak) 93 94 { 94 95 int i; … … 122 123 size_t cchNew = fComplicated ? cchOrg * 2 + 2 : cchOrg + 2; 123 124 char *pszNew = (char *)malloc(cchNew + 1 /*term*/ + 3 /*passthru hack*/); 125 if (!pszNew) 126 return -1; 124 127 125 128 argv[i] = pszNew; … … 208 211 209 212 /*for (i = 0; i < argc; i++) fprintf(stderr, "argv[%u]=%s;;\n", i, argv[i]);*/ 213 return 0; 210 214 } 211 215 -
trunk/src/lib/quote_argv.h
r2851 r2912 34 34 35 35 #include "mytypes.h" 36 extern voidquote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak);36 extern int quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak); 37 37 38 38 #endif
Note:
See TracChangeset
for help on using the changeset viewer.