VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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