VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp@ 98836

Last change on this file since 98836 was 98836, checked in by vboxsync, 2 years ago

Main/VirtualBox: Machine::unregister() and VirtualBox::openMachine() can
deadlock while processing a VM's media registry when run concurrently
for the same VM. If openMachine() is registering a differencing hard
disk while unregister() is unregistering the same disk then the two
threads can deadlock. The openMachine() thread can block in
VirtualBox::i_registerMedium() waiting for the media tree lock which the
unregister() thread holds but the unregister() thread can then block in
Medium::i_removeRegistryAll() waiting for the medium object to move out
of the 'InInit' state which the openMachine() thread will do once it
acquires the media tree lock so they are deadlocked. The fix is to
close the window of opportunity in VirtualBox::i_registerMedium() by not
dropping the media tree lock when called by Medium::initFromSettings().
bugref:6447

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 214.4 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 98836 2023-03-03 21:36:21Z vboxsync $ */
2/** @file
3 * Implementation of IVirtualBox in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.215389.xyz.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_VIRTUALBOX
29#include <iprt/asm.h>
30#include <iprt/base64.h>
31#include <iprt/buildconfig.h>
32#include <iprt/cpp/utils.h>
33#include <iprt/dir.h>
34#include <iprt/env.h>
35#include <iprt/file.h>
36#include <iprt/path.h>
37#include <iprt/process.h>
38#include <iprt/rand.h>
39#include <iprt/sha.h>
40#include <iprt/string.h>
41#include <iprt/stream.h>
42#include <iprt/system.h>
43#include <iprt/thread.h>
44#include <iprt/uuid.h>
45#include <iprt/cpp/xml.h>
46#include <iprt/ctype.h>
47
48#include <VBox/com/com.h>
49#include <VBox/com/array.h>
50#include "VBox/com/EventQueue.h"
51#include "VBox/com/MultiResult.h"
52
53#include <VBox/err.h>
54#include <VBox/param.h>
55#include <VBox/settings.h>
56#include <VBox/sup.h>
57#include <VBox/version.h>
58
59#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
60# include <VBox/GuestHost/SharedClipboard-transfers.h>
61#endif
62
63#include <package-generated.h>
64
65#include <algorithm>
66#include <set>
67#include <vector>
68#include <memory> // for auto_ptr
69
70#include "VirtualBoxImpl.h"
71
72#include "Global.h"
73#include "MachineImpl.h"
74#include "MediumImpl.h"
75#include "SharedFolderImpl.h"
76#include "ProgressImpl.h"
77#include "HostImpl.h"
78#include "USBControllerImpl.h"
79#include "SystemPropertiesImpl.h"
80#include "GuestOSTypeImpl.h"
81#include "NetworkServiceRunner.h"
82#include "DHCPServerImpl.h"
83#include "NATNetworkImpl.h"
84#ifdef VBOX_WITH_VMNET
85#include "HostOnlyNetworkImpl.h"
86#endif /* VBOX_WITH_VMNET */
87#ifdef VBOX_WITH_CLOUD_NET
88#include "CloudNetworkImpl.h"
89#endif /* VBOX_WITH_CLOUD_NET */
90#ifdef VBOX_WITH_RESOURCE_USAGE_API
91# include "PerformanceImpl.h"
92#endif /* VBOX_WITH_RESOURCE_USAGE_API */
93#ifdef VBOX_WITH_UPDATE_AGENT
94# include "UpdateAgentImpl.h"
95#endif
96#include "EventImpl.h"
97#ifdef VBOX_WITH_EXTPACK
98# include "ExtPackManagerImpl.h"
99#endif
100#ifdef VBOX_WITH_UNATTENDED
101# include "UnattendedImpl.h"
102#endif
103#include "AutostartDb.h"
104#include "ClientWatcher.h"
105#include "AutoCaller.h"
106#include "LoggingNew.h"
107#include "CloudProviderManagerImpl.h"
108#include "ThreadTask.h"
109#include "VBoxEvents.h"
110
111#include <QMTranslator.h>
112
113#ifdef RT_OS_WINDOWS
114# include "win/svchlp.h"
115# include "tchar.h"
116#endif
117
118
119////////////////////////////////////////////////////////////////////////////////
120//
121// Definitions
122//
123////////////////////////////////////////////////////////////////////////////////
124
125#define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
126
127////////////////////////////////////////////////////////////////////////////////
128//
129// Global variables
130//
131////////////////////////////////////////////////////////////////////////////////
132
133// static
134com::Utf8Str VirtualBox::sVersion;
135
136// static
137com::Utf8Str VirtualBox::sVersionNormalized;
138
139// static
140ULONG VirtualBox::sRevision;
141
142// static
143com::Utf8Str VirtualBox::sPackageType;
144
145// static
146com::Utf8Str VirtualBox::sAPIVersion;
147
148// static
149std::map<com::Utf8Str, int> VirtualBox::sNatNetworkNameToRefCount;
150
151// static leaked (todo: find better place to free it.)
152RWLockHandle *VirtualBox::spMtxNatNetworkNameToRefCountLock;
153
154
155#if 0 /* obsoleted by AsyncEvent */
156////////////////////////////////////////////////////////////////////////////////
157//
158// CallbackEvent class
159//
160////////////////////////////////////////////////////////////////////////////////
161
162/**
163 * Abstract callback event class to asynchronously call VirtualBox callbacks
164 * on a dedicated event thread. Subclasses reimplement #prepareEventDesc()
165 * to initialize the event depending on the event to be dispatched.
166 *
167 * @note The VirtualBox instance passed to the constructor is strongly
168 * referenced, so that the VirtualBox singleton won't be released until the
169 * event gets handled by the event thread.
170 */
171class VirtualBox::CallbackEvent : public Event
172{
173public:
174
175 CallbackEvent(VirtualBox *aVirtualBox, VBoxEventType_T aWhat)
176 : mVirtualBox(aVirtualBox), mWhat(aWhat)
177 {
178 Assert(aVirtualBox);
179 }
180
181 void *handler();
182
183 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) = 0;
184
185private:
186
187 /**
188 * Note that this is a weak ref -- the CallbackEvent handler thread
189 * is bound to the lifetime of the VirtualBox instance, so it's safe.
190 */
191 VirtualBox *mVirtualBox;
192protected:
193 VBoxEventType_T mWhat;
194};
195#endif
196
197////////////////////////////////////////////////////////////////////////////////
198//
199// AsyncEvent class
200//
201////////////////////////////////////////////////////////////////////////////////
202
203/**
204 * For firing off an event on asynchronously on an event thread.
205 */
206class VirtualBox::AsyncEvent : public Event
207{
208public:
209 AsyncEvent(VirtualBox *a_pVirtualBox, ComPtr<IEvent> const &a_rEvent)
210 : mVirtualBox(a_pVirtualBox), mEvent(a_rEvent)
211 {
212 Assert(a_pVirtualBox);
213 }
214
215 void *handler() RT_OVERRIDE;
216
217private:
218 /**
219 * @note This is a weak ref -- the CallbackEvent handler thread is bound to the
220 * lifetime of the VirtualBox instance, so it's safe.
221 */
222 VirtualBox *mVirtualBox;
223 /** The event. */
224 ComPtr<IEvent> mEvent;
225};
226
227////////////////////////////////////////////////////////////////////////////////
228//
229// VirtualBox private member data definition
230//
231////////////////////////////////////////////////////////////////////////////////
232
233#if defined(RT_OS_WINDOWS) && defined(VBOXSVC_WITH_CLIENT_WATCHER)
234/**
235 * Client process watcher data.
236 */
237class WatchedClientProcess
238{
239public:
240 WatchedClientProcess(RTPROCESS a_pid, HANDLE a_hProcess) RT_NOEXCEPT
241 : m_pid(a_pid)
242 , m_cRefs(1)
243 , m_hProcess(a_hProcess)
244 {
245 }
246
247 ~WatchedClientProcess()
248 {
249 if (m_hProcess != NULL)
250 {
251 ::CloseHandle(m_hProcess);
252 m_hProcess = NULL;
253 }
254 m_pid = NIL_RTPROCESS;
255 }
256
257 /** The client PID. */
258 RTPROCESS m_pid;
259 /** Number of references to this structure. */
260 uint32_t volatile m_cRefs;
261 /** Handle of the client process.
262 * Ideally, we've got full query privileges, but we'll settle for waiting. */
263 HANDLE m_hProcess;
264};
265typedef std::map<RTPROCESS, WatchedClientProcess *> WatchedClientProcessMap;
266#endif
267
268
269typedef ObjectsList<Medium> MediaOList;
270typedef ObjectsList<GuestOSType> GuestOSTypesOList;
271typedef ObjectsList<SharedFolder> SharedFoldersOList;
272typedef ObjectsList<DHCPServer> DHCPServersOList;
273typedef ObjectsList<NATNetwork> NATNetworksOList;
274#ifdef VBOX_WITH_VMNET
275typedef ObjectsList<HostOnlyNetwork> HostOnlyNetworksOList;
276#endif /* VBOX_WITH_VMNET */
277#ifdef VBOX_WITH_CLOUD_NET
278typedef ObjectsList<CloudNetwork> CloudNetworksOList;
279#endif /* VBOX_WITH_CLOUD_NET */
280
281typedef std::map<Guid, ComPtr<IProgress> > ProgressMap;
282typedef std::map<Guid, ComObjPtr<Medium> > HardDiskMap;
283
284/**
285 * Main VirtualBox data structure.
286 * @note |const| members are persistent during lifetime so can be accessed
287 * without locking.
288 */
289struct VirtualBox::Data
290{
291 Data()
292 : pMainConfigFile(NULL)
293 , uuidMediaRegistry("48024e5c-fdd9-470f-93af-ec29f7ea518c")
294 , uRegistryNeedsSaving(0)
295 , lockMachines(LOCKCLASS_LISTOFMACHINES)
296 , allMachines(lockMachines)
297 , lockGuestOSTypes(LOCKCLASS_LISTOFOTHEROBJECTS)
298 , allGuestOSTypes(lockGuestOSTypes)
299 , lockMedia(LOCKCLASS_LISTOFMEDIA)
300 , allHardDisks(lockMedia)
301 , allDVDImages(lockMedia)
302 , allFloppyImages(lockMedia)
303 , lockSharedFolders(LOCKCLASS_LISTOFOTHEROBJECTS)
304 , allSharedFolders(lockSharedFolders)
305 , lockDHCPServers(LOCKCLASS_LISTOFOTHEROBJECTS)
306 , allDHCPServers(lockDHCPServers)
307 , lockNATNetworks(LOCKCLASS_LISTOFOTHEROBJECTS)
308 , allNATNetworks(lockNATNetworks)
309#ifdef VBOX_WITH_VMNET
310 , lockHostOnlyNetworks(LOCKCLASS_LISTOFOTHEROBJECTS)
311 , allHostOnlyNetworks(lockHostOnlyNetworks)
312#endif /* VBOX_WITH_VMNET */
313#ifdef VBOX_WITH_CLOUD_NET
314 , lockCloudNetworks(LOCKCLASS_LISTOFOTHEROBJECTS)
315 , allCloudNetworks(lockCloudNetworks)
316#endif /* VBOX_WITH_CLOUD_NET */
317 , mtxProgressOperations(LOCKCLASS_PROGRESSLIST)
318 , pClientWatcher(NULL)
319 , threadAsyncEvent(NIL_RTTHREAD)
320 , pAsyncEventQ(NULL)
321 , pAutostartDb(NULL)
322 , fSettingsCipherKeySet(false)
323#ifdef VBOX_WITH_MAIN_NLS
324 , pVBoxTranslator(NULL)
325 , pTrComponent(NULL)
326#endif
327#if defined(RT_OS_WINDOWS) && defined(VBOXSVC_WITH_CLIENT_WATCHER)
328 , fWatcherIsReliable(RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
329#endif
330 , hLdrModCrypto(NIL_RTLDRMOD)
331 , cRefsCrypto(0)
332 , pCryptoIf(NULL)
333 {
334#if defined(RT_OS_WINDOWS) && defined(VBOXSVC_WITH_CLIENT_WATCHER)
335 RTCritSectRwInit(&WatcherCritSect);
336#endif
337 }
338
339 ~Data()
340 {
341 if (pMainConfigFile)
342 {
343 delete pMainConfigFile;
344 pMainConfigFile = NULL;
345 }
346 };
347
348 // const data members not requiring locking
349 const Utf8Str strHomeDir;
350
351 // VirtualBox main settings file
352 const Utf8Str strSettingsFilePath;
353 settings::MainConfigFile *pMainConfigFile;
354
355 // constant pseudo-machine ID for global media registry
356 const Guid uuidMediaRegistry;
357
358 // counter if global media registry needs saving, updated using atomic
359 // operations, without requiring any locks
360 uint64_t uRegistryNeedsSaving;
361
362 // const objects not requiring locking
363 const ComObjPtr<Host> pHost;
364 const ComObjPtr<SystemProperties> pSystemProperties;
365#ifdef VBOX_WITH_RESOURCE_USAGE_API
366 const ComObjPtr<PerformanceCollector> pPerformanceCollector;
367#endif /* VBOX_WITH_RESOURCE_USAGE_API */
368
369 // Each of the following lists use a particular lock handle that protects the
370 // list as a whole. As opposed to version 3.1 and earlier, these lists no
371 // longer need the main VirtualBox object lock, but only the respective list
372 // lock. In each case, the locking order is defined that the list must be
373 // requested before object locks of members of the lists (see the order definitions
374 // in AutoLock.h; e.g. LOCKCLASS_LISTOFMACHINES before LOCKCLASS_MACHINEOBJECT).
375 RWLockHandle lockMachines;
376 MachinesOList allMachines;
377
378 RWLockHandle lockGuestOSTypes;
379 GuestOSTypesOList allGuestOSTypes;
380
381 // All the media lists are protected by the following locking handle:
382 RWLockHandle lockMedia;
383 MediaOList allHardDisks, // base images only!
384 allDVDImages,
385 allFloppyImages;
386 // the hard disks map is an additional map sorted by UUID for quick lookup
387 // and contains ALL hard disks (base and differencing); it is protected by
388 // the same lock as the other media lists above
389 HardDiskMap mapHardDisks;
390
391 // list of pending machine renames (also protected by media tree lock;
392 // see VirtualBox::rememberMachineNameChangeForMedia())
393 struct PendingMachineRename
394 {
395 Utf8Str strConfigDirOld;
396 Utf8Str strConfigDirNew;
397 };
398 typedef std::list<PendingMachineRename> PendingMachineRenamesList;
399 PendingMachineRenamesList llPendingMachineRenames;
400
401 RWLockHandle lockSharedFolders;
402 SharedFoldersOList allSharedFolders;
403
404 RWLockHandle lockDHCPServers;
405 DHCPServersOList allDHCPServers;
406
407 RWLockHandle lockNATNetworks;
408 NATNetworksOList allNATNetworks;
409
410#ifdef VBOX_WITH_VMNET
411 RWLockHandle lockHostOnlyNetworks;
412 HostOnlyNetworksOList allHostOnlyNetworks;
413#endif /* VBOX_WITH_VMNET */
414#ifdef VBOX_WITH_CLOUD_NET
415 RWLockHandle lockCloudNetworks;
416 CloudNetworksOList allCloudNetworks;
417#endif /* VBOX_WITH_CLOUD_NET */
418
419 RWLockHandle mtxProgressOperations;
420 ProgressMap mapProgressOperations;
421
422 ClientWatcher * const pClientWatcher;
423
424 // the following are data for the async event thread
425 const RTTHREAD threadAsyncEvent;
426 EventQueue * const pAsyncEventQ;
427 const ComObjPtr<EventSource> pEventSource;
428
429#ifdef VBOX_WITH_EXTPACK
430 /** The extension pack manager object lives here. */
431 const ComObjPtr<ExtPackManager> ptrExtPackManager;
432#endif
433
434 /** The reference to the cloud provider manager singleton. */
435 const ComObjPtr<CloudProviderManager> pCloudProviderManager;
436
437 /** The global autostart database for the user. */
438 AutostartDb * const pAutostartDb;
439
440 /** Settings secret */
441 bool fSettingsCipherKeySet;
442 uint8_t SettingsCipherKey[RTSHA512_HASH_SIZE];
443#ifdef VBOX_WITH_MAIN_NLS
444 VirtualBoxTranslator *pVBoxTranslator;
445 PTRCOMPONENT pTrComponent;
446#endif
447#if defined(RT_OS_WINDOWS) && defined(VBOXSVC_WITH_CLIENT_WATCHER)
448 /** Critical section protecting WatchedProcesses. */
449 RTCRITSECTRW WatcherCritSect;
450 /** Map of processes being watched, key is the PID. */
451 WatchedClientProcessMap WatchedProcesses;
452 /** Set if the watcher is reliable, otherwise cleared.
453 * The watcher goes unreliable when we run out of memory, fail open a client
454 * process, or if the watcher thread gets messed up. */
455 bool fWatcherIsReliable;
456#endif
457
458 /** @name Members related to the cryptographic support interface.
459 * @{ */
460 /** The loaded module handle if loaded. */
461 RTLDRMOD hLdrModCrypto;
462 /** Reference counter tracking how many users of the cryptographic support
463 * are there currently. */
464 volatile uint32_t cRefsCrypto;
465 /** Pointer to the cryptographic support interface. */
466 PCVBOXCRYPTOIF pCryptoIf;
467 /** Critical section protecting the module handle. */
468 RTCRITSECT CritSectModCrypto;
469 /** @} */
470};
471
472// constructor / destructor
473/////////////////////////////////////////////////////////////////////////////
474
475DEFINE_EMPTY_CTOR_DTOR(VirtualBox)
476
477HRESULT VirtualBox::FinalConstruct()
478{
479 LogRelFlowThisFuncEnter();
480 LogRel(("VirtualBox: object creation starts\n"));
481
482 BaseFinalConstruct();
483
484 HRESULT hrc = init();
485
486 LogRelFlowThisFuncLeave();
487 LogRel(("VirtualBox: object created\n"));
488
489 return hrc;
490}
491
492void VirtualBox::FinalRelease()
493{
494 LogRelFlowThisFuncEnter();
495 LogRel(("VirtualBox: object deletion starts\n"));
496
497 uninit();
498
499 BaseFinalRelease();
500
501 LogRel(("VirtualBox: object deleted\n"));
502 LogRelFlowThisFuncLeave();
503}
504
505// public initializer/uninitializer for internal purposes only
506/////////////////////////////////////////////////////////////////////////////
507
508/**
509 * Initializes the VirtualBox object.
510 *
511 * @return COM result code
512 */
513HRESULT VirtualBox::init()
514{
515 LogRelFlowThisFuncEnter();
516 /* Enclose the state transition NotReady->InInit->Ready */
517 AutoInitSpan autoInitSpan(this);
518 AssertReturn(autoInitSpan.isOk(), E_FAIL);
519
520 /* Locking this object for writing during init sounds a bit paradoxical,
521 * but in the current locking mess this avoids that some code gets a
522 * read lock and later calls code which wants the same write lock. */
523 AutoWriteLock lock(this COMMA_LOCKVAL_SRC_POS);
524
525 // allocate our instance data
526 m = new Data;
527
528 LogFlow(("===========================================================\n"));
529 LogFlowThisFuncEnter();
530
531 if (sVersion.isEmpty())
532 sVersion = RTBldCfgVersion();
533 if (sVersionNormalized.isEmpty())
534 {
535 Utf8Str tmp(RTBldCfgVersion());
536 if (tmp.endsWith(VBOX_BUILD_PUBLISHER))
537 tmp = tmp.substr(0, tmp.length() - strlen(VBOX_BUILD_PUBLISHER));
538 sVersionNormalized = tmp;
539 }
540 sRevision = RTBldCfgRevision();
541 if (sPackageType.isEmpty())
542 sPackageType = VBOX_PACKAGE_STRING;
543 if (sAPIVersion.isEmpty())
544 sAPIVersion = VBOX_API_VERSION_STRING;
545 if (!spMtxNatNetworkNameToRefCountLock)
546 spMtxNatNetworkNameToRefCountLock = new RWLockHandle(LOCKCLASS_VIRTUALBOXOBJECT);
547
548 LogFlowThisFunc(("Version: %s, Package: %s, API Version: %s\n", sVersion.c_str(), sPackageType.c_str(), sAPIVersion.c_str()));
549
550 /* Important: DO NOT USE any kind of "early return" (except the single
551 * one above, checking the init span success) in this method. It is vital
552 * for correct error handling that it has only one point of return, which
553 * does all the magic on COM to signal object creation success and
554 * reporting the error later for every API method. COM translates any
555 * unsuccessful object creation to REGDB_E_CLASSNOTREG errors or similar
556 * unhelpful ones which cause us a lot of grief with troubleshooting. */
557
558 HRESULT hrc = S_OK;
559 bool fCreate = false;
560 try
561 {
562 /* Create the event source early as we may fire async event during settings loading (media). */
563 hrc = unconst(m->pEventSource).createObject();
564 if (FAILED(hrc)) throw hrc;
565 hrc = m->pEventSource->init();
566 if (FAILED(hrc)) throw hrc;
567
568
569 /* Get the VirtualBox home directory. */
570 {
571 char szHomeDir[RTPATH_MAX];
572 int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir));
573 if (RT_FAILURE(vrc))
574 throw setErrorBoth(E_FAIL, vrc,
575 tr("Could not create the VirtualBox home directory '%s' (%Rrc)"),
576 szHomeDir, vrc);
577
578 unconst(m->strHomeDir) = szHomeDir;
579 }
580
581 LogRel(("Home directory: '%s'\n", m->strHomeDir.c_str()));
582
583 i_reportDriverVersions();
584
585 /* Create the critical section protecting the cryptographic module handle. */
586 {
587 int vrc = RTCritSectInit(&m->CritSectModCrypto);
588 if (RT_FAILURE(vrc))
589 throw setErrorBoth(E_FAIL, vrc,
590 tr("Could not create the cryptographic module critical section (%Rrc)"),
591 vrc);
592
593 }
594
595 /* compose the VirtualBox.xml file name */
596 unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s",
597 m->strHomeDir.c_str(),
598 RTPATH_DELIMITER,
599 VBOX_GLOBAL_SETTINGS_FILE);
600 // load and parse VirtualBox.xml; this will throw on XML or logic errors
601 try
602 {
603 m->pMainConfigFile = new settings::MainConfigFile(&m->strSettingsFilePath);
604 }
605 catch (xml::EIPRTFailure &e)
606 {
607 // this is thrown by the XML backend if the RTOpen() call fails;
608 // only if the main settings file does not exist, create it,
609 // if there's something more serious, then do fail!
610 if (e.getStatus() == VERR_FILE_NOT_FOUND)
611 fCreate = true;
612 else
613 throw;
614 }
615
616 if (fCreate)
617 m->pMainConfigFile = new settings::MainConfigFile(NULL);
618
619#ifdef VBOX_WITH_RESOURCE_USAGE_API
620 /* create the performance collector object BEFORE host */
621 unconst(m->pPerformanceCollector).createObject();
622 hrc = m->pPerformanceCollector->init();
623 ComAssertComRCThrowRC(hrc);
624#endif /* VBOX_WITH_RESOURCE_USAGE_API */
625
626 /* create the host object early, machines will need it */
627 unconst(m->pHost).createObject();
628 hrc = m->pHost->init(this);
629 ComAssertComRCThrowRC(hrc);
630
631 hrc = m->pHost->i_loadSettings(m->pMainConfigFile->host);
632 if (FAILED(hrc)) throw hrc;
633
634 /*
635 * Create autostart database object early, because the system properties
636 * might need it.
637 */
638 unconst(m->pAutostartDb) = new AutostartDb;
639
640 /* create the system properties object, someone may need it too */
641 hrc = unconst(m->pSystemProperties).createObject();
642 if (SUCCEEDED(hrc))
643 hrc = m->pSystemProperties->init(this);
644 ComAssertComRCThrowRC(hrc);
645
646 hrc = m->pSystemProperties->i_loadSettings(m->pMainConfigFile->systemProperties);
647 if (FAILED(hrc)) throw hrc;
648#ifdef VBOX_WITH_MAIN_NLS
649 m->pVBoxTranslator = VirtualBoxTranslator::instance();
650 /* Do not throw an exception on language errors.
651 * Just do not use translation. */
652 if (m->pVBoxTranslator)
653 {
654
655 char szNlsPath[RTPATH_MAX];
656 int vrc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
657 if (RT_SUCCESS(vrc))
658 vrc = RTPathAppend(szNlsPath, sizeof(szNlsPath), "nls" RTPATH_SLASH_STR "VirtualBoxAPI");
659
660 if (RT_SUCCESS(vrc))
661 {
662 vrc = m->pVBoxTranslator->registerTranslation(szNlsPath, true, &m->pTrComponent);
663 if (RT_SUCCESS(vrc))
664 {
665 com::Utf8Str strLocale;
666 HRESULT hrc2 = m->pSystemProperties->getLanguageId(strLocale);
667 if (SUCCEEDED(hrc2))
668 {
669 vrc = m->pVBoxTranslator->i_loadLanguage(strLocale.c_str());
670 if (RT_FAILURE(vrc))
671 {
672 hrc2 = Global::vboxStatusCodeToCOM(vrc);
673 LogRel(("Load language failed (%Rhrc).\n", hrc2));
674 }
675 }
676 else
677 {
678 LogRel(("Getting language settings failed (%Rhrc).\n", hrc2));
679 m->pVBoxTranslator->release();
680 m->pVBoxTranslator = NULL;
681 m->pTrComponent = NULL;
682 }
683 }
684 else
685 {
686 HRESULT hrc2 = Global::vboxStatusCodeToCOM(vrc);
687 LogRel(("Register translation failed (%Rhrc).\n", hrc2));
688 m->pVBoxTranslator->release();
689 m->pVBoxTranslator = NULL;
690 m->pTrComponent = NULL;
691 }
692 }
693 else
694 {
695 HRESULT hrc2 = Global::vboxStatusCodeToCOM(vrc);
696 LogRel(("Path constructing failed (%Rhrc).\n", hrc2));
697 m->pVBoxTranslator->release();
698 m->pVBoxTranslator = NULL;
699 m->pTrComponent = NULL;
700 }
701 }
702 else
703 LogRel(("Translator creation failed.\n"));
704#endif
705
706#ifdef VBOX_WITH_EXTPACK
707 /*
708 * Initialize extension pack manager before system properties because
709 * it is required for the VD plugins.
710 */
711 hrc = unconst(m->ptrExtPackManager).createObject();
712 if (SUCCEEDED(hrc))
713 hrc = m->ptrExtPackManager->initExtPackManager(this, VBOXEXTPACKCTX_PER_USER_DAEMON);
714 if (FAILED(hrc))
715 throw hrc;
716#endif
717 /* guest OS type objects, needed by machines */
718 for (size_t i = 0; i < Global::cOSTypes; ++i)
719 {
720 ComObjPtr<GuestOSType> guestOSTypeObj;
721 hrc = guestOSTypeObj.createObject();
722 if (SUCCEEDED(hrc))
723 {
724 hrc = guestOSTypeObj->init(Global::sOSTypes[i]);
725 if (SUCCEEDED(hrc))
726 m->allGuestOSTypes.addChild(guestOSTypeObj);
727 }
728 ComAssertComRCThrowRC(hrc);
729 }
730
731 /* all registered media, needed by machines */
732 if (FAILED(hrc = initMedia(m->uuidMediaRegistry,
733 m->pMainConfigFile->mediaRegistry,
734 Utf8Str::Empty))) // const Utf8Str &machineFolder
735 throw hrc;
736
737 /* machines */
738 if (FAILED(hrc = initMachines()))
739 throw hrc;
740
741#ifdef DEBUG
742 LogFlowThisFunc(("Dumping media backreferences\n"));
743 i_dumpAllBackRefs();
744#endif
745
746 /* net services - dhcp services */
747 for (settings::DHCPServersList::const_iterator it = m->pMainConfigFile->llDhcpServers.begin();
748 it != m->pMainConfigFile->llDhcpServers.end();
749 ++it)
750 {
751 const settings::DHCPServer &data = *it;
752
753 ComObjPtr<DHCPServer> pDhcpServer;
754 if (SUCCEEDED(hrc = pDhcpServer.createObject()))
755 hrc = pDhcpServer->init(this, data);
756 if (FAILED(hrc)) throw hrc;
757
758 hrc = i_registerDHCPServer(pDhcpServer, false /* aSaveRegistry */);
759 if (FAILED(hrc)) throw hrc;
760 }
761
762 /* net services - nat networks */
763 for (settings::NATNetworksList::const_iterator it = m->pMainConfigFile->llNATNetworks.begin();
764 it != m->pMainConfigFile->llNATNetworks.end();
765 ++it)
766 {
767 const settings::NATNetwork &net = *it;
768
769 ComObjPtr<NATNetwork> pNATNetwork;
770 hrc = pNATNetwork.createObject();
771 AssertComRCThrowRC(hrc);
772 hrc = pNATNetwork->init(this, "");
773 AssertComRCThrowRC(hrc);
774 hrc = pNATNetwork->i_loadSettings(net);
775 AssertComRCThrowRC(hrc);
776 hrc = i_registerNATNetwork(pNATNetwork, false /* aSaveRegistry */);
777 AssertComRCThrowRC(hrc);
778 }
779
780#ifdef VBOX_WITH_VMNET
781 /* host-only networks */
782 for (settings::HostOnlyNetworksList::const_iterator it = m->pMainConfigFile->llHostOnlyNetworks.begin();
783 it != m->pMainConfigFile->llHostOnlyNetworks.end();
784 ++it)
785 {
786 ComObjPtr<HostOnlyNetwork> pHostOnlyNetwork;
787 hrc = pHostOnlyNetwork.createObject();
788 AssertComRCThrowRC(hrc);
789 hrc = pHostOnlyNetwork->init(this, "TODO???");
790 AssertComRCThrowRC(hrc);
791 hrc = pHostOnlyNetwork->i_loadSettings(*it);
792 AssertComRCThrowRC(hrc);
793 m->allHostOnlyNetworks.addChild(pHostOnlyNetwork);
794 AssertComRCThrowRC(hrc);
795 }
796#endif /* VBOX_WITH_VMNET */
797
798#ifdef VBOX_WITH_CLOUD_NET
799 /* net services - cloud networks */
800 for (settings::CloudNetworksList::const_iterator it = m->pMainConfigFile->llCloudNetworks.begin();
801 it != m->pMainConfigFile->llCloudNetworks.end();
802 ++it)
803 {
804 ComObjPtr<CloudNetwork> pCloudNetwork;
805 hrc = pCloudNetwork.createObject();
806 AssertComRCThrowRC(hrc);
807 hrc = pCloudNetwork->init(this, "");
808 AssertComRCThrowRC(hrc);
809 hrc = pCloudNetwork->i_loadSettings(*it);
810 AssertComRCThrowRC(hrc);
811 m->allCloudNetworks.addChild(pCloudNetwork);
812 AssertComRCThrowRC(hrc);
813 }
814#endif /* VBOX_WITH_CLOUD_NET */
815
816 /* cloud provider manager */
817 hrc = unconst(m->pCloudProviderManager).createObject();
818 if (SUCCEEDED(hrc))
819 hrc = m->pCloudProviderManager->init(this);
820 ComAssertComRCThrowRC(hrc);
821 if (FAILED(hrc)) throw hrc;
822 }
823 catch (HRESULT err)
824 {
825 /* we assume that error info is set by the thrower */
826 hrc = err;
827 }
828 catch (...)
829 {
830 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
831 }
832
833 if (SUCCEEDED(hrc))
834 {
835 /* set up client monitoring */
836 try
837 {
838 unconst(m->pClientWatcher) = new ClientWatcher(this);
839 if (!m->pClientWatcher->isReady())
840 {
841 delete m->pClientWatcher;
842 unconst(m->pClientWatcher) = NULL;
843 hrc = E_FAIL;
844 }
845 }
846 catch (std::bad_alloc &)
847 {
848 hrc = E_OUTOFMEMORY;
849 }
850 }
851
852 if (SUCCEEDED(hrc))
853 {
854 try
855 {
856 /* start the async event handler thread */
857 int vrc = RTThreadCreate(&unconst(m->threadAsyncEvent),
858 AsyncEventHandler,
859 &unconst(m->pAsyncEventQ),
860 0,
861 RTTHREADTYPE_MAIN_WORKER,
862 RTTHREADFLAGS_WAITABLE,
863 "EventHandler");
864 ComAssertRCThrow(vrc, E_FAIL);
865
866 /* wait until the thread sets m->pAsyncEventQ */
867 RTThreadUserWait(m->threadAsyncEvent, RT_INDEFINITE_WAIT);
868 ComAssertThrow(m->pAsyncEventQ, E_FAIL);
869 }
870 catch (HRESULT hrcXcpt)
871 {
872 hrc = hrcXcpt;
873 }
874 }
875
876#ifdef VBOX_WITH_EXTPACK
877 /* Let the extension packs have a go at things. */
878 if (SUCCEEDED(hrc))
879 {
880 lock.release();
881 m->ptrExtPackManager->i_callAllVirtualBoxReadyHooks();
882 }
883#endif
884
885 /* Confirm a successful initialization when it's the case. Must be last,
886 * as on failure it will uninitialize the object. */
887 if (SUCCEEDED(hrc))
888 autoInitSpan.setSucceeded();
889 else
890 autoInitSpan.setFailed(hrc);
891
892 LogFlowThisFunc(("hrc=%Rhrc\n", hrc));
893 LogFlowThisFuncLeave();
894 LogFlow(("===========================================================\n"));
895 /* Unconditionally return success, because the error return is delayed to
896 * the attribute/method calls through the InitFailed object state. */
897 return S_OK;
898}
899
900HRESULT VirtualBox::initMachines()
901{
902 for (settings::MachinesRegistry::const_iterator it = m->pMainConfigFile->llMachines.begin();
903 it != m->pMainConfigFile->llMachines.end();
904 ++it)
905 {
906 HRESULT hrc = S_OK;
907 const settings::MachineRegistryEntry &xmlMachine = *it;
908 Guid uuid = xmlMachine.uuid;
909
910 /* Check if machine record has valid parameters. */
911 if (xmlMachine.strSettingsFile.isEmpty() || uuid.isZero())
912 {
913 LogRel(("Skipped invalid machine record.\n"));
914 continue;
915 }
916
917 ComObjPtr<Machine> pMachine;
918 com::Utf8Str strPassword;
919 if (SUCCEEDED(hrc = pMachine.createObject()))
920 {
921 hrc = pMachine->initFromSettings(this, xmlMachine.strSettingsFile, &uuid, strPassword);
922 if (SUCCEEDED(hrc))
923 hrc = i_registerMachine(pMachine);
924 if (FAILED(hrc))
925 return hrc;
926 }
927 }
928
929 return S_OK;
930}
931
932/**
933 * Loads a media registry from XML and adds the media contained therein to
934 * the global lists of known media.
935 *
936 * This now (4.0) gets called from two locations:
937 *
938 * -- VirtualBox::init(), to load the global media registry from VirtualBox.xml;
939 *
940 * -- Machine::loadMachineDataFromSettings(), to load the per-machine registry
941 * from machine XML, for machines created with VirtualBox 4.0 or later.
942 *
943 * In both cases, the media found are added to the global lists so the
944 * global arrays of media (including the GUI's virtual media manager)
945 * continue to work as before.
946 *
947 * @param uuidRegistry The UUID of the media registry. This is either the
948 * transient UUID created at VirtualBox startup for the global registry or
949 * a machine ID.
950 * @param mediaRegistry The XML settings structure to load, either from VirtualBox.xml
951 * or a machine XML.
952 * @param strMachineFolder The folder of the machine.
953 * @return
954 */
955HRESULT VirtualBox::initMedia(const Guid &uuidRegistry,
956 const settings::MediaRegistry &mediaRegistry,
957 const Utf8Str &strMachineFolder)
958{
959 LogFlow(("VirtualBox::initMedia ENTERING, uuidRegistry=%s, strMachineFolder=%s\n",
960 uuidRegistry.toString().c_str(),
961 strMachineFolder.c_str()));
962
963 AutoWriteLock treeLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
964
965 // the order of notification is critical for GUI, so use std::list<std::pair> instead of map
966 std::list<std::pair<Guid, DeviceType_T> > uIdsForNotify;
967
968 HRESULT hrc = S_OK;
969 settings::MediaList::const_iterator it;
970 for (it = mediaRegistry.llHardDisks.begin();
971 it != mediaRegistry.llHardDisks.end();
972 ++it)
973 {
974 const settings::Medium &xmlHD = *it;
975
976 hrc = Medium::initFromSettings(this,
977 DeviceType_HardDisk,
978 uuidRegistry,
979 strMachineFolder,
980 xmlHD,
981 treeLock,
982 uIdsForNotify);
983 if (FAILED(hrc)) return hrc;
984 }
985
986 for (it = mediaRegistry.llDvdImages.begin();
987 it != mediaRegistry.llDvdImages.end();
988 ++it)
989 {
990 const settings::Medium &xmlDvd = *it;
991
992 hrc = Medium::initFromSettings(this,
993 DeviceType_DVD,
994 uuidRegistry,
995 strMachineFolder,
996 xmlDvd,
997 treeLock,
998 uIdsForNotify);
999 if (FAILED(hrc)) return hrc;
1000 }
1001
1002 for (it = mediaRegistry.llFloppyImages.begin();
1003 it != mediaRegistry.llFloppyImages.end();
1004 ++it)
1005 {
1006 const settings::Medium &xmlFloppy = *it;
1007
1008 hrc = Medium::initFromSettings(this,
1009 DeviceType_Floppy,
1010 uuidRegistry,
1011 strMachineFolder,
1012 xmlFloppy,
1013 treeLock,
1014 uIdsForNotify);
1015 if (FAILED(hrc)) return hrc;
1016 }
1017
1018 for (std::list<std::pair<Guid, DeviceType_T> >::const_iterator itItem = uIdsForNotify.begin();
1019 itItem != uIdsForNotify.end();
1020 ++itItem)
1021 {
1022 i_onMediumRegistered(itItem->first, itItem->second, TRUE);
1023 }
1024
1025 LogFlow(("VirtualBox::initMedia LEAVING\n"));
1026
1027 return S_OK;
1028}
1029
1030void VirtualBox::uninit()
1031{
1032 /* Must be done outside the AutoUninitSpan, as it expects AutoCaller to
1033 * be successful. This needs additional checks to protect against double
1034 * uninit, as then the pointer is NULL. */
1035 if (RT_VALID_PTR(m))
1036 {
1037 Assert(!m->uRegistryNeedsSaving);
1038 if (m->uRegistryNeedsSaving)
1039 i_saveSettings();
1040 }
1041
1042 /* Enclose the state transition Ready->InUninit->NotReady */
1043 AutoUninitSpan autoUninitSpan(this);
1044 if (autoUninitSpan.uninitDone())
1045 return;
1046
1047 LogFlow(("===========================================================\n"));
1048 LogFlowThisFuncEnter();
1049 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1050
1051 /* tell all our child objects we've been uninitialized */
1052
1053 LogFlowThisFunc(("Uninitializing machines (%d)...\n", m->allMachines.size()));
1054 if (m->pHost)
1055 {
1056 /* It is necessary to hold the VirtualBox and Host locks here because
1057 we may have to uninitialize SessionMachines. */
1058 AutoMultiWriteLock2 multilock(this, m->pHost COMMA_LOCKVAL_SRC_POS);
1059 m->allMachines.uninitAll();
1060 }
1061 else
1062 m->allMachines.uninitAll();
1063 m->allFloppyImages.uninitAll();
1064 m->allDVDImages.uninitAll();
1065 m->allHardDisks.uninitAll();
1066 m->allDHCPServers.uninitAll();
1067
1068 m->mapProgressOperations.clear();
1069
1070 m->allGuestOSTypes.uninitAll();
1071
1072 /* Note that we release singleton children after we've all other children.
1073 * In some cases this is important because these other children may use
1074 * some resources of the singletons which would prevent them from
1075 * uninitializing (as for example, mSystemProperties which owns
1076 * MediumFormat objects which Medium objects refer to) */
1077 if (m->pCloudProviderManager)
1078 {
1079 m->pCloudProviderManager->uninit();
1080 unconst(m->pCloudProviderManager).setNull();
1081 }
1082
1083 if (m->pSystemProperties)
1084 {
1085 m->pSystemProperties->uninit();
1086 unconst(m->pSystemProperties).setNull();
1087 }
1088
1089 if (m->pHost)
1090 {
1091 m->pHost->uninit();
1092 unconst(m->pHost).setNull();
1093 }
1094
1095#ifdef VBOX_WITH_RESOURCE_USAGE_API
1096 if (m->pPerformanceCollector)
1097 {
1098 m->pPerformanceCollector->uninit();
1099 unconst(m->pPerformanceCollector).setNull();
1100 }
1101#endif /* VBOX_WITH_RESOURCE_USAGE_API */
1102
1103 /*
1104 * Unload the cryptographic module if loaded before the extension
1105 * pack manager is torn down.
1106 */
1107 Assert(!m->cRefsCrypto);
1108 if (m->hLdrModCrypto != NIL_RTLDRMOD)
1109 {
1110 m->pCryptoIf = NULL;
1111
1112 int vrc = RTLdrClose(m->hLdrModCrypto);
1113 AssertRC(vrc);
1114 m->hLdrModCrypto = NIL_RTLDRMOD;
1115 }
1116
1117 RTCritSectDelete(&m->CritSectModCrypto);
1118
1119#ifdef VBOX_WITH_EXTPACK
1120 if (m->ptrExtPackManager)
1121 {
1122 m->ptrExtPackManager->uninit();
1123 unconst(m->ptrExtPackManager).setNull();
1124 }
1125#endif
1126
1127 LogFlowThisFunc(("Terminating the async event handler...\n"));
1128 if (m->threadAsyncEvent != NIL_RTTHREAD)
1129 {
1130 /* signal to exit the event loop */
1131 if (RT_SUCCESS(m->pAsyncEventQ->interruptEventQueueProcessing()))
1132 {
1133 /*
1134 * Wait for thread termination (only after we've successfully
1135 * interrupted the event queue processing!)
1136 */
1137 int vrc = RTThreadWait(m->threadAsyncEvent, 60000, NULL);
1138 if (RT_FAILURE(vrc))
1139 Log1WarningFunc(("RTThreadWait(%RTthrd) -> %Rrc\n", m->threadAsyncEvent, vrc));
1140 }
1141 else
1142 {
1143 AssertMsgFailed(("interruptEventQueueProcessing() failed\n"));
1144 RTThreadWait(m->threadAsyncEvent, 0, NULL);
1145 }
1146
1147 unconst(m->threadAsyncEvent) = NIL_RTTHREAD;
1148 unconst(m->pAsyncEventQ) = NULL;
1149 }
1150
1151 LogFlowThisFunc(("Releasing event source...\n"));
1152 if (m->pEventSource)
1153 {
1154 // Must uninit the event source here, because it makes no sense that
1155 // it survives longer than the base object. If someone gets an event
1156 // with such an event source then that's life and it has to be dealt
1157 // with appropriately on the API client side.
1158 m->pEventSource->uninit();
1159 unconst(m->pEventSource).setNull();
1160 }
1161
1162 LogFlowThisFunc(("Terminating the client watcher...\n"));
1163 if (m->pClientWatcher)
1164 {
1165 delete m->pClientWatcher;
1166 unconst(m->pClientWatcher) = NULL;
1167 }
1168
1169 delete m->pAutostartDb;
1170#ifdef VBOX_WITH_MAIN_NLS
1171 if (m->pVBoxTranslator)
1172 m->pVBoxTranslator->release();
1173#endif
1174 // clean up our instance data
1175 delete m;
1176 m = NULL;
1177
1178 /* Unload hard disk plugin backends. */
1179 VDShutdown();
1180
1181 LogFlowThisFuncLeave();
1182 LogFlow(("===========================================================\n"));
1183}
1184
1185// Wrapped IVirtualBox properties
1186/////////////////////////////////////////////////////////////////////////////
1187HRESULT VirtualBox::getVersion(com::Utf8Str &aVersion)
1188{
1189 aVersion = sVersion;
1190 return S_OK;
1191}
1192
1193HRESULT VirtualBox::getVersionNormalized(com::Utf8Str &aVersionNormalized)
1194{
1195 aVersionNormalized = sVersionNormalized;
1196 return S_OK;
1197}
1198
1199HRESULT VirtualBox::getRevision(ULONG *aRevision)
1200{
1201 *aRevision = sRevision;
1202 return S_OK;
1203}
1204
1205HRESULT VirtualBox::getPackageType(com::Utf8Str &aPackageType)
1206{
1207 aPackageType = sPackageType;
1208 return S_OK;
1209}
1210
1211HRESULT VirtualBox::getAPIVersion(com::Utf8Str &aAPIVersion)
1212{
1213 aAPIVersion = sAPIVersion;
1214 return S_OK;
1215}
1216
1217HRESULT VirtualBox::getAPIRevision(LONG64 *aAPIRevision)
1218{
1219 AssertCompile(VBOX_VERSION_MAJOR < 128 && VBOX_VERSION_MAJOR > 0);
1220 AssertCompile((uint64_t)VBOX_VERSION_MINOR < 256);
1221 uint64_t uRevision = ((uint64_t)VBOX_VERSION_MAJOR << 56)
1222 | ((uint64_t)VBOX_VERSION_MINOR << 48)
1223 | ((uint64_t)VBOX_VERSION_BUILD << 40);
1224
1225 /** @todo This needs to be the same in OSE and non-OSE, preferrably
1226 * only changing when actual API changes happens. */
1227 uRevision |= 1;
1228
1229 *aAPIRevision = (LONG64)uRevision;
1230
1231 return S_OK;
1232}
1233
1234HRESULT VirtualBox::getHomeFolder(com::Utf8Str &aHomeFolder)
1235{
1236 /* mHomeDir is const and doesn't need a lock */
1237 aHomeFolder = m->strHomeDir;
1238 return S_OK;
1239}
1240
1241HRESULT VirtualBox::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
1242{
1243 /* mCfgFile.mName is const and doesn't need a lock */
1244 aSettingsFilePath = m->strSettingsFilePath;
1245 return S_OK;
1246}
1247
1248HRESULT VirtualBox::getHost(ComPtr<IHost> &aHost)
1249{
1250 /* mHost is const, no need to lock */
1251 m->pHost.queryInterfaceTo(aHost.asOutParam());
1252 return S_OK;
1253}
1254
1255HRESULT VirtualBox::getSystemProperties(ComPtr<ISystemProperties> &aSystemProperties)
1256{
1257 /* mSystemProperties is const, no need to lock */
1258 m->pSystemProperties.queryInterfaceTo(aSystemProperties.asOutParam());
1259 return S_OK;
1260}
1261
1262HRESULT VirtualBox::getMachines(std::vector<ComPtr<IMachine> > &aMachines)
1263{
1264 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1265 aMachines.resize(m->allMachines.size());
1266 size_t i = 0;
1267 for (MachinesOList::const_iterator it= m->allMachines.begin();
1268 it!= m->allMachines.end(); ++it, ++i)
1269 (*it).queryInterfaceTo(aMachines[i].asOutParam());
1270 return S_OK;
1271}
1272
1273HRESULT VirtualBox::getMachineGroups(std::vector<com::Utf8Str> &aMachineGroups)
1274{
1275 std::list<com::Utf8Str> allGroups;
1276
1277 /* get copy of all machine references, to avoid holding the list lock */
1278 MachinesOList::MyList allMachines;
1279 {
1280 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1281 allMachines = m->allMachines.getList();
1282 }
1283 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1284 it != allMachines.end();
1285 ++it)
1286 {
1287 const ComObjPtr<Machine> &pMachine = *it;
1288 AutoCaller autoMachineCaller(pMachine);
1289 if (FAILED(autoMachineCaller.hrc()))
1290 continue;
1291 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1292
1293 if (pMachine->i_isAccessible())
1294 {
1295 const StringsList &thisGroups = pMachine->i_getGroups();
1296 for (StringsList::const_iterator it2 = thisGroups.begin();
1297 it2 != thisGroups.end(); ++it2)
1298 allGroups.push_back(*it2);
1299 }
1300 }
1301
1302 /* throw out any duplicates */
1303 allGroups.sort();
1304 allGroups.unique();
1305 aMachineGroups.resize(allGroups.size());
1306 size_t i = 0;
1307 for (std::list<com::Utf8Str>::const_iterator it = allGroups.begin();
1308 it != allGroups.end(); ++it, ++i)
1309 aMachineGroups[i] = (*it);
1310 return S_OK;
1311}
1312
1313HRESULT VirtualBox::getHardDisks(std::vector<ComPtr<IMedium> > &aHardDisks)
1314{
1315 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1316 aHardDisks.resize(m->allHardDisks.size());
1317 size_t i = 0;
1318 for (MediaOList::const_iterator it = m->allHardDisks.begin();
1319 it != m->allHardDisks.end(); ++it, ++i)
1320 (*it).queryInterfaceTo(aHardDisks[i].asOutParam());
1321 return S_OK;
1322}
1323
1324HRESULT VirtualBox::getDVDImages(std::vector<ComPtr<IMedium> > &aDVDImages)
1325{
1326 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1327 aDVDImages.resize(m->allDVDImages.size());
1328 size_t i = 0;
1329 for (MediaOList::const_iterator it = m->allDVDImages.begin();
1330 it!= m->allDVDImages.end(); ++it, ++i)
1331 (*it).queryInterfaceTo(aDVDImages[i].asOutParam());
1332 return S_OK;
1333}
1334
1335HRESULT VirtualBox::getFloppyImages(std::vector<ComPtr<IMedium> > &aFloppyImages)
1336{
1337 AutoReadLock al(m->allFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1338 aFloppyImages.resize(m->allFloppyImages.size());
1339 size_t i = 0;
1340 for (MediaOList::const_iterator it = m->allFloppyImages.begin();
1341 it != m->allFloppyImages.end(); ++it, ++i)
1342 (*it).queryInterfaceTo(aFloppyImages[i].asOutParam());
1343 return S_OK;
1344}
1345
1346HRESULT VirtualBox::getProgressOperations(std::vector<ComPtr<IProgress> > &aProgressOperations)
1347{
1348 /* protect mProgressOperations */
1349 AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1350 ProgressMap pmap(m->mapProgressOperations);
1351 /* Can release lock now. The following code works on a copy of the map. */
1352 safeLock.release();
1353 aProgressOperations.resize(pmap.size());
1354 size_t i = 0;
1355 for (ProgressMap::iterator it = pmap.begin(); it != pmap.end(); ++it, ++i)
1356 it->second.queryInterfaceTo(aProgressOperations[i].asOutParam());
1357 return S_OK;
1358}
1359
1360HRESULT VirtualBox::getGuestOSTypes(std::vector<ComPtr<IGuestOSType> > &aGuestOSTypes)
1361{
1362 AutoReadLock al(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1363 aGuestOSTypes.resize(m->allGuestOSTypes.size());
1364 size_t i = 0;
1365 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
1366 it != m->allGuestOSTypes.end(); ++it, ++i)
1367 (*it).queryInterfaceTo(aGuestOSTypes[i].asOutParam());
1368 return S_OK;
1369}
1370
1371HRESULT VirtualBox::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
1372{
1373 NOREF(aSharedFolders);
1374
1375 return setError(E_NOTIMPL, tr("Not yet implemented"));
1376}
1377
1378HRESULT VirtualBox::getPerformanceCollector(ComPtr<IPerformanceCollector> &aPerformanceCollector)
1379{
1380#ifdef VBOX_WITH_RESOURCE_USAGE_API
1381 /* mPerformanceCollector is const, no need to lock */
1382 m->pPerformanceCollector.queryInterfaceTo(aPerformanceCollector.asOutParam());
1383
1384 return S_OK;
1385#else /* !VBOX_WITH_RESOURCE_USAGE_API */
1386 NOREF(aPerformanceCollector);
1387 ReturnComNotImplemented();
1388#endif /* !VBOX_WITH_RESOURCE_USAGE_API */
1389}
1390
1391HRESULT VirtualBox::getDHCPServers(std::vector<ComPtr<IDHCPServer> > &aDHCPServers)
1392{
1393 AutoReadLock al(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1394 aDHCPServers.resize(m->allDHCPServers.size());
1395 size_t i = 0;
1396 for (DHCPServersOList::const_iterator it= m->allDHCPServers.begin();
1397 it!= m->allDHCPServers.end(); ++it, ++i)
1398 (*it).queryInterfaceTo(aDHCPServers[i].asOutParam());
1399 return S_OK;
1400}
1401
1402
1403HRESULT VirtualBox::getNATNetworks(std::vector<ComPtr<INATNetwork> > &aNATNetworks)
1404{
1405#ifdef VBOX_WITH_NAT_SERVICE
1406 AutoReadLock al(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1407 aNATNetworks.resize(m->allNATNetworks.size());
1408 size_t i = 0;
1409 for (NATNetworksOList::const_iterator it= m->allNATNetworks.begin();
1410 it!= m->allNATNetworks.end(); ++it, ++i)
1411 (*it).queryInterfaceTo(aNATNetworks[i].asOutParam());
1412 return S_OK;
1413#else
1414 NOREF(aNATNetworks);
1415 return E_NOTIMPL;
1416#endif
1417}
1418
1419HRESULT VirtualBox::getEventSource(ComPtr<IEventSource> &aEventSource)
1420{
1421 /* event source is const, no need to lock */
1422 m->pEventSource.queryInterfaceTo(aEventSource.asOutParam());
1423 return S_OK;
1424}
1425
1426HRESULT VirtualBox::getExtensionPackManager(ComPtr<IExtPackManager> &aExtensionPackManager)
1427{
1428 HRESULT hrc = S_OK;
1429#ifdef VBOX_WITH_EXTPACK
1430 /* The extension pack manager is const, no need to lock. */
1431 hrc = m->ptrExtPackManager.queryInterfaceTo(aExtensionPackManager.asOutParam());
1432#else
1433 hrc = E_NOTIMPL;
1434 NOREF(aExtensionPackManager);
1435#endif
1436 return hrc;
1437}
1438
1439/**
1440 * Host Only Network
1441 */
1442HRESULT VirtualBox::createHostOnlyNetwork(const com::Utf8Str &aNetworkName,
1443 ComPtr<IHostOnlyNetwork> &aNetwork)
1444{
1445#ifdef VBOX_WITH_VMNET
1446 ComObjPtr<HostOnlyNetwork> HostOnlyNetwork;
1447 HostOnlyNetwork.createObject();
1448 HRESULT hrc = HostOnlyNetwork->init(this, aNetworkName);
1449 if (FAILED(hrc)) return hrc;
1450
1451 m->allHostOnlyNetworks.addChild(HostOnlyNetwork);
1452
1453 {
1454 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1455 hrc = i_saveSettings();
1456 vboxLock.release();
1457
1458 if (FAILED(hrc))
1459 m->allHostOnlyNetworks.removeChild(HostOnlyNetwork);
1460 else
1461 HostOnlyNetwork.queryInterfaceTo(aNetwork.asOutParam());
1462 }
1463
1464 return hrc;
1465#else /* !VBOX_WITH_VMNET */
1466 NOREF(aNetworkName);
1467 NOREF(aNetwork);
1468 return E_NOTIMPL;
1469#endif /* !VBOX_WITH_VMNET */
1470}
1471
1472HRESULT VirtualBox::findHostOnlyNetworkByName(const com::Utf8Str &aNetworkName,
1473 ComPtr<IHostOnlyNetwork> &aNetwork)
1474{
1475#ifdef VBOX_WITH_VMNET
1476 Bstr bstrNameToFind(aNetworkName);
1477
1478 AutoReadLock alock(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1479
1480 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
1481 it != m->allHostOnlyNetworks.end();
1482 ++it)
1483 {
1484 Bstr bstrHostOnlyNetworkName;
1485 HRESULT hrc = (*it)->COMGETTER(NetworkName)(bstrHostOnlyNetworkName.asOutParam());
1486 if (FAILED(hrc)) return hrc;
1487
1488 if (bstrHostOnlyNetworkName == bstrNameToFind)
1489 {
1490 it->queryInterfaceTo(aNetwork.asOutParam());
1491 return S_OK;
1492 }
1493 }
1494 return VBOX_E_OBJECT_NOT_FOUND;
1495#else /* !VBOX_WITH_VMNET */
1496 NOREF(aNetworkName);
1497 NOREF(aNetwork);
1498 return E_NOTIMPL;
1499#endif /* !VBOX_WITH_VMNET */
1500}
1501
1502HRESULT VirtualBox::findHostOnlyNetworkById(const com::Guid &aId,
1503 ComPtr<IHostOnlyNetwork> &aNetwork)
1504{
1505#ifdef VBOX_WITH_VMNET
1506 ComObjPtr<HostOnlyNetwork> network;
1507 AutoReadLock alock(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1508
1509 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
1510 it != m->allHostOnlyNetworks.end();
1511 ++it)
1512 {
1513 Bstr bstrHostOnlyNetworkId;
1514 HRESULT hrc = (*it)->COMGETTER(Id)(bstrHostOnlyNetworkId.asOutParam());
1515 if (FAILED(hrc)) return hrc;
1516
1517 if (Guid(bstrHostOnlyNetworkId) == aId)
1518 {
1519 it->queryInterfaceTo(aNetwork.asOutParam());;
1520 return S_OK;
1521 }
1522 }
1523 return VBOX_E_OBJECT_NOT_FOUND;
1524#else /* !VBOX_WITH_VMNET */
1525 NOREF(aId);
1526 NOREF(aNetwork);
1527 return E_NOTIMPL;
1528#endif /* !VBOX_WITH_VMNET */
1529}
1530
1531HRESULT VirtualBox::removeHostOnlyNetwork(const ComPtr<IHostOnlyNetwork> &aNetwork)
1532{
1533#ifdef VBOX_WITH_VMNET
1534 Bstr name;
1535 HRESULT hrc = aNetwork->COMGETTER(NetworkName)(name.asOutParam());
1536 if (FAILED(hrc))
1537 return hrc;
1538 IHostOnlyNetwork *p = aNetwork;
1539 HostOnlyNetwork *network = static_cast<HostOnlyNetwork *>(p);
1540
1541 AutoCaller autoCaller(this);
1542 AssertComRCReturnRC(autoCaller.hrc());
1543
1544 AutoCaller HostOnlyNetworkCaller(network);
1545 AssertComRCReturnRC(HostOnlyNetworkCaller.hrc());
1546
1547 m->allHostOnlyNetworks.removeChild(network);
1548
1549 {
1550 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1551 hrc = i_saveSettings();
1552 vboxLock.release();
1553
1554 if (FAILED(hrc))
1555 m->allHostOnlyNetworks.addChild(network);
1556 }
1557 return hrc;
1558#else /* !VBOX_WITH_VMNET */
1559 NOREF(aNetwork);
1560 return E_NOTIMPL;
1561#endif /* !VBOX_WITH_VMNET */
1562}
1563
1564HRESULT VirtualBox::getHostOnlyNetworks(std::vector<ComPtr<IHostOnlyNetwork> > &aHostOnlyNetworks)
1565{
1566#ifdef VBOX_WITH_VMNET
1567 AutoReadLock al(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1568 aHostOnlyNetworks.resize(m->allHostOnlyNetworks.size());
1569 size_t i = 0;
1570 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
1571 it != m->allHostOnlyNetworks.end(); ++it)
1572 (*it).queryInterfaceTo(aHostOnlyNetworks[i++].asOutParam());
1573 return S_OK;
1574#else /* !VBOX_WITH_VMNET */
1575 NOREF(aHostOnlyNetworks);
1576 return E_NOTIMPL;
1577#endif /* !VBOX_WITH_VMNET */
1578}
1579
1580
1581HRESULT VirtualBox::getInternalNetworks(std::vector<com::Utf8Str> &aInternalNetworks)
1582{
1583 std::list<com::Utf8Str> allInternalNetworks;
1584
1585 /* get copy of all machine references, to avoid holding the list lock */
1586 MachinesOList::MyList allMachines;
1587 {
1588 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1589 allMachines = m->allMachines.getList();
1590 }
1591 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1592 it != allMachines.end(); ++it)
1593 {
1594 const ComObjPtr<Machine> &pMachine = *it;
1595 AutoCaller autoMachineCaller(pMachine);
1596 if (FAILED(autoMachineCaller.hrc()))
1597 continue;
1598 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1599
1600 if (pMachine->i_isAccessible())
1601 {
1602 uint32_t cNetworkAdapters = Global::getMaxNetworkAdapters(pMachine->i_getChipsetType());
1603 for (ULONG i = 0; i < cNetworkAdapters; i++)
1604 {
1605 ComPtr<INetworkAdapter> pNet;
1606 HRESULT hrc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
1607 if (FAILED(hrc) || pNet.isNull())
1608 continue;
1609 Bstr strInternalNetwork;
1610 hrc = pNet->COMGETTER(InternalNetwork)(strInternalNetwork.asOutParam());
1611 if (FAILED(hrc) || strInternalNetwork.isEmpty())
1612 continue;
1613
1614 allInternalNetworks.push_back(Utf8Str(strInternalNetwork));
1615 }
1616 }
1617 }
1618
1619 /* throw out any duplicates */
1620 allInternalNetworks.sort();
1621 allInternalNetworks.unique();
1622 size_t i = 0;
1623 aInternalNetworks.resize(allInternalNetworks.size());
1624 for (std::list<com::Utf8Str>::const_iterator it = allInternalNetworks.begin();
1625 it != allInternalNetworks.end();
1626 ++it, ++i)
1627 aInternalNetworks[i] = *it;
1628 return S_OK;
1629}
1630
1631HRESULT VirtualBox::getGenericNetworkDrivers(std::vector<com::Utf8Str> &aGenericNetworkDrivers)
1632{
1633 std::list<com::Utf8Str> allGenericNetworkDrivers;
1634
1635 /* get copy of all machine references, to avoid holding the list lock */
1636 MachinesOList::MyList allMachines;
1637 {
1638 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1639 allMachines = m->allMachines.getList();
1640 }
1641 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1642 it != allMachines.end();
1643 ++it)
1644 {
1645 const ComObjPtr<Machine> &pMachine = *it;
1646 AutoCaller autoMachineCaller(pMachine);
1647 if (FAILED(autoMachineCaller.hrc()))
1648 continue;
1649 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1650
1651 if (pMachine->i_isAccessible())
1652 {
1653 uint32_t cNetworkAdapters = Global::getMaxNetworkAdapters(pMachine->i_getChipsetType());
1654 for (ULONG i = 0; i < cNetworkAdapters; i++)
1655 {
1656 ComPtr<INetworkAdapter> pNet;
1657 HRESULT hrc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
1658 if (FAILED(hrc) || pNet.isNull())
1659 continue;
1660 Bstr strGenericNetworkDriver;
1661 hrc = pNet->COMGETTER(GenericDriver)(strGenericNetworkDriver.asOutParam());
1662 if (FAILED(hrc) || strGenericNetworkDriver.isEmpty())
1663 continue;
1664
1665 allGenericNetworkDrivers.push_back(Utf8Str(strGenericNetworkDriver).c_str());
1666 }
1667 }
1668 }
1669
1670 /* throw out any duplicates */
1671 allGenericNetworkDrivers.sort();
1672 allGenericNetworkDrivers.unique();
1673 aGenericNetworkDrivers.resize(allGenericNetworkDrivers.size());
1674 size_t i = 0;
1675 for (std::list<com::Utf8Str>::const_iterator it = allGenericNetworkDrivers.begin();
1676 it != allGenericNetworkDrivers.end(); ++it, ++i)
1677 aGenericNetworkDrivers[i] = *it;
1678
1679 return S_OK;
1680}
1681
1682/**
1683 * Cloud Network
1684 */
1685#ifdef VBOX_WITH_CLOUD_NET
1686HRESULT VirtualBox::i_findCloudNetworkByName(const com::Utf8Str &aNetworkName,
1687 ComObjPtr<CloudNetwork> *aNetwork)
1688{
1689 ComPtr<CloudNetwork> found;
1690 Bstr bstrNameToFind(aNetworkName);
1691
1692 AutoReadLock alock(m->allCloudNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1693
1694 for (CloudNetworksOList::const_iterator it = m->allCloudNetworks.begin();
1695 it != m->allCloudNetworks.end();
1696 ++it)
1697 {
1698 Bstr bstrCloudNetworkName;
1699 HRESULT hrc = (*it)->COMGETTER(NetworkName)(bstrCloudNetworkName.asOutParam());
1700 if (FAILED(hrc)) return hrc;
1701
1702 if (bstrCloudNetworkName == bstrNameToFind)
1703 {
1704 *aNetwork = *it;
1705 return S_OK;
1706 }
1707 }
1708 return VBOX_E_OBJECT_NOT_FOUND;
1709}
1710#endif /* VBOX_WITH_CLOUD_NET */
1711
1712HRESULT VirtualBox::createCloudNetwork(const com::Utf8Str &aNetworkName,
1713 ComPtr<ICloudNetwork> &aNetwork)
1714{
1715#ifdef VBOX_WITH_CLOUD_NET
1716 ComObjPtr<CloudNetwork> cloudNetwork;
1717 cloudNetwork.createObject();
1718 HRESULT hrc = cloudNetwork->init(this, aNetworkName);
1719 if (FAILED(hrc)) return hrc;
1720
1721 m->allCloudNetworks.addChild(cloudNetwork);
1722
1723 {
1724 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1725 hrc = i_saveSettings();
1726 vboxLock.release();
1727
1728 if (FAILED(hrc))
1729 m->allCloudNetworks.removeChild(cloudNetwork);
1730 else
1731 cloudNetwork.queryInterfaceTo(aNetwork.asOutParam());
1732 }
1733
1734 return hrc;
1735#else /* !VBOX_WITH_CLOUD_NET */
1736 NOREF(aNetworkName);
1737 NOREF(aNetwork);
1738 return E_NOTIMPL;
1739#endif /* !VBOX_WITH_CLOUD_NET */
1740}
1741
1742HRESULT VirtualBox::findCloudNetworkByName(const com::Utf8Str &aNetworkName,
1743 ComPtr<ICloudNetwork> &aNetwork)
1744{
1745#ifdef VBOX_WITH_CLOUD_NET
1746 ComObjPtr<CloudNetwork> network;
1747 HRESULT hrc = i_findCloudNetworkByName(aNetworkName, &network);
1748 if (SUCCEEDED(hrc))
1749 network.queryInterfaceTo(aNetwork.asOutParam());
1750 return hrc;
1751#else /* !VBOX_WITH_CLOUD_NET */
1752 NOREF(aNetworkName);
1753 NOREF(aNetwork);
1754 return E_NOTIMPL;
1755#endif /* !VBOX_WITH_CLOUD_NET */
1756}
1757
1758HRESULT VirtualBox::removeCloudNetwork(const ComPtr<ICloudNetwork> &aNetwork)
1759{
1760#ifdef VBOX_WITH_CLOUD_NET
1761 Bstr name;
1762 HRESULT hrc = aNetwork->COMGETTER(NetworkName)(name.asOutParam());
1763 if (FAILED(hrc))
1764 return hrc;
1765 ICloudNetwork *p = aNetwork;
1766 CloudNetwork *network = static_cast<CloudNetwork *>(p);
1767
1768 AutoCaller autoCaller(this);
1769 AssertComRCReturnRC(autoCaller.hrc());
1770
1771 AutoCaller cloudNetworkCaller(network);
1772 AssertComRCReturnRC(cloudNetworkCaller.hrc());
1773
1774 m->allCloudNetworks.removeChild(network);
1775
1776 {
1777 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1778 hrc = i_saveSettings();
1779 vboxLock.release();
1780
1781 if (FAILED(hrc))
1782 m->allCloudNetworks.addChild(network);
1783 }
1784 return hrc;
1785#else /* !VBOX_WITH_CLOUD_NET */
1786 NOREF(aNetwork);
1787 return E_NOTIMPL;
1788#endif /* !VBOX_WITH_CLOUD_NET */
1789}
1790
1791HRESULT VirtualBox::getCloudNetworks(std::vector<ComPtr<ICloudNetwork> > &aCloudNetworks)
1792{
1793#ifdef VBOX_WITH_CLOUD_NET
1794 AutoReadLock al(m->allCloudNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1795 aCloudNetworks.resize(m->allCloudNetworks.size());
1796 size_t i = 0;
1797 for (CloudNetworksOList::const_iterator it = m->allCloudNetworks.begin();
1798 it != m->allCloudNetworks.end(); ++it)
1799 (*it).queryInterfaceTo(aCloudNetworks[i++].asOutParam());
1800 return S_OK;
1801#else /* !VBOX_WITH_CLOUD_NET */
1802 NOREF(aCloudNetworks);
1803 return E_NOTIMPL;
1804#endif /* !VBOX_WITH_CLOUD_NET */
1805}
1806
1807#ifdef VBOX_WITH_CLOUD_NET
1808HRESULT VirtualBox::i_getEventSource(ComPtr<IEventSource>& aSource)
1809{
1810 m->pEventSource.queryInterfaceTo(aSource.asOutParam());
1811 return S_OK;
1812}
1813#endif /* VBOX_WITH_CLOUD_NET */
1814
1815HRESULT VirtualBox::getCloudProviderManager(ComPtr<ICloudProviderManager> &aCloudProviderManager)
1816{
1817 HRESULT hrc = m->pCloudProviderManager.queryInterfaceTo(aCloudProviderManager.asOutParam());
1818 return hrc;
1819}
1820
1821HRESULT VirtualBox::checkFirmwarePresent(FirmwareType_T aFirmwareType,
1822 const com::Utf8Str &aVersion,
1823 com::Utf8Str &aUrl,
1824 com::Utf8Str &aFile,
1825 BOOL *aResult)
1826{
1827 NOREF(aVersion);
1828
1829 static const struct
1830 {
1831 FirmwareType_T enmType;
1832 bool fBuiltIn;
1833 const char *pszFileName;
1834 const char *pszUrl;
1835 }
1836 firmwareDesc[] =
1837 {
1838 { FirmwareType_BIOS, true, NULL, NULL },
1839#ifdef VBOX_WITH_EFI_IN_DD2
1840 { FirmwareType_EFI32, true, "VBoxEFI32.fd", NULL },
1841 { FirmwareType_EFI64, true, "VBoxEFI64.fd", NULL },
1842 { FirmwareType_EFIDUAL, true, "VBoxEFIDual.fd", NULL },
1843#else
1844 { FirmwareType_EFI32, false, "VBoxEFI32.fd", "http://virtualbox.org/firmware/VBoxEFI32.fd" },
1845 { FirmwareType_EFI64, false, "VBoxEFI64.fd", "http://virtualbox.org/firmware/VBoxEFI64.fd" },
1846 { FirmwareType_EFIDUAL, false, "VBoxEFIDual.fd", "http://virtualbox.org/firmware/VBoxEFIDual.fd" },
1847#endif
1848 };
1849
1850 for (size_t i = 0; i < sizeof(firmwareDesc) / sizeof(firmwareDesc[0]); i++)
1851 {
1852 if (aFirmwareType != firmwareDesc[i].enmType)
1853 continue;
1854
1855 /* compiled-in firmware */
1856 if (firmwareDesc[i].fBuiltIn)
1857 {
1858 aFile = firmwareDesc[i].pszFileName;
1859 *aResult = TRUE;
1860 break;
1861 }
1862
1863 Utf8Str fullName;
1864 Utf8StrFmt shortName("Firmware%c%s", RTPATH_DELIMITER, firmwareDesc[i].pszFileName);
1865 int vrc = i_calculateFullPath(shortName, fullName);
1866 AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
1867 if (RTFileExists(fullName.c_str()))
1868 {
1869 *aResult = TRUE;
1870 aFile = fullName;
1871 break;
1872 }
1873
1874 char szVBoxPath[RTPATH_MAX];
1875 vrc = RTPathExecDir(szVBoxPath, RTPATH_MAX);
1876 AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
1877 vrc = RTPathAppend(szVBoxPath, sizeof(szVBoxPath), firmwareDesc[i].pszFileName);
1878 AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
1879 if (RTFileExists(szVBoxPath))
1880 {
1881 *aResult = TRUE;
1882 aFile = szVBoxPath;
1883 break;
1884 }
1885
1886 /** @todo account for version in the URL */
1887 aUrl = firmwareDesc[i].pszUrl;
1888 *aResult = FALSE;
1889
1890 /* Assume single record per firmware type */
1891 break;
1892 }
1893
1894 return S_OK;
1895}
1896// Wrapped IVirtualBox methods
1897/////////////////////////////////////////////////////////////////////////////
1898
1899/* Helper for VirtualBox::ComposeMachineFilename */
1900static void sanitiseMachineFilename(Utf8Str &aName);
1901
1902HRESULT VirtualBox::composeMachineFilename(const com::Utf8Str &aName,
1903 const com::Utf8Str &aGroup,
1904 const com::Utf8Str &aCreateFlags,
1905 const com::Utf8Str &aBaseFolder,
1906 com::Utf8Str &aFile)
1907{
1908 if (RT_UNLIKELY(aName.isEmpty()))
1909 return setError(E_INVALIDARG, tr("Machine name is invalid, must not be empty"));
1910
1911 Utf8Str strBase = aBaseFolder;
1912 Utf8Str strName = aName;
1913
1914 LogFlowThisFunc(("aName=\"%s\",aBaseFolder=\"%s\"\n", strName.c_str(), strBase.c_str()));
1915
1916 com::Guid id;
1917 bool fDirectoryIncludesUUID = false;
1918 if (!aCreateFlags.isEmpty())
1919 {
1920 size_t uPos = 0;
1921 com::Utf8Str strKey;
1922 com::Utf8Str strValue;
1923 while ((uPos = aCreateFlags.parseKeyValue(strKey, strValue, uPos)) != com::Utf8Str::npos)
1924 {
1925 if (strKey == "UUID")
1926 id = strValue.c_str();
1927 else if (strKey == "directoryIncludesUUID")
1928 fDirectoryIncludesUUID = (strValue == "1");
1929 }
1930 }
1931
1932 if (id.isZero())
1933 fDirectoryIncludesUUID = false;
1934 else if (!id.isValid())
1935 {
1936 /* do something else */
1937 return setError(E_INVALIDARG,
1938 tr("'%s' is not a valid Guid"),
1939 id.toStringCurly().c_str());
1940 }
1941
1942 Utf8Str strGroup(aGroup);
1943 if (strGroup.isEmpty())
1944 strGroup = "/";
1945 HRESULT hrc = i_validateMachineGroup(strGroup, true);
1946 if (FAILED(hrc))
1947 return hrc;
1948
1949 /* Compose the settings file name using the following scheme:
1950 *
1951 * <base_folder><group>/<machine_name>/<machine_name>.xml
1952 *
1953 * If a non-null and non-empty base folder is specified, the default
1954 * machine folder will be used as a base folder.
1955 * We sanitise the machine name to a safe white list of characters before
1956 * using it.
1957 */
1958 Utf8Str strDirName(strName);
1959 if (fDirectoryIncludesUUID)
1960 strDirName += Utf8StrFmt(" (%RTuuid)", id.raw());
1961 sanitiseMachineFilename(strName);
1962 sanitiseMachineFilename(strDirName);
1963
1964 if (strBase.isEmpty())
1965 /* we use the non-full folder value below to keep the path relative */
1966 i_getDefaultMachineFolder(strBase);
1967
1968 i_calculateFullPath(strBase, strBase);
1969
1970 /* eliminate toplevel group to avoid // in the result */
1971 if (strGroup == "/")
1972 strGroup.setNull();
1973 aFile = com::Utf8StrFmt("%s%s%c%s%c%s.vbox",
1974 strBase.c_str(),
1975 strGroup.c_str(),
1976 RTPATH_DELIMITER,
1977 strDirName.c_str(),
1978 RTPATH_DELIMITER,
1979 strName.c_str());
1980 return S_OK;
1981}
1982
1983/**
1984 * Remove characters from a machine file name which can be problematic on
1985 * particular systems.
1986 * @param strName The file name to sanitise.
1987 */
1988void sanitiseMachineFilename(Utf8Str &strName)
1989{
1990 if (strName.isEmpty())
1991 return;
1992
1993 /* Set of characters which should be safe for use in filenames: some basic
1994 * ASCII, Unicode from Latin-1 alphabetic to the end of Hangul. We try to
1995 * skip anything that could count as a control character in Windows or
1996 * *nix, or be otherwise difficult for shells to handle (I would have
1997 * preferred to remove the space and brackets too). We also remove all
1998 * characters which need UTF-16 surrogate pairs for Windows's benefit.
1999 */
2000 static RTUNICP const s_uszValidRangePairs[] =
2001 {
2002 ' ', ' ',
2003 '(', ')',
2004 '-', '.',
2005 '0', '9',
2006 'A', 'Z',
2007 'a', 'z',
2008 '_', '_',
2009 0xa0, 0xd7af,
2010 '\0'
2011 };
2012
2013 char *pszName = strName.mutableRaw();
2014 ssize_t cReplacements = RTStrPurgeComplementSet(pszName, s_uszValidRangePairs, '_');
2015 Assert(cReplacements >= 0);
2016 NOREF(cReplacements);
2017
2018 /* No leading dot or dash. */
2019 if (pszName[0] == '.' || pszName[0] == '-')
2020 pszName[0] = '_';
2021
2022 /* No trailing dot. */
2023 if (pszName[strName.length() - 1] == '.')
2024 pszName[strName.length() - 1] = '_';
2025
2026 /* Mangle leading and trailing spaces. */
2027 for (size_t i = 0; pszName[i] == ' '; ++i)
2028 pszName[i] = '_';
2029 for (size_t i = strName.length() - 1; i && pszName[i] == ' '; --i)
2030 pszName[i] = '_';
2031}
2032
2033#ifdef DEBUG
2034typedef DECLCALLBACKTYPE(void, FNTESTPRINTF,(const char *, ...));
2035/** Simple unit test/operation examples for sanitiseMachineFilename(). */
2036static unsigned testSanitiseMachineFilename(FNTESTPRINTF *pfnPrintf)
2037{
2038 unsigned cErrors = 0;
2039
2040 /** Expected results of sanitising given file names. */
2041 static struct
2042 {
2043 /** The test file name to be sanitised (Utf-8). */
2044 const char *pcszIn;
2045 /** The expected sanitised output (Utf-8). */
2046 const char *pcszOutExpected;
2047 } aTest[] =
2048 {
2049 { "OS/2 2.1", "OS_2 2.1" },
2050 { "-!My VM!-", "__My VM_-" },
2051 { "\xF0\x90\x8C\xB0", "____" },
2052 { " My VM ", "__My VM__" },
2053 { ".My VM.", "_My VM_" },
2054 { "My VM", "My VM" }
2055 };
2056 for (unsigned i = 0; i < RT_ELEMENTS(aTest); ++i)
2057 {
2058 Utf8Str str(aTest[i].pcszIn);
2059 sanitiseMachineFilename(str);
2060 if (str.compare(aTest[i].pcszOutExpected))
2061 {
2062 ++cErrors;
2063 pfnPrintf("%s: line %d, expected %s, actual %s\n",
2064 __PRETTY_FUNCTION__, i, aTest[i].pcszOutExpected,
2065 str.c_str());
2066 }
2067 }
2068 return cErrors;
2069}
2070
2071/** @todo Proper testcase. */
2072/** @todo Do we have a better method of doing init functions? */
2073namespace
2074{
2075 class TestSanitiseMachineFilename
2076 {
2077 public:
2078 TestSanitiseMachineFilename(void)
2079 {
2080 Assert(!testSanitiseMachineFilename(RTAssertMsg2));
2081 }
2082 };
2083 TestSanitiseMachineFilename s_TestSanitiseMachineFilename;
2084}
2085#endif
2086
2087/** @note Locks mSystemProperties object for reading. */
2088HRESULT VirtualBox::createMachine(const com::Utf8Str &aSettingsFile,
2089 const com::Utf8Str &aName,
2090 const std::vector<com::Utf8Str> &aGroups,
2091 const com::Utf8Str &aOsTypeId,
2092 const com::Utf8Str &aFlags,
2093 const com::Utf8Str &aCipher,
2094 const com::Utf8Str &aPasswordId,
2095 const com::Utf8Str &aPassword,
2096 ComPtr<IMachine> &aMachine)
2097{
2098 LogFlowThisFuncEnter();
2099 LogFlowThisFunc(("aSettingsFile=\"%s\", aName=\"%s\", aOsTypeId =\"%s\", aCreateFlags=\"%s\"\n",
2100 aSettingsFile.c_str(), aName.c_str(), aOsTypeId.c_str(), aFlags.c_str()));
2101
2102 StringsList llGroups;
2103 HRESULT hrc = i_convertMachineGroups(aGroups, &llGroups);
2104 if (FAILED(hrc))
2105 return hrc;
2106
2107 /** @todo r=bird: Would be goot to rewrite this parsing using offset into
2108 * aFlags and drop all the C pointers, strchr, misguided RTStrStr and
2109 * tedious copying of substrings. */
2110 Utf8Str strCreateFlags(aFlags); /** @todo r=bird: WTF is the point of this copy? */
2111 Guid id;
2112 bool fForceOverwrite = false;
2113 bool fDirectoryIncludesUUID = false;
2114 if (!strCreateFlags.isEmpty())
2115 {
2116 const char *pcszNext = strCreateFlags.c_str();
2117 while (*pcszNext != '\0')
2118 {
2119 Utf8Str strFlag;
2120 const char *pcszComma = strchr(pcszNext, ','); /*clueless version: RTStrStr(pcszNext, ","); */
2121 if (!pcszComma)
2122 strFlag = pcszNext;
2123 else
2124 strFlag.assign(pcszNext, (size_t)(pcszComma - pcszNext));
2125
2126 const char *pcszEqual = strchr(strFlag.c_str(), '='); /* more cluelessness: RTStrStr(strFlag.c_str(), "="); */
2127 /* skip over everything which doesn't contain '=' */
2128 if (pcszEqual && pcszEqual != strFlag.c_str())
2129 {
2130 Utf8Str strKey(strFlag.c_str(), (size_t)(pcszEqual - strFlag.c_str()));
2131 Utf8Str strValue(strFlag.c_str() + (pcszEqual - strFlag.c_str() + 1));
2132
2133 if (strKey == "UUID")
2134 id = strValue.c_str();
2135 else if (strKey == "forceOverwrite")
2136 fForceOverwrite = (strValue == "1");
2137 else if (strKey == "directoryIncludesUUID")
2138 fDirectoryIncludesUUID = (strValue == "1");
2139 }
2140
2141 if (!pcszComma)
2142 pcszNext += strFlag.length(); /* you can just 'break' out here... */
2143 else
2144 pcszNext += strFlag.length() + 1;
2145 }
2146 }
2147
2148 /* Create UUID if none was specified. */
2149 if (id.isZero())
2150 id.create();
2151 else if (!id.isValid())
2152 {
2153 /* do something else */
2154 return setError(E_INVALIDARG, tr("'%s' is not a valid Guid"), id.toStringCurly().c_str());
2155 }
2156
2157 /* NULL settings file means compose automatically */
2158 Utf8Str strSettingsFile(aSettingsFile);
2159 if (strSettingsFile.isEmpty())
2160 {
2161 Utf8Str strNewCreateFlags(Utf8StrFmt("UUID=%RTuuid", id.raw()));
2162 if (fDirectoryIncludesUUID)
2163 strNewCreateFlags += ",directoryIncludesUUID=1";
2164
2165 com::Utf8Str blstr;
2166 hrc = composeMachineFilename(aName,
2167 llGroups.front(),
2168 strNewCreateFlags,
2169 blstr /* aBaseFolder */,
2170 strSettingsFile);
2171 if (FAILED(hrc)) return hrc;
2172 }
2173
2174 /* create a new object */
2175 ComObjPtr<Machine> machine;
2176 hrc = machine.createObject();
2177 if (FAILED(hrc)) return hrc;
2178
2179 ComObjPtr<GuestOSType> osType;
2180 if (!aOsTypeId.isEmpty())
2181 i_findGuestOSType(aOsTypeId, osType);
2182
2183 /* initialize the machine object */
2184 hrc = machine->init(this,
2185 strSettingsFile,
2186 aName,
2187 llGroups,
2188 aOsTypeId,
2189 osType,
2190 id,
2191 fForceOverwrite,
2192 fDirectoryIncludesUUID,
2193 aCipher,
2194 aPasswordId,
2195 aPassword);
2196 if (SUCCEEDED(hrc))
2197 {
2198 /* set the return value */
2199 machine.queryInterfaceTo(aMachine.asOutParam());
2200 AssertComRC(hrc);
2201
2202#ifdef VBOX_WITH_EXTPACK
2203 /* call the extension pack hooks */
2204 m->ptrExtPackManager->i_callAllVmCreatedHooks(machine);
2205#endif
2206 }
2207
2208 LogFlowThisFuncLeave();
2209
2210 return hrc;
2211}
2212
2213HRESULT VirtualBox::openMachine(const com::Utf8Str &aSettingsFile,
2214 const com::Utf8Str &aPassword,
2215 ComPtr<IMachine> &aMachine)
2216{
2217 /* create a new object */
2218 ComObjPtr<Machine> machine;
2219 HRESULT hrc = machine.createObject();
2220 if (SUCCEEDED(hrc))
2221 {
2222 /* initialize the machine object */
2223 hrc = machine->initFromSettings(this, aSettingsFile, NULL /* const Guid *aId */, aPassword);
2224 if (SUCCEEDED(hrc))
2225 {
2226 /* set the return value */
2227 machine.queryInterfaceTo(aMachine.asOutParam());
2228 ComAssertComRC(hrc);
2229 }
2230 }
2231
2232 return hrc;
2233}
2234
2235/** @note Locks objects! */
2236HRESULT VirtualBox::registerMachine(const ComPtr<IMachine> &aMachine)
2237{
2238 Bstr name;
2239 HRESULT hrc = aMachine->COMGETTER(Name)(name.asOutParam());
2240 if (FAILED(hrc)) return hrc;
2241
2242 /* We can safely cast child to Machine * here because only Machine
2243 * implementations of IMachine can be among our children. */
2244 IMachine *aM = aMachine;
2245 Machine *pMachine = static_cast<Machine*>(aM);
2246
2247 AutoCaller machCaller(pMachine);
2248 ComAssertComRCRetRC(machCaller.hrc());
2249
2250 hrc = i_registerMachine(pMachine);
2251 /* fire an event */
2252 if (SUCCEEDED(hrc))
2253 i_onMachineRegistered(pMachine->i_getId(), TRUE);
2254
2255 return hrc;
2256}
2257
2258/** @note Locks this object for reading, then some machine objects for reading. */
2259HRESULT VirtualBox::findMachine(const com::Utf8Str &aSettingsFile,
2260 ComPtr<IMachine> &aMachine)
2261{
2262 LogFlowThisFuncEnter();
2263 LogFlowThisFunc(("aSettingsFile=\"%s\", aMachine={%p}\n", aSettingsFile.c_str(), &aMachine));
2264
2265 /* start with not found */
2266 HRESULT hrc = S_OK;
2267 ComObjPtr<Machine> pMachineFound;
2268
2269 Guid id(aSettingsFile);
2270 Utf8Str strFile(aSettingsFile);
2271 if (id.isValid() && !id.isZero())
2272 hrc = i_findMachine(id,
2273 true /* fPermitInaccessible */,
2274 true /* setError */,
2275 &pMachineFound);
2276 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
2277 else
2278 {
2279 hrc = i_findMachineByName(strFile,
2280 true /* setError */,
2281 &pMachineFound);
2282 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
2283 }
2284
2285 /* this will set (*machine) to NULL if machineObj is null */
2286 pMachineFound.queryInterfaceTo(aMachine.asOutParam());
2287
2288 LogFlowThisFunc(("aName=\"%s\", aMachine=%p, hrc=%08X\n", aSettingsFile.c_str(), &aMachine, hrc));
2289 LogFlowThisFuncLeave();
2290
2291 return hrc;
2292}
2293
2294HRESULT VirtualBox::getMachinesByGroups(const std::vector<com::Utf8Str> &aGroups,
2295 std::vector<ComPtr<IMachine> > &aMachines)
2296{
2297 StringsList llGroups;
2298 HRESULT hrc = i_convertMachineGroups(aGroups, &llGroups);
2299 if (FAILED(hrc))
2300 return hrc;
2301
2302 /* we want to rely on sorted groups during compare, to save time */
2303 llGroups.sort();
2304
2305 /* get copy of all machine references, to avoid holding the list lock */
2306 MachinesOList::MyList allMachines;
2307 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2308 allMachines = m->allMachines.getList();
2309
2310 std::vector<ComObjPtr<IMachine> > saMachines;
2311 saMachines.resize(0);
2312 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
2313 it != allMachines.end();
2314 ++it)
2315 {
2316 const ComObjPtr<Machine> &pMachine = *it;
2317 AutoCaller autoMachineCaller(pMachine);
2318 if (FAILED(autoMachineCaller.hrc()))
2319 continue;
2320 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
2321
2322 if (pMachine->i_isAccessible())
2323 {
2324 const StringsList &thisGroups = pMachine->i_getGroups();
2325 for (StringsList::const_iterator it2 = thisGroups.begin();
2326 it2 != thisGroups.end();
2327 ++it2)
2328 {
2329 const Utf8Str &group = *it2;
2330 bool fAppended = false;
2331 for (StringsList::const_iterator it3 = llGroups.begin();
2332 it3 != llGroups.end();
2333 ++it3)
2334 {
2335 int order = it3->compare(group);
2336 if (order == 0)
2337 {
2338 saMachines.push_back(static_cast<IMachine *>(pMachine));
2339 fAppended = true;
2340 break;
2341 }
2342 else if (order > 0)
2343 break;
2344 else
2345 continue;
2346 }
2347 /* avoid duplicates and save time */
2348 if (fAppended)
2349 break;
2350 }
2351 }
2352 }
2353 aMachines.resize(saMachines.size());
2354 size_t i = 0;
2355 for(i = 0; i < saMachines.size(); ++i)
2356 saMachines[i].queryInterfaceTo(aMachines[i].asOutParam());
2357
2358 return S_OK;
2359}
2360
2361HRESULT VirtualBox::getMachineStates(const std::vector<ComPtr<IMachine> > &aMachines,
2362 std::vector<MachineState_T> &aStates)
2363{
2364 com::SafeIfaceArray<IMachine> saMachines(aMachines);
2365 aStates.resize(aMachines.size());
2366 for (size_t i = 0; i < saMachines.size(); i++)
2367 {
2368 ComPtr<IMachine> pMachine = saMachines[i];
2369 MachineState_T state = MachineState_Null;
2370 if (!pMachine.isNull())
2371 {
2372 HRESULT hrc = pMachine->COMGETTER(State)(&state);
2373 if (hrc == E_ACCESSDENIED)
2374 hrc = S_OK;
2375 AssertComRC(hrc);
2376 }
2377 aStates[i] = state;
2378 }
2379 return S_OK;
2380}
2381
2382HRESULT VirtualBox::createUnattendedInstaller(ComPtr<IUnattended> &aUnattended)
2383{
2384#ifdef VBOX_WITH_UNATTENDED
2385 ComObjPtr<Unattended> ptrUnattended;
2386 HRESULT hrc = ptrUnattended.createObject();
2387 if (SUCCEEDED(hrc))
2388 {
2389 AutoReadLock wlock(this COMMA_LOCKVAL_SRC_POS);
2390 hrc = ptrUnattended->initUnattended(this);
2391 if (SUCCEEDED(hrc))
2392 hrc = ptrUnattended.queryInterfaceTo(aUnattended.asOutParam());
2393 }
2394 return hrc;
2395#else
2396 NOREF(aUnattended);
2397 return E_NOTIMPL;
2398#endif
2399}
2400
2401HRESULT VirtualBox::createMedium(const com::Utf8Str &aFormat,
2402 const com::Utf8Str &aLocation,
2403 AccessMode_T aAccessMode,
2404 DeviceType_T aDeviceType,
2405 ComPtr<IMedium> &aMedium)
2406{
2407 NOREF(aAccessMode); /**< @todo r=klaus make use of access mode */
2408
2409 HRESULT hrc = S_OK;
2410
2411 ComObjPtr<Medium> medium;
2412 medium.createObject();
2413 com::Utf8Str format = aFormat;
2414
2415 switch (aDeviceType)
2416 {
2417 case DeviceType_HardDisk:
2418 {
2419
2420 /* we don't access non-const data members so no need to lock */
2421 if (format.isEmpty())
2422 i_getDefaultHardDiskFormat(format);
2423
2424 hrc = medium->init(this,
2425 format,
2426 aLocation,
2427 Guid::Empty /* media registry: none yet */,
2428 aDeviceType);
2429 }
2430 break;
2431
2432 case DeviceType_DVD:
2433 case DeviceType_Floppy:
2434 {
2435
2436 if (format.isEmpty())
2437 return setError(E_INVALIDARG, tr("Format must be Valid Type%s"), format.c_str());
2438
2439 // enforce read-only for DVDs even if caller specified ReadWrite
2440 if (aDeviceType == DeviceType_DVD)
2441 aAccessMode = AccessMode_ReadOnly;
2442
2443 hrc = medium->init(this,
2444 format,
2445 aLocation,
2446 Guid::Empty /* media registry: none yet */,
2447 aDeviceType);
2448
2449 }
2450 break;
2451
2452 default:
2453 return setError(E_INVALIDARG, tr("Device type must be HardDisk, DVD or Floppy %d"), aDeviceType);
2454 }
2455
2456 if (SUCCEEDED(hrc))
2457 {
2458 medium.queryInterfaceTo(aMedium.asOutParam());
2459 com::Guid uMediumId = medium->i_getId();
2460 if (uMediumId.isValid() && !uMediumId.isZero())
2461 i_onMediumRegistered(uMediumId, medium->i_getDeviceType(), TRUE);
2462 }
2463
2464 return hrc;
2465}
2466
2467HRESULT VirtualBox::openMedium(const com::Utf8Str &aLocation,
2468 DeviceType_T aDeviceType,
2469 AccessMode_T aAccessMode,
2470 BOOL aForceNewUuid,
2471 ComPtr<IMedium> &aMedium)
2472{
2473 HRESULT hrc = S_OK;
2474 Guid id(aLocation);
2475 ComObjPtr<Medium> pMedium;
2476
2477 // have to get write lock as the whole find/update sequence must be done
2478 // in one critical section, otherwise there are races which can lead to
2479 // multiple Medium objects with the same content
2480 AutoWriteLock treeLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2481
2482 // check if the device type is correct, and see if a medium for the
2483 // given path has already initialized; if so, return that
2484 switch (aDeviceType)
2485 {
2486 case DeviceType_HardDisk:
2487 if (id.isValid() && !id.isZero())
2488 hrc = i_findHardDiskById(id, false /* setError */, &pMedium);
2489 else
2490 hrc = i_findHardDiskByLocation(aLocation, false, /* aSetError */ &pMedium);
2491 break;
2492
2493 case DeviceType_Floppy:
2494 case DeviceType_DVD:
2495 if (id.isValid() && !id.isZero())
2496 hrc = i_findDVDOrFloppyImage(aDeviceType, &id, Utf8Str::Empty, false /* setError */, &pMedium);
2497 else
2498 hrc = i_findDVDOrFloppyImage(aDeviceType, NULL, aLocation, false /* setError */, &pMedium);
2499
2500 // enforce read-only for DVDs even if caller specified ReadWrite
2501 if (aDeviceType == DeviceType_DVD)
2502 aAccessMode = AccessMode_ReadOnly;
2503 break;
2504
2505 default:
2506 return setError(E_INVALIDARG, tr("Device type must be HardDisk, DVD or Floppy %d"), aDeviceType);
2507 }
2508
2509 bool fMediumRegistered = false;
2510 if (pMedium.isNull())
2511 {
2512 pMedium.createObject();
2513 treeLock.release();
2514 hrc = pMedium->init(this,
2515 aLocation,
2516 (aAccessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
2517 !!aForceNewUuid,
2518 aDeviceType);
2519 treeLock.acquire();
2520
2521 if (SUCCEEDED(hrc))
2522 {
2523 hrc = i_registerMedium(pMedium, &pMedium, treeLock);
2524
2525 treeLock.release();
2526
2527 /* Note that it's important to call uninit() on failure to register
2528 * because the differencing hard disk would have been already associated
2529 * with the parent and this association needs to be broken. */
2530
2531 if (FAILED(hrc))
2532 {
2533 pMedium->uninit();
2534 hrc = VBOX_E_OBJECT_NOT_FOUND;
2535 }
2536 else
2537 fMediumRegistered = true;
2538 }
2539 else if (hrc != VBOX_E_INVALID_OBJECT_STATE)
2540 hrc = VBOX_E_OBJECT_NOT_FOUND;
2541 }
2542
2543 if (SUCCEEDED(hrc))
2544 {
2545 pMedium.queryInterfaceTo(aMedium.asOutParam());
2546 if (fMediumRegistered)
2547 i_onMediumRegistered(pMedium->i_getId(), pMedium->i_getDeviceType() ,TRUE);
2548 }
2549
2550 return hrc;
2551}
2552
2553
2554/** @note Locks this object for reading. */
2555HRESULT VirtualBox::getGuestOSType(const com::Utf8Str &aId,
2556 ComPtr<IGuestOSType> &aType)
2557{
2558 ComObjPtr<GuestOSType> pType;
2559 HRESULT hrc = i_findGuestOSType(aId, pType);
2560 pType.queryInterfaceTo(aType.asOutParam());
2561 return hrc;
2562}
2563
2564HRESULT VirtualBox::createSharedFolder(const com::Utf8Str &aName,
2565 const com::Utf8Str &aHostPath,
2566 BOOL aWritable,
2567 BOOL aAutomount,
2568 const com::Utf8Str &aAutoMountPoint)
2569{
2570 NOREF(aName);
2571 NOREF(aHostPath);
2572 NOREF(aWritable);
2573 NOREF(aAutomount);
2574 NOREF(aAutoMountPoint);
2575
2576 return setError(E_NOTIMPL, tr("Not yet implemented"));
2577}
2578
2579HRESULT VirtualBox::removeSharedFolder(const com::Utf8Str &aName)
2580{
2581 NOREF(aName);
2582 return setError(E_NOTIMPL, tr("Not yet implemented"));
2583}
2584
2585/**
2586 * @note Locks this object for reading.
2587 */
2588HRESULT VirtualBox::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
2589{
2590 using namespace settings;
2591
2592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2593
2594 aKeys.resize(m->pMainConfigFile->mapExtraDataItems.size());
2595 size_t i = 0;
2596 for (StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin();
2597 it != m->pMainConfigFile->mapExtraDataItems.end(); ++it, ++i)
2598 aKeys[i] = it->first;
2599
2600 return S_OK;
2601}
2602
2603/**
2604 * @note Locks this object for reading.
2605 */
2606HRESULT VirtualBox::getExtraData(const com::Utf8Str &aKey,
2607 com::Utf8Str &aValue)
2608{
2609 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(aKey);
2610 if (it != m->pMainConfigFile->mapExtraDataItems.end())
2611 // found:
2612 aValue = it->second; // source is a Utf8Str
2613
2614 /* return the result to caller (may be empty) */
2615
2616 return S_OK;
2617}
2618
2619/**
2620 * @note Locks this object for writing.
2621 */
2622HRESULT VirtualBox::setExtraData(const com::Utf8Str &aKey,
2623 const com::Utf8Str &aValue)
2624{
2625 Utf8Str strKey(aKey);
2626 Utf8Str strValue(aValue);
2627 Utf8Str strOldValue; // empty
2628 HRESULT hrc = S_OK;
2629
2630 /* Because control characters in aKey have caused problems in the settings
2631 * they are rejected unless the key should be deleted. */
2632 if (!strValue.isEmpty())
2633 {
2634 for (size_t i = 0; i < strKey.length(); ++i)
2635 {
2636 char ch = strKey[i];
2637 if (RTLocCIsCntrl(ch))
2638 return E_INVALIDARG;
2639 }
2640 }
2641
2642 // locking note: we only hold the read lock briefly to look up the old value,
2643 // then release it and call the onExtraCanChange callbacks. There is a small
2644 // chance of a race insofar as the callback might be called twice if two callers
2645 // change the same key at the same time, but that's a much better solution
2646 // than the deadlock we had here before. The actual changing of the extradata
2647 // is then performed under the write lock and race-free.
2648
2649 // look up the old value first; if nothing has changed then we need not do anything
2650 {
2651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
2652 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
2653 if (it != m->pMainConfigFile->mapExtraDataItems.end())
2654 strOldValue = it->second;
2655 }
2656
2657 bool fChanged;
2658 if ((fChanged = (strOldValue != strValue)))
2659 {
2660 // ask for permission from all listeners outside the locks;
2661 // onExtraDataCanChange() only briefly requests the VirtualBox
2662 // lock to copy the list of callbacks to invoke
2663 Bstr error;
2664
2665 if (!i_onExtraDataCanChange(Guid::Empty, Bstr(aKey).raw(), Bstr(aValue).raw(), error))
2666 {
2667 const char *sep = error.isEmpty() ? "" : ": ";
2668 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
2669 return setError(E_ACCESSDENIED,
2670 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
2671 strKey.c_str(),
2672 strValue.c_str(),
2673 sep,
2674 error.raw());
2675 }
2676
2677 // data is changing and change not vetoed: then write it out under the lock
2678
2679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2680
2681 if (strValue.isEmpty())
2682 m->pMainConfigFile->mapExtraDataItems.erase(strKey);
2683 else
2684 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
2685 // creates a new key if needed
2686
2687 /* save settings on success */
2688 hrc = i_saveSettings();
2689 if (FAILED(hrc)) return hrc;
2690 }
2691
2692 // fire notification outside the lock
2693 if (fChanged)
2694 i_onExtraDataChanged(Guid::Empty, Bstr(aKey).raw(), Bstr(aValue).raw());
2695
2696 return hrc;
2697}
2698
2699/**
2700 *
2701 */
2702HRESULT VirtualBox::setSettingsSecret(const com::Utf8Str &aPassword)
2703{
2704 i_storeSettingsKey(aPassword);
2705 i_decryptSettings();
2706 return S_OK;
2707}
2708
2709int VirtualBox::i_decryptMediumSettings(Medium *pMedium)
2710{
2711 Bstr bstrCipher;
2712 HRESULT hrc = pMedium->GetProperty(Bstr("InitiatorSecretEncrypted").raw(),
2713 bstrCipher.asOutParam());
2714 if (SUCCEEDED(hrc))
2715 {
2716 Utf8Str strPlaintext;
2717 int vrc = i_decryptSetting(&strPlaintext, bstrCipher);
2718 if (RT_SUCCESS(vrc))
2719 pMedium->i_setPropertyDirect("InitiatorSecret", strPlaintext);
2720 else
2721 return vrc;
2722 }
2723 return VINF_SUCCESS;
2724}
2725
2726/**
2727 * Decrypt all encrypted settings.
2728 *
2729 * So far we only have encrypted iSCSI initiator secrets so we just go through
2730 * all hard disk media and determine the plain 'InitiatorSecret' from
2731 * 'InitiatorSecretEncrypted. The latter is stored as Base64 because medium
2732 * properties need to be null-terminated strings.
2733 */
2734int VirtualBox::i_decryptSettings()
2735{
2736 bool fFailure = false;
2737 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2738 for (MediaList::const_iterator mt = m->allHardDisks.begin();
2739 mt != m->allHardDisks.end();
2740 ++mt)
2741 {
2742 ComObjPtr<Medium> pMedium = *mt;
2743 AutoCaller medCaller(pMedium);
2744 if (FAILED(medCaller.hrc()))
2745 continue;
2746 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
2747 int vrc = i_decryptMediumSettings(pMedium);
2748 if (RT_FAILURE(vrc))
2749 fFailure = true;
2750 }
2751 if (!fFailure)
2752 {
2753 for (MediaList::const_iterator mt = m->allHardDisks.begin();
2754 mt != m->allHardDisks.end();
2755 ++mt)
2756 {
2757 i_onMediumConfigChanged(*mt);
2758 }
2759 }
2760 return fFailure ? VERR_INVALID_PARAMETER : VINF_SUCCESS;
2761}
2762
2763/**
2764 * Encode.
2765 *
2766 * @param aPlaintext plaintext to be encrypted
2767 * @param aCiphertext resulting ciphertext (base64-encoded)
2768 */
2769int VirtualBox::i_encryptSetting(const Utf8Str &aPlaintext, Utf8Str *aCiphertext)
2770{
2771 uint8_t abCiphertext[32];
2772 char szCipherBase64[128];
2773 size_t cchCipherBase64;
2774 int vrc = i_encryptSettingBytes((uint8_t*)aPlaintext.c_str(), abCiphertext, aPlaintext.length()+1, sizeof(abCiphertext));
2775 if (RT_SUCCESS(vrc))
2776 {
2777 vrc = RTBase64Encode(abCiphertext, sizeof(abCiphertext), szCipherBase64, sizeof(szCipherBase64), &cchCipherBase64);
2778 if (RT_SUCCESS(vrc))
2779 *aCiphertext = szCipherBase64;
2780 }
2781 return vrc;
2782}
2783
2784/**
2785 * Decode.
2786 *
2787 * @param aPlaintext resulting plaintext
2788 * @param aCiphertext ciphertext (base64-encoded) to decrypt
2789 */
2790int VirtualBox::i_decryptSetting(Utf8Str *aPlaintext, const Utf8Str &aCiphertext)
2791{
2792 uint8_t abPlaintext[64];
2793 uint8_t abCiphertext[64];
2794 size_t cbCiphertext;
2795 int vrc = RTBase64Decode(aCiphertext.c_str(),
2796 abCiphertext, sizeof(abCiphertext),
2797 &cbCiphertext, NULL);
2798 if (RT_SUCCESS(vrc))
2799 {
2800 vrc = i_decryptSettingBytes(abPlaintext, abCiphertext, cbCiphertext);
2801 if (RT_SUCCESS(vrc))
2802 {
2803 for (unsigned i = 0; i < cbCiphertext; i++)
2804 {
2805 /* sanity check: null-terminated string? */
2806 if (abPlaintext[i] == '\0')
2807 {
2808 /* sanity check: valid UTF8 string? */
2809 if (RTStrIsValidEncoding((const char*)abPlaintext))
2810 {
2811 *aPlaintext = Utf8Str((const char*)abPlaintext);
2812 return VINF_SUCCESS;
2813 }
2814 }
2815 }
2816 vrc = VERR_INVALID_MAGIC;
2817 }
2818 }
2819 return vrc;
2820}
2821
2822/**
2823 * Encrypt secret bytes. Use the m->SettingsCipherKey as key.
2824 *
2825 * @param aPlaintext clear text to be encrypted
2826 * @param aCiphertext resulting encrypted text
2827 * @param aPlaintextSize size of the plaintext
2828 * @param aCiphertextSize size of the ciphertext
2829 */
2830int VirtualBox::i_encryptSettingBytes(const uint8_t *aPlaintext, uint8_t *aCiphertext,
2831 size_t aPlaintextSize, size_t aCiphertextSize) const
2832{
2833 unsigned i, j;
2834 uint8_t aBytes[64];
2835
2836 if (!m->fSettingsCipherKeySet)
2837 return VERR_INVALID_STATE;
2838
2839 if (aCiphertextSize > sizeof(aBytes))
2840 return VERR_BUFFER_OVERFLOW;
2841
2842 if (aCiphertextSize < 32)
2843 return VERR_INVALID_PARAMETER;
2844
2845 AssertCompile(sizeof(m->SettingsCipherKey) >= 32);
2846
2847 /* store the first 8 bytes of the cipherkey for verification */
2848 for (i = 0, j = 0; i < 8; i++, j++)
2849 aCiphertext[i] = m->SettingsCipherKey[j];
2850
2851 for (unsigned k = 0; k < aPlaintextSize && i < aCiphertextSize; i++, k++)
2852 {
2853 aCiphertext[i] = (aPlaintext[k] ^ m->SettingsCipherKey[j]);
2854 if (++j >= sizeof(m->SettingsCipherKey))
2855 j = 0;
2856 }
2857
2858 /* fill with random data to have a minimal length (salt) */
2859 if (i < aCiphertextSize)
2860 {
2861 RTRandBytes(aBytes, aCiphertextSize - i);
2862 for (int k = 0; i < aCiphertextSize; i++, k++)
2863 {
2864 aCiphertext[i] = aBytes[k] ^ m->SettingsCipherKey[j];
2865 if (++j >= sizeof(m->SettingsCipherKey))
2866 j = 0;
2867 }
2868 }
2869
2870 return VINF_SUCCESS;
2871}
2872
2873/**
2874 * Decrypt secret bytes. Use the m->SettingsCipherKey as key.
2875 *
2876 * @param aPlaintext resulting plaintext
2877 * @param aCiphertext ciphertext to be decrypted
2878 * @param aCiphertextSize size of the ciphertext == size of the plaintext
2879 */
2880int VirtualBox::i_decryptSettingBytes(uint8_t *aPlaintext,
2881 const uint8_t *aCiphertext, size_t aCiphertextSize) const
2882{
2883 unsigned i, j;
2884
2885 if (!m->fSettingsCipherKeySet)
2886 return VERR_INVALID_STATE;
2887
2888 if (aCiphertextSize < 32)
2889 return VERR_INVALID_PARAMETER;
2890
2891 /* key verification */
2892 for (i = 0, j = 0; i < 8; i++, j++)
2893 if (aCiphertext[i] != m->SettingsCipherKey[j])
2894 return VERR_INVALID_MAGIC;
2895
2896 /* poison */
2897 memset(aPlaintext, 0xff, aCiphertextSize);
2898 for (int k = 0; i < aCiphertextSize; i++, k++)
2899 {
2900 aPlaintext[k] = aCiphertext[i] ^ m->SettingsCipherKey[j];
2901 if (++j >= sizeof(m->SettingsCipherKey))
2902 j = 0;
2903 }
2904
2905 return VINF_SUCCESS;
2906}
2907
2908/**
2909 * Store a settings key.
2910 *
2911 * @param aKey the key to store
2912 */
2913void VirtualBox::i_storeSettingsKey(const Utf8Str &aKey)
2914{
2915 RTSha512(aKey.c_str(), aKey.length(), m->SettingsCipherKey);
2916 m->fSettingsCipherKeySet = true;
2917}
2918
2919// public methods only for internal purposes
2920/////////////////////////////////////////////////////////////////////////////
2921
2922#ifdef DEBUG
2923void VirtualBox::i_dumpAllBackRefs()
2924{
2925 {
2926 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2927 for (MediaList::const_iterator mt = m->allHardDisks.begin();
2928 mt != m->allHardDisks.end();
2929 ++mt)
2930 {
2931 ComObjPtr<Medium> pMedium = *mt;
2932 pMedium->i_dumpBackRefs();
2933 }
2934 }
2935 {
2936 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2937 for (MediaList::const_iterator mt = m->allDVDImages.begin();
2938 mt != m->allDVDImages.end();
2939 ++mt)
2940 {
2941 ComObjPtr<Medium> pMedium = *mt;
2942 pMedium->i_dumpBackRefs();
2943 }
2944 }
2945}
2946#endif
2947
2948/**
2949 * Posts an event to the event queue that is processed asynchronously
2950 * on a dedicated thread.
2951 *
2952 * Posting events to the dedicated event queue is useful to perform secondary
2953 * actions outside any object locks -- for example, to iterate over a list
2954 * of callbacks and inform them about some change caused by some object's
2955 * method call.
2956 *
2957 * @param event event to post; must have been allocated using |new|, will
2958 * be deleted automatically by the event thread after processing
2959 *
2960 * @note Doesn't lock any object.
2961 */
2962HRESULT VirtualBox::i_postEvent(Event *event)
2963{
2964 AssertReturn(event, E_FAIL);
2965
2966 HRESULT hrc;
2967 AutoCaller autoCaller(this);
2968 if (SUCCEEDED((hrc = autoCaller.hrc())))
2969 {
2970 if (getObjectState().getState() != ObjectState::Ready)
2971 Log1WarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
2972 getObjectState().getState()));
2973 // return S_OK
2974 else if ( (m->pAsyncEventQ)
2975 && (m->pAsyncEventQ->postEvent(event))
2976 )
2977 return S_OK;
2978 else
2979 hrc = E_FAIL;
2980 }
2981
2982 // in any event of failure, we must clean up here, or we'll leak;
2983 // the caller has allocated the object using new()
2984 delete event;
2985 return hrc;
2986}
2987
2988/**
2989 * Adds a progress to the global collection of pending operations.
2990 * Usually gets called upon progress object initialization.
2991 *
2992 * @param aProgress Operation to add to the collection.
2993 *
2994 * @note Doesn't lock objects.
2995 */
2996HRESULT VirtualBox::i_addProgress(IProgress *aProgress)
2997{
2998 CheckComArgNotNull(aProgress);
2999
3000 AutoCaller autoCaller(this);
3001 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3002
3003 Bstr id;
3004 HRESULT hrc = aProgress->COMGETTER(Id)(id.asOutParam());
3005 AssertComRCReturnRC(hrc);
3006
3007 /* protect mProgressOperations */
3008 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
3009
3010 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
3011 return S_OK;
3012}
3013
3014/**
3015 * Removes the progress from the global collection of pending operations.
3016 * Usually gets called upon progress completion.
3017 *
3018 * @param aId UUID of the progress operation to remove
3019 *
3020 * @note Doesn't lock objects.
3021 */
3022HRESULT VirtualBox::i_removeProgress(IN_GUID aId)
3023{
3024 AutoCaller autoCaller(this);
3025 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3026
3027 ComPtr<IProgress> progress;
3028
3029 /* protect mProgressOperations */
3030 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
3031
3032 size_t cnt = m->mapProgressOperations.erase(aId);
3033 Assert(cnt == 1);
3034 NOREF(cnt);
3035
3036 return S_OK;
3037}
3038
3039#ifdef RT_OS_WINDOWS
3040
3041class StartSVCHelperClientData : public ThreadTask
3042{
3043public:
3044 StartSVCHelperClientData()
3045 {
3046 LogFlowFuncEnter();
3047 m_strTaskName = "SVCHelper";
3048 threadVoidData = NULL;
3049 initialized = false;
3050 }
3051
3052 virtual ~StartSVCHelperClientData()
3053 {
3054 LogFlowFuncEnter();
3055 if (threadVoidData!=NULL)
3056 {
3057 delete threadVoidData;
3058 threadVoidData=NULL;
3059 }
3060 };
3061
3062 void handler()
3063 {
3064 VirtualBox::i_SVCHelperClientThreadTask(this);
3065 }
3066
3067 const ComPtr<Progress>& GetProgressObject() const {return progress;}
3068
3069 bool init(VirtualBox* aVbox,
3070 Progress* aProgress,
3071 bool aPrivileged,
3072 VirtualBox::PFN_SVC_HELPER_CLIENT_T aFunc,
3073 void *aUser)
3074 {
3075 LogFlowFuncEnter();
3076 that = aVbox;
3077 progress = aProgress;
3078 privileged = aPrivileged;
3079 func = aFunc;
3080 user = aUser;
3081
3082 initThreadVoidData();
3083
3084 initialized = true;
3085
3086 return initialized;
3087 }
3088
3089 bool isOk() const{ return initialized;}
3090
3091 bool initialized;
3092 ComObjPtr<VirtualBox> that;
3093 ComObjPtr<Progress> progress;
3094 bool privileged;
3095 VirtualBox::PFN_SVC_HELPER_CLIENT_T func;
3096 void *user;
3097 ThreadVoidData *threadVoidData;
3098
3099private:
3100 bool initThreadVoidData()
3101 {
3102 LogFlowFuncEnter();
3103 threadVoidData = static_cast<ThreadVoidData*>(user);
3104 return true;
3105 }
3106};
3107
3108/**
3109 * Helper method that starts a worker thread that:
3110 * - creates a pipe communication channel using SVCHlpClient;
3111 * - starts an SVC Helper process that will inherit this channel;
3112 * - executes the supplied function by passing it the created SVCHlpClient
3113 * and opened instance to communicate to the Helper process and the given
3114 * Progress object.
3115 *
3116 * The user function is supposed to communicate to the helper process
3117 * using the \a aClient argument to do the requested job and optionally expose
3118 * the progress through the \a aProgress object. The user function should never
3119 * call notifyComplete() on it: this will be done automatically using the
3120 * result code returned by the function.
3121 *
3122 * Before the user function is started, the communication channel passed to
3123 * the \a aClient argument is fully set up, the function should start using
3124 * its write() and read() methods directly.
3125 *
3126 * The \a aVrc parameter of the user function may be used to return an error
3127 * code if it is related to communication errors (for example, returned by
3128 * the SVCHlpClient members when they fail). In this case, the correct error
3129 * message using this value will be reported to the caller. Note that the
3130 * value of \a aVrc is inspected only if the user function itself returns
3131 * success.
3132 *
3133 * If a failure happens anywhere before the user function would be normally
3134 * called, it will be called anyway in special "cleanup only" mode indicated
3135 * by \a aClient, \a aProgress and \a aVrc arguments set to NULL. In this mode,
3136 * all the function is supposed to do is to cleanup its aUser argument if
3137 * necessary (it's assumed that the ownership of this argument is passed to
3138 * the user function once #startSVCHelperClient() returns a success, thus
3139 * making it responsible for the cleanup).
3140 *
3141 * After the user function returns, the thread will send the SVCHlpMsg::Null
3142 * message to indicate a process termination.
3143 *
3144 * @param aPrivileged |true| to start the SVC Helper process as a privileged
3145 * user that can perform administrative tasks
3146 * @param aFunc user function to run
3147 * @param aUser argument to the user function
3148 * @param aProgress progress object that will track operation completion
3149 *
3150 * @note aPrivileged is currently ignored (due to some unsolved problems in
3151 * Vista) and the process will be started as a normal (unprivileged)
3152 * process.
3153 *
3154 * @note Doesn't lock anything.
3155 */
3156HRESULT VirtualBox::i_startSVCHelperClient(bool aPrivileged,
3157 PFN_SVC_HELPER_CLIENT_T aFunc,
3158 void *aUser, Progress *aProgress)
3159{
3160 LogFlowFuncEnter();
3161 AssertReturn(aFunc, E_POINTER);
3162 AssertReturn(aProgress, E_POINTER);
3163
3164 AutoCaller autoCaller(this);
3165 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3166
3167 /* create the i_SVCHelperClientThreadTask() argument */
3168
3169 HRESULT hrc = S_OK;
3170 StartSVCHelperClientData *pTask = NULL;
3171 try
3172 {
3173 pTask = new StartSVCHelperClientData();
3174
3175 pTask->init(this, aProgress, aPrivileged, aFunc, aUser);
3176
3177 if (!pTask->isOk())
3178 {
3179 delete pTask;
3180 LogRel(("Could not init StartSVCHelperClientData object \n"));
3181 throw E_FAIL;
3182 }
3183
3184 //this function delete pTask in case of exceptions, so there is no need in the call of delete operator
3185 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_WORKER);
3186
3187 }
3188 catch(std::bad_alloc &)
3189 {
3190 hrc = setError(E_OUTOFMEMORY);
3191 }
3192 catch(...)
3193 {
3194 LogRel(("Could not create thread for StartSVCHelperClientData \n"));
3195 hrc = E_FAIL;
3196 }
3197
3198 return hrc;
3199}
3200
3201/**
3202 * Worker thread for startSVCHelperClient().
3203 */
3204/* static */
3205void VirtualBox::i_SVCHelperClientThreadTask(StartSVCHelperClientData *pTask)
3206{
3207 LogFlowFuncEnter();
3208 HRESULT hrc = S_OK;
3209 bool userFuncCalled = false;
3210
3211 do
3212 {
3213 AssertBreakStmt(pTask, hrc = E_POINTER);
3214 AssertReturnVoid(!pTask->progress.isNull());
3215
3216 /* protect VirtualBox from uninitialization */
3217 AutoCaller autoCaller(pTask->that);
3218 if (!autoCaller.isOk())
3219 {
3220 /* it's too late */
3221 hrc = autoCaller.hrc();
3222 break;
3223 }
3224
3225 int vrc = VINF_SUCCESS;
3226
3227 Guid id;
3228 id.create();
3229 SVCHlpClient client;
3230 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
3231 id.raw()).c_str());
3232 if (RT_FAILURE(vrc))
3233 {
3234 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not create the communication channel (%Rrc)"), vrc);
3235 break;
3236 }
3237
3238 /* get the path to the executable */
3239 char exePathBuf[RTPATH_MAX];
3240 char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX);
3241 if (!exePath)
3242 {
3243 hrc = pTask->that->setError(E_FAIL, tr("Cannot get executable name"));
3244 break;
3245 }
3246
3247 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().c_str());
3248
3249 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.c_str()));
3250
3251 RTPROCESS pid = NIL_RTPROCESS;
3252
3253 if (pTask->privileged)
3254 {
3255 /* Attempt to start a privileged process using the Run As dialog */
3256
3257 Bstr file = exePath;
3258 Bstr parameters = argsStr;
3259
3260 SHELLEXECUTEINFO shExecInfo;
3261
3262 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
3263
3264 shExecInfo.fMask = NULL;
3265 shExecInfo.hwnd = NULL;
3266 shExecInfo.lpVerb = L"runas";
3267 shExecInfo.lpFile = file.raw();
3268 shExecInfo.lpParameters = parameters.raw();
3269 shExecInfo.lpDirectory = NULL;
3270 shExecInfo.nShow = SW_NORMAL;
3271 shExecInfo.hInstApp = NULL;
3272
3273 if (!ShellExecuteEx(&shExecInfo))
3274 {
3275 int vrc2 = RTErrConvertFromWin32(GetLastError());
3276 /* hide excessive details in case of a frequent error
3277 * (pressing the Cancel button to close the Run As dialog) */
3278 if (vrc2 == VERR_CANCELLED)
3279 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Operation canceled by the user"));
3280 else
3281 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not launch a privileged process '%s' (%Rrc)"), exePath, vrc2);
3282 break;
3283 }
3284 }
3285 else
3286 {
3287 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
3288 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
3289 if (RT_FAILURE(vrc))
3290 {
3291 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
3292 break;
3293 }
3294 }
3295
3296 /* wait for the client to connect */
3297 vrc = client.connect();
3298 if (RT_SUCCESS(vrc))
3299 {
3300 /* start the user supplied function */
3301 hrc = pTask->func(&client, pTask->progress, pTask->user, &vrc);
3302 userFuncCalled = true;
3303 }
3304
3305 /* send the termination signal to the process anyway */
3306 {
3307 int vrc2 = client.write(SVCHlpMsg::Null);
3308 if (RT_SUCCESS(vrc))
3309 vrc = vrc2;
3310 }
3311
3312 if (SUCCEEDED(hrc) && RT_FAILURE(vrc))
3313 {
3314 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not operate the communication channel (%Rrc)"), vrc);
3315 break;
3316 }
3317 }
3318 while (0);
3319
3320 if (FAILED(hrc) && !userFuncCalled)
3321 {
3322 /* call the user function in the "cleanup only" mode
3323 * to let it free resources passed to in aUser */
3324 pTask->func(NULL, NULL, pTask->user, NULL);
3325 }
3326
3327 pTask->progress->i_notifyComplete(hrc);
3328
3329 LogFlowFuncLeave();
3330}
3331
3332#endif /* RT_OS_WINDOWS */
3333
3334/**
3335 * Sends a signal to the client watcher to rescan the set of machines
3336 * that have open sessions.
3337 *
3338 * @note Doesn't lock anything.
3339 */
3340void VirtualBox::i_updateClientWatcher()
3341{
3342 AutoCaller autoCaller(this);
3343 AssertComRCReturnVoid(autoCaller.hrc());
3344
3345 AssertPtrReturnVoid(m->pClientWatcher);
3346 m->pClientWatcher->update();
3347}
3348
3349/**
3350 * Adds the given child process ID to the list of processes to be reaped.
3351 * This call should be followed by #i_updateClientWatcher() to take the effect.
3352 *
3353 * @note Doesn't lock anything.
3354 */
3355void VirtualBox::i_addProcessToReap(RTPROCESS pid)
3356{
3357 AutoCaller autoCaller(this);
3358 AssertComRCReturnVoid(autoCaller.hrc());
3359
3360 AssertPtrReturnVoid(m->pClientWatcher);
3361 m->pClientWatcher->addProcess(pid);
3362}
3363
3364/**
3365 * VD plugin load
3366 */
3367int VirtualBox::i_loadVDPlugin(const char *pszPluginLibrary)
3368{
3369 return m->pSystemProperties->i_loadVDPlugin(pszPluginLibrary);
3370}
3371
3372/**
3373 * VD plugin unload
3374 */
3375int VirtualBox::i_unloadVDPlugin(const char *pszPluginLibrary)
3376{
3377 return m->pSystemProperties->i_unloadVDPlugin(pszPluginLibrary);
3378}
3379
3380/**
3381 * @note Doesn't lock any object.
3382 */
3383void VirtualBox::i_onMediumRegistered(const Guid &aMediumId, const DeviceType_T aDevType, const BOOL aRegistered)
3384{
3385 ComPtr<IEvent> ptrEvent;
3386 HRESULT hrc = ::CreateMediumRegisteredEvent(ptrEvent.asOutParam(), m->pEventSource,
3387 aMediumId.toString(), aDevType, aRegistered);
3388 AssertComRCReturnVoid(hrc);
3389 i_postEvent(new AsyncEvent(this, ptrEvent));
3390}
3391
3392void VirtualBox::i_onMediumConfigChanged(IMedium *aMedium)
3393{
3394 ComPtr<IEvent> ptrEvent;
3395 HRESULT hrc = ::CreateMediumConfigChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aMedium);
3396 AssertComRCReturnVoid(hrc);
3397 i_postEvent(new AsyncEvent(this, ptrEvent));
3398}
3399
3400void VirtualBox::i_onMediumChanged(IMediumAttachment *aMediumAttachment)
3401{
3402 ComPtr<IEvent> ptrEvent;
3403 HRESULT hrc = ::CreateMediumChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aMediumAttachment);
3404 AssertComRCReturnVoid(hrc);
3405 i_postEvent(new AsyncEvent(this, ptrEvent));
3406}
3407
3408/**
3409 * @note Doesn't lock any object.
3410 */
3411void VirtualBox::i_onStorageControllerChanged(const Guid &aMachineId, const com::Utf8Str &aControllerName)
3412{
3413 ComPtr<IEvent> ptrEvent;
3414 HRESULT hrc = ::CreateStorageControllerChangedEvent(ptrEvent.asOutParam(), m->pEventSource,
3415 aMachineId.toString(), aControllerName);
3416 AssertComRCReturnVoid(hrc);
3417 i_postEvent(new AsyncEvent(this, ptrEvent));
3418}
3419
3420void VirtualBox::i_onStorageDeviceChanged(IMediumAttachment *aStorageDevice, const BOOL fRemoved, const BOOL fSilent)
3421{
3422 ComPtr<IEvent> ptrEvent;
3423 HRESULT hrc = ::CreateStorageDeviceChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aStorageDevice, fRemoved, fSilent);
3424 AssertComRCReturnVoid(hrc);
3425 i_postEvent(new AsyncEvent(this, ptrEvent));
3426}
3427
3428/**
3429 * @note Doesn't lock any object.
3430 */
3431void VirtualBox::i_onMachineStateChanged(const Guid &aId, MachineState_T aState)
3432{
3433 ComPtr<IEvent> ptrEvent;
3434 HRESULT hrc = ::CreateMachineStateChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aState);
3435 AssertComRCReturnVoid(hrc);
3436 i_postEvent(new AsyncEvent(this, ptrEvent));
3437}
3438
3439/**
3440 * @note Doesn't lock any object.
3441 */
3442void VirtualBox::i_onMachineDataChanged(const Guid &aId, BOOL aTemporary)
3443{
3444 ComPtr<IEvent> ptrEvent;
3445 HRESULT hrc = ::CreateMachineDataChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aTemporary);
3446 AssertComRCReturnVoid(hrc);
3447 i_postEvent(new AsyncEvent(this, ptrEvent));
3448}
3449
3450/**
3451 * @note Doesn't lock any object.
3452 */
3453void VirtualBox::i_onMachineGroupsChanged(const Guid &aId)
3454{
3455 ComPtr<IEvent> ptrEvent;
3456 HRESULT hrc = ::CreateMachineGroupsChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), FALSE /*aDummy*/);
3457 AssertComRCReturnVoid(hrc);
3458 i_postEvent(new AsyncEvent(this, ptrEvent));
3459}
3460
3461/**
3462 * @note Locks this object for reading.
3463 */
3464BOOL VirtualBox::i_onExtraDataCanChange(const Guid &aId, const Utf8Str &aKey, const Utf8Str &aValue, Bstr &aError)
3465{
3466 LogFlowThisFunc(("machine={%RTuuid} aKey={%s} aValue={%s}\n", aId.raw(), aKey.c_str(), aValue.c_str()));
3467
3468 AutoCaller autoCaller(this);
3469 AssertComRCReturn(autoCaller.hrc(), FALSE);
3470
3471 ComPtr<IEvent> ptrEvent;
3472 HRESULT hrc = ::CreateExtraDataCanChangeEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aKey, aValue);
3473 AssertComRCReturn(hrc, TRUE);
3474
3475 VBoxEventDesc EvtDesc(ptrEvent, m->pEventSource);
3476 BOOL fDelivered = EvtDesc.fire(3000); /* Wait up to 3 secs for delivery */
3477 //Assert(fDelivered);
3478 BOOL fAllowChange = TRUE;
3479 if (fDelivered)
3480 {
3481 ComPtr<IExtraDataCanChangeEvent> ptrCanChangeEvent = ptrEvent;
3482 Assert(ptrCanChangeEvent);
3483
3484 BOOL fVetoed = FALSE;
3485 ptrCanChangeEvent->IsVetoed(&fVetoed);
3486 fAllowChange = !fVetoed;
3487
3488 if (!fAllowChange)
3489 {
3490 SafeArray<BSTR> aVetos;
3491 ptrCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos));
3492 if (aVetos.size() > 0)
3493 aError = aVetos[0];
3494 }
3495 }
3496
3497 LogFlowThisFunc(("fAllowChange=%RTbool\n", fAllowChange));
3498 return fAllowChange;
3499}
3500
3501/**
3502 * @note Doesn't lock any object.
3503 */
3504void VirtualBox::i_onExtraDataChanged(const Guid &aId, const Utf8Str &aKey, const Utf8Str &aValue)
3505{
3506 ComPtr<IEvent> ptrEvent;
3507 HRESULT hrc = ::CreateExtraDataChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aKey, aValue);
3508 AssertComRCReturnVoid(hrc);
3509 i_postEvent(new AsyncEvent(this, ptrEvent));
3510}
3511
3512/**
3513 * @note Doesn't lock any object.
3514 */
3515void VirtualBox::i_onMachineRegistered(const Guid &aId, BOOL aRegistered)
3516{
3517 ComPtr<IEvent> ptrEvent;
3518 HRESULT hrc = ::CreateMachineRegisteredEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aRegistered);
3519 AssertComRCReturnVoid(hrc);
3520 i_postEvent(new AsyncEvent(this, ptrEvent));
3521}
3522
3523/**
3524 * @note Doesn't lock any object.
3525 */
3526void VirtualBox::i_onSessionStateChanged(const Guid &aId, SessionState_T aState)
3527{
3528 ComPtr<IEvent> ptrEvent;
3529 HRESULT hrc = ::CreateSessionStateChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aState);
3530 AssertComRCReturnVoid(hrc);
3531 i_postEvent(new AsyncEvent(this, ptrEvent));
3532}
3533
3534/**
3535 * @note Doesn't lock any object.
3536 */
3537void VirtualBox::i_onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
3538{
3539 ComPtr<IEvent> ptrEvent;
3540 HRESULT hrc = ::CreateSnapshotTakenEvent(ptrEvent.asOutParam(), m->pEventSource,
3541 aMachineId.toString(), aSnapshotId.toString());
3542 AssertComRCReturnVoid(hrc);
3543 i_postEvent(new AsyncEvent(this, ptrEvent));
3544}
3545
3546/**
3547 * @note Doesn't lock any object.
3548 */
3549void VirtualBox::i_onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
3550{
3551 ComPtr<IEvent> ptrEvent;
3552 HRESULT hrc = ::CreateSnapshotDeletedEvent(ptrEvent.asOutParam(), m->pEventSource,
3553 aMachineId.toString(), aSnapshotId.toString());
3554 AssertComRCReturnVoid(hrc);
3555 i_postEvent(new AsyncEvent(this, ptrEvent));
3556}
3557
3558/**
3559 * @note Doesn't lock any object.
3560 */
3561void VirtualBox::i_onSnapshotRestored(const Guid &aMachineId, const Guid &aSnapshotId)
3562{
3563 ComPtr<IEvent> ptrEvent;
3564 HRESULT hrc = ::CreateSnapshotRestoredEvent(ptrEvent.asOutParam(), m->pEventSource,
3565 aMachineId.toString(), aSnapshotId.toString());
3566 AssertComRCReturnVoid(hrc);
3567 i_postEvent(new AsyncEvent(this, ptrEvent));
3568}
3569
3570/**
3571 * @note Doesn't lock any object.
3572 */
3573void VirtualBox::i_onSnapshotChanged(const Guid &aMachineId, const Guid &aSnapshotId)
3574{
3575 ComPtr<IEvent> ptrEvent;
3576 HRESULT hrc = ::CreateSnapshotChangedEvent(ptrEvent.asOutParam(), m->pEventSource,
3577 aMachineId.toString(), aSnapshotId.toString());
3578 AssertComRCReturnVoid(hrc);
3579 i_postEvent(new AsyncEvent(this, ptrEvent));
3580}
3581
3582/**
3583 * @note Doesn't lock any object.
3584 */
3585void VirtualBox::i_onGuestPropertyChanged(const Guid &aMachineId, const Utf8Str &aName, const Utf8Str &aValue,
3586 const Utf8Str &aFlags, const BOOL fWasDeleted)
3587{
3588 ComPtr<IEvent> ptrEvent;
3589 HRESULT hrc = ::CreateGuestPropertyChangedEvent(ptrEvent.asOutParam(), m->pEventSource,
3590 aMachineId.toString(), aName, aValue, aFlags, fWasDeleted);
3591 AssertComRCReturnVoid(hrc);
3592 i_postEvent(new AsyncEvent(this, ptrEvent));
3593}
3594
3595/**
3596 * @note Doesn't lock any object.
3597 */
3598void VirtualBox::i_onNatRedirectChanged(const Guid &aMachineId, ULONG ulSlot, bool fRemove, const Utf8Str &aName,
3599 NATProtocol_T aProto, const Utf8Str &aHostIp, uint16_t aHostPort,
3600 const Utf8Str &aGuestIp, uint16_t aGuestPort)
3601{
3602 ::FireNATRedirectEvent(m->pEventSource, aMachineId.toString(), ulSlot, fRemove, aName, aProto, aHostIp,
3603 aHostPort, aGuestIp, aGuestPort);
3604}
3605
3606/** @todo Unused!! */
3607void VirtualBox::i_onNATNetworkChanged(const Utf8Str &aName)
3608{
3609 ::FireNATNetworkChangedEvent(m->pEventSource, aName);
3610}
3611
3612void VirtualBox::i_onNATNetworkStartStop(const Utf8Str &aName, BOOL fStart)
3613{
3614 ::FireNATNetworkStartStopEvent(m->pEventSource, aName, fStart);
3615}
3616
3617void VirtualBox::i_onNATNetworkSetting(const Utf8Str &aNetworkName, BOOL aEnabled,
3618 const Utf8Str &aNetwork, const Utf8Str &aGateway,
3619 BOOL aAdvertiseDefaultIpv6RouteEnabled,
3620 BOOL fNeedDhcpServer)
3621{
3622 ::FireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled, aNetwork, aGateway,
3623 aAdvertiseDefaultIpv6RouteEnabled, fNeedDhcpServer);
3624}
3625
3626void VirtualBox::i_onNATNetworkPortForward(const Utf8Str &aNetworkName, BOOL create, BOOL fIpv6,
3627 const Utf8Str &aRuleName, NATProtocol_T proto,
3628 const Utf8Str &aHostIp, LONG aHostPort,
3629 const Utf8Str &aGuestIp, LONG aGuestPort)
3630{
3631 ::FireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create, fIpv6, aRuleName, proto,
3632 aHostIp, aHostPort, aGuestIp, aGuestPort);
3633}
3634
3635
3636void VirtualBox::i_onHostNameResolutionConfigurationChange()
3637{
3638 if (m->pEventSource)
3639 ::FireHostNameResolutionConfigurationChangeEvent(m->pEventSource);
3640}
3641
3642
3643int VirtualBox::i_natNetworkRefInc(const Utf8Str &aNetworkName)
3644{
3645 AutoWriteLock safeLock(*spMtxNatNetworkNameToRefCountLock COMMA_LOCKVAL_SRC_POS);
3646
3647 if (!sNatNetworkNameToRefCount[aNetworkName])
3648 {
3649 ComPtr<INATNetwork> nat;
3650 HRESULT hrc = findNATNetworkByName(aNetworkName, nat);
3651 if (FAILED(hrc)) return -1;
3652
3653 hrc = nat->Start();
3654 if (SUCCEEDED(hrc))
3655 LogRel(("Started NAT network '%s'\n", aNetworkName.c_str()));
3656 else
3657 LogRel(("Error %Rhrc starting NAT network '%s'\n", hrc, aNetworkName.c_str()));
3658 AssertComRCReturn(hrc, -1);
3659 }
3660
3661 sNatNetworkNameToRefCount[aNetworkName]++;
3662
3663 return sNatNetworkNameToRefCount[aNetworkName];
3664}
3665
3666
3667int VirtualBox::i_natNetworkRefDec(const Utf8Str &aNetworkName)
3668{
3669 AutoWriteLock safeLock(*spMtxNatNetworkNameToRefCountLock COMMA_LOCKVAL_SRC_POS);
3670
3671 if (!sNatNetworkNameToRefCount[aNetworkName])
3672 return 0;
3673
3674 sNatNetworkNameToRefCount[aNetworkName]--;
3675
3676 if (!sNatNetworkNameToRefCount[aNetworkName])
3677 {
3678 ComPtr<INATNetwork> nat;
3679 HRESULT hrc = findNATNetworkByName(aNetworkName, nat);
3680 if (FAILED(hrc)) return -1;
3681
3682 hrc = nat->Stop();
3683 if (SUCCEEDED(hrc))
3684 LogRel(("Stopped NAT network '%s'\n", aNetworkName.c_str()));
3685 else
3686 LogRel(("Error %Rhrc stopping NAT network '%s'\n", hrc, aNetworkName.c_str()));
3687 AssertComRCReturn(hrc, -1);
3688 }
3689
3690 return sNatNetworkNameToRefCount[aNetworkName];
3691}
3692
3693
3694/*
3695 * Export this to NATNetwork so that its setters can refuse to change
3696 * essential network settings when an VBoxNatNet instance is running.
3697 */
3698RWLockHandle *VirtualBox::i_getNatNetLock() const
3699{
3700 return spMtxNatNetworkNameToRefCountLock;
3701}
3702
3703
3704/*
3705 * Export this to NATNetwork so that its setters can refuse to change
3706 * essential network settings when an VBoxNatNet instance is running.
3707 * The caller is expected to hold a read lock on i_getNatNetLock().
3708 */
3709bool VirtualBox::i_isNatNetStarted(const Utf8Str &aNetworkName) const
3710{
3711 return sNatNetworkNameToRefCount[aNetworkName] > 0;
3712}
3713
3714
3715void VirtualBox::i_onCloudProviderListChanged(BOOL aRegistered)
3716{
3717 ::FireCloudProviderListChangedEvent(m->pEventSource, aRegistered);
3718}
3719
3720
3721void VirtualBox::i_onCloudProviderRegistered(const Utf8Str &aProviderId, BOOL aRegistered)
3722{
3723 ::FireCloudProviderRegisteredEvent(m->pEventSource, aProviderId, aRegistered);
3724}
3725
3726
3727void VirtualBox::i_onCloudProviderUninstall(const Utf8Str &aProviderId)
3728{
3729 HRESULT hrc;
3730
3731 ComPtr<IEvent> pEvent;
3732 hrc = CreateCloudProviderUninstallEvent(pEvent.asOutParam(),
3733 m->pEventSource, aProviderId);
3734 if (FAILED(hrc))
3735 return;
3736
3737 BOOL fDelivered = FALSE;
3738 hrc = m->pEventSource->FireEvent(pEvent, /* :timeout */ 10000, &fDelivered);
3739 if (FAILED(hrc))
3740 return;
3741}
3742
3743void VirtualBox::i_onLanguageChanged(const Utf8Str &aLanguageId)
3744{
3745 ComPtr<IEvent> ptrEvent;
3746 HRESULT hrc = ::CreateLanguageChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aLanguageId);
3747 AssertComRCReturnVoid(hrc);
3748 i_postEvent(new AsyncEvent(this, ptrEvent));
3749}
3750
3751void VirtualBox::i_onProgressCreated(const Guid &aId, BOOL aCreated)
3752{
3753 ::FireProgressCreatedEvent(m->pEventSource, aId.toString(), aCreated);
3754}
3755
3756#ifdef VBOX_WITH_UPDATE_AGENT
3757/**
3758 * @note Doesn't lock any object.
3759 */
3760void VirtualBox::i_onUpdateAgentAvailable(IUpdateAgent *aAgent,
3761 const Utf8Str &aVer, UpdateChannel_T aChannel, UpdateSeverity_T aSev,
3762 const Utf8Str &aDownloadURL, const Utf8Str &aWebURL, const Utf8Str &aReleaseNotes)
3763{
3764 ::FireUpdateAgentAvailableEvent(m->pEventSource, aAgent, aVer, aChannel, aSev,
3765 aDownloadURL, aWebURL, aReleaseNotes);
3766}
3767
3768/**
3769 * @note Doesn't lock any object.
3770 */
3771void VirtualBox::i_onUpdateAgentError(IUpdateAgent *aAgent, const Utf8Str &aErrMsg, LONG aRc)
3772{
3773 ::FireUpdateAgentErrorEvent(m->pEventSource, aAgent, aErrMsg, aRc);
3774}
3775
3776/**
3777 * @note Doesn't lock any object.
3778 */
3779void VirtualBox::i_onUpdateAgentStateChanged(IUpdateAgent *aAgent, UpdateState_T aState)
3780{
3781 ::FireUpdateAgentStateChangedEvent(m->pEventSource, aAgent, aState);
3782}
3783
3784/**
3785 * @note Doesn't lock any object.
3786 */
3787void VirtualBox::i_onUpdateAgentSettingsChanged(IUpdateAgent *aAgent, const Utf8Str &aAttributeHint)
3788{
3789 ::FireUpdateAgentSettingsChangedEvent(m->pEventSource, aAgent, aAttributeHint);
3790}
3791#endif /* VBOX_WITH_UPDATE_AGENT */
3792
3793/**
3794 * @note Locks the list of other objects for reading.
3795 */
3796ComObjPtr<GuestOSType> VirtualBox::i_getUnknownOSType()
3797{
3798 ComObjPtr<GuestOSType> type;
3799
3800 /* unknown type must always be the first */
3801 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
3802
3803 return m->allGuestOSTypes.front();
3804}
3805
3806/**
3807 * Returns the list of opened machines (machines having VM sessions opened,
3808 * ignoring other sessions) and optionally the list of direct session controls.
3809 *
3810 * @param aMachines Where to put opened machines (will be empty if none).
3811 * @param aControls Where to put direct session controls (optional).
3812 *
3813 * @note The returned lists contain smart pointers. So, clear it as soon as
3814 * it becomes no more necessary to release instances.
3815 *
3816 * @note It can be possible that a session machine from the list has been
3817 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
3818 * when accessing unprotected data directly.
3819 *
3820 * @note Locks objects for reading.
3821 */
3822void VirtualBox::i_getOpenedMachines(SessionMachinesList &aMachines,
3823 InternalControlList *aControls /*= NULL*/)
3824{
3825 AutoCaller autoCaller(this);
3826 AssertComRCReturnVoid(autoCaller.hrc());
3827
3828 aMachines.clear();
3829 if (aControls)
3830 aControls->clear();
3831
3832 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3833
3834 for (MachinesOList::iterator it = m->allMachines.begin();
3835 it != m->allMachines.end();
3836 ++it)
3837 {
3838 ComObjPtr<SessionMachine> sm;
3839 ComPtr<IInternalSessionControl> ctl;
3840 if ((*it)->i_isSessionOpenVM(sm, &ctl))
3841 {
3842 aMachines.push_back(sm);
3843 if (aControls)
3844 aControls->push_back(ctl);
3845 }
3846 }
3847}
3848
3849/**
3850 * Gets a reference to the machine list. This is the real thing, not a copy,
3851 * so bad things will happen if the caller doesn't hold the necessary lock.
3852 *
3853 * @returns reference to machine list
3854 *
3855 * @note Caller must hold the VirtualBox object lock at least for reading.
3856 */
3857VirtualBox::MachinesOList &VirtualBox::i_getMachinesList(void)
3858{
3859 return m->allMachines;
3860}
3861
3862/**
3863 * Searches for a machine object with the given ID in the collection
3864 * of registered machines.
3865 *
3866 * @param aId Machine UUID to look for.
3867 * @param fPermitInaccessible If true, inaccessible machines will be found;
3868 * if false, this will fail if the given machine is inaccessible.
3869 * @param aSetError If true, set errorinfo if the machine is not found.
3870 * @param aMachine Returned machine, if found.
3871 * @return
3872 */
3873HRESULT VirtualBox::i_findMachine(const Guid &aId,
3874 bool fPermitInaccessible,
3875 bool aSetError,
3876 ComObjPtr<Machine> *aMachine /* = NULL */)
3877{
3878 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
3879
3880 AutoCaller autoCaller(this);
3881 AssertComRCReturnRC(autoCaller.hrc());
3882
3883 {
3884 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3885
3886 for (MachinesOList::iterator it = m->allMachines.begin();
3887 it != m->allMachines.end();
3888 ++it)
3889 {
3890 ComObjPtr<Machine> pMachine = *it;
3891
3892 if (!fPermitInaccessible)
3893 {
3894 // skip inaccessible machines
3895 AutoCaller machCaller(pMachine);
3896 if (FAILED(machCaller.hrc()))
3897 continue;
3898 }
3899
3900 if (pMachine->i_getId() == aId)
3901 {
3902 hrc = S_OK;
3903 if (aMachine)
3904 *aMachine = pMachine;
3905 break;
3906 }
3907 }
3908 }
3909
3910 if (aSetError && FAILED(hrc))
3911 hrc = setError(hrc, tr("Could not find a registered machine with UUID {%RTuuid}"), aId.raw());
3912
3913 return hrc;
3914}
3915
3916/**
3917 * Searches for a machine object with the given name or location in the
3918 * collection of registered machines.
3919 *
3920 * @param aName Machine name or location to look for.
3921 * @param aSetError If true, set errorinfo if the machine is not found.
3922 * @param aMachine Returned machine, if found.
3923 * @return
3924 */
3925HRESULT VirtualBox::i_findMachineByName(const Utf8Str &aName,
3926 bool aSetError,
3927 ComObjPtr<Machine> *aMachine /* = NULL */)
3928{
3929 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
3930
3931 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3932 for (MachinesOList::iterator it = m->allMachines.begin();
3933 it != m->allMachines.end();
3934 ++it)
3935 {
3936 ComObjPtr<Machine> &pMachine = *it;
3937 AutoCaller machCaller(pMachine);
3938 if (!machCaller.isOk())
3939 continue; // we can't ask inaccessible machines for their names
3940
3941 AutoReadLock machLock(pMachine COMMA_LOCKVAL_SRC_POS);
3942 if (pMachine->i_getName() == aName)
3943 {
3944 hrc = S_OK;
3945 if (aMachine)
3946 *aMachine = pMachine;
3947 break;
3948 }
3949 if (!RTPathCompare(pMachine->i_getSettingsFileFull().c_str(), aName.c_str()))
3950 {
3951 hrc = S_OK;
3952 if (aMachine)
3953 *aMachine = pMachine;
3954 break;
3955 }
3956 }
3957
3958 if (aSetError && FAILED(hrc))
3959 hrc = setError(hrc, tr("Could not find a registered machine named '%s'"), aName.c_str());
3960
3961 return hrc;
3962}
3963
3964static HRESULT i_validateMachineGroupHelper(const Utf8Str &aGroup, bool fPrimary, VirtualBox *pVirtualBox)
3965{
3966 /* empty strings are invalid */
3967 if (aGroup.isEmpty())
3968 return E_INVALIDARG;
3969 /* the toplevel group is valid */
3970 if (aGroup == "/")
3971 return S_OK;
3972 /* any other strings of length 1 are invalid */
3973 if (aGroup.length() == 1)
3974 return E_INVALIDARG;
3975 /* must start with a slash */
3976 if (aGroup.c_str()[0] != '/')
3977 return E_INVALIDARG;
3978 /* must not end with a slash */
3979 if (aGroup.c_str()[aGroup.length() - 1] == '/')
3980 return E_INVALIDARG;
3981 /* check the group components */
3982 const char *pStr = aGroup.c_str() + 1; /* first char is /, skip it */
3983 while (pStr)
3984 {
3985 char *pSlash = RTStrStr(pStr, "/");
3986 if (pSlash)
3987 {
3988 /* no empty components (or // sequences in other words) */
3989 if (pSlash == pStr)
3990 return E_INVALIDARG;
3991 /* check if the machine name rules are violated, because that means
3992 * the group components are too close to the limits. */
3993 Utf8Str tmp((const char *)pStr, (size_t)(pSlash - pStr));
3994 Utf8Str tmp2(tmp);
3995 sanitiseMachineFilename(tmp);
3996 if (tmp != tmp2)
3997 return E_INVALIDARG;
3998 if (fPrimary)
3999 {
4000 HRESULT hrc = pVirtualBox->i_findMachineByName(tmp, false /* aSetError */);
4001 if (SUCCEEDED(hrc))
4002 return VBOX_E_VM_ERROR;
4003 }
4004 pStr = pSlash + 1;
4005 }
4006 else
4007 {
4008 /* check if the machine name rules are violated, because that means
4009 * the group components is too close to the limits. */
4010 Utf8Str tmp(pStr);
4011 Utf8Str tmp2(tmp);
4012 sanitiseMachineFilename(tmp);
4013 if (tmp != tmp2)
4014 return E_INVALIDARG;
4015 pStr = NULL;
4016 }
4017 }
4018 return S_OK;
4019}
4020
4021/**
4022 * Validates a machine group.
4023 *
4024 * @param aGroup Machine group.
4025 * @param fPrimary Set if this is the primary group.
4026 *
4027 * @return S_OK or E_INVALIDARG
4028 */
4029HRESULT VirtualBox::i_validateMachineGroup(const Utf8Str &aGroup, bool fPrimary)
4030{
4031 HRESULT hrc = i_validateMachineGroupHelper(aGroup, fPrimary, this);
4032 if (FAILED(hrc))
4033 {
4034 if (hrc == VBOX_E_VM_ERROR)
4035 hrc = setError(E_INVALIDARG, tr("Machine group '%s' conflicts with a virtual machine name"), aGroup.c_str());
4036 else
4037 hrc = setError(hrc, tr("Invalid machine group '%s'"), aGroup.c_str());
4038 }
4039 return hrc;
4040}
4041
4042/**
4043 * Takes a list of machine groups, and sanitizes/validates it.
4044 *
4045 * @param aMachineGroups Array with the machine groups.
4046 * @param pllMachineGroups Pointer to list of strings for the result.
4047 *
4048 * @return S_OK or E_INVALIDARG
4049 */
4050HRESULT VirtualBox::i_convertMachineGroups(const std::vector<com::Utf8Str> aMachineGroups, StringsList *pllMachineGroups)
4051{
4052 pllMachineGroups->clear();
4053 if (aMachineGroups.size())
4054 {
4055 for (size_t i = 0; i < aMachineGroups.size(); i++)
4056 {
4057 Utf8Str group(aMachineGroups[i]);
4058 if (group.length() == 0)
4059 group = "/";
4060
4061 HRESULT hrc = i_validateMachineGroup(group, i == 0);
4062 if (FAILED(hrc))
4063 return hrc;
4064
4065 /* no duplicates please */
4066 if ( find(pllMachineGroups->begin(), pllMachineGroups->end(), group)
4067 == pllMachineGroups->end())
4068 pllMachineGroups->push_back(group);
4069 }
4070 if (pllMachineGroups->size() == 0)
4071 pllMachineGroups->push_back("/");
4072 }
4073 else
4074 pllMachineGroups->push_back("/");
4075
4076 return S_OK;
4077}
4078
4079/**
4080 * Searches for a Medium object with the given ID in the list of registered
4081 * hard disks.
4082 *
4083 * @param aId ID of the hard disk. Must not be empty.
4084 * @param aSetError If @c true , the appropriate error info is set in case
4085 * when the hard disk is not found.
4086 * @param aHardDisk Where to store the found hard disk object (can be NULL).
4087 *
4088 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
4089 *
4090 * @note Locks the media tree for reading.
4091 */
4092HRESULT VirtualBox::i_findHardDiskById(const Guid &aId,
4093 bool aSetError,
4094 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
4095{
4096 AssertReturn(!aId.isZero(), E_INVALIDARG);
4097
4098 // we use the hard disks map, but it is protected by the
4099 // hard disk _list_ lock handle
4100 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4101
4102 HardDiskMap::const_iterator it = m->mapHardDisks.find(aId);
4103 if (it != m->mapHardDisks.end())
4104 {
4105 if (aHardDisk)
4106 *aHardDisk = (*it).second;
4107 return S_OK;
4108 }
4109
4110 if (aSetError)
4111 return setError(VBOX_E_OBJECT_NOT_FOUND,
4112 tr("Could not find an open hard disk with UUID {%RTuuid}"),
4113 aId.raw());
4114
4115 return VBOX_E_OBJECT_NOT_FOUND;
4116}
4117
4118/**
4119 * Searches for a Medium object with the given ID or location in the list of
4120 * registered hard disks. If both ID and location are specified, the first
4121 * object that matches either of them (not necessarily both) is returned.
4122 *
4123 * @param strLocation Full location specification. Must not be empty.
4124 * @param aSetError If @c true , the appropriate error info is set in case
4125 * when the hard disk is not found.
4126 * @param aHardDisk Where to store the found hard disk object (can be NULL).
4127 *
4128 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
4129 *
4130 * @note Locks the media tree for reading.
4131 */
4132HRESULT VirtualBox::i_findHardDiskByLocation(const Utf8Str &strLocation,
4133 bool aSetError,
4134 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
4135{
4136 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
4137
4138 // we use the hard disks map, but it is protected by the
4139 // hard disk _list_ lock handle
4140 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4141
4142 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
4143 it != m->mapHardDisks.end();
4144 ++it)
4145 {
4146 const ComObjPtr<Medium> &pHD = (*it).second;
4147
4148 AutoCaller autoCaller(pHD);
4149 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
4150 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
4151
4152 Utf8Str strLocationFull = pHD->i_getLocationFull();
4153
4154 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
4155 {
4156 if (aHardDisk)
4157 *aHardDisk = pHD;
4158 return S_OK;
4159 }
4160 }
4161
4162 if (aSetError)
4163 return setError(VBOX_E_OBJECT_NOT_FOUND,
4164 tr("Could not find an open hard disk with location '%s'"),
4165 strLocation.c_str());
4166
4167 return VBOX_E_OBJECT_NOT_FOUND;
4168}
4169
4170/**
4171 * Searches for a Medium object with the given ID or location in the list of
4172 * registered DVD or floppy images, depending on the @a mediumType argument.
4173 * If both ID and file path are specified, the first object that matches either
4174 * of them (not necessarily both) is returned.
4175 *
4176 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
4177 * @param aId ID of the image file (unused when NULL).
4178 * @param aLocation Full path to the image file (unused when NULL).
4179 * @param aSetError If @c true, the appropriate error info is set in case when
4180 * the image is not found.
4181 * @param aImage Where to store the found image object (can be NULL).
4182 *
4183 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
4184 *
4185 * @note Locks the media tree for reading.
4186 */
4187HRESULT VirtualBox::i_findDVDOrFloppyImage(DeviceType_T mediumType,
4188 const Guid *aId,
4189 const Utf8Str &aLocation,
4190 bool aSetError,
4191 ComObjPtr<Medium> *aImage /* = NULL */)
4192{
4193 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
4194
4195 Utf8Str location;
4196 if (!aLocation.isEmpty())
4197 {
4198 int vrc = i_calculateFullPath(aLocation, location);
4199 if (RT_FAILURE(vrc))
4200 return setError(VBOX_E_FILE_ERROR,
4201 tr("Invalid image file location '%s' (%Rrc)"),
4202 aLocation.c_str(),
4203 vrc);
4204 }
4205
4206 MediaOList *pMediaList;
4207
4208 switch (mediumType)
4209 {
4210 case DeviceType_DVD:
4211 pMediaList = &m->allDVDImages;
4212 break;
4213
4214 case DeviceType_Floppy:
4215 pMediaList = &m->allFloppyImages;
4216 break;
4217
4218 default:
4219 return E_INVALIDARG;
4220 }
4221
4222 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
4223
4224 bool found = false;
4225
4226 for (MediaList::const_iterator it = pMediaList->begin();
4227 it != pMediaList->end();
4228 ++it)
4229 {
4230 // no AutoCaller, registered image life time is bound to this
4231 Medium *pMedium = *it;
4232 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
4233 const Utf8Str &strLocationFull = pMedium->i_getLocationFull();
4234
4235 found = ( aId
4236 && pMedium->i_getId() == *aId)
4237 || ( !aLocation.isEmpty()
4238 && RTPathCompare(location.c_str(),
4239 strLocationFull.c_str()) == 0);
4240 if (found)
4241 {
4242 if (pMedium->i_getDeviceType() != mediumType)
4243 {
4244 if (mediumType == DeviceType_DVD)
4245 return setError(E_INVALIDARG,
4246 tr("Cannot mount DVD medium '%s' as floppy"), strLocationFull.c_str());
4247 else
4248 return setError(E_INVALIDARG,
4249 tr("Cannot mount floppy medium '%s' as DVD"), strLocationFull.c_str());
4250 }
4251
4252 if (aImage)
4253 *aImage = pMedium;
4254 break;
4255 }
4256 }
4257
4258 HRESULT hrc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
4259
4260 if (aSetError && !found)
4261 {
4262 if (aId)
4263 setError(hrc,
4264 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
4265 aId->raw(),
4266 m->strSettingsFilePath.c_str());
4267 else
4268 setError(hrc,
4269 tr("Could not find an image file with location '%s' in the media registry ('%s')"),
4270 aLocation.c_str(),
4271 m->strSettingsFilePath.c_str());
4272 }
4273
4274 return hrc;
4275}
4276
4277/**
4278 * Searches for an IMedium object that represents the given UUID.
4279 *
4280 * If the UUID is empty (indicating an empty drive), this sets pMedium
4281 * to NULL and returns S_OK.
4282 *
4283 * If the UUID refers to a host drive of the given device type, this
4284 * sets pMedium to the object from the list in IHost and returns S_OK.
4285 *
4286 * If the UUID is an image file, this sets pMedium to the object that
4287 * findDVDOrFloppyImage() returned.
4288 *
4289 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
4290 *
4291 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
4292 * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
4293 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
4294 * @param aSetError
4295 * @param pMedium out: IMedium object found.
4296 * @return
4297 */
4298HRESULT VirtualBox::i_findRemoveableMedium(DeviceType_T mediumType,
4299 const Guid &uuid,
4300 bool fRefresh,
4301 bool aSetError,
4302 ComObjPtr<Medium> &pMedium)
4303{
4304 if (uuid.isZero())
4305 {
4306 // that's easy
4307 pMedium.setNull();
4308 return S_OK;
4309 }
4310 else if (!uuid.isValid())
4311 {
4312 /* handling of case invalid GUID */
4313 return setError(VBOX_E_OBJECT_NOT_FOUND,
4314 tr("Guid '%s' is invalid"),
4315 uuid.toString().c_str());
4316 }
4317
4318 // first search for host drive with that UUID
4319 HRESULT hrc = m->pHost->i_findHostDriveById(mediumType, uuid, fRefresh, pMedium);
4320 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
4321 // then search for an image with that UUID
4322 hrc = i_findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, aSetError, &pMedium);
4323
4324 return hrc;
4325}
4326
4327/* Look for a GuestOSType object */
4328HRESULT VirtualBox::i_findGuestOSType(const Utf8Str &strOSType,
4329 ComObjPtr<GuestOSType> &guestOSType)
4330{
4331 guestOSType.setNull();
4332
4333 AssertMsg(m->allGuestOSTypes.size() != 0,
4334 ("Guest OS types array must be filled"));
4335
4336 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4337 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
4338 it != m->allGuestOSTypes.end();
4339 ++it)
4340 {
4341 const Utf8Str &typeId = (*it)->i_id();
4342 AssertMsg(!typeId.isEmpty(), ("ID must not be NULL"));
4343 if (strOSType.compare(typeId, Utf8Str::CaseInsensitive) == 0)
4344 {
4345 guestOSType = *it;
4346 return S_OK;
4347 }
4348 }
4349
4350 return setError(VBOX_E_OBJECT_NOT_FOUND,
4351 tr("'%s' is not a valid Guest OS type"),
4352 strOSType.c_str());
4353}
4354
4355/**
4356 * Returns the constant pseudo-machine UUID that is used to identify the
4357 * global media registry.
4358 *
4359 * Starting with VirtualBox 4.0 each medium remembers in its instance data
4360 * in which media registry it is saved (if any): this can either be a machine
4361 * UUID, if it's in a per-machine media registry, or this global ID.
4362 *
4363 * This UUID is only used to identify the VirtualBox object while VirtualBox
4364 * is running. It is a compile-time constant and not saved anywhere.
4365 *
4366 * @return
4367 */
4368const Guid& VirtualBox::i_getGlobalRegistryId() const
4369{
4370 return m->uuidMediaRegistry;
4371}
4372
4373const ComObjPtr<Host>& VirtualBox::i_host() const
4374{
4375 return m->pHost;
4376}
4377
4378SystemProperties* VirtualBox::i_getSystemProperties() const
4379{
4380 return m->pSystemProperties;
4381}
4382
4383CloudProviderManager *VirtualBox::i_getCloudProviderManager() const
4384{
4385 return m->pCloudProviderManager;
4386}
4387
4388#ifdef VBOX_WITH_EXTPACK
4389/**
4390 * Getter that SystemProperties and others can use to talk to the extension
4391 * pack manager.
4392 */
4393ExtPackManager* VirtualBox::i_getExtPackManager() const
4394{
4395 return m->ptrExtPackManager;
4396}
4397#endif
4398
4399/**
4400 * Getter that machines can talk to the autostart database.
4401 */
4402AutostartDb* VirtualBox::i_getAutostartDb() const
4403{
4404 return m->pAutostartDb;
4405}
4406
4407#ifdef VBOX_WITH_RESOURCE_USAGE_API
4408const ComObjPtr<PerformanceCollector>& VirtualBox::i_performanceCollector() const
4409{
4410 return m->pPerformanceCollector;
4411}
4412#endif /* VBOX_WITH_RESOURCE_USAGE_API */
4413
4414/**
4415 * Returns the default machine folder from the system properties
4416 * with proper locking.
4417 * @return
4418 */
4419void VirtualBox::i_getDefaultMachineFolder(Utf8Str &str) const
4420{
4421 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
4422 str = m->pSystemProperties->m->strDefaultMachineFolder;
4423}
4424
4425/**
4426 * Returns the default hard disk format from the system properties
4427 * with proper locking.
4428 * @return
4429 */
4430void VirtualBox::i_getDefaultHardDiskFormat(Utf8Str &str) const
4431{
4432 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
4433 str = m->pSystemProperties->m->strDefaultHardDiskFormat;
4434}
4435
4436const Utf8Str& VirtualBox::i_homeDir() const
4437{
4438 return m->strHomeDir;
4439}
4440
4441/**
4442 * Calculates the absolute path of the given path taking the VirtualBox home
4443 * directory as the current directory.
4444 *
4445 * @param strPath Path to calculate the absolute path for.
4446 * @param aResult Where to put the result (used only on success, can be the
4447 * same Utf8Str instance as passed in @a aPath).
4448 * @return IPRT result.
4449 *
4450 * @note Doesn't lock any object.
4451 */
4452int VirtualBox::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
4453{
4454 AutoCaller autoCaller(this);
4455 AssertComRCReturn(autoCaller.hrc(), VERR_GENERAL_FAILURE);
4456
4457 /* no need to lock since strHomeDir is const */
4458
4459 char szFolder[RTPATH_MAX];
4460 size_t cbFolder = sizeof(szFolder);
4461 int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
4462 strPath.c_str(),
4463 RTPATH_STR_F_STYLE_HOST,
4464 szFolder,
4465 &cbFolder);
4466 if (RT_SUCCESS(vrc))
4467 aResult = szFolder;
4468
4469 return vrc;
4470}
4471
4472/**
4473 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
4474 * if it is a subdirectory thereof, or simply copying it otherwise.
4475 *
4476 * @param strSource Path to evalue and copy.
4477 * @param strTarget Buffer to receive target path.
4478 */
4479void VirtualBox::i_copyPathRelativeToConfig(const Utf8Str &strSource,
4480 Utf8Str &strTarget)
4481{
4482 AutoCaller autoCaller(this);
4483 AssertComRCReturnVoid(autoCaller.hrc());
4484
4485 // no need to lock since mHomeDir is const
4486
4487 // use strTarget as a temporary buffer to hold the machine settings dir
4488 strTarget = m->strHomeDir;
4489 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
4490 // is relative: then append what's left
4491 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
4492 else
4493 // is not relative: then overwrite
4494 strTarget = strSource;
4495}
4496
4497// private methods
4498/////////////////////////////////////////////////////////////////////////////
4499
4500/**
4501 * Checks if there is a hard disk, DVD or floppy image with the given ID or
4502 * location already registered.
4503 *
4504 * On return, sets @a aConflict to the string describing the conflicting medium,
4505 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
4506 * either case. A failure is unexpected.
4507 *
4508 * @param aId UUID to check.
4509 * @param aLocation Location to check.
4510 * @param aConflict Where to return parameters of the conflicting medium.
4511 * @param ppMedium Medium reference in case this is simply a duplicate.
4512 *
4513 * @note Locks the media tree and media objects for reading.
4514 */
4515HRESULT VirtualBox::i_checkMediaForConflicts(const Guid &aId,
4516 const Utf8Str &aLocation,
4517 Utf8Str &aConflict,
4518 ComObjPtr<Medium> *ppMedium)
4519{
4520 AssertReturn(!aId.isZero() && !aLocation.isEmpty(), E_FAIL);
4521 AssertReturn(ppMedium, E_INVALIDARG);
4522
4523 aConflict.setNull();
4524 ppMedium->setNull();
4525
4526 AutoReadLock alock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4527
4528 HRESULT hrc = S_OK;
4529
4530 ComObjPtr<Medium> pMediumFound;
4531 const char *pcszType = NULL;
4532
4533 if (aId.isValid() && !aId.isZero())
4534 hrc = i_findHardDiskById(aId, false /* aSetError */, &pMediumFound);
4535 if (FAILED(hrc) && !aLocation.isEmpty())
4536 hrc = i_findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
4537 if (SUCCEEDED(hrc))
4538 pcszType = tr("hard disk");
4539
4540 if (!pcszType)
4541 {
4542 hrc = i_findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
4543 if (SUCCEEDED(hrc))
4544 pcszType = tr("CD/DVD image");
4545 }
4546
4547 if (!pcszType)
4548 {
4549 hrc = i_findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
4550 if (SUCCEEDED(hrc))
4551 pcszType = tr("floppy image");
4552 }
4553
4554 if (pcszType && pMediumFound)
4555 {
4556 /* Note: no AutoCaller since bound to this */
4557 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
4558
4559 Utf8Str strLocFound = pMediumFound->i_getLocationFull();
4560 Guid idFound = pMediumFound->i_getId();
4561
4562 if ( (RTPathCompare(strLocFound.c_str(), aLocation.c_str()) == 0)
4563 && (idFound == aId)
4564 )
4565 *ppMedium = pMediumFound;
4566
4567 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
4568 pcszType,
4569 strLocFound.c_str(),
4570 idFound.raw());
4571 }
4572
4573 return S_OK;
4574}
4575
4576/**
4577 * Checks whether the given UUID is already in use by one medium for the
4578 * given device type.
4579 *
4580 * @returns true if the UUID is already in use
4581 * fale otherwise
4582 * @param aId The UUID to check.
4583 * @param deviceType The device type the UUID is going to be checked for
4584 * conflicts.
4585 */
4586bool VirtualBox::i_isMediaUuidInUse(const Guid &aId, DeviceType_T deviceType)
4587{
4588 /* A zero UUID is invalid here, always claim that it is already used. */
4589 AssertReturn(!aId.isZero(), true);
4590
4591 AutoReadLock alock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4592
4593 bool fInUse = false;
4594
4595 ComObjPtr<Medium> pMediumFound;
4596
4597 HRESULT hrc;
4598 switch (deviceType)
4599 {
4600 case DeviceType_HardDisk:
4601 hrc = i_findHardDiskById(aId, false /* aSetError */, &pMediumFound);
4602 break;
4603 case DeviceType_DVD:
4604 hrc = i_findDVDOrFloppyImage(DeviceType_DVD, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
4605 break;
4606 case DeviceType_Floppy:
4607 hrc = i_findDVDOrFloppyImage(DeviceType_Floppy, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
4608 break;
4609 default:
4610 AssertMsgFailed(("Invalid device type %d\n", deviceType));
4611 hrc = S_OK;
4612 break;
4613 }
4614
4615 if (SUCCEEDED(hrc) && pMediumFound)
4616 fInUse = true;
4617
4618 return fInUse;
4619}
4620
4621/**
4622 * Called from Machine::prepareSaveSettings() when it has detected
4623 * that a machine has been renamed. Such renames will require
4624 * updating the global media registry during the
4625 * VirtualBox::i_saveSettings() that follows later.
4626*
4627 * When a machine is renamed, there may well be media (in particular,
4628 * diff images for snapshots) in the global registry that will need
4629 * to have their paths updated. Before 3.2, Machine::saveSettings
4630 * used to call VirtualBox::i_saveSettings implicitly, which was both
4631 * unintuitive and caused locking order problems. Now, we remember
4632 * such pending name changes with this method so that
4633 * VirtualBox::i_saveSettings() can process them properly.
4634 */
4635void VirtualBox::i_rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
4636 const Utf8Str &strNewConfigDir)
4637{
4638 AutoWriteLock mediaLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4639
4640 Data::PendingMachineRename pmr;
4641 pmr.strConfigDirOld = strOldConfigDir;
4642 pmr.strConfigDirNew = strNewConfigDir;
4643 m->llPendingMachineRenames.push_back(pmr);
4644}
4645
4646static DECLCALLBACK(int) fntSaveMediaRegistries(void *pvUser);
4647
4648class SaveMediaRegistriesDesc : public ThreadTask
4649{
4650
4651public:
4652 SaveMediaRegistriesDesc()
4653 {
4654 m_strTaskName = "SaveMediaReg";
4655 }
4656 virtual ~SaveMediaRegistriesDesc(void) { }
4657
4658private:
4659 void handler()
4660 {
4661 try
4662 {
4663 fntSaveMediaRegistries(this);
4664 }
4665 catch(...)
4666 {
4667 LogRel(("Exception in the function fntSaveMediaRegistries()\n"));
4668 }
4669 }
4670
4671 MediaList llMedia;
4672 ComObjPtr<VirtualBox> pVirtualBox;
4673
4674 friend DECLCALLBACK(int) fntSaveMediaRegistries(void *pvUser);
4675 friend void VirtualBox::i_saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
4676 const Guid &uuidRegistry,
4677 const Utf8Str &strMachineFolder);
4678};
4679
4680DECLCALLBACK(int) fntSaveMediaRegistries(void *pvUser)
4681{
4682 SaveMediaRegistriesDesc *pDesc = (SaveMediaRegistriesDesc *)pvUser;
4683 if (!pDesc)
4684 {
4685 LogRelFunc(("Thread for saving media registries lacks parameters\n"));
4686 return VERR_INVALID_PARAMETER;
4687 }
4688
4689 for (MediaList::const_iterator it = pDesc->llMedia.begin();
4690 it != pDesc->llMedia.end();
4691 ++it)
4692 {
4693 Medium *pMedium = *it;
4694 pMedium->i_markRegistriesModified();
4695 }
4696
4697 pDesc->pVirtualBox->i_saveModifiedRegistries();
4698
4699 pDesc->llMedia.clear();
4700 pDesc->pVirtualBox.setNull();
4701
4702 return VINF_SUCCESS;
4703}
4704
4705/**
4706 * Goes through all known media (hard disks, floppies and DVDs) and saves
4707 * those into the given settings::MediaRegistry structures whose registry
4708 * ID match the given UUID.
4709 *
4710 * Before actually writing to the structures, all media paths (not just the
4711 * ones for the given registry) are updated if machines have been renamed
4712 * since the last call.
4713 *
4714 * This gets called from two contexts:
4715 *
4716 * -- VirtualBox::i_saveSettings() with the UUID of the global registry
4717 * (VirtualBox::Data.uuidRegistry); this will save those media
4718 * which had been loaded from the global registry or have been
4719 * attached to a "legacy" machine which can't save its own registry;
4720 *
4721 * -- Machine::saveSettings() with the UUID of a machine, if a medium
4722 * has been attached to a machine created with VirtualBox 4.0 or later.
4723 *
4724 * Media which have only been temporarily opened without having been
4725 * attached to a machine have a NULL registry UUID and therefore don't
4726 * get saved.
4727 *
4728 * This locks the media tree. Throws HRESULT on errors!
4729 *
4730 * @param mediaRegistry Settings structure to fill.
4731 * @param uuidRegistry The UUID of the media registry; either a machine UUID
4732 * (if machine registry) or the UUID of the global registry.
4733 * @param strMachineFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
4734 */
4735void VirtualBox::i_saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
4736 const Guid &uuidRegistry,
4737 const Utf8Str &strMachineFolder)
4738{
4739 // lock all media for the following; use a write lock because we're
4740 // modifying the PendingMachineRenamesList, which is protected by this
4741 AutoWriteLock mediaLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4742
4743 // if a machine was renamed, then we'll need to refresh media paths
4744 if (m->llPendingMachineRenames.size())
4745 {
4746 // make a single list from the three media lists so we don't need three loops
4747 MediaList llAllMedia;
4748 // with hard disks, we must use the map, not the list, because the list only has base images
4749 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
4750 llAllMedia.push_back(it->second);
4751 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
4752 llAllMedia.push_back(*it);
4753 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
4754 llAllMedia.push_back(*it);
4755
4756 SaveMediaRegistriesDesc *pDesc = new SaveMediaRegistriesDesc();
4757 for (MediaList::iterator it = llAllMedia.begin();
4758 it != llAllMedia.end();
4759 ++it)
4760 {
4761 Medium *pMedium = *it;
4762 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
4763 it2 != m->llPendingMachineRenames.end();
4764 ++it2)
4765 {
4766 const Data::PendingMachineRename &pmr = *it2;
4767 HRESULT hrc = pMedium->i_updatePath(pmr.strConfigDirOld, pmr.strConfigDirNew);
4768 if (SUCCEEDED(hrc))
4769 {
4770 // Remember which medium objects has been changed,
4771 // to trigger saving their registries later.
4772 pDesc->llMedia.push_back(pMedium);
4773 } else if (hrc == VBOX_E_FILE_ERROR)
4774 /* nothing */;
4775 else
4776 AssertComRC(hrc);
4777 }
4778 }
4779 // done, don't do it again until we have more machine renames
4780 m->llPendingMachineRenames.clear();
4781
4782 if (pDesc->llMedia.size())
4783 {
4784 // Handle the media registry saving in a separate thread, to
4785 // avoid giant locking problems and passing up the list many
4786 // levels up to whoever triggered saveSettings, as there are
4787 // lots of places which would need to handle saving more settings.
4788 pDesc->pVirtualBox = this;
4789
4790 //the function createThread() takes ownership of pDesc
4791 //so there is no need to use delete operator for pDesc
4792 //after calling this function
4793 HRESULT hrc = pDesc->createThread();
4794 pDesc = NULL;
4795
4796 if (FAILED(hrc))
4797 {
4798 // failure means that settings aren't saved, but there isn't
4799 // much we can do besides avoiding memory leaks
4800 LogRelFunc(("Failed to create thread for saving media registries (%Rhr)\n", hrc));
4801 }
4802 }
4803 else
4804 delete pDesc;
4805 }
4806
4807 struct {
4808 MediaOList &llSource;
4809 settings::MediaList &llTarget;
4810 } s[] =
4811 {
4812 // hard disks
4813 { m->allHardDisks, mediaRegistry.llHardDisks },
4814 // CD/DVD images
4815 { m->allDVDImages, mediaRegistry.llDvdImages },
4816 // floppy images
4817 { m->allFloppyImages, mediaRegistry.llFloppyImages }
4818 };
4819
4820 for (size_t i = 0; i < RT_ELEMENTS(s); ++i)
4821 {
4822 MediaOList &llSource = s[i].llSource;
4823 settings::MediaList &llTarget = s[i].llTarget;
4824 llTarget.clear();
4825 for (MediaList::const_iterator it = llSource.begin();
4826 it != llSource.end();
4827 ++it)
4828 {
4829 Medium *pMedium = *it;
4830 AutoCaller autoCaller(pMedium);
4831 if (FAILED(autoCaller.hrc())) throw autoCaller.hrc();
4832 AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
4833
4834 if (pMedium->i_isInRegistry(uuidRegistry))
4835 {
4836 llTarget.push_back(settings::Medium::Empty);
4837 HRESULT hrc = pMedium->i_saveSettings(llTarget.back(), strMachineFolder); // this recurses into child hard disks
4838 if (FAILED(hrc))
4839 {
4840 llTarget.pop_back();
4841 throw hrc;
4842 }
4843 }
4844 }
4845 }
4846}
4847
4848/**
4849 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
4850 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
4851 * places internally when settings need saving.
4852 *
4853 * @note Caller must have locked the VirtualBox object for writing and must not hold any
4854 * other locks since this locks all kinds of member objects and trees temporarily,
4855 * which could cause conflicts.
4856 */
4857HRESULT VirtualBox::i_saveSettings()
4858{
4859 AutoCaller autoCaller(this);
4860 AssertComRCReturnRC(autoCaller.hrc());
4861
4862 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
4863 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
4864
4865 i_unmarkRegistryModified(i_getGlobalRegistryId());
4866
4867 HRESULT hrc = S_OK;
4868
4869 try
4870 {
4871 // machines
4872 m->pMainConfigFile->llMachines.clear();
4873 {
4874 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4875 for (MachinesOList::iterator it = m->allMachines.begin();
4876 it != m->allMachines.end();
4877 ++it)
4878 {
4879 Machine *pMachine = *it;
4880 // save actual machine registry entry
4881 settings::MachineRegistryEntry mre;
4882 hrc = pMachine->i_saveRegistryEntry(mre);
4883 m->pMainConfigFile->llMachines.push_back(mre);
4884 }
4885 }
4886
4887 i_saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
4888 m->uuidMediaRegistry, // global media registry ID
4889 Utf8Str::Empty); // strMachineFolder
4890
4891 m->pMainConfigFile->llDhcpServers.clear();
4892 {
4893 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4894 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4895 it != m->allDHCPServers.end();
4896 ++it)
4897 {
4898 settings::DHCPServer d;
4899 hrc = (*it)->i_saveSettings(d);
4900 if (FAILED(hrc)) throw hrc;
4901 m->pMainConfigFile->llDhcpServers.push_back(d);
4902 }
4903 }
4904
4905#ifdef VBOX_WITH_NAT_SERVICE
4906 /* Saving NAT Network configuration */
4907 m->pMainConfigFile->llNATNetworks.clear();
4908 {
4909 AutoReadLock natNetworkLock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4910 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
4911 it != m->allNATNetworks.end();
4912 ++it)
4913 {
4914 settings::NATNetwork n;
4915 hrc = (*it)->i_saveSettings(n);
4916 if (FAILED(hrc)) throw hrc;
4917 m->pMainConfigFile->llNATNetworks.push_back(n);
4918 }
4919 }
4920#endif
4921
4922#ifdef VBOX_WITH_VMNET
4923 m->pMainConfigFile->llHostOnlyNetworks.clear();
4924 {
4925 AutoReadLock hostOnlyNetworkLock(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4926 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
4927 it != m->allHostOnlyNetworks.end();
4928 ++it)
4929 {
4930 settings::HostOnlyNetwork n;
4931 hrc = (*it)->i_saveSettings(n);
4932 if (FAILED(hrc)) throw hrc;
4933 m->pMainConfigFile->llHostOnlyNetworks.push_back(n);
4934 }
4935 }
4936#endif /* VBOX_WITH_VMNET */
4937
4938#ifdef VBOX_WITH_CLOUD_NET
4939 m->pMainConfigFile->llCloudNetworks.clear();
4940 {
4941 AutoReadLock cloudNetworkLock(m->allCloudNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4942 for (CloudNetworksOList::const_iterator it = m->allCloudNetworks.begin();
4943 it != m->allCloudNetworks.end();
4944 ++it)
4945 {
4946 settings::CloudNetwork n;
4947 hrc = (*it)->i_saveSettings(n);
4948 if (FAILED(hrc)) throw hrc;
4949 m->pMainConfigFile->llCloudNetworks.push_back(n);
4950 }
4951 }
4952#endif /* VBOX_WITH_CLOUD_NET */
4953 // leave extra data alone, it's still in the config file
4954
4955 // host data (USB filters)
4956 hrc = m->pHost->i_saveSettings(m->pMainConfigFile->host);
4957 if (FAILED(hrc)) throw hrc;
4958
4959 hrc = m->pSystemProperties->i_saveSettings(m->pMainConfigFile->systemProperties);
4960 if (FAILED(hrc)) throw hrc;
4961
4962 // and write out the XML, still under the lock
4963 m->pMainConfigFile->write(m->strSettingsFilePath);
4964 }
4965 catch (HRESULT hrcXcpt)
4966 {
4967 /* we assume that error info is set by the thrower */
4968 hrc = hrcXcpt;
4969 }
4970 catch (...)
4971 {
4972 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
4973 }
4974
4975 return hrc;
4976}
4977
4978/**
4979 * Helper to register the machine.
4980 *
4981 * When called during VirtualBox startup, adds the given machine to the
4982 * collection of registered machines. Otherwise tries to mark the machine
4983 * as registered, and, if succeeded, adds it to the collection and
4984 * saves global settings.
4985 *
4986 * @note The caller must have added itself as a caller of the @a aMachine
4987 * object if calls this method not on VirtualBox startup.
4988 *
4989 * @param aMachine machine to register
4990 *
4991 * @note Locks objects!
4992 */
4993HRESULT VirtualBox::i_registerMachine(Machine *aMachine)
4994{
4995 ComAssertRet(aMachine, E_INVALIDARG);
4996
4997 AutoCaller autoCaller(this);
4998 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
4999
5000 HRESULT hrc = S_OK;
5001
5002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5003
5004 {
5005 ComObjPtr<Machine> pMachine;
5006 hrc = i_findMachine(aMachine->i_getId(),
5007 true /* fPermitInaccessible */,
5008 false /* aDoSetError */,
5009 &pMachine);
5010 if (SUCCEEDED(hrc))
5011 {
5012 /* sanity */
5013 AutoLimitedCaller machCaller(pMachine);
5014 AssertComRC(machCaller.hrc());
5015
5016 return setError(E_INVALIDARG,
5017 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
5018 aMachine->i_getId().raw(),
5019 pMachine->i_getSettingsFileFull().c_str());
5020 }
5021
5022 ComAssertRet(hrc == VBOX_E_OBJECT_NOT_FOUND, hrc);
5023 hrc = S_OK;
5024 }
5025
5026 if (getObjectState().getState() != ObjectState::InInit)
5027 {
5028 hrc = aMachine->i_prepareRegister();
5029 if (FAILED(hrc)) return hrc;
5030 }
5031
5032 /* add to the collection of registered machines */
5033 m->allMachines.addChild(aMachine);
5034
5035 if (getObjectState().getState() != ObjectState::InInit)
5036 hrc = i_saveSettings();
5037
5038 return hrc;
5039}
5040
5041/**
5042 * Remembers the given medium object by storing it in either the global
5043 * medium registry or a machine one.
5044 *
5045 * @note Caller must hold the media tree lock for writing; in addition, this
5046 * locks @a pMedium for reading
5047 *
5048 * @param pMedium Medium object to remember.
5049 * @param ppMedium Actually stored medium object. Can be different if due
5050 * to an unavoidable race there was a duplicate Medium object
5051 * created.
5052 * @param mediaTreeLock Reference to the AutoWriteLock holding the media tree
5053 * lock, necessary to release it in the right spot.
5054 * @param fCalledFromMediumInit Flag whether this is called from Medium::init().
5055 * @return
5056 */
5057HRESULT VirtualBox::i_registerMedium(const ComObjPtr<Medium> &pMedium,
5058 ComObjPtr<Medium> *ppMedium,
5059 AutoWriteLock &mediaTreeLock,
5060 bool fCalledFromMediumInit)
5061{
5062 AssertReturn(pMedium != NULL, E_INVALIDARG);
5063 AssertReturn(ppMedium != NULL, E_INVALIDARG);
5064
5065 // caller must hold the media tree write lock
5066 Assert(i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5067
5068 AutoCaller autoCaller(this);
5069 AssertComRCReturnRC(autoCaller.hrc());
5070
5071 AutoCaller mediumCaller(pMedium);
5072 AssertComRCReturnRC(mediumCaller.hrc());
5073
5074 bool fAddToGlobalRegistry = false;
5075 const char *pszDevType = NULL;
5076 Guid regId;
5077 ObjectsList<Medium> *pall = NULL;
5078 DeviceType_T devType;
5079 {
5080 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5081 devType = pMedium->i_getDeviceType();
5082
5083 if (!pMedium->i_getFirstRegistryMachineId(regId))
5084 fAddToGlobalRegistry = true;
5085 }
5086 switch (devType)
5087 {
5088 case DeviceType_HardDisk:
5089 pall = &m->allHardDisks;
5090 pszDevType = tr("hard disk");
5091 break;
5092 case DeviceType_DVD:
5093 pszDevType = tr("DVD image");
5094 pall = &m->allDVDImages;
5095 break;
5096 case DeviceType_Floppy:
5097 pszDevType = tr("floppy image");
5098 pall = &m->allFloppyImages;
5099 break;
5100 default:
5101 AssertMsgFailedReturn(("invalid device type %d", devType), E_INVALIDARG);
5102 }
5103
5104 Guid id;
5105 Utf8Str strLocationFull;
5106 ComObjPtr<Medium> pParent;
5107 {
5108 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5109 id = pMedium->i_getId();
5110 strLocationFull = pMedium->i_getLocationFull();
5111 pParent = pMedium->i_getParent();
5112 }
5113
5114 HRESULT hrc;
5115
5116 Utf8Str strConflict;
5117 ComObjPtr<Medium> pDupMedium;
5118 hrc = i_checkMediaForConflicts(id, strLocationFull, strConflict, &pDupMedium);
5119 if (FAILED(hrc)) return hrc;
5120
5121 if (pDupMedium.isNull())
5122 {
5123 if (strConflict.length())
5124 return setError(E_INVALIDARG,
5125 tr("Cannot register the %s '%s' {%RTuuid} because a %s already exists"),
5126 pszDevType,
5127 strLocationFull.c_str(),
5128 id.raw(),
5129 strConflict.c_str(),
5130 m->strSettingsFilePath.c_str());
5131
5132 // add to the collection if it is a base medium
5133 if (pParent.isNull())
5134 pall->getList().push_back(pMedium);
5135
5136 // store all hard disks (even differencing images) in the map
5137 if (devType == DeviceType_HardDisk)
5138 m->mapHardDisks[id] = pMedium;
5139 }
5140
5141 /*
5142 * If we have been called from Medium::initFromSettings() then the Medium object's
5143 * AutoCaller status will be 'InInit' which means that when making the assigment to
5144 * ppMedium below the Medium object will not call Medium::uninit(). By excluding
5145 * this code path from releasing and reacquiring the media tree lock we avoid a
5146 * potential deadlock with other threads which may be operating on the
5147 * disks/DVDs/floppies in the VM's media registry at the same time such as
5148 * Machine::unregister().
5149 */
5150 if (!fCalledFromMediumInit)
5151 {
5152 // pMedium may be the last reference to the Medium object, and the
5153 // caller may have specified the same ComObjPtr as the output parameter.
5154 // In this case the assignment will uninit the object, and we must not
5155 // have a caller pending.
5156 mediumCaller.release();
5157 // release media tree lock, must not be held at uninit time.
5158 mediaTreeLock.release();
5159 // must not hold the media tree write lock any more
5160 Assert(!i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5161 }
5162
5163 *ppMedium = pDupMedium.isNull() ? pMedium : pDupMedium;
5164
5165 if (fAddToGlobalRegistry)
5166 {
5167 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5168 if ( fCalledFromMediumInit
5169 ? (*ppMedium)->i_addRegistryNoCallerCheck(m->uuidMediaRegistry)
5170 : (*ppMedium)->i_addRegistry(m->uuidMediaRegistry))
5171 i_markRegistryModified(m->uuidMediaRegistry);
5172 }
5173
5174 // Restore the initial lock state, so that no unexpected lock changes are
5175 // done by this method, which would need adjustments everywhere.
5176 if (!fCalledFromMediumInit)
5177 mediaTreeLock.acquire();
5178
5179 return hrc;
5180}
5181
5182/**
5183 * Removes the given medium from the respective registry.
5184 *
5185 * @param pMedium Hard disk object to remove.
5186 *
5187 * @note Caller must hold the media tree lock for writing; in addition, this locks @a pMedium for reading
5188 */
5189HRESULT VirtualBox::i_unregisterMedium(Medium *pMedium)
5190{
5191 AssertReturn(pMedium != NULL, E_INVALIDARG);
5192
5193 AutoCaller autoCaller(this);
5194 AssertComRCReturnRC(autoCaller.hrc());
5195
5196 AutoCaller mediumCaller(pMedium);
5197 AssertComRCReturnRC(mediumCaller.hrc());
5198
5199 // caller must hold the media tree write lock
5200 Assert(i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5201
5202 Guid id;
5203 ComObjPtr<Medium> pParent;
5204 DeviceType_T devType;
5205 {
5206 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5207 id = pMedium->i_getId();
5208 pParent = pMedium->i_getParent();
5209 devType = pMedium->i_getDeviceType();
5210 }
5211
5212 ObjectsList<Medium> *pall = NULL;
5213 switch (devType)
5214 {
5215 case DeviceType_HardDisk:
5216 pall = &m->allHardDisks;
5217 break;
5218 case DeviceType_DVD:
5219 pall = &m->allDVDImages;
5220 break;
5221 case DeviceType_Floppy:
5222 pall = &m->allFloppyImages;
5223 break;
5224 default:
5225 AssertMsgFailedReturn(("invalid device type %d", devType), E_INVALIDARG);
5226 }
5227
5228 // remove from the collection if it is a base medium
5229 if (pParent.isNull())
5230 pall->getList().remove(pMedium);
5231
5232 // remove all hard disks (even differencing images) from map
5233 if (devType == DeviceType_HardDisk)
5234 {
5235 size_t cnt = m->mapHardDisks.erase(id);
5236 Assert(cnt == 1);
5237 NOREF(cnt);
5238 }
5239
5240 return S_OK;
5241}
5242
5243/**
5244 * Unregisters all Medium objects which belong to the given machine registry.
5245 * Gets called from Machine::uninit() just before the machine object dies
5246 * and must only be called with a machine UUID as the registry ID.
5247 *
5248 * Locks the media tree.
5249 *
5250 * @param uuidMachine Medium registry ID (always a machine UUID)
5251 * @return
5252 */
5253HRESULT VirtualBox::i_unregisterMachineMedia(const Guid &uuidMachine)
5254{
5255 Assert(!uuidMachine.isZero() && uuidMachine.isValid());
5256
5257 LogFlowFuncEnter();
5258
5259 AutoCaller autoCaller(this);
5260 AssertComRCReturnRC(autoCaller.hrc());
5261
5262 MediaList llMedia2Close;
5263
5264 {
5265 AutoWriteLock tlock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5266
5267 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
5268 it != m->allHardDisks.getList().end();
5269 ++it)
5270 {
5271 ComObjPtr<Medium> pMedium = *it;
5272 AutoCaller medCaller(pMedium);
5273 if (FAILED(medCaller.hrc())) return medCaller.hrc();
5274 AutoReadLock medlock(pMedium COMMA_LOCKVAL_SRC_POS);
5275 Log(("Looking at medium %RTuuid\n", pMedium->i_getId().raw()));
5276
5277 /* If the medium is still in the registry then either some code is
5278 * seriously buggy (unregistering a VM removes it automatically),
5279 * or the reference to a Machine object is destroyed without ever
5280 * being registered. The second condition checks if a medium is
5281 * in no registry, which indicates (set by unregistering) that a
5282 * medium is not used by any other VM and thus can be closed. */
5283 Guid dummy;
5284 if ( pMedium->i_isInRegistry(uuidMachine)
5285 || !pMedium->i_getFirstRegistryMachineId(dummy))
5286 {
5287 /* Collect all medium objects into llMedia2Close,
5288 * in right order for closing. */
5289 MediaList llMediaTodo;
5290 llMediaTodo.push_back(pMedium);
5291
5292 while (llMediaTodo.size() > 0)
5293 {
5294 ComObjPtr<Medium> pCurrent = llMediaTodo.front();
5295 llMediaTodo.pop_front();
5296
5297 /* Add to front, order must be children then parent. */
5298 Log(("Pushing medium %RTuuid (front)\n", pCurrent->i_getId().raw()));
5299 llMedia2Close.push_front(pCurrent);
5300
5301 /* process all children */
5302 MediaList::const_iterator itBegin = pCurrent->i_getChildren().begin();
5303 MediaList::const_iterator itEnd = pCurrent->i_getChildren().end();
5304 for (MediaList::const_iterator it2 = itBegin; it2 != itEnd; ++it2)
5305 llMediaTodo.push_back(*it2);
5306 }
5307 }
5308 }
5309 }
5310
5311 for (MediaList::iterator it = llMedia2Close.begin();
5312 it != llMedia2Close.end();
5313 ++it)
5314 {
5315 ComObjPtr<Medium> pMedium = *it;
5316 Log(("Closing medium %RTuuid\n", pMedium->i_getId().raw()));
5317 AutoCaller mac(pMedium);
5318 pMedium->i_close(mac);
5319 }
5320
5321 LogFlowFuncLeave();
5322
5323 return S_OK;
5324}
5325
5326/**
5327 * Removes the given machine object from the internal list of registered machines.
5328 * Called from Machine::Unregister().
5329 * @param pMachine
5330 * @param aCleanupMode How to handle medium attachments. For
5331 * CleanupMode_UnregisterOnly the associated medium objects will be
5332 * closed when the Machine object is uninitialized, otherwise they will
5333 * go to the global registry if no better registry is found.
5334 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
5335 * @return
5336 */
5337HRESULT VirtualBox::i_unregisterMachine(Machine *pMachine,
5338 CleanupMode_T aCleanupMode,
5339 const Guid &id)
5340{
5341 // remove from the collection of registered machines
5342 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5343 m->allMachines.removeChild(pMachine);
5344 // save the global registry
5345 HRESULT hrc = i_saveSettings();
5346 alock.release();
5347
5348 /*
5349 * Now go over all known media and checks if they were registered in the
5350 * media registry of the given machine. Each such medium is then moved to
5351 * a different media registry to make sure it doesn't get lost since its
5352 * media registry is about to go away.
5353 *
5354 * This fixes the following use case: Image A.vdi of machine A is also used
5355 * by machine B, but registered in the media registry of machine A. If machine
5356 * A is deleted, A.vdi must be moved to the registry of B, or else B will
5357 * become inaccessible.
5358 */
5359 {
5360 AutoReadLock tlock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5361 // iterate over the list of *base* images
5362 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
5363 it != m->allHardDisks.getList().end();
5364 ++it)
5365 {
5366 ComObjPtr<Medium> &pMedium = *it;
5367 AutoCaller medCaller(pMedium);
5368 if (FAILED(medCaller.hrc())) return medCaller.hrc();
5369 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
5370
5371 if (pMedium->i_removeRegistryAll(id))
5372 {
5373 // machine ID was found in base medium's registry list:
5374 // move this base image and all its children to another registry then
5375 // 1) first, find a better registry to add things to
5376 const Guid *puuidBetter = pMedium->i_getAnyMachineBackref(id);
5377 if (puuidBetter)
5378 {
5379 // 2) better registry found: then use that
5380 pMedium->i_addRegistryAll(*puuidBetter);
5381 // 3) and make sure the registry is saved below
5382 mlock.release();
5383 tlock.release();
5384 i_markRegistryModified(*puuidBetter);
5385 tlock.acquire();
5386 mlock.acquire();
5387 }
5388 else if (aCleanupMode != CleanupMode_UnregisterOnly)
5389 {
5390 pMedium->i_addRegistryAll(i_getGlobalRegistryId());
5391 mlock.release();
5392 tlock.release();
5393 i_markRegistryModified(i_getGlobalRegistryId());
5394 tlock.acquire();
5395 mlock.acquire();
5396 }
5397 }
5398 }
5399 }
5400
5401 i_saveModifiedRegistries();
5402
5403 /* fire an event */
5404 i_onMachineRegistered(id, FALSE);
5405
5406 return hrc;
5407}
5408
5409/**
5410 * Marks the registry for @a uuid as modified, so that it's saved in a later
5411 * call to saveModifiedRegistries().
5412 *
5413 * @param uuid
5414 */
5415void VirtualBox::i_markRegistryModified(const Guid &uuid)
5416{
5417 if (uuid == i_getGlobalRegistryId())
5418 ASMAtomicIncU64(&m->uRegistryNeedsSaving);
5419 else
5420 {
5421 ComObjPtr<Machine> pMachine;
5422 HRESULT hrc = i_findMachine(uuid, false /* fPermitInaccessible */, false /* aSetError */, &pMachine);
5423 if (SUCCEEDED(hrc))
5424 {
5425 AutoCaller machineCaller(pMachine);
5426 if (SUCCEEDED(machineCaller.hrc()) && pMachine->i_isAccessible())
5427 ASMAtomicIncU64(&pMachine->uRegistryNeedsSaving);
5428 }
5429 }
5430}
5431
5432/**
5433 * Marks the registry for @a uuid as unmodified, so that it's not saved in
5434 * a later call to saveModifiedRegistries().
5435 *
5436 * @param uuid
5437 */
5438void VirtualBox::i_unmarkRegistryModified(const Guid &uuid)
5439{
5440 uint64_t uOld;
5441 if (uuid == i_getGlobalRegistryId())
5442 {
5443 for (;;)
5444 {
5445 uOld = ASMAtomicReadU64(&m->uRegistryNeedsSaving);
5446 if (!uOld)
5447 break;
5448 if (ASMAtomicCmpXchgU64(&m->uRegistryNeedsSaving, 0, uOld))
5449 break;
5450 ASMNopPause();
5451 }
5452 }
5453 else
5454 {
5455 ComObjPtr<Machine> pMachine;
5456 HRESULT hrc = i_findMachine(uuid, false /* fPermitInaccessible */, false /* aSetError */, &pMachine);
5457 if (SUCCEEDED(hrc))
5458 {
5459 AutoCaller machineCaller(pMachine);
5460 if (SUCCEEDED(machineCaller.hrc()))
5461 {
5462 for (;;)
5463 {
5464 uOld = ASMAtomicReadU64(&pMachine->uRegistryNeedsSaving);
5465 if (!uOld)
5466 break;
5467 if (ASMAtomicCmpXchgU64(&pMachine->uRegistryNeedsSaving, 0, uOld))
5468 break;
5469 ASMNopPause();
5470 }
5471 }
5472 }
5473 }
5474}
5475
5476/**
5477 * Saves all settings files according to the modified flags in the Machine
5478 * objects and in the VirtualBox object.
5479 *
5480 * This locks machines and the VirtualBox object as necessary, so better not
5481 * hold any locks before calling this.
5482 *
5483 * @return
5484 */
5485void VirtualBox::i_saveModifiedRegistries()
5486{
5487 HRESULT hrc = S_OK;
5488 bool fNeedsGlobalSettings = false;
5489 uint64_t uOld;
5490
5491 {
5492 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5493 for (MachinesOList::iterator it = m->allMachines.begin();
5494 it != m->allMachines.end();
5495 ++it)
5496 {
5497 const ComObjPtr<Machine> &pMachine = *it;
5498
5499 for (;;)
5500 {
5501 uOld = ASMAtomicReadU64(&pMachine->uRegistryNeedsSaving);
5502 if (!uOld)
5503 break;
5504 if (ASMAtomicCmpXchgU64(&pMachine->uRegistryNeedsSaving, 0, uOld))
5505 break;
5506 ASMNopPause();
5507 }
5508 if (uOld)
5509 {
5510 AutoCaller autoCaller(pMachine);
5511 if (FAILED(autoCaller.hrc()))
5512 continue;
5513 /* object is already dead, no point in saving settings */
5514 if (getObjectState().getState() != ObjectState::Ready)
5515 continue;
5516 AutoWriteLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
5517 hrc = pMachine->i_saveSettings(&fNeedsGlobalSettings, mlock,
5518 Machine::SaveS_Force); // caller said save, so stop arguing
5519 }
5520 }
5521 }
5522
5523 for (;;)
5524 {
5525 uOld = ASMAtomicReadU64(&m->uRegistryNeedsSaving);
5526 if (!uOld)
5527 break;
5528 if (ASMAtomicCmpXchgU64(&m->uRegistryNeedsSaving, 0, uOld))
5529 break;
5530 ASMNopPause();
5531 }
5532 if (uOld || fNeedsGlobalSettings)
5533 {
5534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5535 hrc = i_saveSettings();
5536 }
5537 NOREF(hrc); /* XXX */
5538}
5539
5540
5541/* static */
5542const com::Utf8Str &VirtualBox::i_getVersionNormalized()
5543{
5544 return sVersionNormalized;
5545}
5546
5547/**
5548 * Checks if the path to the specified file exists, according to the path
5549 * information present in the file name. Optionally the path is created.
5550 *
5551 * Note that the given file name must contain the full path otherwise the
5552 * extracted relative path will be created based on the current working
5553 * directory which is normally unknown.
5554 *
5555 * @param strFileName Full file name which path is checked/created.
5556 * @param fCreate Flag if the path should be created if it doesn't exist.
5557 *
5558 * @return Extended error information on failure to check/create the path.
5559 */
5560/* static */
5561HRESULT VirtualBox::i_ensureFilePathExists(const Utf8Str &strFileName, bool fCreate)
5562{
5563 Utf8Str strDir(strFileName);
5564 strDir.stripFilename();
5565 if (!RTDirExists(strDir.c_str()))
5566 {
5567 if (fCreate)
5568 {
5569 int vrc = RTDirCreateFullPath(strDir.c_str(), 0700);
5570 if (RT_FAILURE(vrc))
5571 return i_setErrorStaticBoth(VBOX_E_IPRT_ERROR, vrc,
5572 tr("Could not create the directory '%s' (%Rrc)"),
5573 strDir.c_str(),
5574 vrc);
5575 }
5576 else
5577 return i_setErrorStaticBoth(VBOX_E_IPRT_ERROR, VERR_FILE_NOT_FOUND,
5578 tr("Directory '%s' does not exist"), strDir.c_str());
5579 }
5580
5581 return S_OK;
5582}
5583
5584const Utf8Str& VirtualBox::i_settingsFilePath()
5585{
5586 return m->strSettingsFilePath;
5587}
5588
5589/**
5590 * Returns the lock handle which protects the machines list. As opposed
5591 * to version 3.1 and earlier, these lists are no longer protected by the
5592 * VirtualBox lock, but by this more specialized lock. Mind the locking
5593 * order: always request this lock after the VirtualBox object lock but
5594 * before the locks of any machine object. See AutoLock.h.
5595 */
5596RWLockHandle& VirtualBox::i_getMachinesListLockHandle()
5597{
5598 return m->lockMachines;
5599}
5600
5601/**
5602 * Returns the lock handle which protects the media trees (hard disks,
5603 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
5604 * are no longer protected by the VirtualBox lock, but by this more
5605 * specialized lock. Mind the locking order: always request this lock
5606 * after the VirtualBox object lock but before the locks of the media
5607 * objects contained in these lists. See AutoLock.h.
5608 */
5609RWLockHandle& VirtualBox::i_getMediaTreeLockHandle()
5610{
5611 return m->lockMedia;
5612}
5613
5614/**
5615 * Thread function that handles custom events posted using #i_postEvent().
5616 */
5617// static
5618DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
5619{
5620 LogFlowFuncEnter();
5621
5622 AssertReturn(pvUser, VERR_INVALID_POINTER);
5623
5624 HRESULT hrc = com::Initialize();
5625 if (FAILED(hrc))
5626 return VERR_COM_UNEXPECTED;
5627
5628 int vrc = VINF_SUCCESS;
5629
5630 try
5631 {
5632 /* Create an event queue for the current thread. */
5633 EventQueue *pEventQueue = new EventQueue();
5634 AssertPtr(pEventQueue);
5635
5636 /* Return the queue to the one who created this thread. */
5637 *(static_cast <EventQueue **>(pvUser)) = pEventQueue;
5638
5639 /* signal that we're ready. */
5640 RTThreadUserSignal(thread);
5641
5642 /*
5643 * In case of spurious wakeups causing VERR_TIMEOUTs and/or other return codes
5644 * we must not stop processing events and delete the pEventQueue object. This must
5645 * be done ONLY when we stop this loop via interruptEventQueueProcessing().
5646 * See @bugref{5724}.
5647 */
5648 for (;;)
5649 {
5650 vrc = pEventQueue->processEventQueue(RT_INDEFINITE_WAIT);
5651 if (vrc == VERR_INTERRUPTED)
5652 {
5653 LogFlow(("Event queue processing ended with vrc=%Rrc\n", vrc));
5654 vrc = VINF_SUCCESS; /* Set success when exiting. */
5655 break;
5656 }
5657 }
5658
5659 delete pEventQueue;
5660 }
5661 catch (std::bad_alloc &ba)
5662 {
5663 vrc = VERR_NO_MEMORY;
5664 NOREF(ba);
5665 }
5666
5667 com::Shutdown();
5668
5669 LogFlowFuncLeaveRC(vrc);
5670 return vrc;
5671}
5672
5673
5674////////////////////////////////////////////////////////////////////////////////
5675
5676#if 0 /* obsoleted by AsyncEvent */
5677/**
5678 * Prepare the event using the overwritten #prepareEventDesc method and fire.
5679 *
5680 * @note Locks the managed VirtualBox object for reading but leaves the lock
5681 * before iterating over callbacks and calling their methods.
5682 */
5683void *VirtualBox::CallbackEvent::handler()
5684{
5685 if (!mVirtualBox)
5686 return NULL;
5687
5688 AutoCaller autoCaller(mVirtualBox);
5689 if (!autoCaller.isOk())
5690 {
5691 Log1WarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
5692 mVirtualBox->getObjectState().getState()));
5693 /* We don't need mVirtualBox any more, so release it */
5694 mVirtualBox = NULL;
5695 return NULL;
5696 }
5697
5698 {
5699 VBoxEventDesc evDesc;
5700 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
5701
5702 evDesc.fire(/* don't wait for delivery */0);
5703 }
5704
5705 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
5706 return NULL;
5707}
5708#endif
5709
5710/**
5711 * Called on the event handler thread.
5712 *
5713 * @note Locks the managed VirtualBox object for reading but leaves the lock
5714 * before iterating over callbacks and calling their methods.
5715 */
5716void *VirtualBox::AsyncEvent::handler()
5717{
5718 if (mVirtualBox)
5719 {
5720 AutoCaller autoCaller(mVirtualBox);
5721 if (autoCaller.isOk())
5722 {
5723 VBoxEventDesc EvtDesc(mEvent, mVirtualBox->m->pEventSource);
5724 EvtDesc.fire(/* don't wait for delivery */0);
5725 }
5726 else
5727 Log1WarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
5728 mVirtualBox->getObjectState().getState()));
5729 mVirtualBox = NULL; /* Old code did this, not really necessary, but whatever. */
5730 }
5731 mEvent.setNull();
5732 return NULL;
5733}
5734
5735//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
5736//{
5737// return E_NOTIMPL;
5738//}
5739
5740HRESULT VirtualBox::createDHCPServer(const com::Utf8Str &aName,
5741 ComPtr<IDHCPServer> &aServer)
5742{
5743 ComObjPtr<DHCPServer> dhcpServer;
5744 dhcpServer.createObject();
5745 HRESULT hrc = dhcpServer->init(this, aName);
5746 if (FAILED(hrc)) return hrc;
5747
5748 hrc = i_registerDHCPServer(dhcpServer, true);
5749 if (FAILED(hrc)) return hrc;
5750
5751 dhcpServer.queryInterfaceTo(aServer.asOutParam());
5752
5753 return hrc;
5754}
5755
5756HRESULT VirtualBox::findDHCPServerByNetworkName(const com::Utf8Str &aName,
5757 ComPtr<IDHCPServer> &aServer)
5758{
5759 ComPtr<DHCPServer> found;
5760
5761 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5762
5763 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
5764 it != m->allDHCPServers.end();
5765 ++it)
5766 {
5767 Bstr bstrNetworkName;
5768 HRESULT hrc = (*it)->COMGETTER(NetworkName)(bstrNetworkName.asOutParam());
5769 if (FAILED(hrc)) return hrc;
5770
5771 if (Utf8Str(bstrNetworkName) == aName)
5772 {
5773 found = *it;
5774 break;
5775 }
5776 }
5777
5778 if (!found)
5779 return E_INVALIDARG;
5780 return found.queryInterfaceTo(aServer.asOutParam());
5781}
5782
5783HRESULT VirtualBox::removeDHCPServer(const ComPtr<IDHCPServer> &aServer)
5784{
5785 IDHCPServer *aP = aServer;
5786 return i_unregisterDHCPServer(static_cast<DHCPServer *>(aP));
5787}
5788
5789/**
5790 * Remembers the given DHCP server in the settings.
5791 *
5792 * @param aDHCPServer DHCP server object to remember.
5793 * @param aSaveSettings @c true to save settings to disk (default).
5794 *
5795 * When @a aSaveSettings is @c true, this operation may fail because of the
5796 * failed #i_saveSettings() method it calls. In this case, the dhcp server object
5797 * will not be remembered. It is therefore the responsibility of the caller to
5798 * call this method as the last step of some action that requires registration
5799 * in order to make sure that only fully functional dhcp server objects get
5800 * registered.
5801 *
5802 * @note Locks this object for writing and @a aDHCPServer for reading.
5803 */
5804HRESULT VirtualBox::i_registerDHCPServer(DHCPServer *aDHCPServer,
5805 bool aSaveSettings /*= true*/)
5806{
5807 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
5808
5809 AutoCaller autoCaller(this);
5810 AssertComRCReturnRC(autoCaller.hrc());
5811
5812 // Acquire a lock on the VirtualBox object early to avoid lock order issues
5813 // when we call i_saveSettings() later on.
5814 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
5815 // need it below, in findDHCPServerByNetworkName (reading) and in
5816 // m->allDHCPServers.addChild, so need to get it here to avoid lock
5817 // order trouble with dhcpServerCaller
5818 AutoWriteLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5819
5820 AutoCaller dhcpServerCaller(aDHCPServer);
5821 AssertComRCReturnRC(dhcpServerCaller.hrc());
5822
5823 Bstr bstrNetworkName;
5824 HRESULT hrc = aDHCPServer->COMGETTER(NetworkName)(bstrNetworkName.asOutParam());
5825 if (FAILED(hrc)) return hrc;
5826
5827 ComPtr<IDHCPServer> existing;
5828 hrc = findDHCPServerByNetworkName(Utf8Str(bstrNetworkName), existing);
5829 if (SUCCEEDED(hrc))
5830 return E_INVALIDARG;
5831 hrc = S_OK;
5832
5833 m->allDHCPServers.addChild(aDHCPServer);
5834 // we need to release the list lock before we attempt to acquire locks
5835 // on other objects in i_saveSettings (see @bugref{7500})
5836 alock.release();
5837
5838 if (aSaveSettings)
5839 {
5840 // we acquired the lock on 'this' earlier to avoid lock order issues
5841 hrc = i_saveSettings();
5842
5843 if (FAILED(hrc))
5844 {
5845 alock.acquire();
5846 m->allDHCPServers.removeChild(aDHCPServer);
5847 }
5848 }
5849
5850 return hrc;
5851}
5852
5853/**
5854 * Removes the given DHCP server from the settings.
5855 *
5856 * @param aDHCPServer DHCP server object to remove.
5857 *
5858 * This operation may fail because of the failed #i_saveSettings() method it
5859 * calls. In this case, the DHCP server will NOT be removed from the settings
5860 * when this method returns.
5861 *
5862 * @note Locks this object for writing.
5863 */
5864HRESULT VirtualBox::i_unregisterDHCPServer(DHCPServer *aDHCPServer)
5865{
5866 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
5867
5868 AutoCaller autoCaller(this);
5869 AssertComRCReturnRC(autoCaller.hrc());
5870
5871 AutoCaller dhcpServerCaller(aDHCPServer);
5872 AssertComRCReturnRC(dhcpServerCaller.hrc());
5873
5874 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
5875 AutoWriteLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5876 m->allDHCPServers.removeChild(aDHCPServer);
5877 // we need to release the list lock before we attempt to acquire locks
5878 // on other objects in i_saveSettings (see @bugref{7500})
5879 alock.release();
5880
5881 HRESULT hrc = i_saveSettings();
5882
5883 // undo the changes if we failed to save them
5884 if (FAILED(hrc))
5885 {
5886 alock.acquire();
5887 m->allDHCPServers.addChild(aDHCPServer);
5888 }
5889
5890 return hrc;
5891}
5892
5893
5894/**
5895 * NAT Network
5896 */
5897HRESULT VirtualBox::createNATNetwork(const com::Utf8Str &aNetworkName,
5898 ComPtr<INATNetwork> &aNetwork)
5899{
5900#ifdef VBOX_WITH_NAT_SERVICE
5901 ComObjPtr<NATNetwork> natNetwork;
5902 natNetwork.createObject();
5903 HRESULT hrc = natNetwork->init(this, aNetworkName);
5904 if (FAILED(hrc)) return hrc;
5905
5906 hrc = i_registerNATNetwork(natNetwork, true);
5907 if (FAILED(hrc)) return hrc;
5908
5909 natNetwork.queryInterfaceTo(aNetwork.asOutParam());
5910
5911 ::FireNATNetworkCreationDeletionEvent(m->pEventSource, aNetworkName, TRUE);
5912
5913 return hrc;
5914#else
5915 NOREF(aNetworkName);
5916 NOREF(aNetwork);
5917 return E_NOTIMPL;
5918#endif
5919}
5920
5921HRESULT VirtualBox::findNATNetworkByName(const com::Utf8Str &aNetworkName,
5922 ComPtr<INATNetwork> &aNetwork)
5923{
5924#ifdef VBOX_WITH_NAT_SERVICE
5925
5926 HRESULT hrc = S_OK;
5927 ComPtr<NATNetwork> found;
5928
5929 AutoReadLock alock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5930
5931 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
5932 it != m->allNATNetworks.end();
5933 ++it)
5934 {
5935 Bstr bstrNATNetworkName;
5936 hrc = (*it)->COMGETTER(NetworkName)(bstrNATNetworkName.asOutParam());
5937 if (FAILED(hrc)) return hrc;
5938
5939 if (Utf8Str(bstrNATNetworkName) == aNetworkName)
5940 {
5941 found = *it;
5942 break;
5943 }
5944 }
5945
5946 if (!found)
5947 return E_INVALIDARG;
5948 found.queryInterfaceTo(aNetwork.asOutParam());
5949 return hrc;
5950#else
5951 NOREF(aNetworkName);
5952 NOREF(aNetwork);
5953 return E_NOTIMPL;
5954#endif
5955}
5956
5957HRESULT VirtualBox::removeNATNetwork(const ComPtr<INATNetwork> &aNetwork)
5958{
5959#ifdef VBOX_WITH_NAT_SERVICE
5960 Bstr name;
5961 HRESULT hrc = aNetwork->COMGETTER(NetworkName)(name.asOutParam());
5962 if (FAILED(hrc))
5963 return hrc;
5964 INATNetwork *p = aNetwork;
5965 NATNetwork *network = static_cast<NATNetwork *>(p);
5966 hrc = i_unregisterNATNetwork(network, true);
5967 ::FireNATNetworkCreationDeletionEvent(m->pEventSource, name.raw(), FALSE);
5968 return hrc;
5969#else
5970 NOREF(aNetwork);
5971 return E_NOTIMPL;
5972#endif
5973
5974}
5975/**
5976 * Remembers the given NAT network in the settings.
5977 *
5978 * @param aNATNetwork NAT Network object to remember.
5979 * @param aSaveSettings @c true to save settings to disk (default).
5980 *
5981 *
5982 * @note Locks this object for writing and @a aNATNetwork for reading.
5983 */
5984HRESULT VirtualBox::i_registerNATNetwork(NATNetwork *aNATNetwork,
5985 bool aSaveSettings /*= true*/)
5986{
5987#ifdef VBOX_WITH_NAT_SERVICE
5988 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
5989
5990 AutoCaller autoCaller(this);
5991 AssertComRCReturnRC(autoCaller.hrc());
5992
5993 AutoCaller natNetworkCaller(aNATNetwork);
5994 AssertComRCReturnRC(natNetworkCaller.hrc());
5995
5996 Bstr name;
5997 HRESULT hrc;
5998 hrc = aNATNetwork->COMGETTER(NetworkName)(name.asOutParam());
5999 AssertComRCReturnRC(hrc);
6000
6001 /* returned value isn't 0 and aSaveSettings is true
6002 * means that we create duplicate, otherwise we just load settings.
6003 */
6004 if ( sNatNetworkNameToRefCount[name]
6005 && aSaveSettings)
6006 AssertComRCReturnRC(E_INVALIDARG);
6007
6008 hrc = S_OK;
6009
6010 sNatNetworkNameToRefCount[name] = 0;
6011
6012 m->allNATNetworks.addChild(aNATNetwork);
6013
6014 if (aSaveSettings)
6015 {
6016 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
6017 hrc = i_saveSettings();
6018 vboxLock.release();
6019
6020 if (FAILED(hrc))
6021 i_unregisterNATNetwork(aNATNetwork, false /* aSaveSettings */);
6022 }
6023
6024 return hrc;
6025#else
6026 NOREF(aNATNetwork);
6027 NOREF(aSaveSettings);
6028 /* No panic please (silently ignore) */
6029 return S_OK;
6030#endif
6031}
6032
6033/**
6034 * Removes the given NAT network from the settings.
6035 *
6036 * @param aNATNetwork NAT network object to remove.
6037 * @param aSaveSettings @c true to save settings to disk (default).
6038 *
6039 * When @a aSaveSettings is @c true, this operation may fail because of the
6040 * failed #i_saveSettings() method it calls. In this case, the DHCP server
6041 * will NOT be removed from the settingsi when this method returns.
6042 *
6043 * @note Locks this object for writing.
6044 */
6045HRESULT VirtualBox::i_unregisterNATNetwork(NATNetwork *aNATNetwork,
6046 bool aSaveSettings /*= true*/)
6047{
6048#ifdef VBOX_WITH_NAT_SERVICE
6049 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
6050
6051 AutoCaller autoCaller(this);
6052 AssertComRCReturnRC(autoCaller.hrc());
6053
6054 AutoCaller natNetworkCaller(aNATNetwork);
6055 AssertComRCReturnRC(natNetworkCaller.hrc());
6056
6057 Bstr name;
6058 HRESULT hrc = aNATNetwork->COMGETTER(NetworkName)(name.asOutParam());
6059 /* Hm, there're still running clients. */
6060 if (FAILED(hrc) || sNatNetworkNameToRefCount[name])
6061 AssertComRCReturnRC(E_INVALIDARG);
6062
6063 m->allNATNetworks.removeChild(aNATNetwork);
6064
6065 if (aSaveSettings)
6066 {
6067 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
6068 hrc = i_saveSettings();
6069 vboxLock.release();
6070
6071 if (FAILED(hrc))
6072 i_registerNATNetwork(aNATNetwork, false /* aSaveSettings */);
6073 }
6074
6075 return hrc;
6076#else
6077 NOREF(aNATNetwork);
6078 NOREF(aSaveSettings);
6079 return E_NOTIMPL;
6080#endif
6081}
6082
6083
6084HRESULT VirtualBox::findProgressById(const com::Guid &aId,
6085 ComPtr<IProgress> &aProgressObject)
6086{
6087 if (!aId.isValid())
6088 return setError(E_INVALIDARG,
6089 tr("The provided progress object GUID is invalid"));
6090
6091 /* protect mProgressOperations */
6092 AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
6093
6094 ProgressMap::const_iterator it = m->mapProgressOperations.find(aId);
6095 if (it != m->mapProgressOperations.end())
6096 {
6097 aProgressObject = it->second;
6098 return S_OK;
6099 }
6100 return setError(E_INVALIDARG,
6101 tr("The progress object with the given GUID could not be found"));
6102}
6103
6104
6105/**
6106 * Retains a reference to the default cryptographic interface.
6107 *
6108 * @returns COM status code.
6109 * @param ppCryptoIf Where to store the pointer to the cryptographic interface on success.
6110 *
6111 * @note Locks this object for writing.
6112 */
6113HRESULT VirtualBox::i_retainCryptoIf(PCVBOXCRYPTOIF *ppCryptoIf)
6114{
6115 AssertReturn(ppCryptoIf != NULL, E_INVALIDARG);
6116
6117 AutoCaller autoCaller(this);
6118 AssertComRCReturnRC(autoCaller.hrc());
6119
6120 /*
6121 * No object lock due to some lock order fun with Machine objects.
6122 * There is a dedicated critical section to protect against concurrency
6123 * issues when loading the module.
6124 */
6125 RTCritSectEnter(&m->CritSectModCrypto);
6126
6127 /* Try to load the extension pack module if it isn't currently. */
6128 HRESULT hrc = S_OK;
6129 if (m->hLdrModCrypto == NIL_RTLDRMOD)
6130 {
6131#ifdef VBOX_WITH_EXTPACK
6132 /*
6133 * Check that a crypto extension pack name is set and resolve it into a
6134 * library path.
6135 */
6136 Utf8Str strExtPack;
6137 hrc = m->pSystemProperties->getDefaultCryptoExtPack(strExtPack);
6138 if (FAILED(hrc))
6139 {
6140 RTCritSectLeave(&m->CritSectModCrypto);
6141 return hrc;
6142 }
6143 if (strExtPack.isEmpty())
6144 {
6145 RTCritSectLeave(&m->CritSectModCrypto);
6146 return setError(VBOX_E_OBJECT_NOT_FOUND,
6147 tr("Ńo extension pack providing a cryptographic support module could be found"));
6148 }
6149
6150 Utf8Str strCryptoLibrary;
6151 int vrc = m->ptrExtPackManager->i_getCryptoLibraryPathForExtPack(&strExtPack, &strCryptoLibrary);
6152 if (RT_SUCCESS(vrc))
6153 {
6154 RTERRINFOSTATIC ErrInfo;
6155 vrc = SUPR3HardenedLdrLoadPlugIn(strCryptoLibrary.c_str(), &m->hLdrModCrypto, RTErrInfoInitStatic(&ErrInfo));
6156 if (RT_SUCCESS(vrc))
6157 {
6158 /* Resolve the entry point and query the pointer to the cryptographic interface. */
6159 PFNVBOXCRYPTOENTRY pfnCryptoEntry = NULL;
6160 vrc = RTLdrGetSymbol(m->hLdrModCrypto, VBOX_CRYPTO_MOD_ENTRY_POINT, (void **)&pfnCryptoEntry);
6161 if (RT_SUCCESS(vrc))
6162 {
6163 vrc = pfnCryptoEntry(&m->pCryptoIf);
6164 if (RT_FAILURE(vrc))
6165 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6166 tr("Failed to query the interface callback table from the cryptographic support module '%s' from extension pack '%s'"),
6167 strCryptoLibrary.c_str(), strExtPack.c_str());
6168 }
6169 else
6170 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6171 tr("Failed to resolve the entry point for the cryptographic support module '%s' from extension pack '%s'"),
6172 strCryptoLibrary.c_str(), strExtPack.c_str());
6173 }
6174 else
6175 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6176 tr("Couldn't load the cryptographic support module '%s' from extension pack '%s' (error: '%s')"),
6177 strCryptoLibrary.c_str(), strExtPack.c_str(), ErrInfo.Core.pszMsg);
6178 }
6179 else
6180 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6181 tr("Couldn't resolve the library path of the crpytographic support module for extension pack '%s'"),
6182 strExtPack.c_str());
6183#else
6184 hrc = setError(VBOX_E_NOT_SUPPORTED,
6185 tr("The cryptographic support module is not supported in this build because extension packs are not supported"));
6186#endif
6187 }
6188
6189 if (SUCCEEDED(hrc))
6190 {
6191 ASMAtomicIncU32(&m->cRefsCrypto);
6192 *ppCryptoIf = m->pCryptoIf;
6193 }
6194
6195 RTCritSectLeave(&m->CritSectModCrypto);
6196
6197 return hrc;
6198}
6199
6200
6201/**
6202 * Releases the reference of the given cryptographic interface.
6203 *
6204 * @returns COM status code.
6205 * @param pCryptoIf Pointer to the cryptographic interface to release.
6206 *
6207 * @note Locks this object for writing.
6208 */
6209HRESULT VirtualBox::i_releaseCryptoIf(PCVBOXCRYPTOIF pCryptoIf)
6210{
6211 AutoCaller autoCaller(this);
6212 AssertComRCReturnRC(autoCaller.hrc());
6213
6214 AssertReturn(pCryptoIf == m->pCryptoIf, E_INVALIDARG);
6215
6216 ASMAtomicDecU32(&m->cRefsCrypto);
6217 return S_OK;
6218}
6219
6220
6221/**
6222 * Tries to unload any loaded cryptographic support module if it is not in use currently.
6223 *
6224 * @returns COM status code.
6225 *
6226 * @note Locks this object for writing.
6227 */
6228HRESULT VirtualBox::i_unloadCryptoIfModule(void)
6229{
6230 AutoCaller autoCaller(this);
6231 AssertComRCReturnRC(autoCaller.hrc());
6232
6233 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
6234
6235 if (m->cRefsCrypto)
6236 return setError(E_ACCESSDENIED,
6237 tr("The cryptographic support module is in use and can't be unloaded"));
6238
6239 RTCritSectEnter(&m->CritSectModCrypto);
6240 if (m->hLdrModCrypto != NIL_RTLDRMOD)
6241 {
6242 int vrc = RTLdrClose(m->hLdrModCrypto);
6243 AssertRC(vrc);
6244 m->hLdrModCrypto = NIL_RTLDRMOD;
6245 }
6246 RTCritSectLeave(&m->CritSectModCrypto);
6247
6248 return S_OK;
6249}
6250
6251
6252#ifdef RT_OS_WINDOWS
6253#include <psapi.h>
6254
6255/**
6256 * Report versions of installed drivers to release log.
6257 */
6258void VirtualBox::i_reportDriverVersions()
6259{
6260 /** @todo r=klaus this code is very confusing, as it uses TCHAR (and
6261 * randomly also _TCHAR, which sounds to me like asking for trouble),
6262 * the "sz" variable prefix but "%ls" for the format string - so the whole
6263 * thing is better compiled with UNICODE and _UNICODE defined. Would be
6264 * far easier to read if it would be coded explicitly for the unicode
6265 * case, as it won't work otherwise. */
6266 DWORD err;
6267 HRESULT hrc;
6268 LPVOID aDrivers[1024];
6269 LPVOID *pDrivers = aDrivers;
6270 UINT cNeeded = 0;
6271 TCHAR szSystemRoot[MAX_PATH];
6272 TCHAR *pszSystemRoot = szSystemRoot;
6273 LPVOID pVerInfo = NULL;
6274 DWORD cbVerInfo = 0;
6275
6276 do
6277 {
6278 cNeeded = GetWindowsDirectory(szSystemRoot, RT_ELEMENTS(szSystemRoot));
6279 if (cNeeded == 0)
6280 {
6281 err = GetLastError();
6282 hrc = HRESULT_FROM_WIN32(err);
6283 AssertLogRelMsgFailed(("GetWindowsDirectory failed, hrc=%Rhrc (0x%x) err=%u\n",
6284 hrc, hrc, err));
6285 break;
6286 }
6287 else if (cNeeded > RT_ELEMENTS(szSystemRoot))
6288 {
6289 /* The buffer is too small, allocate big one. */
6290 pszSystemRoot = (TCHAR *)RTMemTmpAlloc(cNeeded * sizeof(_TCHAR));
6291 if (!pszSystemRoot)
6292 {
6293 AssertLogRelMsgFailed(("RTMemTmpAlloc failed to allocate %d bytes\n", cNeeded));
6294 break;
6295 }
6296 if (GetWindowsDirectory(pszSystemRoot, cNeeded) == 0)
6297 {
6298 err = GetLastError();
6299 hrc = HRESULT_FROM_WIN32(err);
6300 AssertLogRelMsgFailed(("GetWindowsDirectory failed, hrc=%Rhrc (0x%x) err=%u\n",
6301 hrc, hrc, err));
6302 break;
6303 }
6304 }
6305
6306 DWORD cbNeeded = 0;
6307 if (!EnumDeviceDrivers(aDrivers, sizeof(aDrivers), &cbNeeded) || cbNeeded > sizeof(aDrivers))
6308 {
6309 pDrivers = (LPVOID *)RTMemTmpAlloc(cbNeeded);
6310 if (!EnumDeviceDrivers(pDrivers, cbNeeded, &cbNeeded))
6311 {
6312 err = GetLastError();
6313 hrc = HRESULT_FROM_WIN32(err);
6314 AssertLogRelMsgFailed(("EnumDeviceDrivers failed, hrc=%Rhrc (0x%x) err=%u\n",
6315 hrc, hrc, err));
6316 break;
6317 }
6318 }
6319
6320 LogRel(("Installed Drivers:\n"));
6321
6322 TCHAR szDriver[1024];
6323 int cDrivers = cbNeeded / sizeof(pDrivers[0]);
6324 for (int i = 0; i < cDrivers; i++)
6325 {
6326 if (GetDeviceDriverBaseName(pDrivers[i], szDriver, sizeof(szDriver) / sizeof(szDriver[0])))
6327 {
6328 if (_tcsnicmp(TEXT("vbox"), szDriver, 4))
6329 continue;
6330 }
6331 else
6332 continue;
6333 if (GetDeviceDriverFileName(pDrivers[i], szDriver, sizeof(szDriver) / sizeof(szDriver[0])))
6334 {
6335 _TCHAR szTmpDrv[1024];
6336 _TCHAR *pszDrv = szDriver;
6337 if (!_tcsncmp(TEXT("\\SystemRoot"), szDriver, 11))
6338 {
6339 _tcscpy_s(szTmpDrv, pszSystemRoot);
6340 _tcsncat_s(szTmpDrv, szDriver + 11, sizeof(szTmpDrv) / sizeof(szTmpDrv[0]) - _tclen(pszSystemRoot));
6341 pszDrv = szTmpDrv;
6342 }
6343 else if (!_tcsncmp(TEXT("\\??\\"), szDriver, 4))
6344 pszDrv = szDriver + 4;
6345
6346 /* Allocate a buffer for version info. Reuse if large enough. */
6347 DWORD cbNewVerInfo = GetFileVersionInfoSize(pszDrv, NULL);
6348 if (cbNewVerInfo > cbVerInfo)
6349 {
6350 if (pVerInfo)
6351 RTMemTmpFree(pVerInfo);
6352 cbVerInfo = cbNewVerInfo;
6353 pVerInfo = RTMemTmpAlloc(cbVerInfo);
6354 if (!pVerInfo)
6355 {
6356 AssertLogRelMsgFailed(("RTMemTmpAlloc failed to allocate %d bytes\n", cbVerInfo));
6357 break;
6358 }
6359 }
6360
6361 if (GetFileVersionInfo(pszDrv, NULL, cbVerInfo, pVerInfo))
6362 {
6363 UINT cbSize = 0;
6364 LPBYTE lpBuffer = NULL;
6365 if (VerQueryValue(pVerInfo, TEXT("\\"), (VOID FAR* FAR*)&lpBuffer, &cbSize))
6366 {
6367 if (cbSize)
6368 {
6369 VS_FIXEDFILEINFO *pFileInfo = (VS_FIXEDFILEINFO *)lpBuffer;
6370 if (pFileInfo->dwSignature == 0xfeef04bd)
6371 {
6372 LogRel((" %ls (Version: %d.%d.%d.%d)\n", pszDrv,
6373 (pFileInfo->dwFileVersionMS >> 16) & 0xffff,
6374 (pFileInfo->dwFileVersionMS >> 0) & 0xffff,
6375 (pFileInfo->dwFileVersionLS >> 16) & 0xffff,
6376 (pFileInfo->dwFileVersionLS >> 0) & 0xffff));
6377 }
6378 }
6379 }
6380 }
6381 }
6382 }
6383
6384 }
6385 while (0);
6386
6387 if (pVerInfo)
6388 RTMemTmpFree(pVerInfo);
6389
6390 if (pDrivers != aDrivers)
6391 RTMemTmpFree(pDrivers);
6392
6393 if (pszSystemRoot != szSystemRoot)
6394 RTMemTmpFree(pszSystemRoot);
6395}
6396#else /* !RT_OS_WINDOWS */
6397void VirtualBox::i_reportDriverVersions(void)
6398{
6399}
6400#endif /* !RT_OS_WINDOWS */
6401
6402#if defined(RT_OS_WINDOWS) && defined(VBOXSVC_WITH_CLIENT_WATCHER)
6403
6404# include <psapi.h> /* for GetProcessImageFileNameW */
6405
6406/**
6407 * Callout from the wrapper.
6408 */
6409void VirtualBox::i_callHook(const char *a_pszFunction)
6410{
6411 RT_NOREF(a_pszFunction);
6412
6413 /*
6414 * Let'see figure out who is calling.
6415 * Note! Requires Vista+, so skip this entirely on older systems.
6416 */
6417 if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
6418 {
6419 RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
6420 RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
6421 if ( rcRpc == RPC_S_OK
6422 && CallAttribs.ClientPID != 0)
6423 {
6424 RTPROCESS const pidClient = (RTPROCESS)(uintptr_t)CallAttribs.ClientPID;
6425 if (pidClient != RTProcSelf())
6426 {
6427 /** @todo LogRel2 later: */
6428 LogRel(("i_callHook: %Rfn [ClientPID=%#zx/%zu IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid]\n",
6429 a_pszFunction, CallAttribs.ClientPID, CallAttribs.ClientPID, CallAttribs.IsClientLocal,
6430 CallAttribs.ProtocolSequence, CallAttribs.CallStatus, CallAttribs.CallType, CallAttribs.OpNum,
6431 &CallAttribs.InterfaceUuid));
6432
6433 /*
6434 * Do we know this client PID already?
6435 */
6436 RTCritSectRwEnterShared(&m->WatcherCritSect);
6437 WatchedClientProcessMap::iterator It = m->WatchedProcesses.find(pidClient);
6438 if (It != m->WatchedProcesses.end())
6439 RTCritSectRwLeaveShared(&m->WatcherCritSect); /* Known process, nothing to do. */
6440 else
6441 {
6442 /* This is a new client process, start watching it. */
6443 RTCritSectRwLeaveShared(&m->WatcherCritSect);
6444 i_watchClientProcess(pidClient, a_pszFunction);
6445 }
6446 }
6447 }
6448 else
6449 LogRel(("i_callHook: %Rfn - rcRpc=%#x ClientPID=%#zx/%zu !! [IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid]\n",
6450 a_pszFunction, rcRpc, CallAttribs.ClientPID, CallAttribs.ClientPID, CallAttribs.IsClientLocal,
6451 CallAttribs.ProtocolSequence, CallAttribs.CallStatus, CallAttribs.CallType, CallAttribs.OpNum,
6452 &CallAttribs.InterfaceUuid));
6453 }
6454}
6455
6456
6457/**
6458 * Watches @a a_pidClient for termination.
6459 *
6460 * @returns true if successfully enabled watching of it, false if not.
6461 * @param a_pidClient The PID to watch.
6462 * @param a_pszFunction The function we which we detected the client in.
6463 */
6464bool VirtualBox::i_watchClientProcess(RTPROCESS a_pidClient, const char *a_pszFunction)
6465{
6466 RT_NOREF_PV(a_pszFunction);
6467
6468 /*
6469 * Open the client process.
6470 */
6471 HANDLE hClient = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE /*fInherit*/, a_pidClient);
6472 if (hClient == NULL)
6473 hClient = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE , a_pidClient);
6474 if (hClient == NULL)
6475 hClient = OpenProcess(SYNCHRONIZE, FALSE , a_pidClient);
6476 AssertLogRelMsgReturn(hClient != NULL, ("pidClient=%d (%#x) err=%d\n", a_pidClient, a_pidClient, GetLastError()),
6477 m->fWatcherIsReliable = false);
6478
6479 /*
6480 * Create a new watcher structure and try add it to the map.
6481 */
6482 bool fRet = true;
6483 WatchedClientProcess *pWatched = new (std::nothrow) WatchedClientProcess(a_pidClient, hClient);
6484 if (pWatched)
6485 {
6486 RTCritSectRwEnterExcl(&m->WatcherCritSect);
6487
6488 WatchedClientProcessMap::iterator It = m->WatchedProcesses.find(a_pidClient);
6489 if (It == m->WatchedProcesses.end())
6490 {
6491 try
6492 {
6493 m->WatchedProcesses.insert(WatchedClientProcessMap::value_type(a_pidClient, pWatched));
6494 }
6495 catch (std::bad_alloc &)
6496 {
6497 fRet = false;
6498 }
6499 if (fRet)
6500 {
6501 /*
6502 * Schedule it on a watcher thread.
6503 */
6504 /** @todo later. */
6505 RTCritSectRwLeaveExcl(&m->WatcherCritSect);
6506 }
6507 else
6508 {
6509 RTCritSectRwLeaveExcl(&m->WatcherCritSect);
6510 delete pWatched;
6511 LogRel(("VirtualBox::i_watchClientProcess: out of memory inserting into client map!\n"));
6512 }
6513 }
6514 else
6515 {
6516 /*
6517 * Someone raced us here, we lost.
6518 */
6519 RTCritSectRwLeaveExcl(&m->WatcherCritSect);
6520 delete pWatched;
6521 }
6522 }
6523 else
6524 {
6525 LogRel(("VirtualBox::i_watchClientProcess: out of memory!\n"));
6526 CloseHandle(hClient);
6527 m->fWatcherIsReliable = fRet = false;
6528 }
6529 return fRet;
6530}
6531
6532
6533/** Logs the RPC caller info to the release log. */
6534/*static*/ void VirtualBox::i_logCaller(const char *a_pszFormat, ...)
6535{
6536 if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
6537 {
6538 char szTmp[80];
6539 va_list va;
6540 va_start(va, a_pszFormat);
6541 RTStrPrintfV(szTmp, sizeof(szTmp), a_pszFormat, va);
6542 va_end(va);
6543
6544 RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
6545 RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
6546
6547 RTUTF16 wszProcName[256];
6548 wszProcName[0] = '\0';
6549 if (rcRpc == 0 && CallAttribs.ClientPID != 0)
6550 {
6551 HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)(uintptr_t)CallAttribs.ClientPID);
6552 if (hProcess)
6553 {
6554 RT_ZERO(wszProcName);
6555 GetProcessImageFileNameW(hProcess, wszProcName, RT_ELEMENTS(wszProcName) - 1);
6556 CloseHandle(hProcess);
6557 }
6558 }
6559 LogRel(("%s [rcRpc=%#x ClientPID=%#zx/%zu (%ls) IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid]\n",
6560 szTmp, rcRpc, CallAttribs.ClientPID, CallAttribs.ClientPID, wszProcName, CallAttribs.IsClientLocal,
6561 CallAttribs.ProtocolSequence, CallAttribs.CallStatus, CallAttribs.CallType, CallAttribs.OpNum,
6562 &CallAttribs.InterfaceUuid));
6563 }
6564}
6565
6566#endif /* RT_OS_WINDOWS && VBOXSVC_WITH_CLIENT_WATCHER */
6567
6568
6569/* 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