VirtualBox

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

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

Main: spaces

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.5 KB
Line 
1/* $Id: VirtualBoxClientImpl.cpp 66926 2017-05-17 09:07:37Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2010-2016 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#include "VirtualBoxClientImpl.h"
19
20#include "AutoCaller.h"
21#include "VBoxEvents.h"
22#include "Logging.h"
23#include "VBox/com/ErrorInfo.h"
24
25#include <iprt/asm.h>
26#include <iprt/thread.h>
27#include <iprt/critsect.h>
28#include <iprt/semaphore.h>
29#include <iprt/cpp/utils.h>
30#include <iprt/utf16.h>
31#ifdef RT_OS_WINDOWS
32# include <iprt/ldr.h>
33# include <msi.h>
34# include <WbemIdl.h>
35#endif
36
37
38/** Waiting time between probing whether VBoxSVC is alive. */
39#define VBOXCLIENT_DEFAULT_INTERVAL 30000
40
41
42/** Initialize instance counter class variable */
43uint32_t VirtualBoxClient::g_cInstances = 0;
44
45LONG VirtualBoxClient::s_cUnnecessaryAtlModuleLocks = 0;
46
47// constructor / destructor
48/////////////////////////////////////////////////////////////////////////////
49
50HRESULT VirtualBoxClient::FinalConstruct()
51{
52 HRESULT rc = init();
53 BaseFinalConstruct();
54 return rc;
55}
56
57void VirtualBoxClient::FinalRelease()
58{
59 uninit();
60 BaseFinalRelease();
61}
62
63
64// public initializer/uninitializer for internal purposes only
65/////////////////////////////////////////////////////////////////////////////
66
67#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_SDS)
68
69HRESULT CreateVirtualBoxThroughSDS(ComPtr<IVirtualBox> &aVirtualBox, ComPtr<IToken> &aToken)
70{
71 ComPtr<IVirtualBoxSDS> aVirtualBoxSDS;
72 HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, /* the VirtualBoxSDS object */
73 NULL, /* no aggregation */
74 CLSCTX_LOCAL_SERVER, /* the object lives in the current process */
75 IID_IVirtualBoxSDS, /* IID of the interface */
76 (void **)aVirtualBoxSDS.asOutParam());
77 AssertComRCReturn(hrc, hrc);
78
79 hrc = aVirtualBoxSDS->GetVirtualBox(aVirtualBox.asOutParam(), aToken.asOutParam());
80 AssertComRC(hrc);
81
82 return hrc;
83}
84
85
86int VirtualBoxClient::getServiceAccount(const wchar_t *pwszServiceName, wchar_t *pwszAccountName, size_t cwcAccountName)
87{
88 AssertPtr(pwszServiceName);
89 AssertPtr(pwszAccountName);
90 Assert(cwcAccountName);
91 *pwszAccountName = '\0';
92
93 int vrc;
94
95 // Get a handle to the SCM database.
96 SC_HANDLE hSCManager = OpenSCManagerW(NULL /*pwszMachineName*/, NULL /*pwszDatabaseName*/, SC_MANAGER_ALL_ACCESS);
97 if (hSCManager != NULL)
98 {
99 SC_HANDLE hService = OpenServiceW(hSCManager, pwszServiceName, SERVICE_QUERY_CONFIG);
100 if (hService != NULL)
101 {
102 DWORD cbNeeded = sizeof(QUERY_SERVICE_CONFIGW) + 256;
103 if (!QueryServiceConfigW(hService, NULL, 0, &cbNeeded))
104 {
105 Assert(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
106 LPQUERY_SERVICE_CONFIGW pSc = (LPQUERY_SERVICE_CONFIGW)RTMemTmpAllocZ(cbNeeded);
107 if (pSc)
108 {
109 DWORD cbNeeded2 = 0;
110 if (QueryServiceConfigW(hService, pSc, cbNeeded, &cbNeeded2))
111 {
112 vrc = RTUtf16Copy(pwszAccountName, cwcAccountName, pSc->lpServiceStartName);
113 if (RT_FAILURE(vrc))
114 LogRel(("Error: SDS service name is too long (%Rrc): %ls\n", vrc, pSc->lpServiceStartName));
115 }
116 else
117 {
118 int dwError = GetLastError();
119 vrc = RTErrConvertFromWin32(dwError);
120 LogRel(("Error: Failed querying service config: %Rwc (%u) -> %Rrc; cbNeeded=%d cbNeeded2=%d\n",
121 dwError, dwError, vrc, cbNeeded, cbNeeded2));
122 }
123 RTMemTmpFree(pSc);
124 }
125 else
126 {
127 LogRel(("Error: Failed allocating %#x bytes of memory for service config!\n", cbNeeded));
128 vrc = VERR_NO_TMP_MEMORY;
129 }
130 }
131 else
132 {
133 AssertLogRelMsgFailed(("Error: QueryServiceConfigW returns success with zero buffer!\n"));
134 vrc = VERR_IPE_UNEXPECTED_STATUS;
135 }
136 CloseServiceHandle(hService);
137 }
138 else
139 {
140 int dwError = GetLastError();
141 vrc = RTErrConvertFromWin32(dwError);
142 LogRel(("Error: Could not open service: %Rwc (%u) -> %Rrc\n", dwError, dwError, vrc));
143 }
144 CloseServiceHandle(hSCManager);
145 }
146 else
147 {
148 int dwError = GetLastError();
149 vrc = RTErrConvertFromWin32(dwError);
150 LogRel(("Error: Could not open SCM: %Rwc (%u) -> %Rrc\n", dwError, dwError, vrc));
151 }
152 return vrc;
153}
154
155
156HRESULT VirtualBoxClient::isServiceDisabled(const wchar_t *pwszServiceName, bool* pOutIsDisabled)
157{
158 Assert(pOutIsDisabled);
159 Assert(pwszServiceName);
160 ComPtr<IWbemLocator> aLocator;
161 ComPtr<IWbemServices> aService;
162
163 HRESULT hr = CoCreateInstance(CLSID_WbemLocator, 0,
164 CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)aLocator.asOutParam());
165 if (FAILED(hr))
166 {
167 LogRel(("Error: Cannot instantiate WbemLocator: %Rhrc", hr));
168 return hr;
169 }
170
171 hr = aLocator->ConnectServer(
172 com::Bstr(L"ROOT\\CIMV2").raw(), // Object path of WMI namespace
173 NULL, // User name. NULL = current user
174 NULL, // User password. NULL = current
175 0, // Locale. NULL indicates current
176 NULL, // Security flags.
177 0, // Authority (for example, Kerberos)
178 0, // Context object
179 aService.asOutParam() // pointer to IWbemServices proxy
180 );
181 if (FAILED(hr))
182 {
183 LogRel(("Error: Cannot connect to Wbem Service: %Rhrc\n", hr));
184 return hr;
185 }
186
187 // query settings for VBoxSDS windows service
188 ComPtr<IEnumWbemClassObject> aEnumerator;
189 hr = aService->ExecQuery(
190 com::Bstr("WQL").raw(),
191 com::BstrFmt("SELECT * FROM Win32_Service WHERE Name='%ls'", pwszServiceName).raw(),
192 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
193 NULL,
194 aEnumerator.asOutParam());
195 if (FAILED(hr) || aEnumerator == NULL)
196 {
197 LogRel(("Error: querying service settings from WMI: %Rhrc\n", hr));
198 return hr;
199 }
200
201 ULONG uReturn = 0;
202 ComPtr<IWbemClassObject> aVBoxSDSObj;
203 hr = aEnumerator->Next(WBEM_INFINITE, 1, aVBoxSDSObj.asOutParam(), &uReturn);
204 if (FAILED(hr))
205 {
206 LogRel(("Error: Cannot get Service WMI record: %Rhrc\n", hr));
207 return hr;
208 }
209 if (aVBoxSDSObj == NULL || uReturn == 0)
210 {
211 LogRel(("Error: Service record didn't exist in WMI: %Rhrc\n", hr));
212 return hr;
213 }
214
215 VARIANT vtProp;
216 VariantInit(&vtProp);
217
218 // Get "StartMode" property
219 hr = aVBoxSDSObj->Get(L"StartMode", 0, &vtProp, 0, 0);
220 if (FAILED(hr) || (vtProp.vt & VT_NULL) == VT_NULL)
221 {
222 LogRel(("Error: Didn't found StartMode property: %Rhrc\n", hr));
223 return hr;
224 }
225
226 Assert((vtProp.vt & VT_BSTR) == VT_BSTR);
227
228 *pOutIsDisabled = RTUtf16Cmp((RTUTF16*)vtProp.bstrVal,
229 (RTUTF16*)L"Disabled") == 0;
230
231 LogRel(("Service start mode is '%ls' \n", vtProp.bstrVal));
232
233 VariantClear(&vtProp);
234
235 return S_OK;
236}
237
238#endif
239
240/**
241 * Initializes the VirtualBoxClient object.
242 *
243 * @returns COM result indicator
244 */
245HRESULT VirtualBoxClient::init()
246{
247
248#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_SDS)
249 // setup COM Security to enable impersonation
250 // This works for console Virtual Box clients, GUI has own security settings
251 // For GUI Virtual Box it will be second call so can return TOO_LATE error
252 HRESULT hrGUICoInitializeSecurity = CoInitializeSecurity(NULL,
253 -1,
254 NULL,
255 NULL,
256 RPC_C_AUTHN_LEVEL_DEFAULT,
257 RPC_C_IMP_LEVEL_IMPERSONATE,
258 NULL,
259 EOAC_NONE,
260 NULL);
261 NOREF(hrGUICoInitializeSecurity);
262 Assert(SUCCEEDED(hrGUICoInitializeSecurity) || hrGUICoInitializeSecurity == RPC_E_TOO_LATE);
263#endif
264
265 LogFlowThisFuncEnter();
266
267 /* Enclose the state transition NotReady->InInit->Ready */
268 AutoInitSpan autoInitSpan(this);
269 AssertReturn(autoInitSpan.isOk(), E_FAIL);
270
271 /* Important: DO NOT USE any kind of "early return" (except the single
272 * one above, checking the init span success) in this method. It is vital
273 * for correct error handling that it has only one point of return, which
274 * does all the magic on COM to signal object creation success and
275 * reporting the error later for every API method. COM translates any
276 * unsuccessful object creation to REGDB_E_CLASSNOTREG errors or similar
277 * unhelpful ones which cause us a lot of grief with troubleshooting. */
278
279 HRESULT rc = S_OK;
280 try
281 {
282 if (ASMAtomicIncU32(&g_cInstances) != 1)
283 AssertFailedStmt(throw setError(E_FAIL,
284 tr("Attempted to create more than one VirtualBoxClient instance")));
285
286 mData.m_ThreadWatcher = NIL_RTTHREAD;
287 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
288
289#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_SDS)
290 rc = CreateVirtualBoxThroughSDS(mData.m_pVirtualBox, mData.m_pToken);
291#else
292 rc = mData.m_pVirtualBox.createLocalObject(CLSID_VirtualBox);
293#endif
294 if (FAILED(rc))
295#ifdef RT_OS_WINDOWS
296 throw i_investigateVirtualBoxObjectCreationFailure(rc);
297#else
298 throw rc;
299#endif
300
301 /* VirtualBox error return is postponed to method calls, fetch it. */
302 ULONG rev;
303 rc = mData.m_pVirtualBox->COMGETTER(Revision)(&rev);
304 if (FAILED(rc))
305 throw rc;
306
307 rc = unconst(mData.m_pEventSource).createObject();
308 AssertComRCThrow(rc, setError(rc,
309 tr("Could not create EventSource for VirtualBoxClient")));
310 rc = mData.m_pEventSource->init();
311 AssertComRCThrow(rc, setError(rc,
312 tr("Could not initialize EventSource for VirtualBoxClient")));
313
314 /* HACK ALERT! This is for DllCanUnloadNow(). */
315 s_cUnnecessaryAtlModuleLocks++;
316 AssertMsg(s_cUnnecessaryAtlModuleLocks == 1, ("%d\n", s_cUnnecessaryAtlModuleLocks));
317
318 /* Setting up the VBoxSVC watcher thread. If anything goes wrong here it
319 * is not considered important enough to cause any sort of visible
320 * failure. The monitoring will not be done, but that's all. */
321 int vrc = RTSemEventCreate(&mData.m_SemEvWatcher);
322 if (RT_FAILURE(vrc))
323 {
324 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
325 AssertRCStmt(vrc, throw setError(VBOX_E_IPRT_ERROR,
326 tr("Failed to create semaphore (rc=%Rrc)"),
327 vrc));
328 }
329
330 vrc = RTThreadCreate(&mData.m_ThreadWatcher, SVCWatcherThread, this, 0,
331 RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "VBoxSVCWatcher");
332 if (RT_FAILURE(vrc))
333 {
334 RTSemEventDestroy(mData.m_SemEvWatcher);
335 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
336 AssertRCStmt(vrc, throw setError(VBOX_E_IPRT_ERROR,
337 tr("Failed to create watcher thread (rc=%Rrc)"),
338 vrc));
339 }
340 }
341 catch (HRESULT err)
342 {
343 /* we assume that error info is set by the thrower */
344 rc = err;
345 }
346 catch (...)
347 {
348 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
349 }
350
351 /* Confirm a successful initialization when it's the case. Must be last,
352 * as on failure it will uninitialize the object. */
353 if (SUCCEEDED(rc))
354 autoInitSpan.setSucceeded();
355 else
356 autoInitSpan.setFailed(rc);
357
358 LogFlowThisFunc(("rc=%Rhrc\n", rc));
359 LogFlowThisFuncLeave();
360 /* Unconditionally return success, because the error return is delayed to
361 * the attribute/method calls through the InitFailed object state. */
362 return S_OK;
363}
364
365#ifdef RT_OS_WINDOWS
366/**
367 * Looks into why we failed to create the VirtualBox object.
368 *
369 * @returns hrcCaller thru setError.
370 * @param hrcCaller The failure status code.
371 */
372HRESULT VirtualBoxClient::i_investigateVirtualBoxObjectCreationFailure(HRESULT hrcCaller)
373{
374 /*
375 * First step is to try get an IUnknown interface of the VirtualBox object.
376 *
377 * This will succeed even when oleaut32.msm (see @bugref{8016}, @ticketref{12087})
378 * is accidentally installed and messes up COM. It may also succeed when the COM
379 * registration is partially broken (though that's unlikely to happen these days).
380 */
381 IUnknown *pUnknown = NULL;
382
383# ifdef VBOX_WITH_SDS
384 // Check the VBOXSDS service running account name is SYSTEM
385 wchar_t wszBuffer[256];
386 int vrc = getServiceAccount(L"VBoxSDS", wszBuffer, RT_ELEMENTS(wszBuffer));
387 if (RT_FAILURE(vrc))
388 return setError(hrcCaller,
389 tr("Failed to instantiate CLSID_VirtualBox using VBoxSDS: The VBoxSDS is unavailable: %Rrc"), vrc);
390
391 LogRelFunc(("VBoxSDS service is running under the '%ls' account.\n", wszBuffer));
392
393 if (RTUtf16Cmp(wszBuffer, L"LocalSystem") != 0)
394 return setError(hrcCaller,
395 tr("VBoxSDS should be run under SYSTEM account, but it started under '%ls' account:\n"
396 "Change VBoxSDS Windows Service Logon parameters in Service Control Manager. \n%Rhrc"),
397 wszBuffer, hrcCaller);
398
399 bool fIsVBoxSDSDisabled = false;
400 HRESULT hrc = isServiceDisabled(L"VBoxSDS", &fIsVBoxSDSDisabled);
401 if (FAILED(hrc))
402 {
403 LogRelFunc(("Warning: Failed to get information about VBoxSDS using WMI:: %Rhrc & %Rhrc", hrcCaller, hrc));
404 fIsVBoxSDSDisabled = false;
405 //return setError(hrcCaller, tr("Failed to get information about VBoxSDS using WMI:: %Rhrc & %Rhrc"), hrcCaller, hrc);
406 }
407 if (fIsVBoxSDSDisabled)
408 {
409 return setError(hrcCaller,
410 tr("Completely failed to instantiate CLSID_VirtualBox using VBoxSDS: "
411 "VBoxSDS windows service disabled.\n"
412 "Enable VBoxSDS Windows Service using Windows Service Management Console.\n %Rhrc"), hrcCaller);
413 }
414
415 // Check the VBoxSDS windows service is enabled
416 ComPtr<IVirtualBox> aVirtualBox;
417 ComPtr<IToken> aToken;
418 hrc = CreateVirtualBoxThroughSDS(aVirtualBox, aToken);
419 if (FAILED(hrc))
420 {
421 if (hrc == hrcCaller)
422 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox using VBoxSDS: %Rhrc"), hrcCaller);
423 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox using VBoxSDS: %Rhrc & %Rhrc"), hrcCaller, hrc);
424 }
425
426 hrc = aVirtualBox.queryInterfaceTo<IUnknown>(&pUnknown);
427 if (FAILED(hrc))
428 {
429 if (hrc == hrcCaller)
430 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox using VBoxSDS: %Rhrc"), hrcCaller);
431 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox using VBoxSDS: %Rhrc & %Rhrc"), hrcCaller, hrc);
432 }
433# else
434 HRESULT hrc = CoCreateInstance(CLSID_VirtualBox, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void **)&pUnknown);
435 if (FAILED(hrc))
436 {
437 if (hrc == hrcCaller)
438 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox: %Rhrc"), hrcCaller);
439 return setError(hrcCaller, tr("Completely failed to instantiate CLSID_VirtualBox: %Rhrc & %Rhrc"), hrcCaller, hrc);
440 }
441# endif
442
443 /*
444 * Try query the IVirtualBox interface (should fail), if it succeed we return
445 * straight away so we have more columns to spend on long messages below.
446 */
447 IVirtualBox *pVirtualBox;
448 hrc = pUnknown->QueryInterface(IID_IVirtualBox, (void **)&pVirtualBox);
449 if (SUCCEEDED(hrc))
450 {
451 pVirtualBox->Release();
452 pUnknown->Release();
453 return setError(hrcCaller,
454 tr("Failed to instantiate CLSID_VirtualBox the first time, but worked when checking out why ... weird"));
455 }
456
457 /*
458 * Check for oleaut32.msm traces in the registry.
459 */
460 HKEY hKey;
461 LSTATUS lrc = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"CLSID\\{00020420-0000-0000-C000-000000000046}\\InprocServer32",
462 0 /*fFlags*/, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | STANDARD_RIGHTS_READ, &hKey);
463 if (lrc == ERROR_SUCCESS)
464 {
465 wchar_t wszBuf[8192];
466 DWORD cbBuf = sizeof(wszBuf) - sizeof(wchar_t);
467 DWORD dwType = 0;
468 lrc = RegQueryValueExW(hKey, L"InprocServer32", NULL /*pvReserved*/, &dwType, (BYTE *)&wszBuf[0], &cbBuf);
469 if (lrc == ERROR_SUCCESS)
470 {
471 wszBuf[cbBuf / sizeof(wchar_t)] = '\0';
472 bool fSetError = false;
473
474 /*
475 * Try decode the string and improve the message.
476 */
477 typedef UINT (WINAPI *PFNMSIDECOMPOSEDESCRIPTORW)(PCWSTR pwszDescriptor,
478 LPWSTR pwszProductCode /*[40]*/,
479 LPWSTR pwszFeatureId /*[40]*/,
480 LPWSTR pwszComponentCode /*[40]*/,
481 DWORD *poffArguments);
482 PFNMSIDECOMPOSEDESCRIPTORW pfnMsiDecomposeDescriptorW;
483 pfnMsiDecomposeDescriptorW = (PFNMSIDECOMPOSEDESCRIPTORW)RTLdrGetSystemSymbol("msi.dll", "MsiDecomposeDescriptorW");
484 if ( pfnMsiDecomposeDescriptorW
485 && ( dwType == REG_SZ
486 || dwType == REG_MULTI_SZ))
487 {
488 wchar_t wszProductCode[RTUUID_STR_LENGTH + 2 + 16] = { 0 };
489 wchar_t wszFeatureId[RTUUID_STR_LENGTH + 2 + 16] = { 0 };
490 wchar_t wszComponentCode[RTUUID_STR_LENGTH + 2 + 16] = { 0 };
491 DWORD offArguments = ~(DWORD)0;
492 UINT uRc = pfnMsiDecomposeDescriptorW(wszBuf, wszProductCode, wszFeatureId, wszComponentCode, &offArguments);
493 if (uRc == 0)
494 {
495 /*
496 * Can we resolve the product code into a name?
497 */
498 typedef UINT (WINAPI *PFNMSIOPENPRODUCTW)(PCWSTR, MSIHANDLE *);
499 PFNMSIOPENPRODUCTW pfnMsiOpenProductW;
500 pfnMsiOpenProductW = (PFNMSIOPENPRODUCTW)RTLdrGetSystemSymbol("msi.dll", "MsiOpenProductW");
501
502 typedef UINT (WINAPI *PFNMSICLOSEHANDLE)(MSIHANDLE);
503 PFNMSICLOSEHANDLE pfnMsiCloseHandle;
504 pfnMsiCloseHandle = (PFNMSICLOSEHANDLE)RTLdrGetSystemSymbol("msi.dll", "MsiCloseHandle");
505
506 typedef UINT (WINAPI *PFNGETPRODUCTPROPERTYW)(MSIHANDLE, PCWSTR, PWSTR, PDWORD);
507 PFNGETPRODUCTPROPERTYW pfnMsiGetProductPropertyW;
508 pfnMsiGetProductPropertyW = (PFNGETPRODUCTPROPERTYW)RTLdrGetSystemSymbol("msi.dll", "MsiGetProductPropertyW");
509 if ( pfnMsiGetProductPropertyW
510 && pfnMsiCloseHandle
511 && pfnMsiOpenProductW)
512 {
513 MSIHANDLE hMsi = 0;
514 uRc = pfnMsiOpenProductW(wszProductCode, &hMsi);
515 if (uRc == 0)
516 {
517 static wchar_t const * const s_apwszProps[] =
518 {
519 INSTALLPROPERTY_INSTALLEDPRODUCTNAME,
520 INSTALLPROPERTY_PRODUCTNAME,
521 INSTALLPROPERTY_PACKAGENAME,
522 };
523
524 wchar_t wszProductName[1024];
525 DWORD cwcProductName;
526 unsigned i = 0;
527 do
528 {
529 cwcProductName = RT_ELEMENTS(wszProductName) - 1;
530 uRc = pfnMsiGetProductPropertyW(hMsi, s_apwszProps[i], wszProductName, &cwcProductName);
531 }
532 while ( ++i < RT_ELEMENTS(s_apwszProps)
533 && ( uRc != 0
534 || cwcProductName < 2
535 || cwcProductName >= RT_ELEMENTS(wszProductName)) );
536 uRc = pfnMsiCloseHandle(hMsi);
537 if (uRc == 0 && cwcProductName >= 2)
538 {
539 wszProductName[RT_MIN(cwcProductName, RT_ELEMENTS(wszProductName) - 1)] = '\0';
540 setError(hrcCaller,
541 tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, but CLSID_VirtualBox w/ IUnknown works.\n"
542 "PSDispatch looks broken by the '%ls' (%ls) program, suspecting that it features the broken oleaut32.msm module as component %ls.\n"
543 "\n"
544 "We suggest you try uninstall '%ls'.\n"
545 "\n"
546 "See also https://support.microsoft.com/en-us/kb/316911 "),
547 wszProductName, wszProductCode, wszComponentCode, wszProductName);
548 fSetError = true;
549 }
550 }
551 }
552
553 /* MSI uses COM and may mess up our stuff. So, we wait with the fallback till afterwards in this case. */
554 if (!fSetError)
555 {
556 setError(hrcCaller,
557 tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, CLSID_VirtualBox w/ IUnknown works.\n"
558 "PSDispatch looks broken by installer %ls featuring the broken oleaut32.msm module as component %ls.\n"
559 "\n"
560 "See also https://support.microsoft.com/en-us/kb/316911 "),
561 wszProductCode, wszComponentCode);
562 fSetError = true;
563 }
564 }
565 }
566 if (!fSetError)
567 setError(hrcCaller, tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, CLSID_VirtualBox w/ IUnknown works.\n"
568 "PSDispatch looks broken by some installer featuring the broken oleaut32.msm module as a component.\n"
569 "\n"
570 "See also https://support.microsoft.com/en-us/kb/316911 "));
571 }
572 else if (lrc == ERROR_FILE_NOT_FOUND)
573 setError(hrcCaller, tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, but CLSID_VirtualBox w/ IUnknown works.\n"
574 "PSDispatch looks fine. Weird"));
575 else
576 setError(hrcCaller, tr("Failed to instantiate CLSID_VirtualBox w/ IVirtualBox, but CLSID_VirtualBox w/ IUnknown works.\n"
577 "Checking out PSDispatch registration ended with error: %u (%#x)"), lrc, lrc);
578 RegCloseKey(hKey);
579 }
580
581 pUnknown->Release();
582 return hrcCaller;
583}
584#endif /* RT_OS_WINDOWS */
585
586/**
587 * Uninitializes the instance and sets the ready flag to FALSE.
588 * Called either from FinalRelease() or by the parent when it gets destroyed.
589 */
590void VirtualBoxClient::uninit()
591{
592 LogFlowThisFunc(("\n"));
593
594 /* Enclose the state transition Ready->InUninit->NotReady */
595 AutoUninitSpan autoUninitSpan(this);
596 if (autoUninitSpan.uninitDone())
597 return;
598
599 if (mData.m_ThreadWatcher != NIL_RTTHREAD)
600 {
601 /* Signal the event semaphore and wait for the thread to terminate.
602 * if it hangs for some reason exit anyway, this can cause a crash
603 * though as the object will no longer be available. */
604 RTSemEventSignal(mData.m_SemEvWatcher);
605 RTThreadWait(mData.m_ThreadWatcher, 30000, NULL);
606 mData.m_ThreadWatcher = NIL_RTTHREAD;
607 RTSemEventDestroy(mData.m_SemEvWatcher);
608 mData.m_SemEvWatcher = NIL_RTSEMEVENT;
609 }
610
611 mData.m_pToken.setNull();
612 mData.m_pVirtualBox.setNull();
613
614 ASMAtomicDecU32(&g_cInstances);
615}
616
617// IVirtualBoxClient properties
618/////////////////////////////////////////////////////////////////////////////
619
620/**
621 * Returns a reference to the VirtualBox object.
622 *
623 * @returns COM status code
624 * @param aVirtualBox Address of result variable.
625 */
626HRESULT VirtualBoxClient::getVirtualBox(ComPtr<IVirtualBox> &aVirtualBox)
627{
628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
629 aVirtualBox = mData.m_pVirtualBox;
630 return S_OK;
631}
632
633/**
634 * Create a new Session object and return a reference to it.
635 *
636 * @returns COM status code
637 * @param aSession Address of result variable.
638 */
639HRESULT VirtualBoxClient::getSession(ComPtr<ISession> &aSession)
640{
641 /* this is not stored in this object, no need to lock */
642 ComPtr<ISession> pSession;
643 HRESULT rc = pSession.createInprocObject(CLSID_Session);
644 if (SUCCEEDED(rc))
645 aSession = pSession;
646 return rc;
647}
648
649/**
650 * Return reference to the EventSource associated with this object.
651 *
652 * @returns COM status code
653 * @param aEventSource Address of result variable.
654 */
655HRESULT VirtualBoxClient::getEventSource(ComPtr<IEventSource> &aEventSource)
656{
657 /* this is const, no need to lock */
658 aEventSource = mData.m_pEventSource;
659 return aEventSource.isNull() ? E_FAIL : S_OK;
660}
661
662// IVirtualBoxClient methods
663/////////////////////////////////////////////////////////////////////////////
664
665/**
666 * Checks a Machine object for any pending errors.
667 *
668 * @returns COM status code
669 * @param aMachine Machine object to check.
670 */
671HRESULT VirtualBoxClient::checkMachineError(const ComPtr<IMachine> &aMachine)
672{
673 BOOL fAccessible = FALSE;
674 HRESULT rc = aMachine->COMGETTER(Accessible)(&fAccessible);
675 if (FAILED(rc))
676 return setError(rc, tr("Could not check the accessibility status of the VM"));
677 else if (!fAccessible)
678 {
679 ComPtr<IVirtualBoxErrorInfo> pAccessError;
680 rc = aMachine->COMGETTER(AccessError)(pAccessError.asOutParam());
681 if (FAILED(rc))
682 return setError(rc, tr("Could not get the access error message of the VM"));
683 else
684 {
685 ErrorInfo info(pAccessError);
686 ErrorInfoKeeper eik(info);
687 return info.getResultCode();
688 }
689 }
690 return S_OK;
691}
692
693// private methods
694/////////////////////////////////////////////////////////////////////////////
695
696
697// TODO: AM Add pinging of VBoxSDS
698/*static*/
699DECLCALLBACK(int) VirtualBoxClient::SVCWatcherThread(RTTHREAD ThreadSelf,
700 void *pvUser)
701{
702 NOREF(ThreadSelf);
703 Assert(pvUser);
704 VirtualBoxClient *pThis = (VirtualBoxClient *)pvUser;
705 RTSEMEVENT sem = pThis->mData.m_SemEvWatcher;
706 RTMSINTERVAL cMillies = VBOXCLIENT_DEFAULT_INTERVAL;
707 int vrc;
708
709 /* The likelihood of early crashes are high, so start with a short wait. */
710 vrc = RTSemEventWait(sem, cMillies / 2);
711
712 /* As long as the waiting times out keep retrying the wait. */
713 while (RT_FAILURE(vrc))
714 {
715 {
716 HRESULT rc = S_OK;
717 ComPtr<IVirtualBox> pV;
718 {
719 AutoReadLock alock(pThis COMMA_LOCKVAL_SRC_POS);
720 pV = pThis->mData.m_pVirtualBox;
721 }
722 if (!pV.isNull())
723 {
724 ULONG rev;
725 rc = pV->COMGETTER(Revision)(&rev);
726 if (FAILED_DEAD_INTERFACE(rc))
727 {
728 LogRel(("VirtualBoxClient: detected unresponsive VBoxSVC (rc=%Rhrc)\n", rc));
729 {
730 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
731 /* Throw away the VirtualBox reference, it's no longer
732 * usable as VBoxSVC terminated in the mean time. */
733 pThis->mData.m_pVirtualBox.setNull();
734 }
735 fireVBoxSVCAvailabilityChangedEvent(pThis->mData.m_pEventSource, FALSE);
736 }
737 }
738 else
739 {
740 /* Try to get a new VirtualBox reference straight away, and if
741 * this fails use an increased waiting time as very frequent
742 * restart attempts in some wedged config can cause high CPU
743 * and disk load. */
744 ComPtr<IVirtualBox> pVirtualBox;
745 ComPtr<IToken> pToken;
746#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_SDS)
747 rc = CreateVirtualBoxThroughSDS(pVirtualBox, pToken);
748#else
749 rc = pVirtualBox.createLocalObject(CLSID_VirtualBox);
750#endif
751 if (FAILED(rc))
752 cMillies = 3 * VBOXCLIENT_DEFAULT_INTERVAL;
753 else
754 {
755 LogRel(("VirtualBoxClient: detected working VBoxSVC (rc=%Rhrc)\n", rc));
756 {
757 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
758 /* Update the VirtualBox reference, there's a working
759 * VBoxSVC again from now on. */
760 pThis->mData.m_pVirtualBox = pVirtualBox;
761 pThis->mData.m_pToken = pToken;
762 }
763 fireVBoxSVCAvailabilityChangedEvent(pThis->mData.m_pEventSource, TRUE);
764 cMillies = VBOXCLIENT_DEFAULT_INTERVAL;
765 }
766 }
767 }
768 vrc = RTSemEventWait(sem, cMillies);
769 }
770 return 0;
771}
772
773/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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