VirtualBox

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

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

VirtualBoxClientImpl: Style cleaning in the SDS related code; fixed error code logging & handling mess.

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