VirtualBox

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

Last change on this file since 90943 was 90884, checked in by vboxsync, 3 years ago

Main: bugref:1909: Compilation issue.

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