/** * Adjust the profile environment after forking the child process and changing * the UID. * * @returns IRPT status code. * @param hEnvToUse The environment we're going to use with execve. * @param fFlags The process creation flags. * @param hEnv The environment passed in by the user. */ static int rtProcPosixAdjustProfileEnvFromChild(RTENV hEnvToUse, uint32_t fFlags, RTENV hEnv) { int rc = VINF_SUCCESS; #ifdef RT_OS_DARWIN if ( RT_SUCCESS(rc) && (!(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || RTEnvExistEx(hEnv, "TMPDIR")) ) { char szValue[_4K]; size_t cbNeeded = confstr(_CS_DARWIN_USER_TEMP_DIR, szValue, sizeof(szValue)); if (cbNeeded > 0 && cbNeeded < sizeof(szValue)) { char *pszTmp; rc = RTStrCurrentCPToUtf8(&pszTmp, szValue); if (RT_SUCCESS(rc)) { rc = RTEnvSetEx(hEnvToUse, "TMPDIR", pszTmp); RTStrFree(pszTmp); } } else rc = VERR_BUFFER_OVERFLOW; } #endif return rc; }
RTDECL(int) RTEnvPutEx(RTENV Env, const char *pszVarEqualValue) { int rc; AssertPtrReturn(pszVarEqualValue, VERR_INVALID_POINTER); const char *pszEq = strchr(pszVarEqualValue, '='); if (!pszEq) rc = RTEnvUnsetEx(Env, pszVarEqualValue); else { /* * Make a copy of the variable name so we can terminate it * properly and then pass the request on to RTEnvSetEx. */ const char *pszValue = pszEq + 1; size_t cchVar = pszEq - pszVarEqualValue; Assert(cchVar < 1024); char *pszVar = (char *)alloca(cchVar + 1); memcpy(pszVar, pszVarEqualValue, cchVar); pszVar[cchVar] = '\0'; rc = RTEnvSetEx(Env, pszVar, pszValue); } return rc; }
/** * Create a very very basic environment for a user. * * @returns IPRT status code. * @param phEnvToUse Where to return the created environment. * @param pszUser The user name for the profile. */ static int rtProcPosixCreateProfileEnv(PRTENV phEnvToUse, const char *pszUser) { struct passwd Pwd; struct passwd *pPwd = NULL; char achBuf[_4K]; int rc; errno = 0; if (pszUser) rc = getpwnam_r(pszUser, &Pwd, achBuf, sizeof(achBuf), &pPwd); else rc = getpwuid_r(getuid(), &Pwd, achBuf, sizeof(achBuf), &pPwd); if (rc == 0 && pPwd) { char *pszDir; rc = RTStrCurrentCPToUtf8(&pszDir, pPwd->pw_dir); if (RT_SUCCESS(rc)) { char *pszShell; rc = RTStrCurrentCPToUtf8(&pszShell, pPwd->pw_shell); if (RT_SUCCESS(rc)) { char *pszUserFree = NULL; if (!pszUser) { rc = RTStrCurrentCPToUtf8(&pszUserFree, pPwd->pw_name); if (RT_SUCCESS(rc)) pszUser = pszUserFree; } if (RT_SUCCESS(rc)) { rc = RTEnvCreate(phEnvToUse); if (RT_SUCCESS(rc)) { RTENV hEnvToUse = *phEnvToUse; rc = RTEnvSetEx(hEnvToUse, "HOME", pszDir); if (RT_SUCCESS(rc)) rc = RTEnvSetEx(hEnvToUse, "SHELL", pszShell); if (RT_SUCCESS(rc)) rc = RTEnvSetEx(hEnvToUse, "USER", pszUser); if (RT_SUCCESS(rc)) rc = RTEnvSetEx(hEnvToUse, "LOGNAME", pszUser); if (RT_SUCCESS(rc)) rc = RTEnvSetEx(hEnvToUse, "PATH", pPwd->pw_uid == 0 ? _PATH_STDPATH : _PATH_DEFPATH); if (RT_SUCCESS(rc)) { RTStrPrintf(achBuf, sizeof(achBuf), "%s/%s", _PATH_MAILDIR, pszUser); rc = RTEnvSetEx(hEnvToUse, "MAIL", achBuf); } #ifdef RT_OS_DARWIN if (RT_SUCCESS(rc) && !pszUserFree) { size_t cbNeeded = confstr(_CS_DARWIN_USER_TEMP_DIR, achBuf, sizeof(achBuf)); if (cbNeeded > 0 && cbNeeded < sizeof(achBuf)) { char *pszTmp; rc = RTStrCurrentCPToUtf8(&pszTmp, achBuf); if (RT_SUCCESS(rc)) { rc = RTEnvSetEx(hEnvToUse, "TMPDIR", pszTmp); RTStrFree(pszTmp); } } else rc = VERR_BUFFER_OVERFLOW; } #endif /** @todo load /etc/environment, /etc/profile.env and ~/.pam_environment? */ if (RT_FAILURE(rc)) RTEnvDestroy(hEnvToUse); } RTStrFree(pszUserFree); } RTStrFree(pszShell); } RTStrFree(pszDir); } } else rc = errno ? RTErrConvertFromErrno(errno) : VERR_ACCESS_DENIED; return rc; }
/** * Initializes the COM runtime. * * This method must be called on each thread of the client application that * wants to access COM facilities. The initialization must be performed before * calling any other COM method or attempting to instantiate COM objects. * * On platforms using XPCOM, this method uses the following scheme to search for * XPCOM runtime: * * 1. If the VBOX_APP_HOME environment variable is set, the path it specifies * is used to search XPCOM libraries and components. If this method fails to * initialize XPCOM runtime using this path, it will immediately return a * failure and will NOT check for other paths as described below. * * 2. If VBOX_APP_HOME is not set, this methods tries the following paths in the * given order: * * a) Compiled-in application data directory (as returned by * RTPathAppPrivateArch()) * b) "/usr/lib/virtualbox" (Linux only) * c) "/opt/VirtualBox" (Linux only) * * The first path for which the initialization succeeds will be used. * * On MS COM platforms, the COM runtime is provided by the system and does not * need to be searched for. * * Once the COM subsystem is no longer necessary on a given thread, Shutdown() * must be called to free resources allocated for it. Note that a thread may * call Initialize() several times but for each of tese calls there must be a * corresponding Shutdown() call. * * @return S_OK on success and a COM result code in case of failure. */ HRESULT Initialize(bool fGui) { HRESULT rc = E_FAIL; #if !defined(VBOX_WITH_XPCOM) /* * We initialize COM in GUI thread in STA, to be compliant with QT and * OLE requirments (for example to allow D&D), while other threads * initialized in regular MTA. To allow fast proxyless access from * GUI thread to COM objects, we explicitly provide our COM objects * with free threaded marshaller. * !!!!! Please think twice before touching this code !!!!! */ DWORD flags = fGui ? COINIT_APARTMENTTHREADED | COINIT_SPEED_OVER_MEMORY : COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE | COINIT_SPEED_OVER_MEMORY; rc = CoInitializeEx(NULL, flags); /* the overall result must be either S_OK or S_FALSE (S_FALSE means * "already initialized using the same apartment model") */ AssertMsg(rc == S_OK || rc == S_FALSE, ("rc=%08X\n", rc)); /* To be flow compatible with the XPCOM case, we return here if this isn't * the main thread or if it isn't its first initialization call. * Note! CoInitializeEx and CoUninitialize does it's own reference * counting, so this exercise is entirely for the EventQueue init. */ bool fRc; RTTHREAD hSelf = RTThreadSelf(); if (hSelf != NIL_RTTHREAD) ASMAtomicCmpXchgHandle(&gCOMMainThread, hSelf, NIL_RTTHREAD, fRc); else fRc = false; if (fGui) Assert(RTThreadIsMain(hSelf)); if (!fRc) { if ( gCOMMainThread == hSelf && SUCCEEDED(rc)) gCOMMainInitCount++; AssertComRC(rc); return rc; } Assert(RTThreadIsMain(hSelf)); /* this is the first main thread initialization */ Assert(gCOMMainInitCount == 0); if (SUCCEEDED(rc)) gCOMMainInitCount = 1; #else /* !defined (VBOX_WITH_XPCOM) */ /* Unused here */ NOREF(fGui); if (ASMAtomicXchgBool(&gIsXPCOMInitialized, true) == true) { /* XPCOM is already initialized on the main thread, no special * initialization is necessary on additional threads. Just increase * the init counter if it's a main thread again (to correctly support * nested calls to Initialize()/Shutdown() for compatibility with * Win32). */ nsCOMPtr<nsIEventQueue> eventQ; rc = NS_GetMainEventQ(getter_AddRefs(eventQ)); if (NS_SUCCEEDED(rc)) { PRBool isOnMainThread = PR_FALSE; rc = eventQ->IsOnCurrentThread(&isOnMainThread); if (NS_SUCCEEDED(rc) && isOnMainThread) ++gXPCOMInitCount; } AssertComRC(rc); return rc; } Assert(RTThreadIsMain(RTThreadSelf())); /* this is the first initialization */ gXPCOMInitCount = 1; bool const fInitEventQueues = true; /* prepare paths for registry files */ char szCompReg[RTPATH_MAX]; char szXptiDat[RTPATH_MAX]; int vrc = GetVBoxUserHomeDirectory(szCompReg, sizeof(szCompReg)); AssertRCReturn(vrc, NS_ERROR_FAILURE); strcpy(szXptiDat, szCompReg); vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg.dat"); AssertRCReturn(vrc, NS_ERROR_FAILURE); vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti.dat"); AssertRCReturn(vrc, NS_ERROR_FAILURE); LogFlowFunc(("component registry : \"%s\"\n", szCompReg)); LogFlowFunc(("XPTI data file : \"%s\"\n", szXptiDat)); #if defined (XPCOM_GLUE) XPCOMGlueStartup(nsnull); #endif static const char *kAppPathsToProbe[] = { NULL, /* 0: will use VBOX_APP_HOME */ NULL, /* 1: will try RTPathAppPrivateArch() */ #ifdef RT_OS_LINUX "/usr/lib/virtualbox", "/opt/VirtualBox", #elif RT_OS_SOLARIS "/opt/VirtualBox/amd64", "/opt/VirtualBox/i386", #elif RT_OS_DARWIN "/Application/VirtualBox.app/Contents/MacOS", #endif }; /* Find out the directory where VirtualBox binaries are located */ for (size_t i = 0; i < RT_ELEMENTS(kAppPathsToProbe); ++ i) { char szAppHomeDir[RTPATH_MAX]; if (i == 0) { /* Use VBOX_APP_HOME if present */ vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_APP_HOME", szAppHomeDir, sizeof(szAppHomeDir), NULL); if (vrc == VERR_ENV_VAR_NOT_FOUND) continue; AssertRC(vrc); } else if (i == 1) { /* Use RTPathAppPrivateArch() first */ vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir)); AssertRC(vrc); } else { /* Iterate over all other paths */ szAppHomeDir[RTPATH_MAX - 1] = '\0'; strncpy(szAppHomeDir, kAppPathsToProbe[i], RTPATH_MAX - 1); vrc = VINF_SUCCESS; } if (RT_FAILURE(vrc)) { rc = NS_ERROR_FAILURE; continue; } char szCompDir[RTPATH_MAX]; vrc = RTPathAppend(strcpy(szCompDir, szAppHomeDir), sizeof(szCompDir), "components"); if (RT_FAILURE(vrc)) { rc = NS_ERROR_FAILURE; continue; } LogFlowFunc(("component directory : \"%s\"\n", szCompDir)); nsCOMPtr<DirectoryServiceProvider> dsProv; dsProv = new DirectoryServiceProvider(); if (dsProv) rc = dsProv->init(szCompReg, szXptiDat, szCompDir, szAppHomeDir); else rc = NS_ERROR_OUT_OF_MEMORY; if (NS_FAILED(rc)) break; /* Setup the application path for NS_InitXPCOM2. Note that we properly * answer the NS_XPCOM_CURRENT_PROCESS_DIR query in our directory * service provider but it seems to be activated after the directory * service is used for the first time (see the source NS_InitXPCOM2). So * use the same value here to be on the safe side. */ nsCOMPtr <nsIFile> appDir; { char *appDirCP = NULL; vrc = RTStrUtf8ToCurrentCP(&appDirCP, szAppHomeDir); if (RT_SUCCESS(vrc)) { nsCOMPtr<nsILocalFile> file; rc = NS_NewNativeLocalFile(nsEmbedCString(appDirCP), PR_FALSE, getter_AddRefs(file)); if (NS_SUCCEEDED(rc)) appDir = do_QueryInterface(file, &rc); RTStrFree(appDirCP); } else rc = NS_ERROR_FAILURE; } if (NS_FAILED(rc)) break; /* Set VBOX_XPCOM_HOME to the same app path to make XPCOM sources that * still use it instead of the directory service happy */ vrc = RTEnvSetEx(RTENV_DEFAULT, "VBOX_XPCOM_HOME", szAppHomeDir); AssertRC(vrc); /* Finally, initialize XPCOM */ { nsCOMPtr<nsIServiceManager> serviceManager; rc = NS_InitXPCOM2(getter_AddRefs(serviceManager), appDir, dsProv); if (NS_SUCCEEDED(rc)) { nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(serviceManager, &rc); if (NS_SUCCEEDED(rc)) { rc = registrar->AutoRegister(nsnull); if (NS_SUCCEEDED(rc)) { /* We succeeded, stop probing paths */ LogFlowFunc(("Succeeded.\n")); break; } } } } /* clean up before the new try */ rc = NS_ShutdownXPCOM(nsnull); if (i == 0) { /* We failed with VBOX_APP_HOME, don't probe other paths */ break; } } #endif /* !defined (VBOX_WITH_XPCOM) */ // for both COM and XPCOM, we only get here if this is the main thread; // only then initialize the autolock system (AutoLock.cpp) Assert(RTThreadIsMain(RTThreadSelf())); util::InitAutoLockSystem(); AssertComRC(rc); /* * Init the main event queue (ASSUMES it cannot fail). */ if (SUCCEEDED(rc)) EventQueue::init(); return rc; }
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); } }