RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags, PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser, const char *pszPassword, PRTPROCESS phProcess) { int rc; /* * Input validation */ AssertPtrReturn(pszExec, VERR_INVALID_POINTER); AssertReturn(*pszExec, VERR_INVALID_PARAMETER); AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER); AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER); AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER); AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER); AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER); #if defined(RT_OS_OS2) if (fFlags & RTPROC_FLAGS_DETACHED) return VERR_PROC_DETACH_NOT_SUPPORTED; #endif /* * Get the file descriptors for the handles we've been passed. */ PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr }; int aStdFds[3] = { -1, -1, -1 }; for (int i = 0; i < 3; i++) { if (paHandles[i]) { AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER); switch (paHandles[i]->enmType) { case RTHANDLETYPE_FILE: aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE ? (int)RTFileToNative(paHandles[i]->u.hFile) : -2 /* close it */; break; case RTHANDLETYPE_PIPE: aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE ? (int)RTPipeToNative(paHandles[i]->u.hPipe) : -2 /* close it */; break; case RTHANDLETYPE_SOCKET: aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET ? (int)RTSocketToNative(paHandles[i]->u.hSocket) : -2 /* close it */; break; default: AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER); } /** @todo check the close-on-execness of these handles? */ } } for (int i = 0; i < 3; i++) if (aStdFds[i] == i) aStdFds[i] = -1; for (int i = 0; i < 3; i++) AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i, ("%i := %i not possible because we're lazy\n", i, aStdFds[i]), VERR_NOT_SUPPORTED); /* * Resolve the user id if specified. */ uid_t uid = ~(uid_t)0; gid_t gid = ~(gid_t)0; if (pszAsUser) { rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid); if (RT_FAILURE(rc)) return rc; } /* * Create the child environment if either RTPROC_FLAGS_PROFILE or * RTPROC_FLAGS_ENV_CHANGE_RECORD are in effect. */ RTENV hEnvToUse = hEnv; if ( (fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_PROFILE)) && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT) ) { if (fFlags & RTPROC_FLAGS_PROFILE) rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser); else rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT); if (RT_SUCCESS(rc)) { if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT) rc = RTEnvApplyChanges(hEnvToUse, hEnv); if (RT_FAILURE(rc)) RTEnvDestroy(hEnvToUse); } if (RT_FAILURE(rc)) return rc; } /* * Check for execute access to the file. */ char szRealExec[RTPATH_MAX]; if (access(pszExec, X_OK)) { rc = errno; if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH) || rc != ENOENT || RTPathHavePath(pszExec) ) rc = RTErrConvertFromErrno(rc); else { /* search */ char *pszPath = RTEnvDupEx(hEnvToUse, "PATH"); rc = RTPathTraverseList(pszPath, ':', rtPathFindExec, (void *)pszExec, &szRealExec[0]); RTStrFree(pszPath); if (RT_SUCCESS(rc)) pszExec = szRealExec; else rc = rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc; } if (RT_FAILURE(rc)) return rtProcPosixCreateReturn(rc, hEnvToUse, hEnv); } pid_t pid = -1; const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse); AssertPtrReturn(papszEnv, rtProcPosixCreateReturn(VERR_INVALID_HANDLE, hEnvToUse, hEnv)); /* * Take care of detaching the process. * * HACK ALERT! Put the process into a new process group with pgid = pid * to make sure it differs from that of the parent process to ensure that * the IPRT waitpid call doesn't race anyone (read XPCOM) doing group wide * waits. setsid() includes the setpgid() functionality. * 2010-10-11 XPCOM no longer waits for anything, but it cannot hurt. */ #ifndef RT_OS_OS2 if (fFlags & RTPROC_FLAGS_DETACHED) { # ifdef RT_OS_SOLARIS int templateFd = -1; if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) { templateFd = rtSolarisContractPreFork(); if (templateFd == -1) return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv); } # endif /* RT_OS_SOLARIS */ pid = fork(); if (!pid) { # ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkChild(templateFd); # endif setsid(); /* see comment above */ pid = -1; /* Child falls through to the actual spawn code below. */ } else { # ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkParent(templateFd, pid); # endif if (pid > 0) { /* Must wait for the temporary process to avoid a zombie. */ int status = 0; pid_t pidChild = 0; /* Restart if we get interrupted. */ do { pidChild = waitpid(pid, &status, 0); } while ( pidChild == -1 && errno == EINTR); /* Assume that something wasn't found. No detailed info. */ if (status) return rtProcPosixCreateReturn(VERR_PROCESS_NOT_FOUND, hEnvToUse, hEnv); if (phProcess) *phProcess = 0; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv); } } #endif /* * Spawn the child. * * Any spawn code MUST not execute any atexit functions if it is for a * detached process. It would lead to running the atexit functions which * make only sense for the parent. libORBit e.g. gets confused by multiple * execution. Remember, there was only a fork() so far, and until exec() * is successfully run there is nothing which would prevent doing anything * silly with the (duplicated) file descriptors. */ #ifdef HAVE_POSIX_SPAWN /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */ if ( uid == ~(uid_t)0 && gid == ~(gid_t)0) { /* Spawn attributes. */ posix_spawnattr_t Attr; rc = posix_spawnattr_init(&Attr); if (!rc) { /* Indicate that process group and signal mask are to be changed, and that the child should use default signal actions. */ rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF); Assert(rc == 0); /* The child starts in its own process group. */ if (!rc) { rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */); Assert(rc == 0); } /* Unmask all signals. */ if (!rc) { sigset_t SigMask; sigemptyset(&SigMask); rc = posix_spawnattr_setsigmask(&Attr, &SigMask); Assert(rc == 0); } /* File changes. */ posix_spawn_file_actions_t FileActions; posix_spawn_file_actions_t *pFileActions = NULL; if ((aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1) && !rc) { rc = posix_spawn_file_actions_init(&FileActions); if (!rc) { pFileActions = &FileActions; for (int i = 0; i < 3; i++) { int fd = aStdFds[i]; if (fd == -2) rc = posix_spawn_file_actions_addclose(&FileActions, i); else if (fd >= 0 && fd != i) { rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i); if (!rc) { for (int j = i + 1; j < 3; j++) if (aStdFds[j] == fd) { fd = -1; break; } if (fd >= 0) rc = posix_spawn_file_actions_addclose(&FileActions, fd); } } if (rc) break; } } } if (!rc) rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs, (char * const *)papszEnv); /* cleanup */ int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2); if (pFileActions) { rc2 = posix_spawn_file_actions_destroy(pFileActions); Assert(rc2 == 0); } /* return on success.*/ if (!rc) { /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(0); if (phProcess) *phProcess = pid; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } } /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(124); } else #endif { #ifdef RT_OS_SOLARIS int templateFd = -1; if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) { templateFd = rtSolarisContractPreFork(); if (templateFd == -1) return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv); } #endif /* RT_OS_SOLARIS */ pid = fork(); if (!pid) { #ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkChild(templateFd); #endif /* RT_OS_SOLARIS */ if (!(fFlags & RTPROC_FLAGS_DETACHED)) setpgid(0, 0); /* see comment above */ /* * Change group and user if requested. */ #if 1 /** @todo This needs more work, see suplib/hardening. */ if (pszAsUser) { int ret = initgroups(pszAsUser, gid); if (ret) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } if (gid != ~(gid_t)0) { if (setgid(gid)) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } if (uid != ~(uid_t)0) { if (setuid(uid)) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } #endif /* * Some final profile environment tweaks, if running as user. */ if ( (fFlags & RTPROC_FLAGS_PROFILE) && pszAsUser && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT) ) { rc = rtProcPosixAdjustProfileEnvFromChild(hEnvToUse, fFlags, hEnv); papszEnv = RTEnvGetExecEnvP(hEnvToUse); if (RT_FAILURE(rc) || !papszEnv) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(126); else exit(126); } } /* * Unset the signal mask. */ sigset_t SigMask; sigemptyset(&SigMask); rc = sigprocmask(SIG_SETMASK, &SigMask, NULL); Assert(rc == 0); /* * Apply changes to the standard file descriptor and stuff. */ for (int i = 0; i < 3; i++) { int fd = aStdFds[i]; if (fd == -2) close(i); else if (fd >= 0) { int rc2 = dup2(fd, i); if (rc2 != i) { if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(125); else exit(125); } for (int j = i + 1; j < 3; j++) if (aStdFds[j] == fd) { fd = -1; break; } if (fd >= 0) close(fd); } } /* * Finally, execute the requested program. */ rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv); if (errno == ENOEXEC) { /* This can happen when trying to start a shell script without the magic #!/bin/sh */ RTAssertMsg2Weak("Cannot execute this binary format!\n"); } else RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno); RTAssertReleasePanic(); if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(127); else exit(127); } #ifdef RT_OS_SOLARIS if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT)) rtSolarisContractPostForkParent(templateFd, pid); #endif /* RT_OS_SOLARIS */ if (pid > 0) { /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(0); if (phProcess) *phProcess = pid; return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv); } /* For a detached process this happens in the temp process, so * it's not worth doing anything as this process must exit. */ if (fFlags & RTPROC_FLAGS_DETACHED) _Exit(124); return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv); } return rtProcPosixCreateReturn(VERR_NOT_IMPLEMENTED, hEnvToUse, hEnv); }
/** * Generates a kind of report of the hardware, software and whatever else we * think might be useful to know about the testbox. */ static RTEXITCODE handlerReport(int argc, char **argv) { NOREF(argc); NOREF(argv); #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) /* * For now, a simple CPUID dump. Need to figure out how to share code * like this with other bits, putting it in IPRT. */ RTPrintf("CPUID Dump\n" "Leaf eax ebx ecx edx\n" "---------------------------------------------\n"); static uint32_t const s_auRanges[] = { UINT32_C(0x00000000), UINT32_C(0x80000000), UINT32_C(0x80860000), UINT32_C(0xc0000000), UINT32_C(0x40000000), }; for (uint32_t iRange = 0; iRange < RT_ELEMENTS(s_auRanges); iRange++) { uint32_t const uFirst = s_auRanges[iRange]; uint32_t uEax, uEbx, uEcx, uEdx; ASMCpuIdExSlow(uFirst, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx); if (uEax >= uFirst && uEax < uFirst + 100) { uint32_t const cLeafs = RT_MIN(uEax - uFirst + 1, 32); for (uint32_t iLeaf = 0; iLeaf < cLeafs; iLeaf++) { uint32_t uLeaf = uFirst + iLeaf; ASMCpuIdExSlow(uLeaf, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx); /* Clear APIC IDs to avoid submitting new reports all the time. */ if (uLeaf == 1) uEbx &= UINT32_C(0x00ffffff); if (uLeaf == 0xb) uEdx = 0; if (uLeaf == 0x8000001e) uEax = 0; /* Clear some other node/cpu/core/thread ids. */ if (uLeaf == 0x8000001e) { uEbx &= UINT32_C(0xffffff00); uEcx &= UINT32_C(0xffffff00); } RTPrintf("%08x: %08x %08x %08x %08x\n", uLeaf, uEax, uEbx, uEcx, uEdx); } } } RTPrintf("\n"); /* * DMI info. */ RTPrintf("DMI Info\n" "--------\n"); static const struct { const char *pszName; RTSYSDMISTR enmDmiStr; } s_aDmiStrings[] = { { "Product Name", RTSYSDMISTR_PRODUCT_NAME }, { "Product version", RTSYSDMISTR_PRODUCT_VERSION }, { "Product UUID", RTSYSDMISTR_PRODUCT_UUID }, { "Product Serial", RTSYSDMISTR_PRODUCT_SERIAL }, { "System Manufacturer", RTSYSDMISTR_MANUFACTURER }, }; for (uint32_t iDmiString = 0; iDmiString < RT_ELEMENTS(s_aDmiStrings); iDmiString++) { char szTmp[4096]; RT_ZERO(szTmp); int rc = RTSystemQueryDmiString(s_aDmiStrings[iDmiString].enmDmiStr, szTmp, sizeof(szTmp) - 1); if (RT_SUCCESS(rc)) RTPrintf("%25s: %s\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp)); else RTPrintf("%25s: %s [rc=%Rrc]\n", s_aDmiStrings[iDmiString].pszName, RTStrStrip(szTmp), rc); } RTPrintf("\n"); #else #endif /* * Dump the environment. */ RTPrintf("Environment\n" "-----------\n"); RTENV hEnv; int rc = RTEnvClone(&hEnv, RTENV_DEFAULT); if (RT_SUCCESS(rc)) { uint32_t cVars = RTEnvCountEx(hEnv); for (uint32_t iVar = 0; iVar < cVars; iVar++) { char szVar[1024]; char szValue[16384]; rc = RTEnvGetByIndexEx(hEnv, iVar, szVar, sizeof(szVar), szValue, sizeof(szValue)); /* zap the value of variables that are subject to change. */ if ( (RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW) && ( !strcmp(szVar, "TESTBOX_SCRIPT_REV") || !strcmp(szVar, "TESTBOX_ID") || !strcmp(szVar, "TESTBOX_SCRATCH_SIZE") || !strcmp(szVar, "TESTBOX_TIMEOUT") || !strcmp(szVar, "TESTBOX_TIMEOUT_ABS") || !strcmp(szVar, "TESTBOX_TEST_SET_ID") ) ) strcpy(szValue, "<volatile>"); if (RT_SUCCESS(rc)) RTPrintf("%25s=%s\n", szVar, szValue); else if (rc == VERR_BUFFER_OVERFLOW) RTPrintf("%25s=%s [VERR_BUFFER_OVERFLOW]\n", szVar, szValue); else RTPrintf("rc=%Rrc\n", rc); } RTEnvDestroy(hEnv); } /** @todo enumerate volumes and whatnot. */ int cch = RTPrintf("\n"); return cch > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; }
RTDECL(int) RTEnvQueryUtf8Block(RTENV hEnv, bool fSorted, char **ppszzBlock, size_t *pcbBlock) { RTENV hClone = NIL_RTENV; PRTENVINTERNAL pIntEnv; int rc; /* * Validate / simplify input. */ if (hEnv == RTENV_DEFAULT) { rc = RTEnvClone(&hClone, RTENV_DEFAULT); if (RT_FAILURE(rc)) return rc; pIntEnv = hClone; } else { pIntEnv = hEnv; AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE); AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE); rc = VINF_SUCCESS; } RTENV_LOCK(pIntEnv); /* * Sort it, if requested. */ if (fSorted) RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv); /* * Calculate the size. We add one extra terminator just to be on the safe side. */ size_t cbBlock = 2; for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++) cbBlock += strlen(pIntEnv->papszEnv[iVar]) + 1; if (pcbBlock) *pcbBlock = cbBlock - 1; /* * Allocate memory and copy out the variables. */ char *pszzBlock; char *pszz = pszzBlock = (char *)RTMemAlloc(cbBlock); if (pszz) { size_t cbLeft = cbBlock; for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++) { size_t cb = strlen(pIntEnv->papszEnv[iVar]) + 1; AssertBreakStmt(cb + 2 <= cbLeft, rc = VERR_INTERNAL_ERROR_3); memcpy(pszz, pIntEnv->papszEnv[iVar], cb); pszz += cb; cbLeft -= cb; } if (RT_SUCCESS(rc)) { pszz[0] = '\0'; pszz[1] = '\0'; /* The extra one. */ } else { RTMemFree(pszzBlock); pszzBlock = NULL; } } else rc = VERR_NO_MEMORY; RTENV_UNLOCK(pIntEnv); if (hClone != NIL_RTENV) RTEnvDestroy(hClone); if (RT_SUCCESS(rc)) *ppszzBlock = pszzBlock; return rc; }
RTDECL(int) RTEnvQueryUtf16Block(RTENV hEnv, PRTUTF16 *ppwszzBlock) { RTENV hClone = NIL_RTENV; PRTENVINTERNAL pIntEnv; int rc; /* * Validate / simplify input. */ if (hEnv == RTENV_DEFAULT) { rc = RTEnvClone(&hClone, RTENV_DEFAULT); if (RT_FAILURE(rc)) return rc; pIntEnv = hClone; } else { pIntEnv = hEnv; AssertPtrReturn(pIntEnv, VERR_INVALID_HANDLE); AssertReturn(pIntEnv->u32Magic == RTENV_MAGIC, VERR_INVALID_HANDLE); rc = VINF_SUCCESS; } RTENV_LOCK(pIntEnv); /* * Sort it first. */ RTSortApvShell((void **)pIntEnv->papszEnv, pIntEnv->cVars, rtEnvSortCompare, pIntEnv); /* * Calculate the size. */ size_t cwc; size_t cwcTotal = 2; for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++) { rc = RTStrCalcUtf16LenEx(pIntEnv->papszEnv[iVar], RTSTR_MAX, &cwc); AssertRCBreak(rc); cwcTotal += cwc + 1; } PRTUTF16 pwszzBlock = NULL; if (RT_SUCCESS(rc)) { /* * Perform the conversion. */ PRTUTF16 pwszz = pwszzBlock = (PRTUTF16)RTMemAlloc(cwcTotal * sizeof(RTUTF16)); if (pwszz) { size_t cwcLeft = cwcTotal; for (size_t iVar = 0; iVar < pIntEnv->cVars; iVar++) { rc = RTStrToUtf16Ex(pIntEnv->papszEnv[iVar], RTSTR_MAX, &pwszz, cwcTotal - (pwszz - pwszzBlock), &cwc); AssertRCBreak(rc); pwszz += cwc + 1; cwcLeft -= cwc + 1; AssertBreakStmt(cwcLeft >= 2, rc = VERR_INTERNAL_ERROR_3); } AssertStmt(cwcLeft == 2 || RT_FAILURE(rc), rc = VERR_INTERNAL_ERROR_2); if (RT_SUCCESS(rc)) { pwszz[0] = '\0'; pwszz[1] = '\0'; } else { RTMemFree(pwszzBlock); pwszzBlock = NULL; } } else rc = VERR_NO_MEMORY; } RTENV_UNLOCK(pIntEnv); if (hClone != NIL_RTENV) RTEnvDestroy(hClone); if (RT_SUCCESS(rc)) *ppwszzBlock = pwszzBlock; return rc; }
static int tstRTCreateProcEx6Child(int argc, char **argv) { int rc = RTR3InitExeNoArguments(0); if (RT_FAILURE(rc)) return RTMsgInitFailure(rc); int cErrors = 0; char szValue[_16K]; /* * Check for the environment variable we've set in the parent process. */ if (argc >= 3 && strcmp(argv[2], "inherit") == 0) { if (!RTEnvExistEx(RTENV_DEFAULT, "testcase-child-6")) { RTStrmPrintf(g_pStdErr, "child6: Env.var. 'testcase-child-6' was not inherited from parent\n"); cErrors++; } } else if (argc >= 3 && strstr(argv[2], "change-record") != NULL) { rc = RTEnvGetEx(RTENV_DEFAULT, "testcase-child-6", szValue, sizeof(szValue), NULL); if (RT_SUCCESS(rc) && strcmp(szValue, "changed")) { RTStrmPrintf(g_pStdErr, "child6: Env.var. 'testcase-child-6'='%s', expected 'changed'.\n", szValue); cErrors++; } else if (RT_FAILURE(rc)) { RTStrmPrintf(g_pStdErr, "child6: RTEnvGetEx(,'testcase-child-6',,) -> %Rrc\n", rc); cErrors++; } } else { if (RTEnvExistEx(RTENV_DEFAULT, "testcase-child-6")) { RTStrmPrintf(g_pStdErr, "child6: Env.var. 'testcase-child-6' was inherited from parent\n"); cErrors++; } } /* * Check the user name if present we didn't inherit from parent. */ if ( argc >= 4 && argv[3][0] != '\0' && strstr(argv[2], "noinherit") != NULL) { static struct { const char *pszVarNm; bool fReq; } const s_aVars[] = { #ifdef RT_OS_WINDOWS { "USERNAME", true }, #else { "LOGNAME", true }, { "USER", false }, #endif }; for (unsigned i = 0; i < RT_ELEMENTS(s_aVars); i++) { rc = RTEnvGetEx(RTENV_DEFAULT, s_aVars[i].pszVarNm, szValue, sizeof(szValue), NULL); if (RT_SUCCESS(rc)) { if (strcmp(szValue, argv[3])) { RTStrmPrintf(g_pStdErr, "child6: env.var. '%s'='%s', expected '%s'\n", s_aVars[i].pszVarNm, szValue, argv[3]); cErrors++; } } else if (rc != VERR_ENV_VAR_NOT_FOUND || s_aVars[i].fReq) { RTStrmPrintf(g_pStdErr, "child6: RTGetEnv('%s') -> %Rrc\n", s_aVars[i].pszVarNm, rc); cErrors++; } } } #if 1 /* For manual testing. */ if (strcmp(argv[2],"noinherit") == 0) //if (strcmp(argv[2],"noinherit-change-record") == 0) { RTENV hEnv; rc = RTEnvClone(&hEnv, RTENV_DEFAULT); if (RT_SUCCESS(rc)) { uint32_t cVars = RTEnvCountEx(hEnv); for (uint32_t i = 0; i < cVars; i++) { char szVarNm[_1K]; rc = RTEnvGetByIndexEx(hEnv, i, szVarNm, sizeof(szVarNm), szValue, sizeof(szValue)); if (RT_SUCCESS(rc)) RTStrmPrintf(g_pStdErr, "child6: #%u: %s=%s\n", i, szVarNm, szValue); else { RTStrmPrintf(g_pStdErr, "child6: #%u: %Rrc\n", i, rc); cErrors++; } } RTEnvDestroy(hEnv); } else { RTStrmPrintf(g_pStdErr, "child6: RTEnvClone failed: %Rrc\n", rc); cErrors++; } } #endif return cErrors == 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; }
int main(int argc, char **argv) { /* * Deal with child processes first. */ if (argc == 2 && !strcmp(argv[1], "--testcase-child-1")) return tstRTCreateProcEx1Child(); if (argc == 2 && !strcmp(argv[1], "--testcase-child-2")) return tstRTCreateProcEx2Child(); if (argc == 2 && !strcmp(argv[1], "--testcase-child-3")) return tstRTCreateProcEx3Child(); if (argc >= 5 && !strcmp(argv[1], "--testcase-child-4")) return tstRTCreateProcEx4Child(argc, argv); if (argc >= 2 && !strcmp(argv[1], "--testcase-child-5")) return tstRTCreateProcEx5Child(argc, argv); if (argc >= 2 && !strcmp(argv[1], "--testcase-child-6")) return tstRTCreateProcEx6Child(argc, argv); /* * Main process. */ const char *pszAsUser = NULL; const char *pszPassword = NULL; if (argc != 1) { if (argc != 4 || strcmp(argv[1], "--as-user")) return 99; pszAsUser = argv[2]; pszPassword = argv[3]; } RTTEST hTest; int rc = RTTestInitAndCreate("tstRTProcCreateEx", &hTest); if (rc) return rc; RTTestBanner(hTest); /* * Init globals. */ if (!RTProcGetExecutablePath(g_szExecName, sizeof(g_szExecName))) RTStrCopy(g_szExecName, sizeof(g_szExecName), argv[0]); RTTESTI_CHECK_RC(RTEnvClone(&g_hEnvInitial, RTENV_DEFAULT), VINF_SUCCESS); /* * The tests. */ tstRTCreateProcEx1(pszAsUser, pszPassword); tstRTCreateProcEx2(pszAsUser, pszPassword); tstRTCreateProcEx3(pszAsUser, pszPassword); tstRTCreateProcEx4(pszAsUser, pszPassword); if (pszAsUser) tstRTCreateProcEx5(pszAsUser, pszPassword); tstRTCreateProcEx6(pszAsUser, pszPassword); /** @todo Cover files, ++ */ RTEnvDestroy(g_hEnvInitial); /* * Summary. */ return RTTestSummaryAndDestroy(hTest); }
static void tstRTCreateProcEx6(const char *pszAsUser, const char *pszPassword) { RTTestISub("Profile environment"); const char *apszArgs[5] = { g_szExecName, "--testcase-child-6", "inherit", pszAsUser, NULL }; RTTESTI_CHECK_RC_RETV(RTEnvSetEx(RTENV_DEFAULT, "testcase-child-6", "true"), VINF_SUCCESS); /* Use the process environment first. */ RTPROCESS hProc; RTTESTI_CHECK_RC_RETV(RTProcCreateEx(g_szExecName, apszArgs, RTENV_DEFAULT, 0 /*fFlags*/, NULL, NULL, NULL, pszAsUser, pszPassword, &hProc), VINF_SUCCESS); RTPROCSTATUS ProcStatus = { -1, RTPROCEXITREASON_ABEND }; RTTESTI_CHECK_RC(RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus), VINF_SUCCESS); if (ProcStatus.enmReason != RTPROCEXITREASON_NORMAL || ProcStatus.iStatus != 0) RTTestIFailed("enmReason=%d iStatus=%d", ProcStatus.enmReason, ProcStatus.iStatus); /* Use the process environment first with a little change. */ apszArgs[2] = "change-record"; RTENV hEnvChange; RTTESTI_CHECK_RC_RETV(RTEnvCreateChangeRecord(&hEnvChange), VINF_SUCCESS); RTTESTI_CHECK_RC_RETV(RTEnvSetEx(hEnvChange, "testcase-child-6", "changed"), VINF_SUCCESS); int rc; RTTESTI_CHECK_RC(rc = RTProcCreateEx(g_szExecName, apszArgs, hEnvChange, RTPROC_FLAGS_ENV_CHANGE_RECORD, NULL, NULL, NULL, pszAsUser, pszPassword, &hProc), VINF_SUCCESS); if (RT_SUCCESS(rc)) { ProcStatus.enmReason = RTPROCEXITREASON_ABEND; ProcStatus.iStatus = -1; RTTESTI_CHECK_RC(RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus), VINF_SUCCESS); if (ProcStatus.enmReason != RTPROCEXITREASON_NORMAL || ProcStatus.iStatus != 0) RTTestIFailed("enmReason=%d iStatus=%d", ProcStatus.enmReason, ProcStatus.iStatus); } /* Use profile environment this time. */ apszArgs[2] = "noinherit"; RTTESTI_CHECK_RC(rc = RTProcCreateEx(g_szExecName, apszArgs, RTENV_DEFAULT, RTPROC_FLAGS_PROFILE, NULL, NULL, NULL, pszAsUser, pszPassword, &hProc), VINF_SUCCESS); if (RT_SUCCESS(rc)) { ProcStatus.enmReason = RTPROCEXITREASON_ABEND; ProcStatus.iStatus = -1; RTTESTI_CHECK_RC(RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus), VINF_SUCCESS); if (ProcStatus.enmReason != RTPROCEXITREASON_NORMAL || ProcStatus.iStatus != 0) RTTestIFailed("enmReason=%d iStatus=%d", ProcStatus.enmReason, ProcStatus.iStatus); } /* Use profile environment this time. */ apszArgs[2] = "noinherit-change-record"; RTTESTI_CHECK_RC(rc = RTProcCreateEx(g_szExecName, apszArgs, hEnvChange, RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_ENV_CHANGE_RECORD, NULL, NULL, NULL, pszAsUser, pszPassword, &hProc), VINF_SUCCESS); if (RT_SUCCESS(rc)) { ProcStatus.enmReason = RTPROCEXITREASON_ABEND; ProcStatus.iStatus = -1; RTTESTI_CHECK_RC(RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus), VINF_SUCCESS); if (ProcStatus.enmReason != RTPROCEXITREASON_NORMAL || ProcStatus.iStatus != 0) RTTestIFailed("enmReason=%d iStatus=%d", ProcStatus.enmReason, ProcStatus.iStatus); } RTTESTI_CHECK_RC(RTEnvDestroy(hEnvChange), VINF_SUCCESS); /* * Restore the environment and check that the PROFILE flag didn't mess with * the process environment. (Note! The bug may be elsewhere as well.) */ RTTESTI_CHECK_RC(RTEnvUnsetEx(RTENV_DEFAULT, "testcase-child-6"), VINF_SUCCESS); RTENV hEnvCur; RTTESTI_CHECK_RC_RETV(RTEnvClone(&hEnvCur, RTENV_DEFAULT), VINF_SUCCESS); uint32_t cCurrent = RTEnvCountEx(hEnvCur); uint32_t cInitial = RTEnvCountEx(g_hEnvInitial); RTTESTI_CHECK_MSG(cCurrent == cInitial, ("cCurrent=%u cInitial=%u\n", cCurrent, cInitial)); uint32_t cVars1; RTENV hEnv1, hEnv2; const char *pszEnv1, *pszEnv2; if (cCurrent >= cInitial) { hEnv1 = hEnvCur; pszEnv1 = "current"; cVars1 = cCurrent; hEnv2 = g_hEnvInitial; pszEnv2 = "initial"; } else { hEnv2 = hEnvCur; pszEnv2 = "current"; hEnv1 = g_hEnvInitial; pszEnv1 = "initial"; cVars1 = cInitial; } for (uint32_t i = 0; i < cVars1; i++) { char szValue1[_16K]; char szVarNm[_1K]; rc = RTEnvGetByIndexEx(hEnv1, i, szVarNm, sizeof(szVarNm), szValue1, sizeof(szValue1)); if (RT_SUCCESS(rc)) { char szValue2[_16K]; rc = RTEnvGetEx(hEnv2, szVarNm, szValue2, sizeof(szValue2), NULL); if (RT_SUCCESS(rc)) { if (strcmp(szValue1, szValue2) != 0) { RTTestIFailed("Variable '%s' differs", szVarNm); RTTestIFailureDetails("%s: '%s'\n" "%s: '%s'\n", pszEnv1, szValue1, pszEnv2, szValue2); } } else RTTestIFailed("RTEnvGetEx(%s,%s,,) failed: %Rrc", pszEnv2, szVarNm, rc); } else RTTestIFailed("RTEnvGetByIndexEx(%s,%u,,,,) failed: %Rrc", pszEnv1, i, rc); } }