VirtualBox

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

Last change on this file since 91312 was 91312, checked in by vboxsync, 4 years ago

Main: bugref:1909: Prepared the API translation engine to using in ExtPacks and VBoxManage. Added using API translation engine in ExtPacks. Allowed VBox compilation with NLS enabled and GUI disabled. Allowed ExtPacks only compilation with NLS translation enabled.

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