VirtualBox

source: vbox/trunk/src/VBox/Main/glue/initterm.cpp@ 85489

Last change on this file since 85489 was 85489, checked in by vboxsync, 4 years ago

Main: bugref:9784: Moved the call to CoInitializeSecurity into com::Initialize from UICommon.cpp and VirtualBoxClientImpl.cpp

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.2 KB
Line 
1/* $Id: initterm.cpp 85489 2020-07-28 14:51:29Z vboxsync $ */
2/** @file
3 * MS COM / XPCOM Abstraction Layer - Initialization and Termination.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN
19#if !defined(VBOX_WITH_XPCOM)
20
21# if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600
22# undef _WIN32_WINNT
23# define _WIN32_WINNT 0x600 /* GetModuleHandleExW */
24# endif
25# include <iprt/nt/nt-and-windows.h>
26# include <iprt/win/objbase.h>
27# include <iprt/win/rpcproxy.h>
28# include <rpcasync.h>
29
30#else /* !defined(VBOX_WITH_XPCOM) */
31
32# include <stdlib.h>
33
34# include <nsIComponentRegistrar.h>
35# include <nsIServiceManager.h>
36# include <nsCOMPtr.h>
37# include <nsEventQueueUtils.h>
38# include <nsEmbedString.h>
39
40# include <nsILocalFile.h>
41# include <nsIDirectoryService.h>
42# include <nsDirectoryServiceDefs.h>
43
44#endif /* !defined(VBOX_WITH_XPCOM) */
45
46#include "VBox/com/com.h"
47#include "VBox/com/assert.h"
48#include "VBox/com/NativeEventQueue.h"
49#include "VBox/com/AutoLock.h"
50
51#include "../include/LoggingNew.h"
52
53#include <iprt/asm.h>
54#include <iprt/env.h>
55#include <iprt/ldr.h>
56#include <iprt/param.h>
57#include <iprt/path.h>
58#include <iprt/string.h>
59#include <iprt/system.h>
60#include <iprt/thread.h>
61
62#include <VBox/err.h>
63
64namespace com
65{
66
67#if defined(VBOX_WITH_XPCOM)
68
69class DirectoryServiceProvider : public nsIDirectoryServiceProvider
70{
71public:
72
73 NS_DECL_ISUPPORTS
74
75 DirectoryServiceProvider()
76 : mCompRegLocation(NULL), mXPTIDatLocation(NULL)
77 , mComponentDirLocation(NULL), mCurrProcDirLocation(NULL)
78 {}
79
80 virtual ~DirectoryServiceProvider();
81
82 HRESULT init(const char *aCompRegLocation,
83 const char *aXPTIDatLocation,
84 const char *aComponentDirLocation,
85 const char *aCurrProcDirLocation);
86
87 NS_DECL_NSIDIRECTORYSERVICEPROVIDER
88
89private:
90 /** @remarks This is not a UTF-8 string. */
91 char *mCompRegLocation;
92 /** @remarks This is not a UTF-8 string. */
93 char *mXPTIDatLocation;
94 /** @remarks This is not a UTF-8 string. */
95 char *mComponentDirLocation;
96 /** @remarks This is not a UTF-8 string. */
97 char *mCurrProcDirLocation;
98};
99
100NS_IMPL_ISUPPORTS1(DirectoryServiceProvider, nsIDirectoryServiceProvider)
101
102DirectoryServiceProvider::~DirectoryServiceProvider()
103{
104 if (mCompRegLocation)
105 {
106 RTStrFree(mCompRegLocation);
107 mCompRegLocation = NULL;
108 }
109 if (mXPTIDatLocation)
110 {
111 RTStrFree(mXPTIDatLocation);
112 mXPTIDatLocation = NULL;
113 }
114 if (mComponentDirLocation)
115 {
116 RTStrFree(mComponentDirLocation);
117 mComponentDirLocation = NULL;
118 }
119 if (mCurrProcDirLocation)
120 {
121 RTStrFree(mCurrProcDirLocation);
122 mCurrProcDirLocation = NULL;
123 }
124}
125
126/**
127 * @param aCompRegLocation Path to compreg.dat, in Utf8.
128 * @param aXPTIDatLocation Path to xpti.data, in Utf8.
129 */
130HRESULT
131DirectoryServiceProvider::init(const char *aCompRegLocation,
132 const char *aXPTIDatLocation,
133 const char *aComponentDirLocation,
134 const char *aCurrProcDirLocation)
135{
136 AssertReturn(aCompRegLocation, NS_ERROR_INVALID_ARG);
137 AssertReturn(aXPTIDatLocation, NS_ERROR_INVALID_ARG);
138
139/** @todo r=bird: Gotta check how this encoding stuff plays out on darwin!
140 * We get down to [VBoxNsxp]NS_NewNativeLocalFile and that file isn't
141 * nsLocalFileUnix.cpp on 32-bit darwin. On 64-bit darwin it's a question
142 * of what we're doing in IPRT and such... We should probably add a
143 * RTPathConvertToNative for use here. */
144 int vrc = RTStrUtf8ToCurrentCP(&mCompRegLocation, aCompRegLocation);
145 if (RT_SUCCESS(vrc))
146 vrc = RTStrUtf8ToCurrentCP(&mXPTIDatLocation, aXPTIDatLocation);
147 if (RT_SUCCESS(vrc) && aComponentDirLocation)
148 vrc = RTStrUtf8ToCurrentCP(&mComponentDirLocation, aComponentDirLocation);
149 if (RT_SUCCESS(vrc) && aCurrProcDirLocation)
150 vrc = RTStrUtf8ToCurrentCP(&mCurrProcDirLocation, aCurrProcDirLocation);
151
152 return RT_SUCCESS(vrc) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
153}
154
155NS_IMETHODIMP
156DirectoryServiceProvider::GetFile(const char *aProp,
157 PRBool *aPersistent,
158 nsIFile **aRetval)
159{
160 nsCOMPtr <nsILocalFile> localFile;
161 nsresult rv = NS_ERROR_FAILURE;
162
163 *aRetval = nsnull;
164 *aPersistent = PR_TRUE;
165
166 const char *fileLocation = NULL;
167
168 if (strcmp(aProp, NS_XPCOM_COMPONENT_REGISTRY_FILE) == 0)
169 fileLocation = mCompRegLocation;
170 else if (strcmp(aProp, NS_XPCOM_XPTI_REGISTRY_FILE) == 0)
171 fileLocation = mXPTIDatLocation;
172 else if (mComponentDirLocation && strcmp(aProp, NS_XPCOM_COMPONENT_DIR) == 0)
173 fileLocation = mComponentDirLocation;
174 else if (mCurrProcDirLocation && strcmp(aProp, NS_XPCOM_CURRENT_PROCESS_DIR) == 0)
175 fileLocation = mCurrProcDirLocation;
176 else
177 return NS_ERROR_FAILURE;
178
179 rv = NS_NewNativeLocalFile(nsEmbedCString(fileLocation),
180 PR_TRUE, getter_AddRefs(localFile));
181 if (NS_FAILED(rv))
182 return rv;
183
184 return localFile->QueryInterface(NS_GET_IID(nsIFile), (void **)aRetval);
185}
186
187/**
188 * Global XPCOM initialization flag (we maintain it ourselves since XPCOM
189 * doesn't provide such functionality)
190 */
191static bool volatile gIsXPCOMInitialized = false;
192
193/**
194 * Number of Initialize() calls on the main thread.
195 */
196static unsigned int gXPCOMInitCount = 0;
197
198#else /* !defined(VBOX_WITH_XPCOM) */
199
200/**
201 * Replacement function for the InvokeStub method for the IRundown stub.
202 */
203static HRESULT STDMETHODCALLTYPE
204Rundown_InvokeStub(IRpcStubBuffer *pThis, RPCOLEMESSAGE *pMsg, IRpcChannelBuffer *pBuf) RT_NOTHROW_DEF
205{
206 /*
207 * Our mission here is to prevent remote calls to methods #8 and #9,
208 * as these contain raw pointers to callback functions.
209 *
210 * Note! APIs like I_RpcServerInqTransportType, I_RpcBindingInqLocalClientPID
211 * and RpcServerInqCallAttributesW are not usable in this context without
212 * a rpc binding handle (latter two).
213 *
214 * P.S. In more recent windows versions, the buffer implements a interface
215 * IID_IRpcChannelBufferMarshalingContext (undocumented) which has a
216 * GetIMarshallingContextAttribute() method that will return the client PID
217 * when asking for attribute #0x8000000e.
218 */
219 uint32_t const iMethod = pMsg->iMethod & 0xffff; /* Uncertain, but there are hints that the upper bits are flags. */
220 HRESULT hrc;
221 if ( ( iMethod != 8
222 && iMethod != 9)
223 || (pMsg->rpcFlags & RPCFLG_LOCAL_CALL) )
224 hrc = CStdStubBuffer_Invoke(pThis, pMsg, pBuf);
225 else
226 {
227 LogRel(("Rundown_InvokeStub: Rejected call to CRundown::%s: rpcFlags=%#x cbBuffer=%#x dataRepresentation=%d buffer=%p:{%.*Rhxs} reserved1=%p reserved2={%p,%p,%p,%p,%p}\n",
228 pMsg->iMethod == 8 ? "DoCallback" : "DoNonreentrantCallback", pMsg->rpcFlags, pMsg->cbBuffer,
229 pMsg->dataRepresentation, pMsg->Buffer, RT_VALID_PTR(pMsg->Buffer) ? pMsg->cbBuffer : 0, pMsg->Buffer,
230 pMsg->reserved1, pMsg->reserved2[0], pMsg->reserved2[1], pMsg->reserved2[2], pMsg->reserved2[3], pMsg->reserved2[4]));
231 hrc = E_ACCESSDENIED;
232 }
233 return hrc;
234}
235
236/**
237 * Replaces the IRundown InvokeStub method with Rundown_InvokeStub so we can
238 * reject remote calls to a couple of misdesigned methods.
239 */
240void PatchComBugs(void)
241{
242 static volatile bool s_fPatched = false;
243 if (s_fPatched)
244 return;
245
246 /*
247 * The combase.dll / ole32.dll is exporting a DllGetClassObject function
248 * that is implemented using NdrDllGetClassObject just like our own
249 * proxy/stub DLL. This means we can get at the stub interface lists,
250 * since what NdrDllGetClassObject has CStdPSFactoryBuffer as layout.
251 *
252 * Note! Tried using CoRegisterPSClsid instead of this mess, but no luck.
253 */
254 /* Locate the COM DLL, it must be loaded by now: */
255 HMODULE hmod = GetModuleHandleW(L"COMBASE.DLL");
256 if (!hmod)
257 hmod = GetModuleHandleW(L"OLE32.DLL"); /* w7 */
258 AssertReturnVoid(hmod != NULL);
259
260 /* Resolve the class getter: */
261 LPFNGETCLASSOBJECT pfnGetClassObject = (LPFNGETCLASSOBJECT)GetProcAddress(hmod, "DllGetClassObject");
262 AssertReturnVoid(pfnGetClassObject != NULL);
263
264 /* Get the factory instance: */
265 static const CLSID s_PSOlePrx32ClsId = {0x00000320,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
266 CStdPSFactoryBuffer *pFactoryBuffer = NULL;
267 HRESULT hrc = pfnGetClassObject(s_PSOlePrx32ClsId, IID_IPSFactoryBuffer, (void **)&pFactoryBuffer);
268 AssertMsgReturnVoid(SUCCEEDED(hrc), ("hrc=%Rhrc\n", hrc));
269 AssertReturnVoid(pFactoryBuffer != NULL);
270
271 /*
272 * Search thru the file list for the interface we want to patch.
273 */
274 static const IID s_IID_Rundown = {0x00000134,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
275 decltype(CStdStubBuffer_Invoke) *pfnInvoke = (decltype(pfnInvoke))GetProcAddress(hmod, "CStdStubBuffer_Invoke");
276 if (!pfnInvoke)
277 pfnInvoke = (decltype(pfnInvoke))GetProcAddress(GetModuleHandleW(L"RPCRT4.DLL"), "CStdStubBuffer_Invoke");
278
279 unsigned cPatched = 0;
280 unsigned cAlreadyPatched = 0;
281 Assert(pFactoryBuffer->pProxyFileList != NULL);
282 for (ProxyFileInfo const **ppCur = pFactoryBuffer->pProxyFileList; *ppCur != NULL; ppCur++)
283 {
284 ProxyFileInfo const *pCur = *ppCur;
285
286 if (pCur->pStubVtblList)
287 {
288 for (PCInterfaceStubVtblList const *ppCurStub = pCur->pStubVtblList; *ppCurStub != NULL; ppCurStub++)
289 {
290 PCInterfaceStubVtblList const pCurStub = *ppCurStub;
291 IID const *piid = pCurStub->header.piid;
292 if (piid)
293 {
294 if (IsEqualIID(*piid, s_IID_Rundown))
295 {
296 if (pCurStub->Vtbl.Invoke == pfnInvoke)
297 {
298 DWORD fOld = 0;
299 if (VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), PAGE_READWRITE, &fOld))
300 {
301 pCurStub->Vtbl.Invoke = Rundown_InvokeStub;
302 VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), fOld, &fOld);
303 cPatched++;
304 }
305 else
306 AssertMsgFailed(("%d\n", GetLastError()));
307 }
308 else
309 cAlreadyPatched++;
310 }
311 }
312 }
313 }
314 }
315
316 /* done */
317 pFactoryBuffer->lpVtbl->Release((IPSFactoryBuffer *)pFactoryBuffer);
318
319 /*
320 * If we patched anything we should try prevent being unloaded.
321 */
322 if (cPatched > 0)
323 {
324 s_fPatched = true;
325 HMODULE hmodSelf;
326 AssertLogRelMsg(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
327 (LPCWSTR)(uintptr_t)Rundown_InvokeStub, &hmodSelf),
328 ("last error: %u; Rundown_InvokeStub=%p\n", GetLastError(), Rundown_InvokeStub));
329 }
330 else
331 AssertLogRelMsg(cAlreadyPatched > 0, ("COM patching of IRundown failed!\n"));
332}
333
334
335/**
336 * The COM main thread handle. (The first caller of com::Initialize().)
337 */
338static RTTHREAD volatile gCOMMainThread = NIL_RTTHREAD;
339
340/**
341 * Number of Initialize() calls on the main thread.
342 */
343static uint32_t gCOMMainInitCount = 0;
344
345#endif /* !defined(VBOX_WITH_XPCOM) */
346
347
348/**
349 * Initializes the COM runtime.
350 *
351 * This method must be called on each thread of the client application that
352 * wants to access COM facilities. The initialization must be performed before
353 * calling any other COM method or attempting to instantiate COM objects.
354 *
355 * On platforms using XPCOM, this method uses the following scheme to search for
356 * XPCOM runtime:
357 *
358 * 1. If the VBOX_APP_HOME environment variable is set, the path it specifies
359 * is used to search XPCOM libraries and components. If this method fails to
360 * initialize XPCOM runtime using this path, it will immediately return a
361 * failure and will NOT check for other paths as described below.
362 *
363 * 2. If VBOX_APP_HOME is not set, this methods tries the following paths in the
364 * given order:
365 *
366 * a) Compiled-in application data directory (as returned by
367 * RTPathAppPrivateArch())
368 * b) "/usr/lib/virtualbox" (Linux only)
369 * c) "/opt/VirtualBox" (Linux only)
370 *
371 * The first path for which the initialization succeeds will be used.
372 *
373 * On MS COM platforms, the COM runtime is provided by the system and does not
374 * need to be searched for.
375 *
376 * Once the COM subsystem is no longer necessary on a given thread, Shutdown()
377 * must be called to free resources allocated for it. Note that a thread may
378 * call Initialize() several times but for each of tese calls there must be a
379 * corresponding Shutdown() call.
380 *
381 * @return S_OK on success and a COM result code in case of failure.
382 */
383HRESULT Initialize(uint32_t fInitFlags /*=VBOX_COM_INIT_F_DEFAULT*/)
384{
385 HRESULT rc = E_FAIL;
386
387#if !defined(VBOX_WITH_XPCOM)
388
389# ifdef VBOX_WITH_AUTO_COM_REG_UPDATE
390 /*
391 * First time we're called in a process, we refresh the VBox COM
392 * registrations. Use a global mutex to prevent updating when there are
393 * API users already active, as that could lead to a bit of a mess.
394 */
395 if ( (fInitFlags & VBOX_COM_INIT_F_AUTO_REG_UPDATE)
396 && gCOMMainThread == NIL_RTTHREAD)
397 {
398 SetLastError(ERROR_SUCCESS);
399 HANDLE hLeakIt = CreateMutexW(NULL/*pSecAttr*/, FALSE, L"Global\\VirtualBoxComLazyRegistrationMutant");
400 DWORD dwErr = GetLastError();
401 AssertMsg(dwErr == ERROR_SUCCESS || dwErr == ERROR_ALREADY_EXISTS || dwErr == ERROR_ACCESS_DENIED, ("%u\n", dwErr));
402 if (dwErr == ERROR_SUCCESS)
403 {
404 char szPath[RTPATH_MAX];
405 int vrc = RTPathAppPrivateArch(szPath, sizeof(szPath));
406 if (RT_SUCCESS(vrc))
407# ifndef VBOX_IN_32_ON_64_MAIN_API
408 vrc = RTPathAppend(szPath, sizeof(szPath),
409 RT_MAKE_U64(((PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA)->NtMinorVersion,
410 ((PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA)->NtMajorVersion)
411 >= RT_MAKE_U64(1/*Lo*/,6/*Hi*/)
412 ? "VBoxProxyStub.dll" : "VBoxProxyStubLegacy.dll");
413# else
414 vrc = RTPathAppend(szPath, sizeof(szPath), "x86\\VBoxProxyStub-x86.dll");
415# endif
416 if (RT_SUCCESS(vrc))
417 {
418 RTLDRMOD hMod;
419 vrc = RTLdrLoad(szPath, &hMod);
420 if (RT_SUCCESS(vrc))
421 {
422 union
423 {
424 void *pv;
425 DECLCALLBACKMEMBER(uint32_t, pfnRegUpdate,(void));
426 } u;
427 vrc = RTLdrGetSymbol(hMod, "VbpsUpdateRegistrations", &u.pv);
428 if (RT_SUCCESS(vrc))
429 u.pfnRegUpdate();
430 /* Just keep it loaded. */
431 }
432 }
433 Assert(hLeakIt != NULL); NOREF(hLeakIt);
434 }
435 }
436# endif
437
438 /*
439 * We initialize COM in GUI thread in STA, to be compliant with QT and
440 * OLE requirments (for example to allow D&D), while other threads
441 * initialized in regular MTA. To allow fast proxyless access from
442 * GUI thread to COM objects, we explicitly provide our COM objects
443 * with free threaded marshaller.
444 * !!!!! Please think twice before touching this code !!!!!
445 */
446 DWORD flags = fInitFlags & VBOX_COM_INIT_F_GUI
447 ? COINIT_APARTMENTTHREADED
448 | COINIT_SPEED_OVER_MEMORY
449 : COINIT_MULTITHREADED
450 | COINIT_DISABLE_OLE1DDE
451 | COINIT_SPEED_OVER_MEMORY;
452
453 rc = CoInitializeEx(NULL, flags);
454
455 /* the overall result must be either S_OK or S_FALSE (S_FALSE means
456 * "already initialized using the same apartment model") */
457 AssertMsg(rc == S_OK || rc == S_FALSE, ("rc=%08X\n", rc));
458
459#if defined(VBOX_WITH_SDS)
460 // Setup COM Security to enable impersonation
461 HRESULT hrGUICoInitializeSecurity = CoInitializeSecurity(NULL,
462 -1,
463 NULL,
464 NULL,
465 RPC_C_AUTHN_LEVEL_DEFAULT,
466 RPC_C_IMP_LEVEL_IMPERSONATE,
467 NULL,
468 EOAC_NONE,
469 NULL);
470 NOREF(hrGUICoInitializeSecurity);
471 Assert(SUCCEEDED(hrGUICoInitializeSecurity) || hrGUICoInitializeSecurity == RPC_E_TOO_LATE);
472#endif
473
474 /*
475 * IRundown has unsafe two methods we need to patch to prevent remote access.
476 * Do that before we start using COM and open ourselves to possible attacks.
477 */
478 if (!(fInitFlags & VBOX_COM_INIT_F_NO_COM_PATCHING))
479 PatchComBugs();
480
481 /* To be flow compatible with the XPCOM case, we return here if this isn't
482 * the main thread or if it isn't its first initialization call.
483 * Note! CoInitializeEx and CoUninitialize does it's own reference
484 * counting, so this exercise is entirely for the EventQueue init. */
485 bool fRc;
486 RTTHREAD hSelf = RTThreadSelf();
487 if (hSelf != NIL_RTTHREAD)
488 ASMAtomicCmpXchgHandle(&gCOMMainThread, hSelf, NIL_RTTHREAD, fRc);
489 else
490 fRc = false;
491
492 if (fInitFlags & VBOX_COM_INIT_F_GUI)
493 Assert(RTThreadIsMain(hSelf));
494
495 if (!fRc)
496 {
497 if ( gCOMMainThread == hSelf
498 && SUCCEEDED(rc))
499 gCOMMainInitCount++;
500
501 AssertComRC(rc);
502 return rc;
503 }
504 Assert(RTThreadIsMain(hSelf));
505
506 /* this is the first main thread initialization */
507 Assert(gCOMMainInitCount == 0);
508 if (SUCCEEDED(rc))
509 gCOMMainInitCount = 1;
510
511#else /* !defined(VBOX_WITH_XPCOM) */
512
513 /* Unused here */
514 RT_NOREF(fInitFlags);
515
516 if (ASMAtomicXchgBool(&gIsXPCOMInitialized, true) == true)
517 {
518 /* XPCOM is already initialized on the main thread, no special
519 * initialization is necessary on additional threads. Just increase
520 * the init counter if it's a main thread again (to correctly support
521 * nested calls to Initialize()/Shutdown() for compatibility with
522 * Win32). */
523
524 nsCOMPtr<nsIEventQueue> eventQ;
525 rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
526
527 if (NS_SUCCEEDED(rc))
528 {
529 PRBool isOnMainThread = PR_FALSE;
530 rc = eventQ->IsOnCurrentThread(&isOnMainThread);
531 if (NS_SUCCEEDED(rc) && isOnMainThread)
532 ++gXPCOMInitCount;
533 }
534
535 AssertComRC(rc);
536 return rc;
537 }
538 Assert(RTThreadIsMain(RTThreadSelf()));
539
540 /* this is the first initialization */
541 gXPCOMInitCount = 1;
542
543 /* prepare paths for registry files */
544 char szCompReg[RTPATH_MAX];
545 char szXptiDat[RTPATH_MAX];
546
547 int vrc = GetVBoxUserHomeDirectory(szCompReg, sizeof(szCompReg));
548 if (vrc == VERR_ACCESS_DENIED)
549 return NS_ERROR_FILE_ACCESS_DENIED;
550 AssertRCReturn(vrc, NS_ERROR_FAILURE);
551 vrc = RTStrCopy(szXptiDat, sizeof(szXptiDat), szCompReg);
552 AssertRCReturn(vrc, NS_ERROR_FAILURE);
553# ifdef VBOX_IN_32_ON_64_MAIN_API
554 vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg-x86.dat");
555 AssertRCReturn(vrc, NS_ERROR_FAILURE);
556 vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti-x86.dat");
557 AssertRCReturn(vrc, NS_ERROR_FAILURE);
558# else
559 vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg.dat");
560 AssertRCReturn(vrc, NS_ERROR_FAILURE);
561 vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti.dat");
562 AssertRCReturn(vrc, NS_ERROR_FAILURE);
563# endif
564
565 LogFlowFunc(("component registry : \"%s\"\n", szCompReg));
566 LogFlowFunc(("XPTI data file : \"%s\"\n", szXptiDat));
567
568 static const char *kAppPathsToProbe[] =
569 {
570 NULL, /* 0: will use VBOX_APP_HOME */
571 NULL, /* 1: will try RTPathAppPrivateArch(), correctly installed release builds will never go further */
572 NULL, /* 2: will try parent directory of RTPathAppPrivateArch(), only for testcases in non-hardened builds */
573 /* There used to be hard coded paths, but they only caused trouble
574 * because they often led to mixing of builds or even versions.
575 * If you feel tempted to add anything here, think again. They would
576 * only be used if option 1 would not work, which is a sign of a big
577 * problem, as it returns a fixed location defined at compile time.
578 * It is better to fail than blindly trying to cover the problem. */
579 };
580
581 /* Find out the directory where VirtualBox binaries are located */
582 for (size_t i = 0; i < RT_ELEMENTS(kAppPathsToProbe); ++ i)
583 {
584 char szAppHomeDir[RTPATH_MAX];
585
586 if (i == 0)
587 {
588 /* Use VBOX_APP_HOME if present */
589 vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_APP_HOME", szAppHomeDir, sizeof(szAppHomeDir), NULL);
590 if (vrc == VERR_ENV_VAR_NOT_FOUND)
591 continue;
592 AssertRC(vrc);
593 }
594 else if (i == 1)
595 {
596 /* Use RTPathAppPrivateArch() first */
597 vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
598 AssertRC(vrc);
599 }
600 else if (i == 2)
601 {
602# ifdef VBOX_WITH_HARDENING
603 continue;
604# else /* !VBOX_WITH_HARDENING */
605 /* Use parent of RTPathAppPrivateArch() if ends with "testcase" */
606 vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
607 AssertRC(vrc);
608 vrc = RTPathStripTrailingSlash(szAppHomeDir);
609 AssertRC(vrc);
610 char *filename = RTPathFilename(szAppHomeDir);
611 if (!filename || strcmp(filename, "testcase"))
612 continue;
613 RTPathStripFilename(szAppHomeDir);
614# endif /* !VBOX_WITH_HARDENING */
615 }
616 else
617 {
618 /* Iterate over all other paths */
619 RTStrCopy(szAppHomeDir, sizeof(szAppHomeDir), kAppPathsToProbe[i]);
620 vrc = VINF_SUCCESS;
621 }
622 if (RT_FAILURE(vrc))
623 {
624 rc = NS_ERROR_FAILURE;
625 continue;
626 }
627 char szCompDir[RTPATH_MAX];
628 vrc = RTStrCopy(szCompDir, sizeof(szCompDir), szAppHomeDir);
629 if (RT_FAILURE(vrc))
630 {
631 rc = NS_ERROR_FAILURE;
632 continue;
633 }
634 vrc = RTPathAppend(szCompDir, sizeof(szCompDir), "components");
635 if (RT_FAILURE(vrc))
636 {
637 rc = NS_ERROR_FAILURE;
638 continue;
639 }
640 LogFlowFunc(("component directory : \"%s\"\n", szCompDir));
641
642 nsCOMPtr<DirectoryServiceProvider> dsProv;
643 dsProv = new DirectoryServiceProvider();
644 if (dsProv)
645 rc = dsProv->init(szCompReg, szXptiDat, szCompDir, szAppHomeDir);
646 else
647 rc = NS_ERROR_OUT_OF_MEMORY;
648 if (NS_FAILED(rc))
649 break;
650
651 /* Setup the application path for NS_InitXPCOM2. Note that we properly
652 * answer the NS_XPCOM_CURRENT_PROCESS_DIR query in our directory
653 * service provider but it seems to be activated after the directory
654 * service is used for the first time (see the source NS_InitXPCOM2). So
655 * use the same value here to be on the safe side. */
656 nsCOMPtr <nsIFile> appDir;
657 {
658 char *appDirCP = NULL;
659 vrc = RTStrUtf8ToCurrentCP(&appDirCP, szAppHomeDir);
660 if (RT_SUCCESS(vrc))
661 {
662 nsCOMPtr<nsILocalFile> file;
663 rc = NS_NewNativeLocalFile(nsEmbedCString(appDirCP),
664 PR_FALSE, getter_AddRefs(file));
665 if (NS_SUCCEEDED(rc))
666 appDir = do_QueryInterface(file, &rc);
667
668 RTStrFree(appDirCP);
669 }
670 else
671 rc = NS_ERROR_FAILURE;
672 }
673 if (NS_FAILED(rc))
674 break;
675
676 /* Set VBOX_XPCOM_HOME to the same app path to make XPCOM sources that
677 * still use it instead of the directory service happy */
678 vrc = RTEnvSetEx(RTENV_DEFAULT, "VBOX_XPCOM_HOME", szAppHomeDir);
679 AssertRC(vrc);
680
681 /* Finally, initialize XPCOM */
682 {
683 nsCOMPtr<nsIServiceManager> serviceManager;
684 rc = NS_InitXPCOM2(getter_AddRefs(serviceManager), appDir, dsProv);
685 if (NS_SUCCEEDED(rc))
686 {
687 nsCOMPtr<nsIComponentRegistrar> registrar =
688 do_QueryInterface(serviceManager, &rc);
689 if (NS_SUCCEEDED(rc))
690 {
691 rc = registrar->AutoRegister(nsnull);
692 if (NS_SUCCEEDED(rc))
693 {
694 /* We succeeded, stop probing paths */
695 LogFlowFunc(("Succeeded.\n"));
696 break;
697 }
698 }
699 }
700 }
701
702 /* clean up before the new try */
703 HRESULT rc2 = NS_ShutdownXPCOM(nsnull);
704 if (SUCCEEDED(rc))
705 rc = rc2;
706
707 if (i == 0)
708 {
709 /* We failed with VBOX_APP_HOME, don't probe other paths */
710 break;
711 }
712 }
713
714#endif /* !defined(VBOX_WITH_XPCOM) */
715
716 AssertComRCReturnRC(rc);
717
718 // for both COM and XPCOM, we only get here if this is the main thread;
719 // only then initialize the autolock system (AutoLock.cpp)
720 Assert(RTThreadIsMain(RTThreadSelf()));
721 util::InitAutoLockSystem();
722
723 /*
724 * Init the main event queue (ASSUMES it cannot fail).
725 */
726 if (SUCCEEDED(rc))
727 NativeEventQueue::init();
728
729 return rc;
730}
731
732HRESULT Shutdown()
733{
734 HRESULT rc = S_OK;
735
736#if !defined(VBOX_WITH_XPCOM)
737
738 /* EventQueue::uninit reference counting fun. */
739 RTTHREAD hSelf = RTThreadSelf();
740 if ( hSelf == gCOMMainThread
741 && hSelf != NIL_RTTHREAD)
742 {
743 if (-- gCOMMainInitCount == 0)
744 {
745 NativeEventQueue::uninit();
746 ASMAtomicWriteHandle(&gCOMMainThread, NIL_RTTHREAD);
747 }
748 }
749
750 CoUninitialize();
751
752#else /* !defined(VBOX_WITH_XPCOM) */
753
754 nsCOMPtr<nsIEventQueue> eventQ;
755 rc = NS_GetMainEventQ(getter_AddRefs(eventQ));
756
757 if (NS_SUCCEEDED(rc) || rc == NS_ERROR_NOT_AVAILABLE)
758 {
759 /* NS_ERROR_NOT_AVAILABLE seems to mean that
760 * nsIEventQueue::StopAcceptingEvents() has been called (see
761 * nsEventQueueService.cpp). We hope that this error code always means
762 * just that in this case and assume that we're on the main thread
763 * (it's a kind of unexpected behavior if a non-main thread ever calls
764 * StopAcceptingEvents() on the main event queue). */
765
766 PRBool isOnMainThread = PR_FALSE;
767 if (NS_SUCCEEDED(rc))
768 {
769 rc = eventQ->IsOnCurrentThread(&isOnMainThread);
770 eventQ = nsnull; /* early release before shutdown */
771 }
772 else
773 {
774 isOnMainThread = RTThreadIsMain(RTThreadSelf());
775 rc = NS_OK;
776 }
777
778 if (NS_SUCCEEDED(rc) && isOnMainThread)
779 {
780 /* only the main thread needs to uninitialize XPCOM and only if
781 * init counter drops to zero */
782 if (--gXPCOMInitCount == 0)
783 {
784 NativeEventQueue::uninit();
785 rc = NS_ShutdownXPCOM(nsnull);
786
787 /* This is a thread initialized XPCOM and set gIsXPCOMInitialized to
788 * true. Reset it back to false. */
789 bool wasInited = ASMAtomicXchgBool(&gIsXPCOMInitialized, false);
790 Assert(wasInited == true);
791 NOREF(wasInited);
792 }
793 }
794 }
795
796#endif /* !defined(VBOX_WITH_XPCOM) */
797
798 AssertComRC(rc);
799
800 return rc;
801}
802
803} /* namespace com */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette