VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/VirtualBoxClientImpl.cpp@ 69783

Last change on this file since 69783 was 69783, checked in by vboxsync, 8 years ago

Main: SDS plan B it is.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.7 KB
Line 
1/* $Id: VirtualBoxClientImpl.cpp 69783 2017-11-20 18:58:25Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2010-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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_VIRTUALBOXCLIENT
19#include "LoggingNew.h"
20
21#include "VirtualBoxClientImpl.h"
22
23#include "AutoCaller.h"
24#include "VBoxEvents.h"
25#include "VBox/com/ErrorInfo.h"
26
27#include <iprt/asm.h>
28#include <iprt/thread.h>
29#include <iprt/critsect.h>
30#include <iprt/semaphore.h>
31#include <iprt/cpp/utils.h>
32#include <iprt/utf16.h>
33#ifdef RT_OS_WINDOWS
34# include <iprt/ldr.h>
35# include <msi.h>
36# include <WbemIdl.h>
37#endif
38
39
40/** Waiting time between probing whether VBoxSVC is alive. */
41#define VBOXCLIENT_DEFAULT_INTERVAL 30000
42
43
44/** Initialize instance counter class variable */
45uint32_t VirtualBoxClient::g_cInstances = 0;
46
47LONG VirtualBoxClient::s_cUnnecessaryAtlModuleLocks = 0;
48
49// constructor / destructor
50/////////////////////////////////////////////////////////////////////////////
51
52HRESULT VirtualBoxClient::FinalConstruct()
53{
54 HRESULT rc = init();
55 BaseFinalConstruct();
56 return rc;
57}
58
59void VirtualBoxClient::FinalRelease()
60{
61 uninit();
62 BaseFinalRelease();
63}
64
65
66// public initializer/uninitializer for internal purposes only
67/////////////////////////////////////////////////////////////////////////////
68
69/**
70 * Initializes the VirtualBoxClient object.
71 *
72 * @returns COM result indicator
73 */
74HRESULT VirtualBoxClient::init()
75{
76
77#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_SDS)
78 // setup COM Security to enable impersonation
79 // This works for console VirtualBox clients, GUI has own security settings
80 // For GUI Virtual Box it will be second call so can return TOO_LATE error
81 HRESULT hrGUICoInitializeSecurity = CoInitializeSecurity(NULL,
82 -1,
83 NULL,
84 NULL,
85 RPC_C_AUTHN_LEVEL_DEFAULT,
86 RPC_C_IMP_LEVEL_IMPERSONATE,
87 NULL,
88 EOAC_NONE,
89 NULL);
90 NOREF(hrGUICoInitializeSecurity);
91 Assert(SUCCEEDED(hrGUICoInitializeSecurity) || hrGUICoInitializeSecurity == RPC_E_TOO_LATE);
92#endif
93
94 LogFlowThisFuncEnter();
95
96 /* Enclose the state transition NotReady->InInit->Ready */
97 AutoInitSpan autoInitSpan(this);
98 AssertReturn(autoInitSpan.isOk(), E_FAIL);
99
100 /* Important: DO NOT USE any kind of "early return" (except the single
101 * one above, checking the init span success) in this method. It is vital
102 * for correct error handling that it has only one point of return, which
103 * does all the magic on COM to signal object creation success and
104 * reporting the error later for every API method. COM translates any
105 * unsuccessful object creation to REGDB_E_CLASSNOTREG errors or similar
106 * unhelpful ones which cause us a lot of grief with troubleshooting. */
107
108 HRESULT rc = S_OK;
109 try
110 {
111 if (ASMAtomicIncU32(&g_cInstances) != 1)
112 AssertFailedStmt(throw setError(E_FAIL,
113 tr("Attempted to create more than one VirtualBoxClient instance")));
114
115 mData.m_ThreadWatcher = NIL_RTTHREAD;
116 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
117
118 rc = mData.m_pVirtualBox.createLocalObject(CLSID_VirtualBox);
119 if (FAILED(rc))
120#ifdef RT_OS_WINDOWS
121 throw i_investigateVirtualBoxObjectCreationFailure(rc);
122#else
123 throw rc;
124#endif
125
126 /* VirtualBox error return is postponed to method calls, fetch it. */
127 ULONG rev;
128 rc = mData.m_pVirtualBox->COMGETTER(Revision)(&rev);
129 if (FAILED(rc))
130 throw rc;
131
132 rc = unconst(mData.m_pEventSource).createObject();
133 AssertComRCThrow(rc, setError(rc,
134 tr("Could not create EventSource for VirtualBoxClient")));
135 rc = mData.m_pEventSource->init();
136 AssertComRCThrow(rc, setError(rc,
137 tr("Could not initialize EventSource for VirtualBoxClient")));
138
139 /* HACK ALERT! This is for DllCanUnloadNow(). */
140 s_cUnnecessaryAtlModuleLocks++;
141 AssertMsg(s_cUnnecessaryAtlModuleLocks == 1, ("%d\n", s_cUnnecessaryAtlModuleLocks));
142
143 /* Setting up the VBoxSVC watcher thread. If anything goes wrong here it
144 * is not considered important enough to cause any sort of visible
145 * failure. The monitoring will not be done, but that's all. */
146 int vrc = RTSemEventCreate(&mData.m_SemEvWatcher);
147 if (RT_FAILURE(vrc))
148 {
149 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
150 AssertRCStmt(vrc, throw setError(VBOX_E_IPRT_ERROR,
151 tr("Failed to create semaphore (rc=%Rrc)"),
152 vrc));
153 }
154
155 vrc = RTThreadCreate(&mData.m_ThreadWatcher, SVCWatcherThread, this, 0,
156 RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "VBoxSVCWatcher");
157 if (RT_FAILURE(vrc))
158 {
159 RTSemEventDestroy(mData.m_SemEvWatcher);
160 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
161 AssertRCStmt(vrc, throw setError(VBOX_E_IPRT_ERROR,
162 tr("Failed to create watcher thread (rc=%Rrc)"),
163 vrc));
164 }
165 }
166 catch (HRESULT err)
167 {
168 /* we assume that error info is set by the thrower */
169 rc = err;
170 }
171 catch (...)
172 {
173 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
174 }
175
176 /* Confirm a successful initialization when it's the case. Must be last,
177 * as on failure it will uninitialize the object. */
178 if (SUCCEEDED(rc))
179 autoInitSpan.setSucceeded();
180 else
181 autoInitSpan.setFailed(rc);
182
183 LogFlowThisFunc(("rc=%Rhrc\n", rc));
184 LogFlowThisFuncLeave();
185 /* Unconditionally return success, because the error return is delayed to
186 * the attribute/method calls through the InitFailed object state. */
187 return S_OK;
188}
189
190#ifdef RT_OS_WINDOWS
191/**
192 * Looks into why we failed to create the VirtualBox object.
193 *
194 * @returns hrcCaller thru setError.
195 * @param hrcCaller The failure status code.
196 */
197HRESULT VirtualBoxClient::i_investigateVirtualBoxObjectCreationFailure(HRESULT hrcCaller)
198{
199 /*
200 * First step is to try get an IUnknown interface of the VirtualBox object.
201 *
202 * This will succeed even when oleaut32.msm (see @bugref{8016}, @ticketref{12087})
203 * is accidentally installed and messes up COM. It may also succeed when the COM
204 * registration is partially broken (though that's unlikely to happen these days).
205 */
206 IUnknown *pUnknown = NULL;
207 HRESULT hrc = CoCreateInstance(CLSID_VirtualBox, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void **)&pUnknown);
208 if (FAILED(hrc))
209 {
210 if (hrc == hrcCaller)
211 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox: %Rhrc"), hrcCaller);
212 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox: %Rhrc & %Rhrc"), hrcCaller, hrc);
213 }
214
215 /*
216 * Try query the IVirtualBox interface (should fail), if it succeed we return
217 * straight away so we have more columns to spend on long messages below.
218 */
219 IVirtualBox *pVirtualBox;
220 hrc = pUnknown->QueryInterface(IID_IVirtualBox, (void **)&pVirtualBox);
221 if (SUCCEEDED(hrc))
222 {
223 pVirtualBox->Release();
224 pUnknown->Release();
225 return setError(hrcCaller,
226 tr("Failed to instantiate CLSID_VirtualBox the first time, but worked when checking out why ... weird"));
227 }
228
229 /*
230 * Check for oleaut32.msm traces in the registry.
231 */
232 HKEY hKey;
233 LSTATUS lrc = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"CLSID\\{00020420-0000-0000-C000-000000000046}\\InprocServer32",
234 0 /*fFlags*/, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | STANDARD_RIGHTS_READ, &hKey);
235 if (lrc == ERROR_SUCCESS)
236 {
237 wchar_t wszBuf[8192];
238 DWORD cbBuf = sizeof(wszBuf) - sizeof(wchar_t);
239 DWORD dwType = 0;
240 lrc = RegQueryValueExW(hKey, L"InprocServer32", NULL /*pvReserved*/, &dwType, (BYTE *)&wszBuf[0], &cbBuf);
241 if (lrc == ERROR_SUCCESS)
242 {
243 wszBuf[cbBuf / sizeof(wchar_t)] = '\0';
244 bool fSetError = false;
245
246 /*
247 * Try decode the string and improve the message.
248 */
249 typedef UINT (WINAPI *PFNMSIDECOMPOSEDESCRIPTORW)(PCWSTR pwszDescriptor,
250 LPWSTR pwszProductCode /*[40]*/,
251 LPWSTR pwszFeatureId /*[40]*/,
252 LPWSTR pwszComponentCode /*[40]*/,
253 DWORD *poffArguments);
254 PFNMSIDECOMPOSEDESCRIPTORW pfnMsiDecomposeDescriptorW;
255 pfnMsiDecomposeDescriptorW = (PFNMSIDECOMPOSEDESCRIPTORW)RTLdrGetSystemSymbol("msi.dll", "MsiDecomposeDescriptorW");
256 if ( pfnMsiDecomposeDescriptorW
257 && ( dwType == REG_SZ
258 || dwType == REG_MULTI_SZ))
259 {
260 wchar_t wszProductCode[RTUUID_STR_LENGTH + 2 + 16] = { 0 };
261 wchar_t wszFeatureId[RTUUID_STR_LENGTH + 2 + 16] = { 0 };
262 wchar_t wszComponentCode[RTUUID_STR_LENGTH + 2 + 16] = { 0 };
263 DWORD offArguments = ~(DWORD)0;
264 UINT uRc = pfnMsiDecomposeDescriptorW(wszBuf, wszProductCode, wszFeatureId, wszComponentCode, &offArguments);
265 if (uRc == 0)
266 {
267 /*
268 * Can we resolve the product code into a name?
269 */
270 typedef UINT (WINAPI *PFNMSIOPENPRODUCTW)(PCWSTR, MSIHANDLE *);
271 PFNMSIOPENPRODUCTW pfnMsiOpenProductW;
272 pfnMsiOpenProductW = (PFNMSIOPENPRODUCTW)RTLdrGetSystemSymbol("msi.dll", "MsiOpenProductW");
273
274 typedef UINT (WINAPI *PFNMSICLOSEHANDLE)(MSIHANDLE);
275 PFNMSICLOSEHANDLE pfnMsiCloseHandle;
276 pfnMsiCloseHandle = (PFNMSICLOSEHANDLE)RTLdrGetSystemSymbol("msi.dll", "MsiCloseHandle");
277
278 typedef UINT (WINAPI *PFNGETPRODUCTPROPERTYW)(MSIHANDLE, PCWSTR, PWSTR, PDWORD);
279 PFNGETPRODUCTPROPERTYW pfnMsiGetProductPropertyW;
280 pfnMsiGetProductPropertyW = (PFNGETPRODUCTPROPERTYW)RTLdrGetSystemSymbol("msi.dll", "MsiGetProductPropertyW");
281 if ( pfnMsiGetProductPropertyW
282 && pfnMsiCloseHandle
283 && pfnMsiOpenProductW)
284 {
285 MSIHANDLE hMsi = 0;
286 uRc = pfnMsiOpenProductW(wszProductCode, &hMsi);
287 if (uRc == 0)
288 {
289 static wchar_t const * const s_apwszProps[] =
290 {
291 INSTALLPROPERTY_INSTALLEDPRODUCTNAME,
292 INSTALLPROPERTY_PRODUCTNAME,
293 INSTALLPROPERTY_PACKAGENAME,
294 };
295
296 wchar_t wszProductName[1024];
297 DWORD cwcProductName;
298 unsigned i = 0;
299 do
300 {
301 cwcProductName = RT_ELEMENTS(wszProductName) - 1;
302 uRc = pfnMsiGetProductPropertyW(hMsi, s_apwszProps[i], wszProductName, &cwcProductName);
303 }
304 while ( ++i < RT_ELEMENTS(s_apwszProps)
305 && ( uRc != 0
306 || cwcProductName < 2
307 || cwcProductName >= RT_ELEMENTS(wszProductName)) );
308 uRc = pfnMsiCloseHandle(hMsi);
309 if (uRc == 0 && cwcProductName >= 2)
310 {
311 wszProductName[RT_MIN(cwcProductName, RT_ELEMENTS(wszProductName) - 1)] = '\0';
312 setError(hrcCaller,
313 tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, but CLSID_VirtualBox w/ IUnknown works.\n"
314 "PSDispatch looks broken by the '%ls' (%ls) program, suspecting that it features the broken oleaut32.msm module as component %ls.\n"
315 "\n"
316 "We suggest you try uninstall '%ls'.\n"
317 "\n"
318 "See also https://support.microsoft.com/en-us/kb/316911 "),
319 wszProductName, wszProductCode, wszComponentCode, wszProductName);
320 fSetError = true;
321 }
322 }
323 }
324
325 /* MSI uses COM and may mess up our stuff. So, we wait with the fallback till afterwards in this case. */
326 if (!fSetError)
327 {
328 setError(hrcCaller,
329 tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, CLSID_VirtualBox w/ IUnknown works.\n"
330 "PSDispatch looks broken by installer %ls featuring the broken oleaut32.msm module as component %ls.\n"
331 "\n"
332 "See also https://support.microsoft.com/en-us/kb/316911 "),
333 wszProductCode, wszComponentCode);
334 fSetError = true;
335 }
336 }
337 }
338 if (!fSetError)
339 setError(hrcCaller, tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, CLSID_VirtualBox w/ IUnknown works.\n"
340 "PSDispatch looks broken by some installer featuring the broken oleaut32.msm module as a component.\n"
341 "\n"
342 "See also https://support.microsoft.com/en-us/kb/316911 "));
343 }
344 else if (lrc == ERROR_FILE_NOT_FOUND)
345 setError(hrcCaller, tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, but CLSID_VirtualBox w/ IUnknown works.\n"
346 "PSDispatch looks fine. Weird"));
347 else
348 setError(hrcCaller, tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, but CLSID_VirtualBox w/ IUnknown works.\n"
349 "Checking out PSDispatch registration ended with error: %u (%#x)"), lrc, lrc);
350 RegCloseKey(hKey);
351 }
352
353 pUnknown->Release();
354 return hrcCaller;
355}
356#endif /* RT_OS_WINDOWS */
357
358/**
359 * Uninitializes the instance and sets the ready flag to FALSE.
360 * Called either from FinalRelease() or by the parent when it gets destroyed.
361 */
362void VirtualBoxClient::uninit()
363{
364 LogFlowThisFunc(("\n"));
365
366 /* Enclose the state transition Ready->InUninit->NotReady */
367 AutoUninitSpan autoUninitSpan(this);
368 if (autoUninitSpan.uninitDone())
369 return;
370
371 if (mData.m_ThreadWatcher != NIL_RTTHREAD)
372 {
373 /* Signal the event semaphore and wait for the thread to terminate.
374 * if it hangs for some reason exit anyway, this can cause a crash
375 * though as the object will no longer be available. */
376 RTSemEventSignal(mData.m_SemEvWatcher);
377 RTThreadWait(mData.m_ThreadWatcher, 30000, NULL);
378 mData.m_ThreadWatcher = NIL_RTTHREAD;
379 RTSemEventDestroy(mData.m_SemEvWatcher);
380 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
381 }
382
383 mData.m_pToken.setNull();
384 mData.m_pVirtualBox.setNull();
385
386 ASMAtomicDecU32(&g_cInstances);
387}
388
389// IVirtualBoxClient properties
390/////////////////////////////////////////////////////////////////////////////
391
392/**
393 * Returns a reference to the VirtualBox object.
394 *
395 * @returns COM status code
396 * @param aVirtualBox Address of result variable.
397 */
398HRESULT VirtualBoxClient::getVirtualBox(ComPtr<IVirtualBox> &aVirtualBox)
399{
400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
401 aVirtualBox = mData.m_pVirtualBox;
402 return S_OK;
403}
404
405/**
406 * Create a new Session object and return a reference to it.
407 *
408 * @returns COM status code
409 * @param aSession Address of result variable.
410 */
411HRESULT VirtualBoxClient::getSession(ComPtr<ISession> &aSession)
412{
413 /* this is not stored in this object, no need to lock */
414 ComPtr<ISession> pSession;
415 HRESULT rc = pSession.createInprocObject(CLSID_Session);
416 if (SUCCEEDED(rc))
417 aSession = pSession;
418 return rc;
419}
420
421/**
422 * Return reference to the EventSource associated with this object.
423 *
424 * @returns COM status code
425 * @param aEventSource Address of result variable.
426 */
427HRESULT VirtualBoxClient::getEventSource(ComPtr<IEventSource> &aEventSource)
428{
429 /* this is const, no need to lock */
430 aEventSource = mData.m_pEventSource;
431 return aEventSource.isNull() ? E_FAIL : S_OK;
432}
433
434// IVirtualBoxClient methods
435/////////////////////////////////////////////////////////////////////////////
436
437/**
438 * Checks a Machine object for any pending errors.
439 *
440 * @returns COM status code
441 * @param aMachine Machine object to check.
442 */
443HRESULT VirtualBoxClient::checkMachineError(const ComPtr<IMachine> &aMachine)
444{
445 BOOL fAccessible = FALSE;
446 HRESULT rc = aMachine->COMGETTER(Accessible)(&fAccessible);
447 if (FAILED(rc))
448 return setError(rc, tr("Could not check the accessibility status of the VM"));
449 else if (!fAccessible)
450 {
451 ComPtr<IVirtualBoxErrorInfo> pAccessError;
452 rc = aMachine->COMGETTER(AccessError)(pAccessError.asOutParam());
453 if (FAILED(rc))
454 return setError(rc, tr("Could not get the access error message of the VM"));
455 else
456 {
457 ErrorInfo info(pAccessError);
458 ErrorInfoKeeper eik(info);
459 return info.getResultCode();
460 }
461 }
462 return S_OK;
463}
464
465// private methods
466/////////////////////////////////////////////////////////////////////////////
467
468
469/// @todo AM Add pinging of VBoxSDS
470/*static*/
471DECLCALLBACK(int) VirtualBoxClient::SVCWatcherThread(RTTHREAD ThreadSelf,
472 void *pvUser)
473{
474 NOREF(ThreadSelf);
475 Assert(pvUser);
476 VirtualBoxClient *pThis = (VirtualBoxClient *)pvUser;
477 RTSEMEVENT sem = pThis->mData.m_SemEvWatcher;
478 RTMSINTERVAL cMillies = VBOXCLIENT_DEFAULT_INTERVAL;
479 int vrc;
480
481 /* The likelihood of early crashes are high, so start with a short wait. */
482 vrc = RTSemEventWait(sem, cMillies / 2);
483
484 /* As long as the waiting times out keep retrying the wait. */
485 while (RT_FAILURE(vrc))
486 {
487 {
488 HRESULT rc = S_OK;
489 ComPtr<IVirtualBox> pV;
490 {
491 AutoReadLock alock(pThis COMMA_LOCKVAL_SRC_POS);
492 pV = pThis->mData.m_pVirtualBox;
493 }
494 if (!pV.isNull())
495 {
496 ULONG rev;
497 rc = pV->COMGETTER(Revision)(&rev);
498 if (FAILED_DEAD_INTERFACE(rc))
499 {
500 LogRel(("VirtualBoxClient: detected unresponsive VBoxSVC (rc=%Rhrc)\n", rc));
501 {
502 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
503 /* Throw away the VirtualBox reference, it's no longer
504 * usable as VBoxSVC terminated in the mean time. */
505 pThis->mData.m_pVirtualBox.setNull();
506 }
507 fireVBoxSVCAvailabilityChangedEvent(pThis->mData.m_pEventSource, FALSE);
508 }
509 }
510 else
511 {
512 /* Try to get a new VirtualBox reference straight away, and if
513 * this fails use an increased waiting time as very frequent
514 * restart attempts in some wedged config can cause high CPU
515 * and disk load. */
516 ComPtr<IVirtualBox> pVirtualBox;
517 ComPtr<IToken> pToken;
518 rc = pVirtualBox.createLocalObject(CLSID_VirtualBox);
519 if (FAILED(rc))
520 cMillies = 3 * VBOXCLIENT_DEFAULT_INTERVAL;
521 else
522 {
523 LogRel(("VirtualBoxClient: detected working VBoxSVC (rc=%Rhrc)\n", rc));
524 {
525 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
526 /* Update the VirtualBox reference, there's a working
527 * VBoxSVC again from now on. */
528 pThis->mData.m_pVirtualBox = pVirtualBox;
529 pThis->mData.m_pToken = pToken;
530 }
531 fireVBoxSVCAvailabilityChangedEvent(pThis->mData.m_pEventSource, TRUE);
532 cMillies = VBOXCLIENT_DEFAULT_INTERVAL;
533 }
534 }
535 }
536 vrc = RTSemEventWait(sem, cMillies);
537 }
538 return 0;
539}
540
541/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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