VirtualBox

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

Last change on this file since 103131 was 101593, checked in by vboxsync, 12 months ago

doc/manual,Frontends/VBoxManage,Main/{Global,GuestOSType,VirtualBox.xidl}:
Revisit the OS guest OS changes which added a new 'variant' field to the
Global::sOSTypes[] table along with a new VBoxManage subcommand and
IVirtualBox methods for accessing the guest OS variant information
(r159137). After discussion with the documentation team the identifier
'variant' is being changed to the clearer 'subtype' which also doesn't
overlap with the 'variant' terminology used with medium management
(IMedium::variant(), the MediumVariant enum, etc.). bugref:5936

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 225.6 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 101593 2023-10-25 15:37:09Z 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.virtualbox.org.
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
473/**
474 * VirtualBox firmware descriptor.
475 */
476typedef struct VBOXFWDESC
477{
478 FirmwareType_T enmType;
479 bool fBuiltIn;
480 const char *pszFileName;
481 const char *pszUrl;
482} VBoxFwDesc;
483typedef const VBOXFWDESC *PVBOXFWDESC;
484
485
486// constructor / destructor
487/////////////////////////////////////////////////////////////////////////////
488
489DEFINE_EMPTY_CTOR_DTOR(VirtualBox)
490
491HRESULT VirtualBox::FinalConstruct()
492{
493 LogRelFlowThisFuncEnter();
494 LogRel(("VirtualBox: object creation starts\n"));
495
496 BaseFinalConstruct();
497
498 HRESULT hrc = init();
499
500 LogRelFlowThisFuncLeave();
501 LogRel(("VirtualBox: object created\n"));
502
503 return hrc;
504}
505
506void VirtualBox::FinalRelease()
507{
508 LogRelFlowThisFuncEnter();
509 LogRel(("VirtualBox: object deletion starts\n"));
510
511 uninit();
512
513 BaseFinalRelease();
514
515 LogRel(("VirtualBox: object deleted\n"));
516 LogRelFlowThisFuncLeave();
517}
518
519// public initializer/uninitializer for internal purposes only
520/////////////////////////////////////////////////////////////////////////////
521
522/**
523 * Initializes the VirtualBox object.
524 *
525 * @return COM result code
526 */
527HRESULT VirtualBox::init()
528{
529 LogRelFlowThisFuncEnter();
530 /* Enclose the state transition NotReady->InInit->Ready */
531 AutoInitSpan autoInitSpan(this);
532 AssertReturn(autoInitSpan.isOk(), E_FAIL);
533
534 /* Locking this object for writing during init sounds a bit paradoxical,
535 * but in the current locking mess this avoids that some code gets a
536 * read lock and later calls code which wants the same write lock. */
537 AutoWriteLock lock(this COMMA_LOCKVAL_SRC_POS);
538
539 // allocate our instance data
540 m = new Data;
541
542 LogFlow(("===========================================================\n"));
543 LogFlowThisFuncEnter();
544
545 if (sVersion.isEmpty())
546 sVersion = RTBldCfgVersion();
547 if (sVersionNormalized.isEmpty())
548 {
549 Utf8Str tmp(RTBldCfgVersion());
550 if (tmp.endsWith(VBOX_BUILD_PUBLISHER))
551 tmp = tmp.substr(0, tmp.length() - strlen(VBOX_BUILD_PUBLISHER));
552 sVersionNormalized = tmp;
553 }
554 sRevision = RTBldCfgRevision();
555 if (sPackageType.isEmpty())
556 sPackageType = VBOX_PACKAGE_STRING;
557 if (sAPIVersion.isEmpty())
558 sAPIVersion = VBOX_API_VERSION_STRING;
559 if (!spMtxNatNetworkNameToRefCountLock)
560 spMtxNatNetworkNameToRefCountLock = new RWLockHandle(LOCKCLASS_VIRTUALBOXOBJECT);
561
562 LogFlowThisFunc(("Version: %s, Package: %s, API Version: %s\n", sVersion.c_str(), sPackageType.c_str(), sAPIVersion.c_str()));
563
564 /* Important: DO NOT USE any kind of "early return" (except the single
565 * one above, checking the init span success) in this method. It is vital
566 * for correct error handling that it has only one point of return, which
567 * does all the magic on COM to signal object creation success and
568 * reporting the error later for every API method. COM translates any
569 * unsuccessful object creation to REGDB_E_CLASSNOTREG errors or similar
570 * unhelpful ones which cause us a lot of grief with troubleshooting. */
571
572 HRESULT hrc = S_OK;
573 bool fCreate = false;
574 try
575 {
576 /* Create the event source early as we may fire async event during settings loading (media). */
577 hrc = unconst(m->pEventSource).createObject();
578 if (FAILED(hrc)) throw hrc;
579 hrc = m->pEventSource->init();
580 if (FAILED(hrc)) throw hrc;
581
582
583 /* Get the VirtualBox home directory. */
584 {
585 char szHomeDir[RTPATH_MAX];
586 int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir));
587 if (RT_FAILURE(vrc))
588 throw setErrorBoth(E_FAIL, vrc,
589 tr("Could not create the VirtualBox home directory '%s' (%Rrc)"),
590 szHomeDir, vrc);
591
592 unconst(m->strHomeDir) = szHomeDir;
593 }
594
595 LogRel(("Home directory: '%s'\n", m->strHomeDir.c_str()));
596
597 i_reportDriverVersions();
598
599 /* Create the critical section protecting the cryptographic module handle. */
600 {
601 int vrc = RTCritSectInit(&m->CritSectModCrypto);
602 if (RT_FAILURE(vrc))
603 throw setErrorBoth(E_FAIL, vrc,
604 tr("Could not create the cryptographic module critical section (%Rrc)"),
605 vrc);
606
607 }
608
609 /* compose the VirtualBox.xml file name */
610 unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s",
611 m->strHomeDir.c_str(),
612 RTPATH_DELIMITER,
613 VBOX_GLOBAL_SETTINGS_FILE);
614 // load and parse VirtualBox.xml; this will throw on XML or logic errors
615 try
616 {
617 m->pMainConfigFile = new settings::MainConfigFile(&m->strSettingsFilePath);
618 }
619 catch (xml::EIPRTFailure &e)
620 {
621 // this is thrown by the XML backend if the RTOpen() call fails;
622 // only if the main settings file does not exist, create it,
623 // if there's something more serious, then do fail!
624 if (e.getStatus() == VERR_FILE_NOT_FOUND)
625 fCreate = true;
626 else
627 throw;
628 }
629
630 if (fCreate)
631 m->pMainConfigFile = new settings::MainConfigFile(NULL);
632
633#ifdef VBOX_WITH_RESOURCE_USAGE_API
634 /* create the performance collector object BEFORE host */
635 unconst(m->pPerformanceCollector).createObject();
636 hrc = m->pPerformanceCollector->init();
637 ComAssertComRCThrowRC(hrc);
638#endif /* VBOX_WITH_RESOURCE_USAGE_API */
639
640 /* create the host object early, machines will need it */
641 unconst(m->pHost).createObject();
642 hrc = m->pHost->init(this);
643 ComAssertComRCThrowRC(hrc);
644
645 hrc = m->pHost->i_loadSettings(m->pMainConfigFile->host);
646 if (FAILED(hrc)) throw hrc;
647
648 /*
649 * Create autostart database object early, because the system properties
650 * might need it.
651 */
652 unconst(m->pAutostartDb) = new AutostartDb;
653
654 /* create the system properties object, someone may need it too */
655 hrc = unconst(m->pSystemProperties).createObject();
656 if (SUCCEEDED(hrc))
657 hrc = m->pSystemProperties->init(this);
658 ComAssertComRCThrowRC(hrc);
659
660 hrc = m->pSystemProperties->i_loadSettings(m->pMainConfigFile->systemProperties);
661 if (FAILED(hrc)) throw hrc;
662#ifdef VBOX_WITH_MAIN_NLS
663 m->pVBoxTranslator = VirtualBoxTranslator::instance();
664 /* Do not throw an exception on language errors.
665 * Just do not use translation. */
666 if (m->pVBoxTranslator)
667 {
668
669 char szNlsPath[RTPATH_MAX];
670 int vrc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
671 if (RT_SUCCESS(vrc))
672 vrc = RTPathAppend(szNlsPath, sizeof(szNlsPath), "nls" RTPATH_SLASH_STR "VirtualBoxAPI");
673
674 if (RT_SUCCESS(vrc))
675 {
676 vrc = m->pVBoxTranslator->registerTranslation(szNlsPath, true, &m->pTrComponent);
677 if (RT_SUCCESS(vrc))
678 {
679 com::Utf8Str strLocale;
680 HRESULT hrc2 = m->pSystemProperties->getLanguageId(strLocale);
681 if (SUCCEEDED(hrc2))
682 {
683 vrc = m->pVBoxTranslator->i_loadLanguage(strLocale.c_str());
684 if (RT_FAILURE(vrc))
685 {
686 hrc2 = Global::vboxStatusCodeToCOM(vrc);
687 LogRel(("Load language failed (%Rhrc).\n", hrc2));
688 }
689 }
690 else
691 {
692 LogRel(("Getting language settings failed (%Rhrc).\n", hrc2));
693 m->pVBoxTranslator->release();
694 m->pVBoxTranslator = NULL;
695 m->pTrComponent = NULL;
696 }
697 }
698 else
699 {
700 HRESULT hrc2 = Global::vboxStatusCodeToCOM(vrc);
701 LogRel(("Register translation failed (%Rhrc).\n", hrc2));
702 m->pVBoxTranslator->release();
703 m->pVBoxTranslator = NULL;
704 m->pTrComponent = NULL;
705 }
706 }
707 else
708 {
709 HRESULT hrc2 = Global::vboxStatusCodeToCOM(vrc);
710 LogRel(("Path constructing failed (%Rhrc).\n", hrc2));
711 m->pVBoxTranslator->release();
712 m->pVBoxTranslator = NULL;
713 m->pTrComponent = NULL;
714 }
715 }
716 else
717 LogRel(("Translator creation failed.\n"));
718#endif
719
720#ifdef VBOX_WITH_EXTPACK
721 /*
722 * Initialize extension pack manager before system properties because
723 * it is required for the VD plugins.
724 */
725 hrc = unconst(m->ptrExtPackManager).createObject();
726 if (SUCCEEDED(hrc))
727 hrc = m->ptrExtPackManager->initExtPackManager(this, VBOXEXTPACKCTX_PER_USER_DAEMON);
728 if (FAILED(hrc))
729 throw hrc;
730#endif
731 /* guest OS type objects, needed by machines */
732 for (size_t i = 0; i < Global::cOSTypes; ++i)
733 {
734 ComObjPtr<GuestOSType> guestOSTypeObj;
735 hrc = guestOSTypeObj.createObject();
736 if (SUCCEEDED(hrc))
737 {
738 hrc = guestOSTypeObj->init(Global::sOSTypes[i]);
739 if (SUCCEEDED(hrc))
740 m->allGuestOSTypes.addChild(guestOSTypeObj);
741 }
742 ComAssertComRCThrowRC(hrc);
743 }
744
745 /* all registered media, needed by machines */
746 if (FAILED(hrc = initMedia(m->uuidMediaRegistry,
747 m->pMainConfigFile->mediaRegistry,
748 Utf8Str::Empty))) // const Utf8Str &machineFolder
749 throw hrc;
750
751 /* machines */
752 if (FAILED(hrc = initMachines()))
753 throw hrc;
754
755#ifdef DEBUG
756 LogFlowThisFunc(("Dumping media backreferences\n"));
757 i_dumpAllBackRefs();
758#endif
759
760 /* net services - dhcp services */
761 for (settings::DHCPServersList::const_iterator it = m->pMainConfigFile->llDhcpServers.begin();
762 it != m->pMainConfigFile->llDhcpServers.end();
763 ++it)
764 {
765 const settings::DHCPServer &data = *it;
766
767 ComObjPtr<DHCPServer> pDhcpServer;
768 if (SUCCEEDED(hrc = pDhcpServer.createObject()))
769 hrc = pDhcpServer->init(this, data);
770 if (FAILED(hrc)) throw hrc;
771
772 hrc = i_registerDHCPServer(pDhcpServer, false /* aSaveRegistry */);
773 if (FAILED(hrc)) throw hrc;
774 }
775
776 /* net services - nat networks */
777 for (settings::NATNetworksList::const_iterator it = m->pMainConfigFile->llNATNetworks.begin();
778 it != m->pMainConfigFile->llNATNetworks.end();
779 ++it)
780 {
781 const settings::NATNetwork &net = *it;
782
783 ComObjPtr<NATNetwork> pNATNetwork;
784 hrc = pNATNetwork.createObject();
785 AssertComRCThrowRC(hrc);
786 hrc = pNATNetwork->init(this, "");
787 AssertComRCThrowRC(hrc);
788 hrc = pNATNetwork->i_loadSettings(net);
789 AssertComRCThrowRC(hrc);
790 hrc = i_registerNATNetwork(pNATNetwork, false /* aSaveRegistry */);
791 AssertComRCThrowRC(hrc);
792 }
793
794#ifdef VBOX_WITH_VMNET
795 /* host-only networks */
796 for (settings::HostOnlyNetworksList::const_iterator it = m->pMainConfigFile->llHostOnlyNetworks.begin();
797 it != m->pMainConfigFile->llHostOnlyNetworks.end();
798 ++it)
799 {
800 ComObjPtr<HostOnlyNetwork> pHostOnlyNetwork;
801 hrc = pHostOnlyNetwork.createObject();
802 AssertComRCThrowRC(hrc);
803 hrc = pHostOnlyNetwork->init(this, "TODO???");
804 AssertComRCThrowRC(hrc);
805 hrc = pHostOnlyNetwork->i_loadSettings(*it);
806 AssertComRCThrowRC(hrc);
807 m->allHostOnlyNetworks.addChild(pHostOnlyNetwork);
808 AssertComRCThrowRC(hrc);
809 }
810#endif /* VBOX_WITH_VMNET */
811
812#ifdef VBOX_WITH_CLOUD_NET
813 /* net services - cloud networks */
814 for (settings::CloudNetworksList::const_iterator it = m->pMainConfigFile->llCloudNetworks.begin();
815 it != m->pMainConfigFile->llCloudNetworks.end();
816 ++it)
817 {
818 ComObjPtr<CloudNetwork> pCloudNetwork;
819 hrc = pCloudNetwork.createObject();
820 AssertComRCThrowRC(hrc);
821 hrc = pCloudNetwork->init(this, "");
822 AssertComRCThrowRC(hrc);
823 hrc = pCloudNetwork->i_loadSettings(*it);
824 AssertComRCThrowRC(hrc);
825 m->allCloudNetworks.addChild(pCloudNetwork);
826 AssertComRCThrowRC(hrc);
827 }
828#endif /* VBOX_WITH_CLOUD_NET */
829
830 /* cloud provider manager */
831 hrc = unconst(m->pCloudProviderManager).createObject();
832 if (SUCCEEDED(hrc))
833 hrc = m->pCloudProviderManager->init(this);
834 ComAssertComRCThrowRC(hrc);
835 if (FAILED(hrc)) throw hrc;
836 }
837 catch (HRESULT err)
838 {
839 /* we assume that error info is set by the thrower */
840 hrc = err;
841 }
842 catch (...)
843 {
844 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
845 }
846
847 if (SUCCEEDED(hrc))
848 {
849 /* set up client monitoring */
850 try
851 {
852 unconst(m->pClientWatcher) = new ClientWatcher(this);
853 if (!m->pClientWatcher->isReady())
854 {
855 delete m->pClientWatcher;
856 unconst(m->pClientWatcher) = NULL;
857 hrc = E_FAIL;
858 }
859 }
860 catch (std::bad_alloc &)
861 {
862 hrc = E_OUTOFMEMORY;
863 }
864 }
865
866 if (SUCCEEDED(hrc))
867 {
868 try
869 {
870 /* start the async event handler thread */
871 int vrc = RTThreadCreate(&unconst(m->threadAsyncEvent),
872 AsyncEventHandler,
873 &unconst(m->pAsyncEventQ),
874 0,
875 RTTHREADTYPE_MAIN_WORKER,
876 RTTHREADFLAGS_WAITABLE,
877 "EventHandler");
878 ComAssertRCThrow(vrc, E_FAIL);
879
880 /* wait until the thread sets m->pAsyncEventQ */
881 RTThreadUserWait(m->threadAsyncEvent, RT_INDEFINITE_WAIT);
882 ComAssertThrow(m->pAsyncEventQ, E_FAIL);
883 }
884 catch (HRESULT hrcXcpt)
885 {
886 hrc = hrcXcpt;
887 }
888 }
889
890#ifdef VBOX_WITH_EXTPACK
891 /* Let the extension packs have a go at things. */
892 if (SUCCEEDED(hrc))
893 {
894 lock.release();
895 m->ptrExtPackManager->i_callAllVirtualBoxReadyHooks();
896 }
897#endif
898
899 /* Confirm a successful initialization when it's the case. Must be last,
900 * as on failure it will uninitialize the object. */
901 if (SUCCEEDED(hrc))
902 autoInitSpan.setSucceeded();
903 else
904 autoInitSpan.setFailed(hrc);
905
906 LogFlowThisFunc(("hrc=%Rhrc\n", hrc));
907 LogFlowThisFuncLeave();
908 LogFlow(("===========================================================\n"));
909 /* Unconditionally return success, because the error return is delayed to
910 * the attribute/method calls through the InitFailed object state. */
911 return S_OK;
912}
913
914HRESULT VirtualBox::initMachines()
915{
916 for (settings::MachinesRegistry::const_iterator it = m->pMainConfigFile->llMachines.begin();
917 it != m->pMainConfigFile->llMachines.end();
918 ++it)
919 {
920 HRESULT hrc = S_OK;
921 const settings::MachineRegistryEntry &xmlMachine = *it;
922 Guid uuid = xmlMachine.uuid;
923
924 /* Check if machine record has valid parameters. */
925 if (xmlMachine.strSettingsFile.isEmpty() || uuid.isZero())
926 {
927 LogRel(("Skipped invalid machine record.\n"));
928 continue;
929 }
930
931 ComObjPtr<Machine> pMachine;
932 com::Utf8Str strPassword;
933 if (SUCCEEDED(hrc = pMachine.createObject()))
934 {
935 hrc = pMachine->initFromSettings(this, xmlMachine.strSettingsFile, &uuid, strPassword);
936 if (SUCCEEDED(hrc))
937 hrc = i_registerMachine(pMachine);
938 if (FAILED(hrc))
939 return hrc;
940 }
941 }
942
943 return S_OK;
944}
945
946/**
947 * Loads a media registry from XML and adds the media contained therein to
948 * the global lists of known media.
949 *
950 * This now (4.0) gets called from two locations:
951 *
952 * -- VirtualBox::init(), to load the global media registry from VirtualBox.xml;
953 *
954 * -- Machine::loadMachineDataFromSettings(), to load the per-machine registry
955 * from machine XML, for machines created with VirtualBox 4.0 or later.
956 *
957 * In both cases, the media found are added to the global lists so the
958 * global arrays of media (including the GUI's virtual media manager)
959 * continue to work as before.
960 *
961 * @param uuidRegistry The UUID of the media registry. This is either the
962 * transient UUID created at VirtualBox startup for the global registry or
963 * a machine ID.
964 * @param mediaRegistry The XML settings structure to load, either from VirtualBox.xml
965 * or a machine XML.
966 * @param strMachineFolder The folder of the machine.
967 * @return
968 */
969HRESULT VirtualBox::initMedia(const Guid &uuidRegistry,
970 const settings::MediaRegistry &mediaRegistry,
971 const Utf8Str &strMachineFolder)
972{
973 LogFlow(("VirtualBox::initMedia ENTERING, uuidRegistry=%s, strMachineFolder=%s\n",
974 uuidRegistry.toString().c_str(),
975 strMachineFolder.c_str()));
976
977 AutoWriteLock treeLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
978
979 // the order of notification is critical for GUI, so use std::list<std::pair> instead of map
980 std::list<std::pair<Guid, DeviceType_T> > uIdsForNotify;
981
982 HRESULT hrc = S_OK;
983 settings::MediaList::const_iterator it;
984 for (it = mediaRegistry.llHardDisks.begin();
985 it != mediaRegistry.llHardDisks.end();
986 ++it)
987 {
988 const settings::Medium &xmlHD = *it;
989
990 hrc = Medium::initFromSettings(this,
991 DeviceType_HardDisk,
992 uuidRegistry,
993 strMachineFolder,
994 xmlHD,
995 treeLock,
996 uIdsForNotify);
997 if (FAILED(hrc)) return hrc;
998 }
999
1000 for (it = mediaRegistry.llDvdImages.begin();
1001 it != mediaRegistry.llDvdImages.end();
1002 ++it)
1003 {
1004 const settings::Medium &xmlDvd = *it;
1005
1006 hrc = Medium::initFromSettings(this,
1007 DeviceType_DVD,
1008 uuidRegistry,
1009 strMachineFolder,
1010 xmlDvd,
1011 treeLock,
1012 uIdsForNotify);
1013 if (FAILED(hrc)) return hrc;
1014 }
1015
1016 for (it = mediaRegistry.llFloppyImages.begin();
1017 it != mediaRegistry.llFloppyImages.end();
1018 ++it)
1019 {
1020 const settings::Medium &xmlFloppy = *it;
1021
1022 hrc = Medium::initFromSettings(this,
1023 DeviceType_Floppy,
1024 uuidRegistry,
1025 strMachineFolder,
1026 xmlFloppy,
1027 treeLock,
1028 uIdsForNotify);
1029 if (FAILED(hrc)) return hrc;
1030 }
1031
1032 for (std::list<std::pair<Guid, DeviceType_T> >::const_iterator itItem = uIdsForNotify.begin();
1033 itItem != uIdsForNotify.end();
1034 ++itItem)
1035 {
1036 i_onMediumRegistered(itItem->first, itItem->second, TRUE);
1037 }
1038
1039 LogFlow(("VirtualBox::initMedia LEAVING\n"));
1040
1041 return S_OK;
1042}
1043
1044void VirtualBox::uninit()
1045{
1046 /* Must be done outside the AutoUninitSpan, as it expects AutoCaller to
1047 * be successful. This needs additional checks to protect against double
1048 * uninit, as then the pointer is NULL. */
1049 if (RT_VALID_PTR(m))
1050 {
1051 Assert(!m->uRegistryNeedsSaving);
1052 if (m->uRegistryNeedsSaving)
1053 i_saveSettings();
1054 }
1055
1056 /* Enclose the state transition Ready->InUninit->NotReady */
1057 AutoUninitSpan autoUninitSpan(this);
1058 if (autoUninitSpan.uninitDone())
1059 return;
1060
1061 LogFlow(("===========================================================\n"));
1062 LogFlowThisFuncEnter();
1063 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1064
1065 /* tell all our child objects we've been uninitialized */
1066
1067 LogFlowThisFunc(("Uninitializing machines (%d)...\n", m->allMachines.size()));
1068 if (m->pHost)
1069 {
1070 /* It is necessary to hold the VirtualBox and Host locks here because
1071 we may have to uninitialize SessionMachines. */
1072 AutoMultiWriteLock2 multilock(this, m->pHost COMMA_LOCKVAL_SRC_POS);
1073 m->allMachines.uninitAll();
1074 }
1075 else
1076 m->allMachines.uninitAll();
1077 m->allFloppyImages.uninitAll();
1078 m->allDVDImages.uninitAll();
1079 m->allHardDisks.uninitAll();
1080 m->allDHCPServers.uninitAll();
1081
1082 m->mapProgressOperations.clear();
1083
1084 m->allGuestOSTypes.uninitAll();
1085
1086 /* Note that we release singleton children after we've all other children.
1087 * In some cases this is important because these other children may use
1088 * some resources of the singletons which would prevent them from
1089 * uninitializing (as for example, mSystemProperties which owns
1090 * MediumFormat objects which Medium objects refer to) */
1091 if (m->pCloudProviderManager)
1092 {
1093 m->pCloudProviderManager->uninit();
1094 unconst(m->pCloudProviderManager).setNull();
1095 }
1096
1097 if (m->pSystemProperties)
1098 {
1099 m->pSystemProperties->uninit();
1100 unconst(m->pSystemProperties).setNull();
1101 }
1102
1103 if (m->pHost)
1104 {
1105 m->pHost->uninit();
1106 unconst(m->pHost).setNull();
1107 }
1108
1109#ifdef VBOX_WITH_RESOURCE_USAGE_API
1110 if (m->pPerformanceCollector)
1111 {
1112 m->pPerformanceCollector->uninit();
1113 unconst(m->pPerformanceCollector).setNull();
1114 }
1115#endif /* VBOX_WITH_RESOURCE_USAGE_API */
1116
1117 /*
1118 * Unload the cryptographic module if loaded before the extension
1119 * pack manager is torn down.
1120 */
1121 Assert(!m->cRefsCrypto);
1122 if (m->hLdrModCrypto != NIL_RTLDRMOD)
1123 {
1124 m->pCryptoIf = NULL;
1125
1126 int vrc = RTLdrClose(m->hLdrModCrypto);
1127 AssertRC(vrc);
1128 m->hLdrModCrypto = NIL_RTLDRMOD;
1129 }
1130
1131 RTCritSectDelete(&m->CritSectModCrypto);
1132
1133#ifdef VBOX_WITH_EXTPACK
1134 if (m->ptrExtPackManager)
1135 {
1136 m->ptrExtPackManager->uninit();
1137 unconst(m->ptrExtPackManager).setNull();
1138 }
1139#endif
1140
1141 LogFlowThisFunc(("Terminating the async event handler...\n"));
1142 if (m->threadAsyncEvent != NIL_RTTHREAD)
1143 {
1144 /* signal to exit the event loop */
1145 if (RT_SUCCESS(m->pAsyncEventQ->interruptEventQueueProcessing()))
1146 {
1147 /*
1148 * Wait for thread termination (only after we've successfully
1149 * interrupted the event queue processing!)
1150 */
1151 int vrc = RTThreadWait(m->threadAsyncEvent, 60000, NULL);
1152 if (RT_FAILURE(vrc))
1153 Log1WarningFunc(("RTThreadWait(%RTthrd) -> %Rrc\n", m->threadAsyncEvent, vrc));
1154 }
1155 else
1156 {
1157 AssertMsgFailed(("interruptEventQueueProcessing() failed\n"));
1158 RTThreadWait(m->threadAsyncEvent, 0, NULL);
1159 }
1160
1161 unconst(m->threadAsyncEvent) = NIL_RTTHREAD;
1162 unconst(m->pAsyncEventQ) = NULL;
1163 }
1164
1165 LogFlowThisFunc(("Releasing event source...\n"));
1166 if (m->pEventSource)
1167 {
1168 // Must uninit the event source here, because it makes no sense that
1169 // it survives longer than the base object. If someone gets an event
1170 // with such an event source then that's life and it has to be dealt
1171 // with appropriately on the API client side.
1172 m->pEventSource->uninit();
1173 unconst(m->pEventSource).setNull();
1174 }
1175
1176 LogFlowThisFunc(("Terminating the client watcher...\n"));
1177 if (m->pClientWatcher)
1178 {
1179 delete m->pClientWatcher;
1180 unconst(m->pClientWatcher) = NULL;
1181 }
1182
1183 delete m->pAutostartDb;
1184#ifdef VBOX_WITH_MAIN_NLS
1185 if (m->pVBoxTranslator)
1186 m->pVBoxTranslator->release();
1187#endif
1188 // clean up our instance data
1189 delete m;
1190 m = NULL;
1191
1192 /* Unload hard disk plugin backends. */
1193 VDShutdown();
1194
1195 LogFlowThisFuncLeave();
1196 LogFlow(("===========================================================\n"));
1197}
1198
1199// Wrapped IVirtualBox properties
1200/////////////////////////////////////////////////////////////////////////////
1201HRESULT VirtualBox::getVersion(com::Utf8Str &aVersion)
1202{
1203 aVersion = sVersion;
1204 return S_OK;
1205}
1206
1207HRESULT VirtualBox::getVersionNormalized(com::Utf8Str &aVersionNormalized)
1208{
1209 aVersionNormalized = sVersionNormalized;
1210 return S_OK;
1211}
1212
1213HRESULT VirtualBox::getRevision(ULONG *aRevision)
1214{
1215 *aRevision = sRevision;
1216 return S_OK;
1217}
1218
1219HRESULT VirtualBox::getPackageType(com::Utf8Str &aPackageType)
1220{
1221 aPackageType = sPackageType;
1222 return S_OK;
1223}
1224
1225HRESULT VirtualBox::getAPIVersion(com::Utf8Str &aAPIVersion)
1226{
1227 aAPIVersion = sAPIVersion;
1228 return S_OK;
1229}
1230
1231HRESULT VirtualBox::getAPIRevision(LONG64 *aAPIRevision)
1232{
1233 AssertCompile(VBOX_VERSION_MAJOR < 128 && VBOX_VERSION_MAJOR > 0);
1234 AssertCompile((uint64_t)VBOX_VERSION_MINOR < 256);
1235 uint64_t uRevision = ((uint64_t)VBOX_VERSION_MAJOR << 56)
1236 | ((uint64_t)VBOX_VERSION_MINOR << 48)
1237 | ((uint64_t)VBOX_VERSION_BUILD << 40);
1238
1239 /** @todo This needs to be the same in OSE and non-OSE, preferrably
1240 * only changing when actual API changes happens. */
1241 uRevision |= 1;
1242
1243 *aAPIRevision = (LONG64)uRevision;
1244
1245 return S_OK;
1246}
1247
1248HRESULT VirtualBox::getHomeFolder(com::Utf8Str &aHomeFolder)
1249{
1250 /* mHomeDir is const and doesn't need a lock */
1251 aHomeFolder = m->strHomeDir;
1252 return S_OK;
1253}
1254
1255HRESULT VirtualBox::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
1256{
1257 /* mCfgFile.mName is const and doesn't need a lock */
1258 aSettingsFilePath = m->strSettingsFilePath;
1259 return S_OK;
1260}
1261
1262HRESULT VirtualBox::getHost(ComPtr<IHost> &aHost)
1263{
1264 /* mHost is const, no need to lock */
1265 m->pHost.queryInterfaceTo(aHost.asOutParam());
1266 return S_OK;
1267}
1268
1269HRESULT VirtualBox::getPlatformProperties(PlatformArchitecture_T platformArchitecture,
1270 ComPtr<IPlatformProperties> &aPlatformProperties)
1271{
1272 ComObjPtr<PlatformProperties> platformProperties;
1273 HRESULT hrc = platformProperties.createObject();
1274 AssertComRCReturn(hrc, hrc);
1275
1276 hrc = platformProperties->init(this);
1277 AssertComRCReturn(hrc, hrc);
1278
1279 hrc = platformProperties->i_setArchitecture(platformArchitecture);
1280 AssertComRCReturn(hrc, hrc);
1281
1282 return platformProperties.queryInterfaceTo(aPlatformProperties.asOutParam());
1283}
1284
1285HRESULT VirtualBox::getSystemProperties(ComPtr<ISystemProperties> &aSystemProperties)
1286{
1287 /* mSystemProperties is const, no need to lock */
1288 m->pSystemProperties.queryInterfaceTo(aSystemProperties.asOutParam());
1289 return S_OK;
1290}
1291
1292HRESULT VirtualBox::getMachines(std::vector<ComPtr<IMachine> > &aMachines)
1293{
1294 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1295 aMachines.resize(m->allMachines.size());
1296 size_t i = 0;
1297 for (MachinesOList::const_iterator it= m->allMachines.begin();
1298 it!= m->allMachines.end(); ++it, ++i)
1299 (*it).queryInterfaceTo(aMachines[i].asOutParam());
1300 return S_OK;
1301}
1302
1303HRESULT VirtualBox::getMachineGroups(std::vector<com::Utf8Str> &aMachineGroups)
1304{
1305 std::list<com::Utf8Str> allGroups;
1306
1307 /* get copy of all machine references, to avoid holding the list lock */
1308 MachinesOList::MyList allMachines;
1309 {
1310 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1311 allMachines = m->allMachines.getList();
1312 }
1313 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1314 it != allMachines.end();
1315 ++it)
1316 {
1317 const ComObjPtr<Machine> &pMachine = *it;
1318 AutoCaller autoMachineCaller(pMachine);
1319 if (FAILED(autoMachineCaller.hrc()))
1320 continue;
1321 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1322
1323 if (pMachine->i_isAccessible())
1324 {
1325 const StringsList &thisGroups = pMachine->i_getGroups();
1326 for (StringsList::const_iterator it2 = thisGroups.begin();
1327 it2 != thisGroups.end(); ++it2)
1328 allGroups.push_back(*it2);
1329 }
1330 }
1331
1332 /* throw out any duplicates */
1333 allGroups.sort();
1334 allGroups.unique();
1335 aMachineGroups.resize(allGroups.size());
1336 size_t i = 0;
1337 for (std::list<com::Utf8Str>::const_iterator it = allGroups.begin();
1338 it != allGroups.end(); ++it, ++i)
1339 aMachineGroups[i] = (*it);
1340 return S_OK;
1341}
1342
1343HRESULT VirtualBox::getHardDisks(std::vector<ComPtr<IMedium> > &aHardDisks)
1344{
1345 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1346 aHardDisks.resize(m->allHardDisks.size());
1347 size_t i = 0;
1348 for (MediaOList::const_iterator it = m->allHardDisks.begin();
1349 it != m->allHardDisks.end(); ++it, ++i)
1350 (*it).queryInterfaceTo(aHardDisks[i].asOutParam());
1351 return S_OK;
1352}
1353
1354HRESULT VirtualBox::getDVDImages(std::vector<ComPtr<IMedium> > &aDVDImages)
1355{
1356 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1357 aDVDImages.resize(m->allDVDImages.size());
1358 size_t i = 0;
1359 for (MediaOList::const_iterator it = m->allDVDImages.begin();
1360 it!= m->allDVDImages.end(); ++it, ++i)
1361 (*it).queryInterfaceTo(aDVDImages[i].asOutParam());
1362 return S_OK;
1363}
1364
1365HRESULT VirtualBox::getFloppyImages(std::vector<ComPtr<IMedium> > &aFloppyImages)
1366{
1367 AutoReadLock al(m->allFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1368 aFloppyImages.resize(m->allFloppyImages.size());
1369 size_t i = 0;
1370 for (MediaOList::const_iterator it = m->allFloppyImages.begin();
1371 it != m->allFloppyImages.end(); ++it, ++i)
1372 (*it).queryInterfaceTo(aFloppyImages[i].asOutParam());
1373 return S_OK;
1374}
1375
1376HRESULT VirtualBox::getProgressOperations(std::vector<ComPtr<IProgress> > &aProgressOperations)
1377{
1378 /* protect mProgressOperations */
1379 AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1380 ProgressMap pmap(m->mapProgressOperations);
1381 /* Can release lock now. The following code works on a copy of the map. */
1382 safeLock.release();
1383 aProgressOperations.resize(pmap.size());
1384 size_t i = 0;
1385 for (ProgressMap::iterator it = pmap.begin(); it != pmap.end(); ++it, ++i)
1386 it->second.queryInterfaceTo(aProgressOperations[i].asOutParam());
1387 return S_OK;
1388}
1389
1390
1391/**
1392 * Returns all supported guest OS types for one ore more platform architecture(s).
1393 *
1394 * @returns HRESULT
1395 * @param aArchitectures Platform architectures to return supported guest OS types for.
1396 * If empty, all supported guest OS for all platform architectures will be returned.
1397 * @param aGuestOSTypes Where to return the supported guest OS types.
1398 * Will be empty if none supported.
1399 */
1400HRESULT VirtualBox::i_getSupportedGuestOSTypes(std::vector<PlatformArchitecture_T> aArchitectures,
1401 std::vector<ComPtr<IGuestOSType> > &aGuestOSTypes)
1402{
1403 AutoReadLock al(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1404
1405 aGuestOSTypes.clear();
1406
1407 /** @todo We might want to redo this at some point, to better group / hash this. */
1408
1409 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin(); it != m->allGuestOSTypes.end(); ++it)
1410 {
1411 bool fFound = false;
1412 if (aArchitectures.size() == 0) /* If empty, we add all types we have. */
1413 fFound = true;
1414 else
1415 {
1416 for (size_t i = 0; i < aArchitectures.size(); i++)
1417 {
1418 if (aArchitectures[i] == (*it)->i_platformArchitecture())
1419 {
1420 fFound = true;
1421 break;
1422 }
1423 }
1424 }
1425
1426 if (fFound)
1427 {
1428 ComPtr<IGuestOSType> osType;
1429 (*it).queryInterfaceTo(osType.asOutParam());
1430 aGuestOSTypes.push_back(osType);
1431 }
1432 }
1433
1434 return S_OK;
1435}
1436
1437HRESULT VirtualBox::getGuestOSTypes(std::vector<ComPtr<IGuestOSType> > &aGuestOSTypes)
1438{
1439 std::vector<PlatformArchitecture_T> vecArchitectures; /* Stays empty to return all guest OS types. */
1440 return VirtualBox::i_getSupportedGuestOSTypes(vecArchitectures, aGuestOSTypes);
1441}
1442
1443HRESULT VirtualBox::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
1444{
1445 NOREF(aSharedFolders);
1446
1447 return setError(E_NOTIMPL, tr("Not yet implemented"));
1448}
1449
1450HRESULT VirtualBox::getPerformanceCollector(ComPtr<IPerformanceCollector> &aPerformanceCollector)
1451{
1452#ifdef VBOX_WITH_RESOURCE_USAGE_API
1453 /* mPerformanceCollector is const, no need to lock */
1454 m->pPerformanceCollector.queryInterfaceTo(aPerformanceCollector.asOutParam());
1455
1456 return S_OK;
1457#else /* !VBOX_WITH_RESOURCE_USAGE_API */
1458 NOREF(aPerformanceCollector);
1459 ReturnComNotImplemented();
1460#endif /* !VBOX_WITH_RESOURCE_USAGE_API */
1461}
1462
1463HRESULT VirtualBox::getDHCPServers(std::vector<ComPtr<IDHCPServer> > &aDHCPServers)
1464{
1465 AutoReadLock al(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1466 aDHCPServers.resize(m->allDHCPServers.size());
1467 size_t i = 0;
1468 for (DHCPServersOList::const_iterator it= m->allDHCPServers.begin();
1469 it!= m->allDHCPServers.end(); ++it, ++i)
1470 (*it).queryInterfaceTo(aDHCPServers[i].asOutParam());
1471 return S_OK;
1472}
1473
1474
1475HRESULT VirtualBox::getNATNetworks(std::vector<ComPtr<INATNetwork> > &aNATNetworks)
1476{
1477#ifdef VBOX_WITH_NAT_SERVICE
1478 AutoReadLock al(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1479 aNATNetworks.resize(m->allNATNetworks.size());
1480 size_t i = 0;
1481 for (NATNetworksOList::const_iterator it= m->allNATNetworks.begin();
1482 it!= m->allNATNetworks.end(); ++it, ++i)
1483 (*it).queryInterfaceTo(aNATNetworks[i].asOutParam());
1484 return S_OK;
1485#else
1486 NOREF(aNATNetworks);
1487 return E_NOTIMPL;
1488#endif
1489}
1490
1491HRESULT VirtualBox::getEventSource(ComPtr<IEventSource> &aEventSource)
1492{
1493 /* event source is const, no need to lock */
1494 m->pEventSource.queryInterfaceTo(aEventSource.asOutParam());
1495 return S_OK;
1496}
1497
1498HRESULT VirtualBox::getExtensionPackManager(ComPtr<IExtPackManager> &aExtensionPackManager)
1499{
1500 HRESULT hrc = S_OK;
1501#ifdef VBOX_WITH_EXTPACK
1502 /* The extension pack manager is const, no need to lock. */
1503 hrc = m->ptrExtPackManager.queryInterfaceTo(aExtensionPackManager.asOutParam());
1504#else
1505 hrc = E_NOTIMPL;
1506 NOREF(aExtensionPackManager);
1507#endif
1508 return hrc;
1509}
1510
1511/**
1512 * Host Only Network
1513 */
1514HRESULT VirtualBox::createHostOnlyNetwork(const com::Utf8Str &aNetworkName,
1515 ComPtr<IHostOnlyNetwork> &aNetwork)
1516{
1517#ifdef VBOX_WITH_VMNET
1518 ComObjPtr<HostOnlyNetwork> HostOnlyNetwork;
1519 HostOnlyNetwork.createObject();
1520 HRESULT hrc = HostOnlyNetwork->init(this, aNetworkName);
1521 if (FAILED(hrc)) return hrc;
1522
1523 m->allHostOnlyNetworks.addChild(HostOnlyNetwork);
1524
1525 {
1526 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1527 hrc = i_saveSettings();
1528 vboxLock.release();
1529
1530 if (FAILED(hrc))
1531 m->allHostOnlyNetworks.removeChild(HostOnlyNetwork);
1532 else
1533 HostOnlyNetwork.queryInterfaceTo(aNetwork.asOutParam());
1534 }
1535
1536 return hrc;
1537#else /* !VBOX_WITH_VMNET */
1538 NOREF(aNetworkName);
1539 NOREF(aNetwork);
1540 return E_NOTIMPL;
1541#endif /* !VBOX_WITH_VMNET */
1542}
1543
1544HRESULT VirtualBox::findHostOnlyNetworkByName(const com::Utf8Str &aNetworkName,
1545 ComPtr<IHostOnlyNetwork> &aNetwork)
1546{
1547#ifdef VBOX_WITH_VMNET
1548 Bstr bstrNameToFind(aNetworkName);
1549
1550 AutoReadLock alock(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1551
1552 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
1553 it != m->allHostOnlyNetworks.end();
1554 ++it)
1555 {
1556 Bstr bstrHostOnlyNetworkName;
1557 HRESULT hrc = (*it)->COMGETTER(NetworkName)(bstrHostOnlyNetworkName.asOutParam());
1558 if (FAILED(hrc)) return hrc;
1559
1560 if (bstrHostOnlyNetworkName == bstrNameToFind)
1561 {
1562 it->queryInterfaceTo(aNetwork.asOutParam());
1563 return S_OK;
1564 }
1565 }
1566 return VBOX_E_OBJECT_NOT_FOUND;
1567#else /* !VBOX_WITH_VMNET */
1568 NOREF(aNetworkName);
1569 NOREF(aNetwork);
1570 return E_NOTIMPL;
1571#endif /* !VBOX_WITH_VMNET */
1572}
1573
1574HRESULT VirtualBox::findHostOnlyNetworkById(const com::Guid &aId,
1575 ComPtr<IHostOnlyNetwork> &aNetwork)
1576{
1577#ifdef VBOX_WITH_VMNET
1578 ComObjPtr<HostOnlyNetwork> network;
1579 AutoReadLock alock(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1580
1581 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
1582 it != m->allHostOnlyNetworks.end();
1583 ++it)
1584 {
1585 Bstr bstrHostOnlyNetworkId;
1586 HRESULT hrc = (*it)->COMGETTER(Id)(bstrHostOnlyNetworkId.asOutParam());
1587 if (FAILED(hrc)) return hrc;
1588
1589 if (Guid(bstrHostOnlyNetworkId) == aId)
1590 {
1591 it->queryInterfaceTo(aNetwork.asOutParam());;
1592 return S_OK;
1593 }
1594 }
1595 return VBOX_E_OBJECT_NOT_FOUND;
1596#else /* !VBOX_WITH_VMNET */
1597 NOREF(aId);
1598 NOREF(aNetwork);
1599 return E_NOTIMPL;
1600#endif /* !VBOX_WITH_VMNET */
1601}
1602
1603HRESULT VirtualBox::removeHostOnlyNetwork(const ComPtr<IHostOnlyNetwork> &aNetwork)
1604{
1605#ifdef VBOX_WITH_VMNET
1606 Bstr name;
1607 HRESULT hrc = aNetwork->COMGETTER(NetworkName)(name.asOutParam());
1608 if (FAILED(hrc))
1609 return hrc;
1610 IHostOnlyNetwork *p = aNetwork;
1611 HostOnlyNetwork *network = static_cast<HostOnlyNetwork *>(p);
1612
1613 AutoCaller autoCaller(this);
1614 AssertComRCReturnRC(autoCaller.hrc());
1615
1616 AutoCaller HostOnlyNetworkCaller(network);
1617 AssertComRCReturnRC(HostOnlyNetworkCaller.hrc());
1618
1619 m->allHostOnlyNetworks.removeChild(network);
1620
1621 {
1622 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1623 hrc = i_saveSettings();
1624 vboxLock.release();
1625
1626 if (FAILED(hrc))
1627 m->allHostOnlyNetworks.addChild(network);
1628 }
1629 return hrc;
1630#else /* !VBOX_WITH_VMNET */
1631 NOREF(aNetwork);
1632 return E_NOTIMPL;
1633#endif /* !VBOX_WITH_VMNET */
1634}
1635
1636HRESULT VirtualBox::getHostOnlyNetworks(std::vector<ComPtr<IHostOnlyNetwork> > &aHostOnlyNetworks)
1637{
1638#ifdef VBOX_WITH_VMNET
1639 AutoReadLock al(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1640 aHostOnlyNetworks.resize(m->allHostOnlyNetworks.size());
1641 size_t i = 0;
1642 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
1643 it != m->allHostOnlyNetworks.end(); ++it)
1644 (*it).queryInterfaceTo(aHostOnlyNetworks[i++].asOutParam());
1645 return S_OK;
1646#else /* !VBOX_WITH_VMNET */
1647 NOREF(aHostOnlyNetworks);
1648 return E_NOTIMPL;
1649#endif /* !VBOX_WITH_VMNET */
1650}
1651
1652
1653HRESULT VirtualBox::getInternalNetworks(std::vector<com::Utf8Str> &aInternalNetworks)
1654{
1655 std::list<com::Utf8Str> allInternalNetworks;
1656
1657 /* get copy of all machine references, to avoid holding the list lock */
1658 MachinesOList::MyList allMachines;
1659 {
1660 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1661 allMachines = m->allMachines.getList();
1662 }
1663 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1664 it != allMachines.end(); ++it)
1665 {
1666 const ComObjPtr<Machine> &pMachine = *it;
1667 AutoCaller autoMachineCaller(pMachine);
1668 if (FAILED(autoMachineCaller.hrc()))
1669 continue;
1670 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1671
1672 if (pMachine->i_isAccessible())
1673 {
1674 ChipsetType_T enmChipsetType;
1675 HRESULT hrc = pMachine->i_getPlatform()->getChipsetType(&enmChipsetType);
1676 ComAssertComRC(hrc);
1677
1678 uint32_t const cNetworkAdapters = PlatformProperties::s_getMaxNetworkAdapters(enmChipsetType);
1679 for (ULONG i = 0; i < cNetworkAdapters; i++)
1680 {
1681 ComPtr<INetworkAdapter> pNet;
1682 hrc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
1683 if (FAILED(hrc) || pNet.isNull())
1684 continue;
1685 Bstr strInternalNetwork;
1686 hrc = pNet->COMGETTER(InternalNetwork)(strInternalNetwork.asOutParam());
1687 if (FAILED(hrc) || strInternalNetwork.isEmpty())
1688 continue;
1689
1690 allInternalNetworks.push_back(Utf8Str(strInternalNetwork));
1691 }
1692 }
1693 }
1694
1695 /* throw out any duplicates */
1696 allInternalNetworks.sort();
1697 allInternalNetworks.unique();
1698 size_t i = 0;
1699 aInternalNetworks.resize(allInternalNetworks.size());
1700 for (std::list<com::Utf8Str>::const_iterator it = allInternalNetworks.begin();
1701 it != allInternalNetworks.end();
1702 ++it, ++i)
1703 aInternalNetworks[i] = *it;
1704 return S_OK;
1705}
1706
1707HRESULT VirtualBox::getGenericNetworkDrivers(std::vector<com::Utf8Str> &aGenericNetworkDrivers)
1708{
1709 std::list<com::Utf8Str> allGenericNetworkDrivers;
1710
1711 /* get copy of all machine references, to avoid holding the list lock */
1712 MachinesOList::MyList allMachines;
1713 {
1714 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1715 allMachines = m->allMachines.getList();
1716 }
1717 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1718 it != allMachines.end();
1719 ++it)
1720 {
1721 const ComObjPtr<Machine> &pMachine = *it;
1722 AutoCaller autoMachineCaller(pMachine);
1723 if (FAILED(autoMachineCaller.hrc()))
1724 continue;
1725 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1726
1727 if (pMachine->i_isAccessible())
1728 {
1729 ChipsetType_T enmChipsetType;
1730 HRESULT hrc = pMachine->i_getPlatform()->getChipsetType(&enmChipsetType);
1731 ComAssertComRC(hrc);
1732
1733 uint32_t const cNetworkAdapters = PlatformProperties::s_getMaxNetworkAdapters(enmChipsetType);
1734 for (ULONG i = 0; i < cNetworkAdapters; i++)
1735 {
1736 ComPtr<INetworkAdapter> pNet;
1737 hrc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
1738 if (FAILED(hrc) || pNet.isNull())
1739 continue;
1740 Bstr strGenericNetworkDriver;
1741 hrc = pNet->COMGETTER(GenericDriver)(strGenericNetworkDriver.asOutParam());
1742 if (FAILED(hrc) || strGenericNetworkDriver.isEmpty())
1743 continue;
1744
1745 allGenericNetworkDrivers.push_back(Utf8Str(strGenericNetworkDriver).c_str());
1746 }
1747 }
1748 }
1749
1750 /* throw out any duplicates */
1751 allGenericNetworkDrivers.sort();
1752 allGenericNetworkDrivers.unique();
1753 aGenericNetworkDrivers.resize(allGenericNetworkDrivers.size());
1754 size_t i = 0;
1755 for (std::list<com::Utf8Str>::const_iterator it = allGenericNetworkDrivers.begin();
1756 it != allGenericNetworkDrivers.end(); ++it, ++i)
1757 aGenericNetworkDrivers[i] = *it;
1758
1759 return S_OK;
1760}
1761
1762/**
1763 * Cloud Network
1764 */
1765#ifdef VBOX_WITH_CLOUD_NET
1766HRESULT VirtualBox::i_findCloudNetworkByName(const com::Utf8Str &aNetworkName,
1767 ComObjPtr<CloudNetwork> *aNetwork)
1768{
1769 ComPtr<CloudNetwork> found;
1770 Bstr bstrNameToFind(aNetworkName);
1771
1772 AutoReadLock alock(m->allCloudNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1773
1774 for (CloudNetworksOList::const_iterator it = m->allCloudNetworks.begin();
1775 it != m->allCloudNetworks.end();
1776 ++it)
1777 {
1778 Bstr bstrCloudNetworkName;
1779 HRESULT hrc = (*it)->COMGETTER(NetworkName)(bstrCloudNetworkName.asOutParam());
1780 if (FAILED(hrc)) return hrc;
1781
1782 if (bstrCloudNetworkName == bstrNameToFind)
1783 {
1784 *aNetwork = *it;
1785 return S_OK;
1786 }
1787 }
1788 return VBOX_E_OBJECT_NOT_FOUND;
1789}
1790#endif /* VBOX_WITH_CLOUD_NET */
1791
1792HRESULT VirtualBox::createCloudNetwork(const com::Utf8Str &aNetworkName,
1793 ComPtr<ICloudNetwork> &aNetwork)
1794{
1795#ifdef VBOX_WITH_CLOUD_NET
1796 ComObjPtr<CloudNetwork> cloudNetwork;
1797 cloudNetwork.createObject();
1798 HRESULT hrc = cloudNetwork->init(this, aNetworkName);
1799 if (FAILED(hrc)) return hrc;
1800
1801 m->allCloudNetworks.addChild(cloudNetwork);
1802
1803 {
1804 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1805 hrc = i_saveSettings();
1806 vboxLock.release();
1807
1808 if (FAILED(hrc))
1809 m->allCloudNetworks.removeChild(cloudNetwork);
1810 else
1811 cloudNetwork.queryInterfaceTo(aNetwork.asOutParam());
1812 }
1813
1814 return hrc;
1815#else /* !VBOX_WITH_CLOUD_NET */
1816 NOREF(aNetworkName);
1817 NOREF(aNetwork);
1818 return E_NOTIMPL;
1819#endif /* !VBOX_WITH_CLOUD_NET */
1820}
1821
1822HRESULT VirtualBox::findCloudNetworkByName(const com::Utf8Str &aNetworkName,
1823 ComPtr<ICloudNetwork> &aNetwork)
1824{
1825#ifdef VBOX_WITH_CLOUD_NET
1826 ComObjPtr<CloudNetwork> network;
1827 HRESULT hrc = i_findCloudNetworkByName(aNetworkName, &network);
1828 if (SUCCEEDED(hrc))
1829 network.queryInterfaceTo(aNetwork.asOutParam());
1830 return hrc;
1831#else /* !VBOX_WITH_CLOUD_NET */
1832 NOREF(aNetworkName);
1833 NOREF(aNetwork);
1834 return E_NOTIMPL;
1835#endif /* !VBOX_WITH_CLOUD_NET */
1836}
1837
1838HRESULT VirtualBox::removeCloudNetwork(const ComPtr<ICloudNetwork> &aNetwork)
1839{
1840#ifdef VBOX_WITH_CLOUD_NET
1841 Bstr name;
1842 HRESULT hrc = aNetwork->COMGETTER(NetworkName)(name.asOutParam());
1843 if (FAILED(hrc))
1844 return hrc;
1845 ICloudNetwork *p = aNetwork;
1846 CloudNetwork *network = static_cast<CloudNetwork *>(p);
1847
1848 AutoCaller autoCaller(this);
1849 AssertComRCReturnRC(autoCaller.hrc());
1850
1851 AutoCaller cloudNetworkCaller(network);
1852 AssertComRCReturnRC(cloudNetworkCaller.hrc());
1853
1854 m->allCloudNetworks.removeChild(network);
1855
1856 {
1857 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1858 hrc = i_saveSettings();
1859 vboxLock.release();
1860
1861 if (FAILED(hrc))
1862 m->allCloudNetworks.addChild(network);
1863 }
1864 return hrc;
1865#else /* !VBOX_WITH_CLOUD_NET */
1866 NOREF(aNetwork);
1867 return E_NOTIMPL;
1868#endif /* !VBOX_WITH_CLOUD_NET */
1869}
1870
1871HRESULT VirtualBox::getCloudNetworks(std::vector<ComPtr<ICloudNetwork> > &aCloudNetworks)
1872{
1873#ifdef VBOX_WITH_CLOUD_NET
1874 AutoReadLock al(m->allCloudNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1875 aCloudNetworks.resize(m->allCloudNetworks.size());
1876 size_t i = 0;
1877 for (CloudNetworksOList::const_iterator it = m->allCloudNetworks.begin();
1878 it != m->allCloudNetworks.end(); ++it)
1879 (*it).queryInterfaceTo(aCloudNetworks[i++].asOutParam());
1880 return S_OK;
1881#else /* !VBOX_WITH_CLOUD_NET */
1882 NOREF(aCloudNetworks);
1883 return E_NOTIMPL;
1884#endif /* !VBOX_WITH_CLOUD_NET */
1885}
1886
1887#ifdef VBOX_WITH_CLOUD_NET
1888HRESULT VirtualBox::i_getEventSource(ComPtr<IEventSource>& aSource)
1889{
1890 m->pEventSource.queryInterfaceTo(aSource.asOutParam());
1891 return S_OK;
1892}
1893#endif /* VBOX_WITH_CLOUD_NET */
1894
1895HRESULT VirtualBox::getCloudProviderManager(ComPtr<ICloudProviderManager> &aCloudProviderManager)
1896{
1897 HRESULT hrc = m->pCloudProviderManager.queryInterfaceTo(aCloudProviderManager.asOutParam());
1898 return hrc;
1899}
1900
1901HRESULT VirtualBox::checkFirmwarePresent(PlatformArchitecture_T aPlatformArchitecture,
1902 FirmwareType_T aFirmwareType,
1903 const com::Utf8Str &aVersion,
1904 com::Utf8Str &aUrl,
1905 com::Utf8Str &aFile,
1906 BOOL *aResult)
1907{
1908 NOREF(aVersion);
1909
1910 static const VBOXFWDESC s_FwDescX86[] =
1911 {
1912 { FirmwareType_BIOS, true, NULL, NULL },
1913#ifdef VBOX_WITH_EFI_IN_DD2
1914 { FirmwareType_EFI32, true, "VBoxEFI32.fd", NULL },
1915 { FirmwareType_EFI64, true, "VBoxEFI64.fd", NULL },
1916 { FirmwareType_EFIDUAL, true, "VBoxEFIDual.fd", NULL },
1917#else
1918 { FirmwareType_EFI32, false, "VBoxEFI32.fd", "http://virtualbox.org/firmware/VBoxEFI32.fd" },
1919 { FirmwareType_EFI64, false, "VBoxEFI64.fd", "http://virtualbox.org/firmware/VBoxEFI64.fd" },
1920 { FirmwareType_EFIDUAL, false, "VBoxEFIDual.fd", "http://virtualbox.org/firmware/VBoxEFIDual.fd" },
1921#endif
1922 };
1923
1924 static const VBOXFWDESC s_FwDescArm[] =
1925 {
1926#ifdef VBOX_WITH_EFI_IN_DD2
1927 { FirmwareType_EFI32, true, "VBoxEFIAArch32.fd", NULL },
1928 { FirmwareType_EFI64, true, "VBoxEFIAArch64.fd", NULL },
1929#else
1930 { FirmwareType_EFI32, false, "VBoxEFIAArch32.fd", "http://virtualbox.org/firmware/VBoxEFIAArch32.fd" },
1931 { FirmwareType_EFI64, false, "VBoxEFIAArch64.fd", "http://virtualbox.org/firmware/VBoxEFIAArch64.fd" },
1932#endif
1933 };
1934
1935 PVBOXFWDESC pFwDesc = NULL;
1936 uint32_t cFwDesc = 0;
1937 if (aPlatformArchitecture == PlatformArchitecture_x86)
1938 {
1939 pFwDesc = &s_FwDescX86[0];
1940 cFwDesc = RT_ELEMENTS(s_FwDescX86);
1941 }
1942 else if (aPlatformArchitecture == PlatformArchitecture_ARM)
1943 {
1944 pFwDesc = &s_FwDescArm[0];
1945 cFwDesc = RT_ELEMENTS(s_FwDescArm);
1946 }
1947 else
1948 return E_INVALIDARG;
1949
1950 for (size_t i = 0; i < cFwDesc; i++)
1951 {
1952 if (aFirmwareType != pFwDesc->enmType)
1953 {
1954 pFwDesc++;
1955 continue;
1956 }
1957
1958 /* compiled-in firmware */
1959 if (pFwDesc->fBuiltIn)
1960 {
1961 aFile = pFwDesc->pszFileName;
1962 *aResult = TRUE;
1963 break;
1964 }
1965
1966 Utf8Str fullName;
1967 Utf8StrFmt shortName("Firmware%c%s", RTPATH_DELIMITER, pFwDesc->pszFileName);
1968 int vrc = i_calculateFullPath(shortName, fullName);
1969 AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
1970 if (RTFileExists(fullName.c_str()))
1971 {
1972 *aResult = TRUE;
1973 aFile = fullName;
1974 break;
1975 }
1976
1977 char szVBoxPath[RTPATH_MAX];
1978 vrc = RTPathExecDir(szVBoxPath, RTPATH_MAX);
1979 AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
1980 vrc = RTPathAppend(szVBoxPath, sizeof(szVBoxPath), pFwDesc->pszFileName);
1981 AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
1982 if (RTFileExists(szVBoxPath))
1983 {
1984 *aResult = TRUE;
1985 aFile = szVBoxPath;
1986 break;
1987 }
1988
1989 /** @todo account for version in the URL */
1990 aUrl = pFwDesc->pszUrl;
1991 *aResult = FALSE;
1992
1993 /* Assume single record per firmware type */
1994 break;
1995 }
1996
1997 return S_OK;
1998}
1999
2000/**
2001 * Walk the list of GuestOSType objects and return a list of all known guest
2002 * OS families.
2003 *
2004 * @param aOSFamilies Where to store the list of guest OS families.
2005 *
2006 * @note Locks the guest OS types list for reading.
2007 */
2008HRESULT VirtualBox::getGuestOSFamilies(std::vector<com::Utf8Str> &aOSFamilies)
2009{
2010 std::list<com::Utf8Str> allOSFamilies;
2011
2012 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2013
2014 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
2015 it != m->allGuestOSTypes.end(); ++it)
2016 {
2017 const Utf8Str &familyId = (*it)->i_familyId();
2018 AssertMsg(!familyId.isEmpty(), ("familfyId must not be NULL"));
2019 allOSFamilies.push_back(familyId);
2020 }
2021
2022 /* throw out any duplicates */
2023 allOSFamilies.sort();
2024 allOSFamilies.unique();
2025
2026 aOSFamilies.resize(allOSFamilies.size());
2027 size_t i = 0;
2028 for (std::list<com::Utf8Str>::const_iterator it = allOSFamilies.begin();
2029 it != allOSFamilies.end(); ++it, ++i)
2030 aOSFamilies[i] = (*it);
2031
2032 return S_OK;
2033}
2034
2035// Wrapped IVirtualBox methods
2036/////////////////////////////////////////////////////////////////////////////
2037
2038/* Helper for VirtualBox::ComposeMachineFilename */
2039static void sanitiseMachineFilename(Utf8Str &aName);
2040
2041HRESULT VirtualBox::composeMachineFilename(const com::Utf8Str &aName,
2042 const com::Utf8Str &aGroup,
2043 const com::Utf8Str &aCreateFlags,
2044 const com::Utf8Str &aBaseFolder,
2045 com::Utf8Str &aFile)
2046{
2047 if (RT_UNLIKELY(aName.isEmpty()))
2048 return setError(E_INVALIDARG, tr("Machine name is invalid, must not be empty"));
2049
2050 Utf8Str strBase = aBaseFolder;
2051 Utf8Str strName = aName;
2052
2053 LogFlowThisFunc(("aName=\"%s\",aBaseFolder=\"%s\"\n", strName.c_str(), strBase.c_str()));
2054
2055 com::Guid id;
2056 bool fDirectoryIncludesUUID = false;
2057 if (!aCreateFlags.isEmpty())
2058 {
2059 size_t uPos = 0;
2060 com::Utf8Str strKey;
2061 com::Utf8Str strValue;
2062 while ((uPos = aCreateFlags.parseKeyValue(strKey, strValue, uPos)) != com::Utf8Str::npos)
2063 {
2064 if (strKey == "UUID")
2065 id = strValue.c_str();
2066 else if (strKey == "directoryIncludesUUID")
2067 fDirectoryIncludesUUID = (strValue == "1");
2068 }
2069 }
2070
2071 if (id.isZero())
2072 fDirectoryIncludesUUID = false;
2073 else if (!id.isValid())
2074 {
2075 /* do something else */
2076 return setError(E_INVALIDARG,
2077 tr("'%s' is not a valid Guid"),
2078 id.toStringCurly().c_str());
2079 }
2080
2081 Utf8Str strGroup(aGroup);
2082 if (strGroup.isEmpty())
2083 strGroup = "/";
2084 HRESULT hrc = i_validateMachineGroup(strGroup, true);
2085 if (FAILED(hrc))
2086 return hrc;
2087
2088 /* Compose the settings file name using the following scheme:
2089 *
2090 * <base_folder><group>/<machine_name>/<machine_name>.xml
2091 *
2092 * If a non-null and non-empty base folder is specified, the default
2093 * machine folder will be used as a base folder.
2094 * We sanitise the machine name to a safe white list of characters before
2095 * using it.
2096 */
2097 Utf8Str strDirName(strName);
2098 if (fDirectoryIncludesUUID)
2099 strDirName += Utf8StrFmt(" (%RTuuid)", id.raw());
2100 sanitiseMachineFilename(strName);
2101 sanitiseMachineFilename(strDirName);
2102
2103 if (strBase.isEmpty())
2104 /* we use the non-full folder value below to keep the path relative */
2105 i_getDefaultMachineFolder(strBase);
2106
2107 i_calculateFullPath(strBase, strBase);
2108
2109 /* eliminate toplevel group to avoid // in the result */
2110 if (strGroup == "/")
2111 strGroup.setNull();
2112 aFile = com::Utf8StrFmt("%s%s%c%s%c%s.vbox",
2113 strBase.c_str(),
2114 strGroup.c_str(),
2115 RTPATH_DELIMITER,
2116 strDirName.c_str(),
2117 RTPATH_DELIMITER,
2118 strName.c_str());
2119 return S_OK;
2120}
2121
2122/**
2123 * Remove characters from a machine file name which can be problematic on
2124 * particular systems.
2125 * @param strName The file name to sanitise.
2126 */
2127void sanitiseMachineFilename(Utf8Str &strName)
2128{
2129 if (strName.isEmpty())
2130 return;
2131
2132 /* Set of characters which should be safe for use in filenames: some basic
2133 * ASCII, Unicode from Latin-1 alphabetic to the end of Hangul. We try to
2134 * skip anything that could count as a control character in Windows or
2135 * *nix, or be otherwise difficult for shells to handle (I would have
2136 * preferred to remove the space and brackets too). We also remove all
2137 * characters which need UTF-16 surrogate pairs for Windows's benefit.
2138 */
2139 static RTUNICP const s_uszValidRangePairs[] =
2140 {
2141 ' ', ' ',
2142 '(', ')',
2143 '-', '.',
2144 '0', '9',
2145 'A', 'Z',
2146 'a', 'z',
2147 '_', '_',
2148 0xa0, 0xd7af,
2149 '\0'
2150 };
2151
2152 char *pszName = strName.mutableRaw();
2153 ssize_t cReplacements = RTStrPurgeComplementSet(pszName, s_uszValidRangePairs, '_');
2154 Assert(cReplacements >= 0);
2155 NOREF(cReplacements);
2156
2157 /* No leading dot or dash. */
2158 if (pszName[0] == '.' || pszName[0] == '-')
2159 pszName[0] = '_';
2160
2161 /* No trailing dot. */
2162 if (pszName[strName.length() - 1] == '.')
2163 pszName[strName.length() - 1] = '_';
2164
2165 /* Mangle leading and trailing spaces. */
2166 for (size_t i = 0; pszName[i] == ' '; ++i)
2167 pszName[i] = '_';
2168 for (size_t i = strName.length() - 1; i && pszName[i] == ' '; --i)
2169 pszName[i] = '_';
2170}
2171
2172#ifdef DEBUG
2173typedef DECLCALLBACKTYPE(void, FNTESTPRINTF,(const char *, ...));
2174/** Simple unit test/operation examples for sanitiseMachineFilename(). */
2175static unsigned testSanitiseMachineFilename(FNTESTPRINTF *pfnPrintf)
2176{
2177 unsigned cErrors = 0;
2178
2179 /** Expected results of sanitising given file names. */
2180 static struct
2181 {
2182 /** The test file name to be sanitised (Utf-8). */
2183 const char *pcszIn;
2184 /** The expected sanitised output (Utf-8). */
2185 const char *pcszOutExpected;
2186 } aTest[] =
2187 {
2188 { "OS/2 2.1", "OS_2 2.1" },
2189 { "-!My VM!-", "__My VM_-" },
2190 { "\xF0\x90\x8C\xB0", "____" },
2191 { " My VM ", "__My VM__" },
2192 { ".My VM.", "_My VM_" },
2193 { "My VM", "My VM" }
2194 };
2195 for (unsigned i = 0; i < RT_ELEMENTS(aTest); ++i)
2196 {
2197 Utf8Str str(aTest[i].pcszIn);
2198 sanitiseMachineFilename(str);
2199 if (str.compare(aTest[i].pcszOutExpected))
2200 {
2201 ++cErrors;
2202 pfnPrintf("%s: line %d, expected %s, actual %s\n",
2203 __PRETTY_FUNCTION__, i, aTest[i].pcszOutExpected,
2204 str.c_str());
2205 }
2206 }
2207 return cErrors;
2208}
2209
2210/** @todo Proper testcase. */
2211/** @todo Do we have a better method of doing init functions? */
2212namespace
2213{
2214 class TestSanitiseMachineFilename
2215 {
2216 public:
2217 TestSanitiseMachineFilename(void)
2218 {
2219 Assert(!testSanitiseMachineFilename(RTAssertMsg2));
2220 }
2221 };
2222 TestSanitiseMachineFilename s_TestSanitiseMachineFilename;
2223}
2224#endif
2225
2226/** @note Locks mSystemProperties object for reading. */
2227HRESULT VirtualBox::createMachine(const com::Utf8Str &aSettingsFile,
2228 const com::Utf8Str &aName,
2229 PlatformArchitecture_T aArchitecture,
2230 const std::vector<com::Utf8Str> &aGroups,
2231 const com::Utf8Str &aOsTypeId,
2232 const com::Utf8Str &aFlags,
2233 const com::Utf8Str &aCipher,
2234 const com::Utf8Str &aPasswordId,
2235 const com::Utf8Str &aPassword,
2236 ComPtr<IMachine> &aMachine)
2237{
2238 if (aArchitecture == PlatformArchitecture_None)
2239 return setError(E_INVALIDARG, tr("'Must specify a valid platform architecture"));
2240
2241 LogFlowThisFuncEnter();
2242 LogFlowThisFunc(("aSettingsFile=\"%s\", aName=\"%s\", aArchitecture=%#x, aOsTypeId =\"%s\", aCreateFlags=\"%s\"\n",
2243 aSettingsFile.c_str(), aName.c_str(), aArchitecture, aOsTypeId.c_str(), aFlags.c_str()));
2244
2245#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
2246 if (aArchitecture != PlatformArchitecture_x86)/* x86 hosts only allows creating x86 VMs for now. */
2247 return setError(VBOX_E_PLATFORM_ARCH_NOT_SUPPORTED, tr("'Creating VMs for platform architecture %s not supported on %s"),
2248 Global::stringifyPlatformArchitecture(aArchitecture),
2249 Global::stringifyPlatformArchitecture(PlatformArchitecture_x86));
2250#endif
2251
2252 StringsList llGroups;
2253 HRESULT hrc = i_convertMachineGroups(aGroups, &llGroups);
2254 if (FAILED(hrc))
2255 return hrc;
2256
2257 /** @todo r=bird: Would be good to rewrite this parsing using offset into
2258 * aFlags and drop all the C pointers, strchr, misguided RTStrStr and
2259 * tedious copying of substrings. */
2260 Utf8Str strCreateFlags(aFlags); /** @todo r=bird: WTF is the point of this copy? */
2261 Guid id;
2262 bool fForceOverwrite = false;
2263 bool fDirectoryIncludesUUID = false;
2264 if (!strCreateFlags.isEmpty())
2265 {
2266 const char *pcszNext = strCreateFlags.c_str();
2267 while (*pcszNext != '\0')
2268 {
2269 Utf8Str strFlag;
2270 const char *pcszComma = strchr(pcszNext, ','); /*clueless version: RTStrStr(pcszNext, ","); */
2271 if (!pcszComma)
2272 strFlag = pcszNext;
2273 else
2274 strFlag.assign(pcszNext, (size_t)(pcszComma - pcszNext));
2275
2276 const char *pcszEqual = strchr(strFlag.c_str(), '='); /* more cluelessness: RTStrStr(strFlag.c_str(), "="); */
2277 /* skip over everything which doesn't contain '=' */
2278 if (pcszEqual && pcszEqual != strFlag.c_str())
2279 {
2280 Utf8Str strKey(strFlag.c_str(), (size_t)(pcszEqual - strFlag.c_str()));
2281 Utf8Str strValue(strFlag.c_str() + (pcszEqual - strFlag.c_str() + 1));
2282
2283 if (strKey == "UUID")
2284 id = strValue.c_str();
2285 else if (strKey == "forceOverwrite")
2286 fForceOverwrite = (strValue == "1");
2287 else if (strKey == "directoryIncludesUUID")
2288 fDirectoryIncludesUUID = (strValue == "1");
2289 }
2290
2291 if (!pcszComma)
2292 pcszNext += strFlag.length(); /* you can just 'break' out here... */
2293 else
2294 pcszNext += strFlag.length() + 1;
2295 }
2296 }
2297
2298 /* Create UUID if none was specified. */
2299 if (id.isZero())
2300 id.create();
2301 else if (!id.isValid())
2302 {
2303 /* do something else */
2304 return setError(E_INVALIDARG, tr("'%s' is not a valid Guid"), id.toStringCurly().c_str());
2305 }
2306
2307 /* NULL settings file means compose automatically */
2308 Utf8Str strSettingsFile(aSettingsFile);
2309 if (strSettingsFile.isEmpty())
2310 {
2311 Utf8Str strNewCreateFlags(Utf8StrFmt("UUID=%RTuuid", id.raw()));
2312 if (fDirectoryIncludesUUID)
2313 strNewCreateFlags += ",directoryIncludesUUID=1";
2314
2315 com::Utf8Str blstr;
2316 hrc = composeMachineFilename(aName,
2317 llGroups.front(),
2318 strNewCreateFlags,
2319 blstr /* aBaseFolder */,
2320 strSettingsFile);
2321 if (FAILED(hrc)) return hrc;
2322 }
2323
2324 /* create a new object */
2325 ComObjPtr<Machine> machine;
2326 hrc = machine.createObject();
2327 if (FAILED(hrc)) return hrc;
2328
2329 ComObjPtr<GuestOSType> osType;
2330 if (!aOsTypeId.isEmpty())
2331 i_findGuestOSType(aOsTypeId, osType);
2332
2333 /* initialize the machine object */
2334 hrc = machine->init(this,
2335 strSettingsFile,
2336 aName,
2337 aArchitecture,
2338 llGroups,
2339 aOsTypeId,
2340 osType,
2341 id,
2342 fForceOverwrite,
2343 fDirectoryIncludesUUID,
2344 aCipher,
2345 aPasswordId,
2346 aPassword);
2347 if (SUCCEEDED(hrc))
2348 {
2349 /* set the return value */
2350 machine.queryInterfaceTo(aMachine.asOutParam());
2351 AssertComRC(hrc);
2352
2353#ifdef VBOX_WITH_EXTPACK
2354 /* call the extension pack hooks */
2355 m->ptrExtPackManager->i_callAllVmCreatedHooks(machine);
2356#endif
2357 }
2358
2359 LogFlowThisFuncLeave();
2360
2361 return hrc;
2362}
2363
2364HRESULT VirtualBox::openMachine(const com::Utf8Str &aSettingsFile,
2365 const com::Utf8Str &aPassword,
2366 ComPtr<IMachine> &aMachine)
2367{
2368 /* create a new object */
2369 ComObjPtr<Machine> machine;
2370 HRESULT hrc = machine.createObject();
2371 if (SUCCEEDED(hrc))
2372 {
2373 /* initialize the machine object */
2374 hrc = machine->initFromSettings(this, aSettingsFile, NULL /* const Guid *aId */, aPassword);
2375 if (SUCCEEDED(hrc))
2376 {
2377 /* set the return value */
2378 machine.queryInterfaceTo(aMachine.asOutParam());
2379 ComAssertComRC(hrc);
2380 }
2381 }
2382
2383 return hrc;
2384}
2385
2386/** @note Locks objects! */
2387HRESULT VirtualBox::registerMachine(const ComPtr<IMachine> &aMachine)
2388{
2389 Bstr name;
2390 HRESULT hrc = aMachine->COMGETTER(Name)(name.asOutParam());
2391 if (FAILED(hrc)) return hrc;
2392
2393 /* We can safely cast child to Machine * here because only Machine
2394 * implementations of IMachine can be among our children. */
2395 IMachine *aM = aMachine;
2396 Machine *pMachine = static_cast<Machine*>(aM);
2397
2398 AutoCaller machCaller(pMachine);
2399 ComAssertComRCRetRC(machCaller.hrc());
2400
2401 hrc = i_registerMachine(pMachine);
2402 /* fire an event */
2403 if (SUCCEEDED(hrc))
2404 i_onMachineRegistered(pMachine->i_getId(), TRUE);
2405
2406 return hrc;
2407}
2408
2409/** @note Locks this object for reading, then some machine objects for reading. */
2410HRESULT VirtualBox::findMachine(const com::Utf8Str &aSettingsFile,
2411 ComPtr<IMachine> &aMachine)
2412{
2413 LogFlowThisFuncEnter();
2414 LogFlowThisFunc(("aSettingsFile=\"%s\", aMachine={%p}\n", aSettingsFile.c_str(), &aMachine));
2415
2416 /* start with not found */
2417 HRESULT hrc = S_OK;
2418 ComObjPtr<Machine> pMachineFound;
2419
2420 Guid id(aSettingsFile);
2421 Utf8Str strFile(aSettingsFile);
2422 if (id.isValid() && !id.isZero())
2423 hrc = i_findMachine(id,
2424 true /* fPermitInaccessible */,
2425 true /* setError */,
2426 &pMachineFound);
2427 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
2428 else
2429 {
2430 hrc = i_findMachineByName(strFile,
2431 true /* setError */,
2432 &pMachineFound);
2433 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
2434 }
2435
2436 /* this will set (*machine) to NULL if machineObj is null */
2437 pMachineFound.queryInterfaceTo(aMachine.asOutParam());
2438
2439 LogFlowThisFunc(("aName=\"%s\", aMachine=%p, hrc=%08X\n", aSettingsFile.c_str(), &aMachine, hrc));
2440 LogFlowThisFuncLeave();
2441
2442 return hrc;
2443}
2444
2445HRESULT VirtualBox::getMachinesByGroups(const std::vector<com::Utf8Str> &aGroups,
2446 std::vector<ComPtr<IMachine> > &aMachines)
2447{
2448 StringsList llGroups;
2449 HRESULT hrc = i_convertMachineGroups(aGroups, &llGroups);
2450 if (FAILED(hrc))
2451 return hrc;
2452
2453 /* we want to rely on sorted groups during compare, to save time */
2454 llGroups.sort();
2455
2456 /* get copy of all machine references, to avoid holding the list lock */
2457 MachinesOList::MyList allMachines;
2458 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2459 allMachines = m->allMachines.getList();
2460
2461 std::vector<ComObjPtr<IMachine> > saMachines;
2462 saMachines.resize(0);
2463 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
2464 it != allMachines.end();
2465 ++it)
2466 {
2467 const ComObjPtr<Machine> &pMachine = *it;
2468 AutoCaller autoMachineCaller(pMachine);
2469 if (FAILED(autoMachineCaller.hrc()))
2470 continue;
2471 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
2472
2473 if (pMachine->i_isAccessible())
2474 {
2475 const StringsList &thisGroups = pMachine->i_getGroups();
2476 for (StringsList::const_iterator it2 = thisGroups.begin();
2477 it2 != thisGroups.end();
2478 ++it2)
2479 {
2480 const Utf8Str &group = *it2;
2481 bool fAppended = false;
2482 for (StringsList::const_iterator it3 = llGroups.begin();
2483 it3 != llGroups.end();
2484 ++it3)
2485 {
2486 int order = it3->compare(group);
2487 if (order == 0)
2488 {
2489 saMachines.push_back(static_cast<IMachine *>(pMachine));
2490 fAppended = true;
2491 break;
2492 }
2493 else if (order > 0)
2494 break;
2495 else
2496 continue;
2497 }
2498 /* avoid duplicates and save time */
2499 if (fAppended)
2500 break;
2501 }
2502 }
2503 }
2504 aMachines.resize(saMachines.size());
2505 size_t i = 0;
2506 for(i = 0; i < saMachines.size(); ++i)
2507 saMachines[i].queryInterfaceTo(aMachines[i].asOutParam());
2508
2509 return S_OK;
2510}
2511
2512HRESULT VirtualBox::getMachineStates(const std::vector<ComPtr<IMachine> > &aMachines,
2513 std::vector<MachineState_T> &aStates)
2514{
2515 com::SafeIfaceArray<IMachine> saMachines(aMachines);
2516 aStates.resize(aMachines.size());
2517 for (size_t i = 0; i < saMachines.size(); i++)
2518 {
2519 ComPtr<IMachine> pMachine = saMachines[i];
2520 MachineState_T state = MachineState_Null;
2521 if (!pMachine.isNull())
2522 {
2523 HRESULT hrc = pMachine->COMGETTER(State)(&state);
2524 if (hrc == E_ACCESSDENIED)
2525 hrc = S_OK;
2526 AssertComRC(hrc);
2527 }
2528 aStates[i] = state;
2529 }
2530 return S_OK;
2531}
2532
2533HRESULT VirtualBox::createUnattendedInstaller(ComPtr<IUnattended> &aUnattended)
2534{
2535#ifdef VBOX_WITH_UNATTENDED
2536 ComObjPtr<Unattended> ptrUnattended;
2537 HRESULT hrc = ptrUnattended.createObject();
2538 if (SUCCEEDED(hrc))
2539 {
2540 AutoReadLock wlock(this COMMA_LOCKVAL_SRC_POS);
2541 hrc = ptrUnattended->initUnattended(this);
2542 if (SUCCEEDED(hrc))
2543 hrc = ptrUnattended.queryInterfaceTo(aUnattended.asOutParam());
2544 }
2545 return hrc;
2546#else
2547 NOREF(aUnattended);
2548 return E_NOTIMPL;
2549#endif
2550}
2551
2552HRESULT VirtualBox::createMedium(const com::Utf8Str &aFormat,
2553 const com::Utf8Str &aLocation,
2554 AccessMode_T aAccessMode,
2555 DeviceType_T aDeviceType,
2556 ComPtr<IMedium> &aMedium)
2557{
2558 NOREF(aAccessMode); /**< @todo r=klaus make use of access mode */
2559
2560 HRESULT hrc = S_OK;
2561
2562 ComObjPtr<Medium> medium;
2563 medium.createObject();
2564 com::Utf8Str format = aFormat;
2565
2566 switch (aDeviceType)
2567 {
2568 case DeviceType_HardDisk:
2569 {
2570
2571 /* we don't access non-const data members so no need to lock */
2572 if (format.isEmpty())
2573 i_getDefaultHardDiskFormat(format);
2574
2575 hrc = medium->init(this,
2576 format,
2577 aLocation,
2578 Guid::Empty /* media registry: none yet */,
2579 aDeviceType);
2580 }
2581 break;
2582
2583 case DeviceType_DVD:
2584 case DeviceType_Floppy:
2585 {
2586
2587 if (format.isEmpty())
2588 return setError(E_INVALIDARG, tr("Format must be Valid Type%s"), format.c_str());
2589
2590 // enforce read-only for DVDs even if caller specified ReadWrite
2591 if (aDeviceType == DeviceType_DVD)
2592 aAccessMode = AccessMode_ReadOnly;
2593
2594 hrc = medium->init(this,
2595 format,
2596 aLocation,
2597 Guid::Empty /* media registry: none yet */,
2598 aDeviceType);
2599
2600 }
2601 break;
2602
2603 default:
2604 return setError(E_INVALIDARG, tr("Device type must be HardDisk, DVD or Floppy %d"), aDeviceType);
2605 }
2606
2607 if (SUCCEEDED(hrc))
2608 {
2609 medium.queryInterfaceTo(aMedium.asOutParam());
2610 com::Guid uMediumId = medium->i_getId();
2611 if (uMediumId.isValid() && !uMediumId.isZero())
2612 i_onMediumRegistered(uMediumId, medium->i_getDeviceType(), TRUE);
2613 }
2614
2615 return hrc;
2616}
2617
2618HRESULT VirtualBox::openMedium(const com::Utf8Str &aLocation,
2619 DeviceType_T aDeviceType,
2620 AccessMode_T aAccessMode,
2621 BOOL aForceNewUuid,
2622 ComPtr<IMedium> &aMedium)
2623{
2624 HRESULT hrc = S_OK;
2625 Guid id(aLocation);
2626 ComObjPtr<Medium> pMedium;
2627
2628 // have to get write lock as the whole find/update sequence must be done
2629 // in one critical section, otherwise there are races which can lead to
2630 // multiple Medium objects with the same content
2631 AutoWriteLock treeLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2632
2633 // check if the device type is correct, and see if a medium for the
2634 // given path has already initialized; if so, return that
2635 switch (aDeviceType)
2636 {
2637 case DeviceType_HardDisk:
2638 if (id.isValid() && !id.isZero())
2639 hrc = i_findHardDiskById(id, false /* setError */, &pMedium);
2640 else
2641 hrc = i_findHardDiskByLocation(aLocation, false, /* aSetError */ &pMedium);
2642 break;
2643
2644 case DeviceType_Floppy:
2645 case DeviceType_DVD:
2646 if (id.isValid() && !id.isZero())
2647 hrc = i_findDVDOrFloppyImage(aDeviceType, &id, Utf8Str::Empty, false /* setError */, &pMedium);
2648 else
2649 hrc = i_findDVDOrFloppyImage(aDeviceType, NULL, aLocation, false /* setError */, &pMedium);
2650
2651 // enforce read-only for DVDs even if caller specified ReadWrite
2652 if (aDeviceType == DeviceType_DVD)
2653 aAccessMode = AccessMode_ReadOnly;
2654 break;
2655
2656 default:
2657 return setError(E_INVALIDARG, tr("Device type must be HardDisk, DVD or Floppy %d"), aDeviceType);
2658 }
2659
2660 bool fMediumRegistered = false;
2661 if (pMedium.isNull())
2662 {
2663 pMedium.createObject();
2664 treeLock.release();
2665 hrc = pMedium->init(this,
2666 aLocation,
2667 (aAccessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
2668 !!aForceNewUuid,
2669 aDeviceType);
2670 treeLock.acquire();
2671
2672 if (SUCCEEDED(hrc))
2673 {
2674 hrc = i_registerMedium(pMedium, &pMedium, treeLock);
2675
2676 treeLock.release();
2677
2678 /* Note that it's important to call uninit() on failure to register
2679 * because the differencing hard disk would have been already associated
2680 * with the parent and this association needs to be broken. */
2681
2682 if (FAILED(hrc))
2683 {
2684 pMedium->uninit();
2685 hrc = VBOX_E_OBJECT_NOT_FOUND;
2686 }
2687 else
2688 fMediumRegistered = true;
2689 }
2690 else if (hrc != VBOX_E_INVALID_OBJECT_STATE)
2691 hrc = VBOX_E_OBJECT_NOT_FOUND;
2692 }
2693
2694 if (SUCCEEDED(hrc))
2695 {
2696 pMedium.queryInterfaceTo(aMedium.asOutParam());
2697 if (fMediumRegistered)
2698 i_onMediumRegistered(pMedium->i_getId(), pMedium->i_getDeviceType() ,TRUE);
2699 }
2700
2701 return hrc;
2702}
2703
2704
2705/** @note Locks this object for reading. */
2706HRESULT VirtualBox::getGuestOSType(const com::Utf8Str &aId,
2707 ComPtr<IGuestOSType> &aType)
2708{
2709 ComObjPtr<GuestOSType> pType;
2710 HRESULT hrc = i_findGuestOSType(aId, pType);
2711 pType.queryInterfaceTo(aType.asOutParam());
2712 return hrc;
2713}
2714
2715HRESULT VirtualBox::createSharedFolder(const com::Utf8Str &aName,
2716 const com::Utf8Str &aHostPath,
2717 BOOL aWritable,
2718 BOOL aAutomount,
2719 const com::Utf8Str &aAutoMountPoint)
2720{
2721 NOREF(aName);
2722 NOREF(aHostPath);
2723 NOREF(aWritable);
2724 NOREF(aAutomount);
2725 NOREF(aAutoMountPoint);
2726
2727 return setError(E_NOTIMPL, tr("Not yet implemented"));
2728}
2729
2730HRESULT VirtualBox::removeSharedFolder(const com::Utf8Str &aName)
2731{
2732 NOREF(aName);
2733 return setError(E_NOTIMPL, tr("Not yet implemented"));
2734}
2735
2736/**
2737 * @note Locks this object for reading.
2738 */
2739HRESULT VirtualBox::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
2740{
2741 using namespace settings;
2742
2743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2744
2745 aKeys.resize(m->pMainConfigFile->mapExtraDataItems.size());
2746 size_t i = 0;
2747 for (StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin();
2748 it != m->pMainConfigFile->mapExtraDataItems.end(); ++it, ++i)
2749 aKeys[i] = it->first;
2750
2751 return S_OK;
2752}
2753
2754/**
2755 * @note Locks this object for reading.
2756 */
2757HRESULT VirtualBox::getExtraData(const com::Utf8Str &aKey,
2758 com::Utf8Str &aValue)
2759{
2760 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(aKey);
2761 if (it != m->pMainConfigFile->mapExtraDataItems.end())
2762 // found:
2763 aValue = it->second; // source is a Utf8Str
2764
2765 /* return the result to caller (may be empty) */
2766
2767 return S_OK;
2768}
2769
2770/**
2771 * @note Locks this object for writing.
2772 */
2773HRESULT VirtualBox::setExtraData(const com::Utf8Str &aKey,
2774 const com::Utf8Str &aValue)
2775{
2776 Utf8Str strKey(aKey);
2777 Utf8Str strValue(aValue);
2778 Utf8Str strOldValue; // empty
2779 HRESULT hrc = S_OK;
2780
2781 /* Because control characters in aKey have caused problems in the settings
2782 * they are rejected unless the key should be deleted. */
2783 if (!strValue.isEmpty())
2784 {
2785 for (size_t i = 0; i < strKey.length(); ++i)
2786 {
2787 char ch = strKey[i];
2788 if (RTLocCIsCntrl(ch))
2789 return E_INVALIDARG;
2790 }
2791 }
2792
2793 // locking note: we only hold the read lock briefly to look up the old value,
2794 // then release it and call the onExtraCanChange callbacks. There is a small
2795 // chance of a race insofar as the callback might be called twice if two callers
2796 // change the same key at the same time, but that's a much better solution
2797 // than the deadlock we had here before. The actual changing of the extradata
2798 // is then performed under the write lock and race-free.
2799
2800 // look up the old value first; if nothing has changed then we need not do anything
2801 {
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
2803 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
2804 if (it != m->pMainConfigFile->mapExtraDataItems.end())
2805 strOldValue = it->second;
2806 }
2807
2808 bool fChanged;
2809 if ((fChanged = (strOldValue != strValue)))
2810 {
2811 // ask for permission from all listeners outside the locks;
2812 // onExtraDataCanChange() only briefly requests the VirtualBox
2813 // lock to copy the list of callbacks to invoke
2814 Bstr error;
2815
2816 if (!i_onExtraDataCanChange(Guid::Empty, Bstr(aKey).raw(), Bstr(aValue).raw(), error))
2817 {
2818 const char *sep = error.isEmpty() ? "" : ": ";
2819 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
2820 return setError(E_ACCESSDENIED,
2821 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
2822 strKey.c_str(),
2823 strValue.c_str(),
2824 sep,
2825 error.raw());
2826 }
2827
2828 // data is changing and change not vetoed: then write it out under the lock
2829
2830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2831
2832 if (strValue.isEmpty())
2833 m->pMainConfigFile->mapExtraDataItems.erase(strKey);
2834 else
2835 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
2836 // creates a new key if needed
2837
2838 /* save settings on success */
2839 hrc = i_saveSettings();
2840 if (FAILED(hrc)) return hrc;
2841 }
2842
2843 // fire notification outside the lock
2844 if (fChanged)
2845 i_onExtraDataChanged(Guid::Empty, Bstr(aKey).raw(), Bstr(aValue).raw());
2846
2847 return hrc;
2848}
2849
2850/**
2851 *
2852 */
2853HRESULT VirtualBox::setSettingsSecret(const com::Utf8Str &aPassword)
2854{
2855 i_storeSettingsKey(aPassword);
2856 i_decryptSettings();
2857 return S_OK;
2858}
2859
2860int VirtualBox::i_decryptMediumSettings(Medium *pMedium)
2861{
2862 Bstr bstrCipher;
2863 HRESULT hrc = pMedium->GetProperty(Bstr("InitiatorSecretEncrypted").raw(),
2864 bstrCipher.asOutParam());
2865 if (SUCCEEDED(hrc))
2866 {
2867 Utf8Str strPlaintext;
2868 int vrc = i_decryptSetting(&strPlaintext, bstrCipher);
2869 if (RT_SUCCESS(vrc))
2870 pMedium->i_setPropertyDirect("InitiatorSecret", strPlaintext);
2871 else
2872 return vrc;
2873 }
2874 return VINF_SUCCESS;
2875}
2876
2877/**
2878 * Decrypt all encrypted settings.
2879 *
2880 * So far we only have encrypted iSCSI initiator secrets so we just go through
2881 * all hard disk media and determine the plain 'InitiatorSecret' from
2882 * 'InitiatorSecretEncrypted. The latter is stored as Base64 because medium
2883 * properties need to be null-terminated strings.
2884 */
2885int VirtualBox::i_decryptSettings()
2886{
2887 bool fFailure = false;
2888 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2889 for (MediaList::const_iterator mt = m->allHardDisks.begin();
2890 mt != m->allHardDisks.end();
2891 ++mt)
2892 {
2893 ComObjPtr<Medium> pMedium = *mt;
2894 AutoCaller medCaller(pMedium);
2895 if (FAILED(medCaller.hrc()))
2896 continue;
2897 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
2898 int vrc = i_decryptMediumSettings(pMedium);
2899 if (RT_FAILURE(vrc))
2900 fFailure = true;
2901 }
2902 if (!fFailure)
2903 {
2904 for (MediaList::const_iterator mt = m->allHardDisks.begin();
2905 mt != m->allHardDisks.end();
2906 ++mt)
2907 {
2908 i_onMediumConfigChanged(*mt);
2909 }
2910 }
2911 return fFailure ? VERR_INVALID_PARAMETER : VINF_SUCCESS;
2912}
2913
2914/**
2915 * Encode.
2916 *
2917 * @param aPlaintext plaintext to be encrypted
2918 * @param aCiphertext resulting ciphertext (base64-encoded)
2919 */
2920int VirtualBox::i_encryptSetting(const Utf8Str &aPlaintext, Utf8Str *aCiphertext)
2921{
2922 uint8_t abCiphertext[32];
2923 char szCipherBase64[128];
2924 size_t cchCipherBase64;
2925 int vrc = i_encryptSettingBytes((uint8_t*)aPlaintext.c_str(), abCiphertext, aPlaintext.length()+1, sizeof(abCiphertext));
2926 if (RT_SUCCESS(vrc))
2927 {
2928 vrc = RTBase64Encode(abCiphertext, sizeof(abCiphertext), szCipherBase64, sizeof(szCipherBase64), &cchCipherBase64);
2929 if (RT_SUCCESS(vrc))
2930 *aCiphertext = szCipherBase64;
2931 }
2932 return vrc;
2933}
2934
2935/**
2936 * Decode.
2937 *
2938 * @param aPlaintext resulting plaintext
2939 * @param aCiphertext ciphertext (base64-encoded) to decrypt
2940 */
2941int VirtualBox::i_decryptSetting(Utf8Str *aPlaintext, const Utf8Str &aCiphertext)
2942{
2943 uint8_t abPlaintext[64];
2944 uint8_t abCiphertext[64];
2945 size_t cbCiphertext;
2946 int vrc = RTBase64Decode(aCiphertext.c_str(),
2947 abCiphertext, sizeof(abCiphertext),
2948 &cbCiphertext, NULL);
2949 if (RT_SUCCESS(vrc))
2950 {
2951 vrc = i_decryptSettingBytes(abPlaintext, abCiphertext, cbCiphertext);
2952 if (RT_SUCCESS(vrc))
2953 {
2954 for (unsigned i = 0; i < cbCiphertext; i++)
2955 {
2956 /* sanity check: null-terminated string? */
2957 if (abPlaintext[i] == '\0')
2958 {
2959 /* sanity check: valid UTF8 string? */
2960 if (RTStrIsValidEncoding((const char*)abPlaintext))
2961 {
2962 *aPlaintext = Utf8Str((const char*)abPlaintext);
2963 return VINF_SUCCESS;
2964 }
2965 }
2966 }
2967 vrc = VERR_INVALID_MAGIC;
2968 }
2969 }
2970 return vrc;
2971}
2972
2973/**
2974 * Encrypt secret bytes. Use the m->SettingsCipherKey as key.
2975 *
2976 * @param aPlaintext clear text to be encrypted
2977 * @param aCiphertext resulting encrypted text
2978 * @param aPlaintextSize size of the plaintext
2979 * @param aCiphertextSize size of the ciphertext
2980 */
2981int VirtualBox::i_encryptSettingBytes(const uint8_t *aPlaintext, uint8_t *aCiphertext,
2982 size_t aPlaintextSize, size_t aCiphertextSize) const
2983{
2984 unsigned i, j;
2985 uint8_t aBytes[64];
2986
2987 if (!m->fSettingsCipherKeySet)
2988 return VERR_INVALID_STATE;
2989
2990 if (aCiphertextSize > sizeof(aBytes))
2991 return VERR_BUFFER_OVERFLOW;
2992
2993 if (aCiphertextSize < 32)
2994 return VERR_INVALID_PARAMETER;
2995
2996 AssertCompile(sizeof(m->SettingsCipherKey) >= 32);
2997
2998 /* store the first 8 bytes of the cipherkey for verification */
2999 for (i = 0, j = 0; i < 8; i++, j++)
3000 aCiphertext[i] = m->SettingsCipherKey[j];
3001
3002 for (unsigned k = 0; k < aPlaintextSize && i < aCiphertextSize; i++, k++)
3003 {
3004 aCiphertext[i] = (aPlaintext[k] ^ m->SettingsCipherKey[j]);
3005 if (++j >= sizeof(m->SettingsCipherKey))
3006 j = 0;
3007 }
3008
3009 /* fill with random data to have a minimal length (salt) */
3010 if (i < aCiphertextSize)
3011 {
3012 RTRandBytes(aBytes, aCiphertextSize - i);
3013 for (int k = 0; i < aCiphertextSize; i++, k++)
3014 {
3015 aCiphertext[i] = aBytes[k] ^ m->SettingsCipherKey[j];
3016 if (++j >= sizeof(m->SettingsCipherKey))
3017 j = 0;
3018 }
3019 }
3020
3021 return VINF_SUCCESS;
3022}
3023
3024/**
3025 * Decrypt secret bytes. Use the m->SettingsCipherKey as key.
3026 *
3027 * @param aPlaintext resulting plaintext
3028 * @param aCiphertext ciphertext to be decrypted
3029 * @param aCiphertextSize size of the ciphertext == size of the plaintext
3030 */
3031int VirtualBox::i_decryptSettingBytes(uint8_t *aPlaintext,
3032 const uint8_t *aCiphertext, size_t aCiphertextSize) const
3033{
3034 unsigned i, j;
3035
3036 if (!m->fSettingsCipherKeySet)
3037 return VERR_INVALID_STATE;
3038
3039 if (aCiphertextSize < 32)
3040 return VERR_INVALID_PARAMETER;
3041
3042 /* key verification */
3043 for (i = 0, j = 0; i < 8; i++, j++)
3044 if (aCiphertext[i] != m->SettingsCipherKey[j])
3045 return VERR_INVALID_MAGIC;
3046
3047 /* poison */
3048 memset(aPlaintext, 0xff, aCiphertextSize);
3049 for (int k = 0; i < aCiphertextSize; i++, k++)
3050 {
3051 aPlaintext[k] = aCiphertext[i] ^ m->SettingsCipherKey[j];
3052 if (++j >= sizeof(m->SettingsCipherKey))
3053 j = 0;
3054 }
3055
3056 return VINF_SUCCESS;
3057}
3058
3059/**
3060 * Store a settings key.
3061 *
3062 * @param aKey the key to store
3063 */
3064void VirtualBox::i_storeSettingsKey(const Utf8Str &aKey)
3065{
3066 RTSha512(aKey.c_str(), aKey.length(), m->SettingsCipherKey);
3067 m->fSettingsCipherKeySet = true;
3068}
3069
3070// public methods only for internal purposes
3071/////////////////////////////////////////////////////////////////////////////
3072
3073#ifdef DEBUG
3074void VirtualBox::i_dumpAllBackRefs()
3075{
3076 {
3077 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3078 for (MediaList::const_iterator mt = m->allHardDisks.begin();
3079 mt != m->allHardDisks.end();
3080 ++mt)
3081 {
3082 ComObjPtr<Medium> pMedium = *mt;
3083 pMedium->i_dumpBackRefs();
3084 }
3085 }
3086 {
3087 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3088 for (MediaList::const_iterator mt = m->allDVDImages.begin();
3089 mt != m->allDVDImages.end();
3090 ++mt)
3091 {
3092 ComObjPtr<Medium> pMedium = *mt;
3093 pMedium->i_dumpBackRefs();
3094 }
3095 }
3096}
3097#endif
3098
3099/**
3100 * Posts an event to the event queue that is processed asynchronously
3101 * on a dedicated thread.
3102 *
3103 * Posting events to the dedicated event queue is useful to perform secondary
3104 * actions outside any object locks -- for example, to iterate over a list
3105 * of callbacks and inform them about some change caused by some object's
3106 * method call.
3107 *
3108 * @param event event to post; must have been allocated using |new|, will
3109 * be deleted automatically by the event thread after processing
3110 *
3111 * @note Doesn't lock any object.
3112 */
3113HRESULT VirtualBox::i_postEvent(Event *event)
3114{
3115 AssertReturn(event, E_FAIL);
3116
3117 HRESULT hrc;
3118 AutoCaller autoCaller(this);
3119 if (SUCCEEDED((hrc = autoCaller.hrc())))
3120 {
3121 if (getObjectState().getState() != ObjectState::Ready)
3122 Log1WarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
3123 getObjectState().getState()));
3124 // return S_OK
3125 else if ( (m->pAsyncEventQ)
3126 && (m->pAsyncEventQ->postEvent(event))
3127 )
3128 return S_OK;
3129 else
3130 hrc = E_FAIL;
3131 }
3132
3133 // in any event of failure, we must clean up here, or we'll leak;
3134 // the caller has allocated the object using new()
3135 delete event;
3136 return hrc;
3137}
3138
3139/**
3140 * Adds a progress to the global collection of pending operations.
3141 * Usually gets called upon progress object initialization.
3142 *
3143 * @param aProgress Operation to add to the collection.
3144 *
3145 * @note Doesn't lock objects.
3146 */
3147HRESULT VirtualBox::i_addProgress(IProgress *aProgress)
3148{
3149 CheckComArgNotNull(aProgress);
3150
3151 AutoCaller autoCaller(this);
3152 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3153
3154 Bstr id;
3155 HRESULT hrc = aProgress->COMGETTER(Id)(id.asOutParam());
3156 AssertComRCReturnRC(hrc);
3157
3158 /* protect mProgressOperations */
3159 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
3160
3161 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
3162 return S_OK;
3163}
3164
3165/**
3166 * Removes the progress from the global collection of pending operations.
3167 * Usually gets called upon progress completion.
3168 *
3169 * @param aId UUID of the progress operation to remove
3170 *
3171 * @note Doesn't lock objects.
3172 */
3173HRESULT VirtualBox::i_removeProgress(IN_GUID aId)
3174{
3175 AutoCaller autoCaller(this);
3176 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3177
3178 ComPtr<IProgress> progress;
3179
3180 /* protect mProgressOperations */
3181 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
3182
3183 size_t cnt = m->mapProgressOperations.erase(aId);
3184 Assert(cnt == 1);
3185 NOREF(cnt);
3186
3187 return S_OK;
3188}
3189
3190#ifdef RT_OS_WINDOWS
3191
3192class StartSVCHelperClientData : public ThreadTask
3193{
3194public:
3195 StartSVCHelperClientData()
3196 {
3197 LogFlowFuncEnter();
3198 m_strTaskName = "SVCHelper";
3199 threadVoidData = NULL;
3200 initialized = false;
3201 }
3202
3203 virtual ~StartSVCHelperClientData()
3204 {
3205 LogFlowFuncEnter();
3206 if (threadVoidData!=NULL)
3207 {
3208 delete threadVoidData;
3209 threadVoidData=NULL;
3210 }
3211 };
3212
3213 void handler()
3214 {
3215 VirtualBox::i_SVCHelperClientThreadTask(this);
3216 }
3217
3218 const ComPtr<Progress>& GetProgressObject() const {return progress;}
3219
3220 bool init(VirtualBox* aVbox,
3221 Progress* aProgress,
3222 bool aPrivileged,
3223 VirtualBox::PFN_SVC_HELPER_CLIENT_T aFunc,
3224 void *aUser)
3225 {
3226 LogFlowFuncEnter();
3227 that = aVbox;
3228 progress = aProgress;
3229 privileged = aPrivileged;
3230 func = aFunc;
3231 user = aUser;
3232
3233 initThreadVoidData();
3234
3235 initialized = true;
3236
3237 return initialized;
3238 }
3239
3240 bool isOk() const{ return initialized;}
3241
3242 bool initialized;
3243 ComObjPtr<VirtualBox> that;
3244 ComObjPtr<Progress> progress;
3245 bool privileged;
3246 VirtualBox::PFN_SVC_HELPER_CLIENT_T func;
3247 void *user;
3248 ThreadVoidData *threadVoidData;
3249
3250private:
3251 bool initThreadVoidData()
3252 {
3253 LogFlowFuncEnter();
3254 threadVoidData = static_cast<ThreadVoidData*>(user);
3255 return true;
3256 }
3257};
3258
3259/**
3260 * Helper method that starts a worker thread that:
3261 * - creates a pipe communication channel using SVCHlpClient;
3262 * - starts an SVC Helper process that will inherit this channel;
3263 * - executes the supplied function by passing it the created SVCHlpClient
3264 * and opened instance to communicate to the Helper process and the given
3265 * Progress object.
3266 *
3267 * The user function is supposed to communicate to the helper process
3268 * using the \a aClient argument to do the requested job and optionally expose
3269 * the progress through the \a aProgress object. The user function should never
3270 * call notifyComplete() on it: this will be done automatically using the
3271 * result code returned by the function.
3272 *
3273 * Before the user function is started, the communication channel passed to
3274 * the \a aClient argument is fully set up, the function should start using
3275 * its write() and read() methods directly.
3276 *
3277 * The \a aVrc parameter of the user function may be used to return an error
3278 * code if it is related to communication errors (for example, returned by
3279 * the SVCHlpClient members when they fail). In this case, the correct error
3280 * message using this value will be reported to the caller. Note that the
3281 * value of \a aVrc is inspected only if the user function itself returns
3282 * success.
3283 *
3284 * If a failure happens anywhere before the user function would be normally
3285 * called, it will be called anyway in special "cleanup only" mode indicated
3286 * by \a aClient, \a aProgress and \a aVrc arguments set to NULL. In this mode,
3287 * all the function is supposed to do is to cleanup its aUser argument if
3288 * necessary (it's assumed that the ownership of this argument is passed to
3289 * the user function once #startSVCHelperClient() returns a success, thus
3290 * making it responsible for the cleanup).
3291 *
3292 * After the user function returns, the thread will send the SVCHlpMsg::Null
3293 * message to indicate a process termination.
3294 *
3295 * @param aPrivileged |true| to start the SVC Helper process as a privileged
3296 * user that can perform administrative tasks
3297 * @param aFunc user function to run
3298 * @param aUser argument to the user function
3299 * @param aProgress progress object that will track operation completion
3300 *
3301 * @note aPrivileged is currently ignored (due to some unsolved problems in
3302 * Vista) and the process will be started as a normal (unprivileged)
3303 * process.
3304 *
3305 * @note Doesn't lock anything.
3306 */
3307HRESULT VirtualBox::i_startSVCHelperClient(bool aPrivileged,
3308 PFN_SVC_HELPER_CLIENT_T aFunc,
3309 void *aUser, Progress *aProgress)
3310{
3311 LogFlowFuncEnter();
3312 AssertReturn(aFunc, E_POINTER);
3313 AssertReturn(aProgress, E_POINTER);
3314
3315 AutoCaller autoCaller(this);
3316 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3317
3318 /* create the i_SVCHelperClientThreadTask() argument */
3319
3320 HRESULT hrc = S_OK;
3321 StartSVCHelperClientData *pTask = NULL;
3322 try
3323 {
3324 pTask = new StartSVCHelperClientData();
3325
3326 pTask->init(this, aProgress, aPrivileged, aFunc, aUser);
3327
3328 if (!pTask->isOk())
3329 {
3330 delete pTask;
3331 LogRel(("Could not init StartSVCHelperClientData object \n"));
3332 throw E_FAIL;
3333 }
3334
3335 //this function delete pTask in case of exceptions, so there is no need in the call of delete operator
3336 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_WORKER);
3337
3338 }
3339 catch(std::bad_alloc &)
3340 {
3341 hrc = setError(E_OUTOFMEMORY);
3342 }
3343 catch(...)
3344 {
3345 LogRel(("Could not create thread for StartSVCHelperClientData \n"));
3346 hrc = E_FAIL;
3347 }
3348
3349 return hrc;
3350}
3351
3352/**
3353 * Worker thread for startSVCHelperClient().
3354 */
3355/* static */
3356void VirtualBox::i_SVCHelperClientThreadTask(StartSVCHelperClientData *pTask)
3357{
3358 LogFlowFuncEnter();
3359 HRESULT hrc = S_OK;
3360 bool userFuncCalled = false;
3361
3362 do
3363 {
3364 AssertBreakStmt(pTask, hrc = E_POINTER);
3365 AssertReturnVoid(!pTask->progress.isNull());
3366
3367 /* protect VirtualBox from uninitialization */
3368 AutoCaller autoCaller(pTask->that);
3369 if (!autoCaller.isOk())
3370 {
3371 /* it's too late */
3372 hrc = autoCaller.hrc();
3373 break;
3374 }
3375
3376 int vrc = VINF_SUCCESS;
3377
3378 Guid id;
3379 id.create();
3380 SVCHlpClient client;
3381 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
3382 id.raw()).c_str());
3383 if (RT_FAILURE(vrc))
3384 {
3385 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not create the communication channel (%Rrc)"), vrc);
3386 break;
3387 }
3388
3389 /* get the path to the executable */
3390 char exePathBuf[RTPATH_MAX];
3391 char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX);
3392 if (!exePath)
3393 {
3394 hrc = pTask->that->setError(E_FAIL, tr("Cannot get executable name"));
3395 break;
3396 }
3397
3398 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().c_str());
3399
3400 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.c_str()));
3401
3402 RTPROCESS pid = NIL_RTPROCESS;
3403
3404 if (pTask->privileged)
3405 {
3406 /* Attempt to start a privileged process using the Run As dialog */
3407
3408 Bstr file = exePath;
3409 Bstr parameters = argsStr;
3410
3411 SHELLEXECUTEINFO shExecInfo;
3412
3413 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
3414
3415 shExecInfo.fMask = NULL;
3416 shExecInfo.hwnd = NULL;
3417 shExecInfo.lpVerb = L"runas";
3418 shExecInfo.lpFile = file.raw();
3419 shExecInfo.lpParameters = parameters.raw();
3420 shExecInfo.lpDirectory = NULL;
3421 shExecInfo.nShow = SW_NORMAL;
3422 shExecInfo.hInstApp = NULL;
3423
3424 if (!ShellExecuteEx(&shExecInfo))
3425 {
3426 int vrc2 = RTErrConvertFromWin32(GetLastError());
3427 /* hide excessive details in case of a frequent error
3428 * (pressing the Cancel button to close the Run As dialog) */
3429 if (vrc2 == VERR_CANCELLED)
3430 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Operation canceled by the user"));
3431 else
3432 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not launch a privileged process '%s' (%Rrc)"), exePath, vrc2);
3433 break;
3434 }
3435 }
3436 else
3437 {
3438 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
3439 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
3440 if (RT_FAILURE(vrc))
3441 {
3442 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
3443 break;
3444 }
3445 }
3446
3447 /* wait for the client to connect */
3448 vrc = client.connect();
3449 if (RT_SUCCESS(vrc))
3450 {
3451 /* start the user supplied function */
3452 hrc = pTask->func(&client, pTask->progress, pTask->user, &vrc);
3453 userFuncCalled = true;
3454 }
3455
3456 /* send the termination signal to the process anyway */
3457 {
3458 int vrc2 = client.write(SVCHlpMsg::Null);
3459 if (RT_SUCCESS(vrc))
3460 vrc = vrc2;
3461 }
3462
3463 if (SUCCEEDED(hrc) && RT_FAILURE(vrc))
3464 {
3465 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not operate the communication channel (%Rrc)"), vrc);
3466 break;
3467 }
3468 }
3469 while (0);
3470
3471 if (FAILED(hrc) && !userFuncCalled)
3472 {
3473 /* call the user function in the "cleanup only" mode
3474 * to let it free resources passed to in aUser */
3475 pTask->func(NULL, NULL, pTask->user, NULL);
3476 }
3477
3478 pTask->progress->i_notifyComplete(hrc);
3479
3480 LogFlowFuncLeave();
3481}
3482
3483#endif /* RT_OS_WINDOWS */
3484
3485/**
3486 * Sends a signal to the client watcher to rescan the set of machines
3487 * that have open sessions.
3488 *
3489 * @note Doesn't lock anything.
3490 */
3491void VirtualBox::i_updateClientWatcher()
3492{
3493 AutoCaller autoCaller(this);
3494 AssertComRCReturnVoid(autoCaller.hrc());
3495
3496 AssertPtrReturnVoid(m->pClientWatcher);
3497 m->pClientWatcher->update();
3498}
3499
3500/**
3501 * Adds the given child process ID to the list of processes to be reaped.
3502 * This call should be followed by #i_updateClientWatcher() to take the effect.
3503 *
3504 * @note Doesn't lock anything.
3505 */
3506void VirtualBox::i_addProcessToReap(RTPROCESS pid)
3507{
3508 AutoCaller autoCaller(this);
3509 AssertComRCReturnVoid(autoCaller.hrc());
3510
3511 AssertPtrReturnVoid(m->pClientWatcher);
3512 m->pClientWatcher->addProcess(pid);
3513}
3514
3515/**
3516 * VD plugin load
3517 */
3518int VirtualBox::i_loadVDPlugin(const char *pszPluginLibrary)
3519{
3520 return m->pSystemProperties->i_loadVDPlugin(pszPluginLibrary);
3521}
3522
3523/**
3524 * VD plugin unload
3525 */
3526int VirtualBox::i_unloadVDPlugin(const char *pszPluginLibrary)
3527{
3528 return m->pSystemProperties->i_unloadVDPlugin(pszPluginLibrary);
3529}
3530
3531/**
3532 * @note Doesn't lock any object.
3533 */
3534void VirtualBox::i_onMediumRegistered(const Guid &aMediumId, const DeviceType_T aDevType, const BOOL aRegistered)
3535{
3536 ComPtr<IEvent> ptrEvent;
3537 HRESULT hrc = ::CreateMediumRegisteredEvent(ptrEvent.asOutParam(), m->pEventSource,
3538 aMediumId.toString(), aDevType, aRegistered);
3539 AssertComRCReturnVoid(hrc);
3540 i_postEvent(new AsyncEvent(this, ptrEvent));
3541}
3542
3543void VirtualBox::i_onMediumConfigChanged(IMedium *aMedium)
3544{
3545 ComPtr<IEvent> ptrEvent;
3546 HRESULT hrc = ::CreateMediumConfigChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aMedium);
3547 AssertComRCReturnVoid(hrc);
3548 i_postEvent(new AsyncEvent(this, ptrEvent));
3549}
3550
3551void VirtualBox::i_onMediumChanged(IMediumAttachment *aMediumAttachment)
3552{
3553 ComPtr<IEvent> ptrEvent;
3554 HRESULT hrc = ::CreateMediumChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aMediumAttachment);
3555 AssertComRCReturnVoid(hrc);
3556 i_postEvent(new AsyncEvent(this, ptrEvent));
3557}
3558
3559/**
3560 * @note Doesn't lock any object.
3561 */
3562void VirtualBox::i_onStorageControllerChanged(const Guid &aMachineId, const com::Utf8Str &aControllerName)
3563{
3564 ComPtr<IEvent> ptrEvent;
3565 HRESULT hrc = ::CreateStorageControllerChangedEvent(ptrEvent.asOutParam(), m->pEventSource,
3566 aMachineId.toString(), aControllerName);
3567 AssertComRCReturnVoid(hrc);
3568 i_postEvent(new AsyncEvent(this, ptrEvent));
3569}
3570
3571void VirtualBox::i_onStorageDeviceChanged(IMediumAttachment *aStorageDevice, const BOOL fRemoved, const BOOL fSilent)
3572{
3573 ComPtr<IEvent> ptrEvent;
3574 HRESULT hrc = ::CreateStorageDeviceChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aStorageDevice, fRemoved, fSilent);
3575 AssertComRCReturnVoid(hrc);
3576 i_postEvent(new AsyncEvent(this, ptrEvent));
3577}
3578
3579/**
3580 * @note Doesn't lock any object.
3581 */
3582void VirtualBox::i_onMachineStateChanged(const Guid &aId, MachineState_T aState)
3583{
3584 ComPtr<IEvent> ptrEvent;
3585 HRESULT hrc = ::CreateMachineStateChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aState);
3586 AssertComRCReturnVoid(hrc);
3587 i_postEvent(new AsyncEvent(this, ptrEvent));
3588}
3589
3590/**
3591 * @note Doesn't lock any object.
3592 */
3593void VirtualBox::i_onMachineDataChanged(const Guid &aId, BOOL aTemporary)
3594{
3595 ComPtr<IEvent> ptrEvent;
3596 HRESULT hrc = ::CreateMachineDataChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aTemporary);
3597 AssertComRCReturnVoid(hrc);
3598 i_postEvent(new AsyncEvent(this, ptrEvent));
3599}
3600
3601/**
3602 * @note Doesn't lock any object.
3603 */
3604void VirtualBox::i_onMachineGroupsChanged(const Guid &aId)
3605{
3606 ComPtr<IEvent> ptrEvent;
3607 HRESULT hrc = ::CreateMachineGroupsChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), FALSE /*aDummy*/);
3608 AssertComRCReturnVoid(hrc);
3609 i_postEvent(new AsyncEvent(this, ptrEvent));
3610}
3611
3612/**
3613 * @note Locks this object for reading.
3614 */
3615BOOL VirtualBox::i_onExtraDataCanChange(const Guid &aId, const Utf8Str &aKey, const Utf8Str &aValue, Bstr &aError)
3616{
3617 LogFlowThisFunc(("machine={%RTuuid} aKey={%s} aValue={%s}\n", aId.raw(), aKey.c_str(), aValue.c_str()));
3618
3619 AutoCaller autoCaller(this);
3620 AssertComRCReturn(autoCaller.hrc(), FALSE);
3621
3622 ComPtr<IEvent> ptrEvent;
3623 HRESULT hrc = ::CreateExtraDataCanChangeEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aKey, aValue);
3624 AssertComRCReturn(hrc, TRUE);
3625
3626 VBoxEventDesc EvtDesc(ptrEvent, m->pEventSource);
3627 BOOL fDelivered = EvtDesc.fire(3000); /* Wait up to 3 secs for delivery */
3628 //Assert(fDelivered);
3629 BOOL fAllowChange = TRUE;
3630 if (fDelivered)
3631 {
3632 ComPtr<IExtraDataCanChangeEvent> ptrCanChangeEvent = ptrEvent;
3633 Assert(ptrCanChangeEvent);
3634
3635 BOOL fVetoed = FALSE;
3636 ptrCanChangeEvent->IsVetoed(&fVetoed);
3637 fAllowChange = !fVetoed;
3638
3639 if (!fAllowChange)
3640 {
3641 SafeArray<BSTR> aVetos;
3642 ptrCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos));
3643 if (aVetos.size() > 0)
3644 aError = aVetos[0];
3645 }
3646 }
3647
3648 LogFlowThisFunc(("fAllowChange=%RTbool\n", fAllowChange));
3649 return fAllowChange;
3650}
3651
3652/**
3653 * @note Doesn't lock any object.
3654 */
3655void VirtualBox::i_onExtraDataChanged(const Guid &aId, const Utf8Str &aKey, const Utf8Str &aValue)
3656{
3657 ComPtr<IEvent> ptrEvent;
3658 HRESULT hrc = ::CreateExtraDataChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aKey, aValue);
3659 AssertComRCReturnVoid(hrc);
3660 i_postEvent(new AsyncEvent(this, ptrEvent));
3661}
3662
3663/**
3664 * @note Doesn't lock any object.
3665 */
3666void VirtualBox::i_onMachineRegistered(const Guid &aId, BOOL aRegistered)
3667{
3668 ComPtr<IEvent> ptrEvent;
3669 HRESULT hrc = ::CreateMachineRegisteredEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aRegistered);
3670 AssertComRCReturnVoid(hrc);
3671 i_postEvent(new AsyncEvent(this, ptrEvent));
3672}
3673
3674/**
3675 * @note Doesn't lock any object.
3676 */
3677void VirtualBox::i_onSessionStateChanged(const Guid &aId, SessionState_T aState)
3678{
3679 ComPtr<IEvent> ptrEvent;
3680 HRESULT hrc = ::CreateSessionStateChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aState);
3681 AssertComRCReturnVoid(hrc);
3682 i_postEvent(new AsyncEvent(this, ptrEvent));
3683}
3684
3685/**
3686 * @note Doesn't lock any object.
3687 */
3688void VirtualBox::i_onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
3689{
3690 ComPtr<IEvent> ptrEvent;
3691 HRESULT hrc = ::CreateSnapshotTakenEvent(ptrEvent.asOutParam(), m->pEventSource,
3692 aMachineId.toString(), aSnapshotId.toString());
3693 AssertComRCReturnVoid(hrc);
3694 i_postEvent(new AsyncEvent(this, ptrEvent));
3695}
3696
3697/**
3698 * @note Doesn't lock any object.
3699 */
3700void VirtualBox::i_onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
3701{
3702 ComPtr<IEvent> ptrEvent;
3703 HRESULT hrc = ::CreateSnapshotDeletedEvent(ptrEvent.asOutParam(), m->pEventSource,
3704 aMachineId.toString(), aSnapshotId.toString());
3705 AssertComRCReturnVoid(hrc);
3706 i_postEvent(new AsyncEvent(this, ptrEvent));
3707}
3708
3709/**
3710 * @note Doesn't lock any object.
3711 */
3712void VirtualBox::i_onSnapshotRestored(const Guid &aMachineId, const Guid &aSnapshotId)
3713{
3714 ComPtr<IEvent> ptrEvent;
3715 HRESULT hrc = ::CreateSnapshotRestoredEvent(ptrEvent.asOutParam(), m->pEventSource,
3716 aMachineId.toString(), aSnapshotId.toString());
3717 AssertComRCReturnVoid(hrc);
3718 i_postEvent(new AsyncEvent(this, ptrEvent));
3719}
3720
3721/**
3722 * @note Doesn't lock any object.
3723 */
3724void VirtualBox::i_onSnapshotChanged(const Guid &aMachineId, const Guid &aSnapshotId)
3725{
3726 ComPtr<IEvent> ptrEvent;
3727 HRESULT hrc = ::CreateSnapshotChangedEvent(ptrEvent.asOutParam(), m->pEventSource,
3728 aMachineId.toString(), aSnapshotId.toString());
3729 AssertComRCReturnVoid(hrc);
3730 i_postEvent(new AsyncEvent(this, ptrEvent));
3731}
3732
3733/**
3734 * @note Doesn't lock any object.
3735 */
3736void VirtualBox::i_onGuestPropertyChanged(const Guid &aMachineId, const Utf8Str &aName, const Utf8Str &aValue,
3737 const Utf8Str &aFlags, const BOOL fWasDeleted)
3738{
3739 ComPtr<IEvent> ptrEvent;
3740 HRESULT hrc = ::CreateGuestPropertyChangedEvent(ptrEvent.asOutParam(), m->pEventSource,
3741 aMachineId.toString(), aName, aValue, aFlags, fWasDeleted);
3742 AssertComRCReturnVoid(hrc);
3743 i_postEvent(new AsyncEvent(this, ptrEvent));
3744}
3745
3746/**
3747 * @note Doesn't lock any object.
3748 */
3749void VirtualBox::i_onNatRedirectChanged(const Guid &aMachineId, ULONG ulSlot, bool fRemove, const Utf8Str &aName,
3750 NATProtocol_T aProto, const Utf8Str &aHostIp, uint16_t aHostPort,
3751 const Utf8Str &aGuestIp, uint16_t aGuestPort)
3752{
3753 ::FireNATRedirectEvent(m->pEventSource, aMachineId.toString(), ulSlot, fRemove, aName, aProto, aHostIp,
3754 aHostPort, aGuestIp, aGuestPort);
3755}
3756
3757/** @todo Unused!! */
3758void VirtualBox::i_onNATNetworkChanged(const Utf8Str &aName)
3759{
3760 ::FireNATNetworkChangedEvent(m->pEventSource, aName);
3761}
3762
3763void VirtualBox::i_onNATNetworkStartStop(const Utf8Str &aName, BOOL fStart)
3764{
3765 ::FireNATNetworkStartStopEvent(m->pEventSource, aName, fStart);
3766}
3767
3768void VirtualBox::i_onNATNetworkSetting(const Utf8Str &aNetworkName, BOOL aEnabled,
3769 const Utf8Str &aNetwork, const Utf8Str &aGateway,
3770 BOOL aAdvertiseDefaultIpv6RouteEnabled,
3771 BOOL fNeedDhcpServer)
3772{
3773 ::FireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled, aNetwork, aGateway,
3774 aAdvertiseDefaultIpv6RouteEnabled, fNeedDhcpServer);
3775}
3776
3777void VirtualBox::i_onNATNetworkPortForward(const Utf8Str &aNetworkName, BOOL create, BOOL fIpv6,
3778 const Utf8Str &aRuleName, NATProtocol_T proto,
3779 const Utf8Str &aHostIp, LONG aHostPort,
3780 const Utf8Str &aGuestIp, LONG aGuestPort)
3781{
3782 ::FireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create, fIpv6, aRuleName, proto,
3783 aHostIp, aHostPort, aGuestIp, aGuestPort);
3784}
3785
3786
3787void VirtualBox::i_onHostNameResolutionConfigurationChange()
3788{
3789 if (m->pEventSource)
3790 ::FireHostNameResolutionConfigurationChangeEvent(m->pEventSource);
3791}
3792
3793
3794int VirtualBox::i_natNetworkRefInc(const Utf8Str &aNetworkName)
3795{
3796 AutoWriteLock safeLock(*spMtxNatNetworkNameToRefCountLock COMMA_LOCKVAL_SRC_POS);
3797
3798 if (!sNatNetworkNameToRefCount[aNetworkName])
3799 {
3800 ComPtr<INATNetwork> nat;
3801 HRESULT hrc = findNATNetworkByName(aNetworkName, nat);
3802 if (FAILED(hrc)) return -1;
3803
3804 hrc = nat->Start();
3805 if (SUCCEEDED(hrc))
3806 LogRel(("Started NAT network '%s'\n", aNetworkName.c_str()));
3807 else
3808 LogRel(("Error %Rhrc starting NAT network '%s'\n", hrc, aNetworkName.c_str()));
3809 AssertComRCReturn(hrc, -1);
3810 }
3811
3812 sNatNetworkNameToRefCount[aNetworkName]++;
3813
3814 return sNatNetworkNameToRefCount[aNetworkName];
3815}
3816
3817
3818int VirtualBox::i_natNetworkRefDec(const Utf8Str &aNetworkName)
3819{
3820 AutoWriteLock safeLock(*spMtxNatNetworkNameToRefCountLock COMMA_LOCKVAL_SRC_POS);
3821
3822 if (!sNatNetworkNameToRefCount[aNetworkName])
3823 return 0;
3824
3825 sNatNetworkNameToRefCount[aNetworkName]--;
3826
3827 if (!sNatNetworkNameToRefCount[aNetworkName])
3828 {
3829 ComPtr<INATNetwork> nat;
3830 HRESULT hrc = findNATNetworkByName(aNetworkName, nat);
3831 if (FAILED(hrc)) return -1;
3832
3833 hrc = nat->Stop();
3834 if (SUCCEEDED(hrc))
3835 LogRel(("Stopped NAT network '%s'\n", aNetworkName.c_str()));
3836 else
3837 LogRel(("Error %Rhrc stopping NAT network '%s'\n", hrc, aNetworkName.c_str()));
3838 AssertComRCReturn(hrc, -1);
3839 }
3840
3841 return sNatNetworkNameToRefCount[aNetworkName];
3842}
3843
3844
3845/*
3846 * Export this to NATNetwork so that its setters can refuse to change
3847 * essential network settings when an VBoxNatNet instance is running.
3848 */
3849RWLockHandle *VirtualBox::i_getNatNetLock() const
3850{
3851 return spMtxNatNetworkNameToRefCountLock;
3852}
3853
3854
3855/*
3856 * Export this to NATNetwork so that its setters can refuse to change
3857 * essential network settings when an VBoxNatNet instance is running.
3858 * The caller is expected to hold a read lock on i_getNatNetLock().
3859 */
3860bool VirtualBox::i_isNatNetStarted(const Utf8Str &aNetworkName) const
3861{
3862 return sNatNetworkNameToRefCount[aNetworkName] > 0;
3863}
3864
3865
3866void VirtualBox::i_onCloudProviderListChanged(BOOL aRegistered)
3867{
3868 ::FireCloudProviderListChangedEvent(m->pEventSource, aRegistered);
3869}
3870
3871
3872void VirtualBox::i_onCloudProviderRegistered(const Utf8Str &aProviderId, BOOL aRegistered)
3873{
3874 ::FireCloudProviderRegisteredEvent(m->pEventSource, aProviderId, aRegistered);
3875}
3876
3877
3878void VirtualBox::i_onCloudProviderUninstall(const Utf8Str &aProviderId)
3879{
3880 HRESULT hrc;
3881
3882 ComPtr<IEvent> pEvent;
3883 hrc = CreateCloudProviderUninstallEvent(pEvent.asOutParam(),
3884 m->pEventSource, aProviderId);
3885 if (FAILED(hrc))
3886 return;
3887
3888 BOOL fDelivered = FALSE;
3889 hrc = m->pEventSource->FireEvent(pEvent, /* :timeout */ 10000, &fDelivered);
3890 if (FAILED(hrc))
3891 return;
3892}
3893
3894void VirtualBox::i_onLanguageChanged(const Utf8Str &aLanguageId)
3895{
3896 ComPtr<IEvent> ptrEvent;
3897 HRESULT hrc = ::CreateLanguageChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aLanguageId);
3898 AssertComRCReturnVoid(hrc);
3899 i_postEvent(new AsyncEvent(this, ptrEvent));
3900}
3901
3902void VirtualBox::i_onProgressCreated(const Guid &aId, BOOL aCreated)
3903{
3904 ::FireProgressCreatedEvent(m->pEventSource, aId.toString(), aCreated);
3905}
3906
3907#ifdef VBOX_WITH_UPDATE_AGENT
3908/**
3909 * @note Doesn't lock any object.
3910 */
3911void VirtualBox::i_onUpdateAgentAvailable(IUpdateAgent *aAgent,
3912 const Utf8Str &aVer, UpdateChannel_T aChannel, UpdateSeverity_T aSev,
3913 const Utf8Str &aDownloadURL, const Utf8Str &aWebURL, const Utf8Str &aReleaseNotes)
3914{
3915 ::FireUpdateAgentAvailableEvent(m->pEventSource, aAgent, aVer, aChannel, aSev,
3916 aDownloadURL, aWebURL, aReleaseNotes);
3917}
3918
3919/**
3920 * @note Doesn't lock any object.
3921 */
3922void VirtualBox::i_onUpdateAgentError(IUpdateAgent *aAgent, const Utf8Str &aErrMsg, LONG aRc)
3923{
3924 ::FireUpdateAgentErrorEvent(m->pEventSource, aAgent, aErrMsg, aRc);
3925}
3926
3927/**
3928 * @note Doesn't lock any object.
3929 */
3930void VirtualBox::i_onUpdateAgentStateChanged(IUpdateAgent *aAgent, UpdateState_T aState)
3931{
3932 ::FireUpdateAgentStateChangedEvent(m->pEventSource, aAgent, aState);
3933}
3934
3935/**
3936 * @note Doesn't lock any object.
3937 */
3938void VirtualBox::i_onUpdateAgentSettingsChanged(IUpdateAgent *aAgent, const Utf8Str &aAttributeHint)
3939{
3940 ::FireUpdateAgentSettingsChangedEvent(m->pEventSource, aAgent, aAttributeHint);
3941}
3942#endif /* VBOX_WITH_UPDATE_AGENT */
3943
3944#ifdef VBOX_WITH_EXTPACK
3945void VirtualBox::i_onExtPackInstalled(const Utf8Str &aExtPackName)
3946{
3947 ::FireExtPackInstalledEvent(m->pEventSource, aExtPackName);
3948}
3949
3950void VirtualBox::i_onExtPackUninstalled(const Utf8Str &aExtPackName)
3951{
3952 ::FireExtPackUninstalledEvent(m->pEventSource, aExtPackName);
3953}
3954#endif
3955
3956/**
3957 * @note Locks the list of other objects for reading.
3958 */
3959ComObjPtr<GuestOSType> VirtualBox::i_getUnknownOSType()
3960{
3961 ComObjPtr<GuestOSType> type;
3962
3963 /* unknown type must always be the first */
3964 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
3965
3966 return m->allGuestOSTypes.front();
3967}
3968
3969/**
3970 * Returns the list of opened machines (machines having VM sessions opened,
3971 * ignoring other sessions) and optionally the list of direct session controls.
3972 *
3973 * @param aMachines Where to put opened machines (will be empty if none).
3974 * @param aControls Where to put direct session controls (optional).
3975 *
3976 * @note The returned lists contain smart pointers. So, clear it as soon as
3977 * it becomes no more necessary to release instances.
3978 *
3979 * @note It can be possible that a session machine from the list has been
3980 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
3981 * when accessing unprotected data directly.
3982 *
3983 * @note Locks objects for reading.
3984 */
3985void VirtualBox::i_getOpenedMachines(SessionMachinesList &aMachines,
3986 InternalControlList *aControls /*= NULL*/)
3987{
3988 AutoCaller autoCaller(this);
3989 AssertComRCReturnVoid(autoCaller.hrc());
3990
3991 aMachines.clear();
3992 if (aControls)
3993 aControls->clear();
3994
3995 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3996
3997 for (MachinesOList::iterator it = m->allMachines.begin();
3998 it != m->allMachines.end();
3999 ++it)
4000 {
4001 ComObjPtr<SessionMachine> sm;
4002 ComPtr<IInternalSessionControl> ctl;
4003 if ((*it)->i_isSessionOpenVM(sm, &ctl))
4004 {
4005 aMachines.push_back(sm);
4006 if (aControls)
4007 aControls->push_back(ctl);
4008 }
4009 }
4010}
4011
4012/**
4013 * Gets a reference to the machine list. This is the real thing, not a copy,
4014 * so bad things will happen if the caller doesn't hold the necessary lock.
4015 *
4016 * @returns reference to machine list
4017 *
4018 * @note Caller must hold the VirtualBox object lock at least for reading.
4019 */
4020VirtualBox::MachinesOList &VirtualBox::i_getMachinesList(void)
4021{
4022 return m->allMachines;
4023}
4024
4025/**
4026 * Searches for a machine object with the given ID in the collection
4027 * of registered machines.
4028 *
4029 * @param aId Machine UUID to look for.
4030 * @param fPermitInaccessible If true, inaccessible machines will be found;
4031 * if false, this will fail if the given machine is inaccessible.
4032 * @param aSetError If true, set errorinfo if the machine is not found.
4033 * @param aMachine Returned machine, if found.
4034 * @return
4035 */
4036HRESULT VirtualBox::i_findMachine(const Guid &aId,
4037 bool fPermitInaccessible,
4038 bool aSetError,
4039 ComObjPtr<Machine> *aMachine /* = NULL */)
4040{
4041 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
4042
4043 AutoCaller autoCaller(this);
4044 AssertComRCReturnRC(autoCaller.hrc());
4045
4046 {
4047 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4048
4049 for (MachinesOList::iterator it = m->allMachines.begin();
4050 it != m->allMachines.end();
4051 ++it)
4052 {
4053 ComObjPtr<Machine> pMachine = *it;
4054
4055 if (!fPermitInaccessible)
4056 {
4057 // skip inaccessible machines
4058 AutoCaller machCaller(pMachine);
4059 if (FAILED(machCaller.hrc()))
4060 continue;
4061 }
4062
4063 if (pMachine->i_getId() == aId)
4064 {
4065 hrc = S_OK;
4066 if (aMachine)
4067 *aMachine = pMachine;
4068 break;
4069 }
4070 }
4071 }
4072
4073 if (aSetError && FAILED(hrc))
4074 hrc = setError(hrc, tr("Could not find a registered machine with UUID {%RTuuid}"), aId.raw());
4075
4076 return hrc;
4077}
4078
4079/**
4080 * Searches for a machine object with the given name or location in the
4081 * collection of registered machines.
4082 *
4083 * @param aName Machine name or location to look for.
4084 * @param aSetError If true, set errorinfo if the machine is not found.
4085 * @param aMachine Returned machine, if found.
4086 * @return
4087 */
4088HRESULT VirtualBox::i_findMachineByName(const Utf8Str &aName,
4089 bool aSetError,
4090 ComObjPtr<Machine> *aMachine /* = NULL */)
4091{
4092 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
4093
4094 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4095 for (MachinesOList::iterator it = m->allMachines.begin();
4096 it != m->allMachines.end();
4097 ++it)
4098 {
4099 ComObjPtr<Machine> &pMachine = *it;
4100 AutoCaller machCaller(pMachine);
4101 if (!machCaller.isOk())
4102 continue; // we can't ask inaccessible machines for their names
4103
4104 AutoReadLock machLock(pMachine COMMA_LOCKVAL_SRC_POS);
4105 if (pMachine->i_getName() == aName)
4106 {
4107 hrc = S_OK;
4108 if (aMachine)
4109 *aMachine = pMachine;
4110 break;
4111 }
4112 if (!RTPathCompare(pMachine->i_getSettingsFileFull().c_str(), aName.c_str()))
4113 {
4114 hrc = S_OK;
4115 if (aMachine)
4116 *aMachine = pMachine;
4117 break;
4118 }
4119 }
4120
4121 if (aSetError && FAILED(hrc))
4122 hrc = setError(hrc, tr("Could not find a registered machine named '%s'"), aName.c_str());
4123
4124 return hrc;
4125}
4126
4127static HRESULT i_validateMachineGroupHelper(const Utf8Str &aGroup, bool fPrimary, VirtualBox *pVirtualBox)
4128{
4129 /* empty strings are invalid */
4130 if (aGroup.isEmpty())
4131 return E_INVALIDARG;
4132 /* the toplevel group is valid */
4133 if (aGroup == "/")
4134 return S_OK;
4135 /* any other strings of length 1 are invalid */
4136 if (aGroup.length() == 1)
4137 return E_INVALIDARG;
4138 /* must start with a slash */
4139 if (aGroup.c_str()[0] != '/')
4140 return E_INVALIDARG;
4141 /* must not end with a slash */
4142 if (aGroup.c_str()[aGroup.length() - 1] == '/')
4143 return E_INVALIDARG;
4144 /* check the group components */
4145 const char *pStr = aGroup.c_str() + 1; /* first char is /, skip it */
4146 while (pStr)
4147 {
4148 char *pSlash = RTStrStr(pStr, "/");
4149 if (pSlash)
4150 {
4151 /* no empty components (or // sequences in other words) */
4152 if (pSlash == pStr)
4153 return E_INVALIDARG;
4154 /* check if the machine name rules are violated, because that means
4155 * the group components are too close to the limits. */
4156 Utf8Str tmp((const char *)pStr, (size_t)(pSlash - pStr));
4157 Utf8Str tmp2(tmp);
4158 sanitiseMachineFilename(tmp);
4159 if (tmp != tmp2)
4160 return E_INVALIDARG;
4161 if (fPrimary)
4162 {
4163 HRESULT hrc = pVirtualBox->i_findMachineByName(tmp, false /* aSetError */);
4164 if (SUCCEEDED(hrc))
4165 return VBOX_E_VM_ERROR;
4166 }
4167 pStr = pSlash + 1;
4168 }
4169 else
4170 {
4171 /* check if the machine name rules are violated, because that means
4172 * the group components is too close to the limits. */
4173 Utf8Str tmp(pStr);
4174 Utf8Str tmp2(tmp);
4175 sanitiseMachineFilename(tmp);
4176 if (tmp != tmp2)
4177 return E_INVALIDARG;
4178 pStr = NULL;
4179 }
4180 }
4181 return S_OK;
4182}
4183
4184/**
4185 * Validates a machine group.
4186 *
4187 * @param aGroup Machine group.
4188 * @param fPrimary Set if this is the primary group.
4189 *
4190 * @return S_OK or E_INVALIDARG
4191 */
4192HRESULT VirtualBox::i_validateMachineGroup(const Utf8Str &aGroup, bool fPrimary)
4193{
4194 HRESULT hrc = i_validateMachineGroupHelper(aGroup, fPrimary, this);
4195 if (FAILED(hrc))
4196 {
4197 if (hrc == VBOX_E_VM_ERROR)
4198 hrc = setError(E_INVALIDARG, tr("Machine group '%s' conflicts with a virtual machine name"), aGroup.c_str());
4199 else
4200 hrc = setError(hrc, tr("Invalid machine group '%s'"), aGroup.c_str());
4201 }
4202 return hrc;
4203}
4204
4205/**
4206 * Takes a list of machine groups, and sanitizes/validates it.
4207 *
4208 * @param aMachineGroups Array with the machine groups.
4209 * @param pllMachineGroups Pointer to list of strings for the result.
4210 *
4211 * @return S_OK or E_INVALIDARG
4212 */
4213HRESULT VirtualBox::i_convertMachineGroups(const std::vector<com::Utf8Str> aMachineGroups, StringsList *pllMachineGroups)
4214{
4215 pllMachineGroups->clear();
4216 if (aMachineGroups.size())
4217 {
4218 for (size_t i = 0; i < aMachineGroups.size(); i++)
4219 {
4220 Utf8Str group(aMachineGroups[i]);
4221 if (group.length() == 0)
4222 group = "/";
4223
4224 HRESULT hrc = i_validateMachineGroup(group, i == 0);
4225 if (FAILED(hrc))
4226 return hrc;
4227
4228 /* no duplicates please */
4229 if ( find(pllMachineGroups->begin(), pllMachineGroups->end(), group)
4230 == pllMachineGroups->end())
4231 pllMachineGroups->push_back(group);
4232 }
4233 if (pllMachineGroups->size() == 0)
4234 pllMachineGroups->push_back("/");
4235 }
4236 else
4237 pllMachineGroups->push_back("/");
4238
4239 return S_OK;
4240}
4241
4242/**
4243 * Searches for a Medium object with the given ID in the list of registered
4244 * hard disks.
4245 *
4246 * @param aId ID of the hard disk. Must not be empty.
4247 * @param aSetError If @c true , the appropriate error info is set in case
4248 * when the hard disk is not found.
4249 * @param aHardDisk Where to store the found hard disk object (can be NULL).
4250 *
4251 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
4252 *
4253 * @note Locks the media tree for reading.
4254 */
4255HRESULT VirtualBox::i_findHardDiskById(const Guid &aId,
4256 bool aSetError,
4257 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
4258{
4259 AssertReturn(!aId.isZero(), E_INVALIDARG);
4260
4261 // we use the hard disks map, but it is protected by the
4262 // hard disk _list_ lock handle
4263 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4264
4265 HardDiskMap::const_iterator it = m->mapHardDisks.find(aId);
4266 if (it != m->mapHardDisks.end())
4267 {
4268 if (aHardDisk)
4269 *aHardDisk = (*it).second;
4270 return S_OK;
4271 }
4272
4273 if (aSetError)
4274 return setError(VBOX_E_OBJECT_NOT_FOUND,
4275 tr("Could not find an open hard disk with UUID {%RTuuid}"),
4276 aId.raw());
4277
4278 return VBOX_E_OBJECT_NOT_FOUND;
4279}
4280
4281/**
4282 * Searches for a Medium object with the given ID or location in the list of
4283 * registered hard disks. If both ID and location are specified, the first
4284 * object that matches either of them (not necessarily both) is returned.
4285 *
4286 * @param strLocation Full location specification. Must not be empty.
4287 * @param aSetError If @c true , the appropriate error info is set in case
4288 * when the hard disk is not found.
4289 * @param aHardDisk Where to store the found hard disk object (can be NULL).
4290 *
4291 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
4292 *
4293 * @note Locks the media tree for reading.
4294 */
4295HRESULT VirtualBox::i_findHardDiskByLocation(const Utf8Str &strLocation,
4296 bool aSetError,
4297 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
4298{
4299 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
4300
4301 // we use the hard disks map, but it is protected by the
4302 // hard disk _list_ lock handle
4303 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4304
4305 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
4306 it != m->mapHardDisks.end();
4307 ++it)
4308 {
4309 const ComObjPtr<Medium> &pHD = (*it).second;
4310
4311 AutoCaller autoCaller(pHD);
4312 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
4313 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
4314
4315 Utf8Str strLocationFull = pHD->i_getLocationFull();
4316
4317 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
4318 {
4319 if (aHardDisk)
4320 *aHardDisk = pHD;
4321 return S_OK;
4322 }
4323 }
4324
4325 if (aSetError)
4326 return setError(VBOX_E_OBJECT_NOT_FOUND,
4327 tr("Could not find an open hard disk with location '%s'"),
4328 strLocation.c_str());
4329
4330 return VBOX_E_OBJECT_NOT_FOUND;
4331}
4332
4333/**
4334 * Searches for a Medium object with the given ID or location in the list of
4335 * registered DVD or floppy images, depending on the @a mediumType argument.
4336 * If both ID and file path are specified, the first object that matches either
4337 * of them (not necessarily both) is returned.
4338 *
4339 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
4340 * @param aId ID of the image file (unused when NULL).
4341 * @param aLocation Full path to the image file (unused when NULL).
4342 * @param aSetError If @c true, the appropriate error info is set in case when
4343 * the image is not found.
4344 * @param aImage Where to store the found image object (can be NULL).
4345 *
4346 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
4347 *
4348 * @note Locks the media tree for reading.
4349 */
4350HRESULT VirtualBox::i_findDVDOrFloppyImage(DeviceType_T mediumType,
4351 const Guid *aId,
4352 const Utf8Str &aLocation,
4353 bool aSetError,
4354 ComObjPtr<Medium> *aImage /* = NULL */)
4355{
4356 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
4357
4358 Utf8Str location;
4359 if (!aLocation.isEmpty())
4360 {
4361 int vrc = i_calculateFullPath(aLocation, location);
4362 if (RT_FAILURE(vrc))
4363 return setError(VBOX_E_FILE_ERROR,
4364 tr("Invalid image file location '%s' (%Rrc)"),
4365 aLocation.c_str(),
4366 vrc);
4367 }
4368
4369 MediaOList *pMediaList;
4370
4371 switch (mediumType)
4372 {
4373 case DeviceType_DVD:
4374 pMediaList = &m->allDVDImages;
4375 break;
4376
4377 case DeviceType_Floppy:
4378 pMediaList = &m->allFloppyImages;
4379 break;
4380
4381 default:
4382 return E_INVALIDARG;
4383 }
4384
4385 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
4386
4387 bool found = false;
4388
4389 for (MediaList::const_iterator it = pMediaList->begin();
4390 it != pMediaList->end();
4391 ++it)
4392 {
4393 // no AutoCaller, registered image life time is bound to this
4394 Medium *pMedium = *it;
4395 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
4396 const Utf8Str &strLocationFull = pMedium->i_getLocationFull();
4397
4398 found = ( aId
4399 && pMedium->i_getId() == *aId)
4400 || ( !aLocation.isEmpty()
4401 && RTPathCompare(location.c_str(),
4402 strLocationFull.c_str()) == 0);
4403 if (found)
4404 {
4405 if (pMedium->i_getDeviceType() != mediumType)
4406 {
4407 if (mediumType == DeviceType_DVD)
4408 return setError(E_INVALIDARG,
4409 tr("Cannot mount DVD medium '%s' as floppy"), strLocationFull.c_str());
4410 else
4411 return setError(E_INVALIDARG,
4412 tr("Cannot mount floppy medium '%s' as DVD"), strLocationFull.c_str());
4413 }
4414
4415 if (aImage)
4416 *aImage = pMedium;
4417 break;
4418 }
4419 }
4420
4421 HRESULT hrc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
4422
4423 if (aSetError && !found)
4424 {
4425 if (aId)
4426 setError(hrc,
4427 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
4428 aId->raw(),
4429 m->strSettingsFilePath.c_str());
4430 else
4431 setError(hrc,
4432 tr("Could not find an image file with location '%s' in the media registry ('%s')"),
4433 aLocation.c_str(),
4434 m->strSettingsFilePath.c_str());
4435 }
4436
4437 return hrc;
4438}
4439
4440/**
4441 * Searches for an IMedium object that represents the given UUID.
4442 *
4443 * If the UUID is empty (indicating an empty drive), this sets pMedium
4444 * to NULL and returns S_OK.
4445 *
4446 * If the UUID refers to a host drive of the given device type, this
4447 * sets pMedium to the object from the list in IHost and returns S_OK.
4448 *
4449 * If the UUID is an image file, this sets pMedium to the object that
4450 * findDVDOrFloppyImage() returned.
4451 *
4452 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
4453 *
4454 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
4455 * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
4456 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
4457 * @param aSetError
4458 * @param pMedium out: IMedium object found.
4459 * @return
4460 */
4461HRESULT VirtualBox::i_findRemoveableMedium(DeviceType_T mediumType,
4462 const Guid &uuid,
4463 bool fRefresh,
4464 bool aSetError,
4465 ComObjPtr<Medium> &pMedium)
4466{
4467 if (uuid.isZero())
4468 {
4469 // that's easy
4470 pMedium.setNull();
4471 return S_OK;
4472 }
4473 else if (!uuid.isValid())
4474 {
4475 /* handling of case invalid GUID */
4476 return setError(VBOX_E_OBJECT_NOT_FOUND,
4477 tr("Guid '%s' is invalid"),
4478 uuid.toString().c_str());
4479 }
4480
4481 // first search for host drive with that UUID
4482 HRESULT hrc = m->pHost->i_findHostDriveById(mediumType, uuid, fRefresh, pMedium);
4483 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
4484 // then search for an image with that UUID
4485 hrc = i_findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, aSetError, &pMedium);
4486
4487 return hrc;
4488}
4489
4490/* Look for a GuestOSType object */
4491HRESULT VirtualBox::i_findGuestOSType(const Utf8Str &strOSType,
4492 ComObjPtr<GuestOSType> &guestOSType)
4493{
4494 guestOSType.setNull();
4495
4496 AssertMsg(m->allGuestOSTypes.size() != 0,
4497 ("Guest OS types array must be filled"));
4498
4499 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4500 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
4501 it != m->allGuestOSTypes.end();
4502 ++it)
4503 {
4504 const Utf8Str &typeId = (*it)->i_id();
4505 AssertMsg(!typeId.isEmpty(), ("ID must not be NULL"));
4506 if (strOSType.compare(typeId, Utf8Str::CaseInsensitive) == 0)
4507 {
4508 guestOSType = *it;
4509 return S_OK;
4510 }
4511 }
4512
4513 return setError(VBOX_E_OBJECT_NOT_FOUND,
4514 tr("'%s' is not a valid Guest OS type"),
4515 strOSType.c_str());
4516}
4517
4518/**
4519 * Walk the list of GuestOSType objects and return a list of guest OS
4520 * subtypes which correspond to the supplied guest OS family ID.
4521 *
4522 * @param strOSFamily Guest OS family ID.
4523 * @param aOSSubtypes Where to store the list of guest OS subtypes.
4524 *
4525 * @note Locks the guest OS types list for reading.
4526 */
4527HRESULT VirtualBox::getGuestOSSubtypesByFamilyId(const Utf8Str &strOSFamily,
4528 std::vector<com::Utf8Str> &aOSSubtypes)
4529{
4530 std::list<com::Utf8Str> allOSSubtypes;
4531
4532 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4533
4534 bool fFoundGuestOSType = false;
4535 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
4536 it != m->allGuestOSTypes.end(); ++it)
4537 {
4538 const Utf8Str &familyId = (*it)->i_familyId();
4539 AssertMsg(!familyId.isEmpty(), ("familfyId must not be NULL"));
4540 if (familyId.compare(strOSFamily, Utf8Str::CaseInsensitive) == 0)
4541 {
4542 fFoundGuestOSType = true;
4543 break;
4544 }
4545 }
4546
4547 if (!fFoundGuestOSType)
4548 return setError(VBOX_E_OBJECT_NOT_FOUND,
4549 tr("'%s' is not a valid guest OS family identifier."), strOSFamily.c_str());
4550
4551 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
4552 it != m->allGuestOSTypes.end(); ++it)
4553 {
4554 const Utf8Str &familyId = (*it)->i_familyId();
4555 AssertMsg(!familyId.isEmpty(), ("familfyId must not be NULL"));
4556 if (familyId.compare(strOSFamily, Utf8Str::CaseInsensitive) == 0)
4557 {
4558 const Utf8Str &strOSSubtype = (*it)->i_subtype();
4559 if (!strOSSubtype.isEmpty())
4560 allOSSubtypes.push_back(strOSSubtype);
4561 }
4562 }
4563
4564 /* throw out any duplicates */
4565 allOSSubtypes.sort();
4566 allOSSubtypes.unique();
4567
4568 aOSSubtypes.resize(allOSSubtypes.size());
4569 size_t i = 0;
4570 for (std::list<com::Utf8Str>::const_iterator it = allOSSubtypes.begin();
4571 it != allOSSubtypes.end(); ++it, ++i)
4572 aOSSubtypes[i] = (*it);
4573
4574 return S_OK;
4575}
4576
4577/**
4578 * Walk the list of GuestOSType objects and return a list of guest OS
4579 * descriptions which correspond to the supplied guest OS subtype.
4580 *
4581 * @param strOSSubtype Guest OS subtype.
4582 * @param aGuestOSDescs Where to store the list of guest OS descriptions..
4583 *
4584 * @note Locks the guest OS types list for reading.
4585 */
4586HRESULT VirtualBox::getGuestOSDescsBySubtype(const Utf8Str &strOSSubtype,
4587 std::vector<com::Utf8Str> &aGuestOSDescs)
4588{
4589 std::list<com::Utf8Str> allOSDescs;
4590
4591 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4592
4593 bool fFoundGuestOSSubtype = false;
4594 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
4595 it != m->allGuestOSTypes.end(); ++it)
4596 {
4597 const Utf8Str &guestOSSubtype = (*it)->i_subtype();
4598 /* Only some guest OS types have a populated subtype value. */
4599 if (guestOSSubtype.isNotEmpty() &&
4600 guestOSSubtype.compare(strOSSubtype, Utf8Str::CaseInsensitive) == 0)
4601 {
4602 fFoundGuestOSSubtype = true;
4603 break;
4604 }
4605 }
4606
4607 if (!fFoundGuestOSSubtype)
4608 return setError(VBOX_E_OBJECT_NOT_FOUND,
4609 tr("'%s' is not a valid guest OS subtype."), strOSSubtype.c_str());
4610
4611 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
4612 it != m->allGuestOSTypes.end(); ++it)
4613 {
4614 const Utf8Str &guestOSSubtype = (*it)->i_subtype();
4615 /* Only some guest OS types have a populated subtype value. */
4616 if (guestOSSubtype.isNotEmpty() &&
4617 guestOSSubtype.compare(strOSSubtype, Utf8Str::CaseInsensitive) == 0)
4618 {
4619 const Utf8Str &strOSDesc = (*it)->i_description();
4620 allOSDescs.push_back(strOSDesc);
4621 }
4622 }
4623
4624 aGuestOSDescs.resize(allOSDescs.size());
4625 size_t i = 0;
4626 for (std::list<com::Utf8Str>::const_iterator it = allOSDescs.begin();
4627 it != allOSDescs.end(); ++it, ++i)
4628 aGuestOSDescs[i] = (*it);
4629
4630 return S_OK;
4631}
4632
4633/**
4634 * Returns the constant pseudo-machine UUID that is used to identify the
4635 * global media registry.
4636 *
4637 * Starting with VirtualBox 4.0 each medium remembers in its instance data
4638 * in which media registry it is saved (if any): this can either be a machine
4639 * UUID, if it's in a per-machine media registry, or this global ID.
4640 *
4641 * This UUID is only used to identify the VirtualBox object while VirtualBox
4642 * is running. It is a compile-time constant and not saved anywhere.
4643 *
4644 * @return
4645 */
4646const Guid& VirtualBox::i_getGlobalRegistryId() const
4647{
4648 return m->uuidMediaRegistry;
4649}
4650
4651const ComObjPtr<Host>& VirtualBox::i_host() const
4652{
4653 return m->pHost;
4654}
4655
4656SystemProperties* VirtualBox::i_getSystemProperties() const
4657{
4658 return m->pSystemProperties;
4659}
4660
4661CloudProviderManager *VirtualBox::i_getCloudProviderManager() const
4662{
4663 return m->pCloudProviderManager;
4664}
4665
4666#ifdef VBOX_WITH_EXTPACK
4667/**
4668 * Getter that SystemProperties and others can use to talk to the extension
4669 * pack manager.
4670 */
4671ExtPackManager* VirtualBox::i_getExtPackManager() const
4672{
4673 return m->ptrExtPackManager;
4674}
4675#endif
4676
4677/**
4678 * Getter that machines can talk to the autostart database.
4679 */
4680AutostartDb* VirtualBox::i_getAutostartDb() const
4681{
4682 return m->pAutostartDb;
4683}
4684
4685#ifdef VBOX_WITH_RESOURCE_USAGE_API
4686const ComObjPtr<PerformanceCollector>& VirtualBox::i_performanceCollector() const
4687{
4688 return m->pPerformanceCollector;
4689}
4690#endif /* VBOX_WITH_RESOURCE_USAGE_API */
4691
4692/**
4693 * Returns the default machine folder from the system properties
4694 * with proper locking.
4695 */
4696void VirtualBox::i_getDefaultMachineFolder(Utf8Str &str) const
4697{
4698 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
4699 str = m->pSystemProperties->m->strDefaultMachineFolder;
4700}
4701
4702/**
4703 * Returns the default hard disk format from the system properties
4704 * with proper locking.
4705 */
4706void VirtualBox::i_getDefaultHardDiskFormat(Utf8Str &str) const
4707{
4708 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
4709 str = m->pSystemProperties->m->strDefaultHardDiskFormat;
4710}
4711
4712const Utf8Str& VirtualBox::i_homeDir() const
4713{
4714 return m->strHomeDir;
4715}
4716
4717/**
4718 * Calculates the absolute path of the given path taking the VirtualBox home
4719 * directory as the current directory.
4720 *
4721 * @param strPath Path to calculate the absolute path for.
4722 * @param aResult Where to put the result (used only on success, can be the
4723 * same Utf8Str instance as passed in @a aPath).
4724 * @return IPRT result.
4725 *
4726 * @note Doesn't lock any object.
4727 */
4728int VirtualBox::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
4729{
4730 AutoCaller autoCaller(this);
4731 AssertComRCReturn(autoCaller.hrc(), VERR_GENERAL_FAILURE);
4732
4733 /* no need to lock since strHomeDir is const */
4734
4735 char szFolder[RTPATH_MAX];
4736 size_t cbFolder = sizeof(szFolder);
4737 int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
4738 strPath.c_str(),
4739 RTPATH_STR_F_STYLE_HOST,
4740 szFolder,
4741 &cbFolder);
4742 if (RT_SUCCESS(vrc))
4743 aResult = szFolder;
4744
4745 return vrc;
4746}
4747
4748/**
4749 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
4750 * if it is a subdirectory thereof, or simply copying it otherwise.
4751 *
4752 * @param strSource Path to evalue and copy.
4753 * @param strTarget Buffer to receive target path.
4754 */
4755void VirtualBox::i_copyPathRelativeToConfig(const Utf8Str &strSource,
4756 Utf8Str &strTarget)
4757{
4758 AutoCaller autoCaller(this);
4759 AssertComRCReturnVoid(autoCaller.hrc());
4760
4761 // no need to lock since mHomeDir is const
4762
4763 // use strTarget as a temporary buffer to hold the machine settings dir
4764 strTarget = m->strHomeDir;
4765 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
4766 // is relative: then append what's left
4767 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
4768 else
4769 // is not relative: then overwrite
4770 strTarget = strSource;
4771}
4772
4773// private methods
4774/////////////////////////////////////////////////////////////////////////////
4775
4776/**
4777 * Checks if there is a hard disk, DVD or floppy image with the given ID or
4778 * location already registered.
4779 *
4780 * On return, sets @a aConflict to the string describing the conflicting medium,
4781 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
4782 * either case. A failure is unexpected.
4783 *
4784 * @param aId UUID to check.
4785 * @param aLocation Location to check.
4786 * @param aConflict Where to return parameters of the conflicting medium.
4787 * @param ppMedium Medium reference in case this is simply a duplicate.
4788 *
4789 * @note Locks the media tree and media objects for reading.
4790 */
4791HRESULT VirtualBox::i_checkMediaForConflicts(const Guid &aId,
4792 const Utf8Str &aLocation,
4793 Utf8Str &aConflict,
4794 ComObjPtr<Medium> *ppMedium)
4795{
4796 AssertReturn(!aId.isZero() && !aLocation.isEmpty(), E_FAIL);
4797 AssertReturn(ppMedium, E_INVALIDARG);
4798
4799 aConflict.setNull();
4800 ppMedium->setNull();
4801
4802 AutoReadLock alock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4803
4804 HRESULT hrc = S_OK;
4805
4806 ComObjPtr<Medium> pMediumFound;
4807 const char *pcszType = NULL;
4808
4809 if (aId.isValid() && !aId.isZero())
4810 hrc = i_findHardDiskById(aId, false /* aSetError */, &pMediumFound);
4811 if (FAILED(hrc) && !aLocation.isEmpty())
4812 hrc = i_findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
4813 if (SUCCEEDED(hrc))
4814 pcszType = tr("hard disk");
4815
4816 if (!pcszType)
4817 {
4818 hrc = i_findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
4819 if (SUCCEEDED(hrc))
4820 pcszType = tr("CD/DVD image");
4821 }
4822
4823 if (!pcszType)
4824 {
4825 hrc = i_findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
4826 if (SUCCEEDED(hrc))
4827 pcszType = tr("floppy image");
4828 }
4829
4830 if (pcszType && pMediumFound)
4831 {
4832 /* Note: no AutoCaller since bound to this */
4833 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
4834
4835 Utf8Str strLocFound = pMediumFound->i_getLocationFull();
4836 Guid idFound = pMediumFound->i_getId();
4837
4838 if ( (RTPathCompare(strLocFound.c_str(), aLocation.c_str()) == 0)
4839 && (idFound == aId)
4840 )
4841 *ppMedium = pMediumFound;
4842
4843 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
4844 pcszType,
4845 strLocFound.c_str(),
4846 idFound.raw());
4847 }
4848
4849 return S_OK;
4850}
4851
4852/**
4853 * Checks whether the given UUID is already in use by one medium for the
4854 * given device type.
4855 *
4856 * @returns true if the UUID is already in use
4857 * fale otherwise
4858 * @param aId The UUID to check.
4859 * @param deviceType The device type the UUID is going to be checked for
4860 * conflicts.
4861 */
4862bool VirtualBox::i_isMediaUuidInUse(const Guid &aId, DeviceType_T deviceType)
4863{
4864 /* A zero UUID is invalid here, always claim that it is already used. */
4865 AssertReturn(!aId.isZero(), true);
4866
4867 AutoReadLock alock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4868
4869 bool fInUse = false;
4870
4871 ComObjPtr<Medium> pMediumFound;
4872
4873 HRESULT hrc;
4874 switch (deviceType)
4875 {
4876 case DeviceType_HardDisk:
4877 hrc = i_findHardDiskById(aId, false /* aSetError */, &pMediumFound);
4878 break;
4879 case DeviceType_DVD:
4880 hrc = i_findDVDOrFloppyImage(DeviceType_DVD, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
4881 break;
4882 case DeviceType_Floppy:
4883 hrc = i_findDVDOrFloppyImage(DeviceType_Floppy, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
4884 break;
4885 default:
4886 AssertMsgFailed(("Invalid device type %d\n", deviceType));
4887 hrc = S_OK;
4888 break;
4889 }
4890
4891 if (SUCCEEDED(hrc) && pMediumFound)
4892 fInUse = true;
4893
4894 return fInUse;
4895}
4896
4897/**
4898 * Called from Machine::prepareSaveSettings() when it has detected
4899 * that a machine has been renamed. Such renames will require
4900 * updating the global media registry during the
4901 * VirtualBox::i_saveSettings() that follows later.
4902*
4903 * When a machine is renamed, there may well be media (in particular,
4904 * diff images for snapshots) in the global registry that will need
4905 * to have their paths updated. Before 3.2, Machine::saveSettings
4906 * used to call VirtualBox::i_saveSettings implicitly, which was both
4907 * unintuitive and caused locking order problems. Now, we remember
4908 * such pending name changes with this method so that
4909 * VirtualBox::i_saveSettings() can process them properly.
4910 */
4911void VirtualBox::i_rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
4912 const Utf8Str &strNewConfigDir)
4913{
4914 AutoWriteLock mediaLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4915
4916 Data::PendingMachineRename pmr;
4917 pmr.strConfigDirOld = strOldConfigDir;
4918 pmr.strConfigDirNew = strNewConfigDir;
4919 m->llPendingMachineRenames.push_back(pmr);
4920}
4921
4922static DECLCALLBACK(int) fntSaveMediaRegistries(void *pvUser);
4923
4924class SaveMediaRegistriesDesc : public ThreadTask
4925{
4926
4927public:
4928 SaveMediaRegistriesDesc()
4929 {
4930 m_strTaskName = "SaveMediaReg";
4931 }
4932 virtual ~SaveMediaRegistriesDesc(void) { }
4933
4934private:
4935 void handler()
4936 {
4937 try
4938 {
4939 fntSaveMediaRegistries(this);
4940 }
4941 catch(...)
4942 {
4943 LogRel(("Exception in the function fntSaveMediaRegistries()\n"));
4944 }
4945 }
4946
4947 MediaList llMedia;
4948 ComObjPtr<VirtualBox> pVirtualBox;
4949
4950 friend DECLCALLBACK(int) fntSaveMediaRegistries(void *pvUser);
4951 friend void VirtualBox::i_saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
4952 const Guid &uuidRegistry,
4953 const Utf8Str &strMachineFolder);
4954};
4955
4956DECLCALLBACK(int) fntSaveMediaRegistries(void *pvUser)
4957{
4958 SaveMediaRegistriesDesc *pDesc = (SaveMediaRegistriesDesc *)pvUser;
4959 if (!pDesc)
4960 {
4961 LogRelFunc(("Thread for saving media registries lacks parameters\n"));
4962 return VERR_INVALID_PARAMETER;
4963 }
4964
4965 for (MediaList::const_iterator it = pDesc->llMedia.begin();
4966 it != pDesc->llMedia.end();
4967 ++it)
4968 {
4969 Medium *pMedium = *it;
4970 pMedium->i_markRegistriesModified();
4971 }
4972
4973 pDesc->pVirtualBox->i_saveModifiedRegistries();
4974
4975 pDesc->llMedia.clear();
4976 pDesc->pVirtualBox.setNull();
4977
4978 return VINF_SUCCESS;
4979}
4980
4981/**
4982 * Goes through all known media (hard disks, floppies and DVDs) and saves
4983 * those into the given settings::MediaRegistry structures whose registry
4984 * ID match the given UUID.
4985 *
4986 * Before actually writing to the structures, all media paths (not just the
4987 * ones for the given registry) are updated if machines have been renamed
4988 * since the last call.
4989 *
4990 * This gets called from two contexts:
4991 *
4992 * -- VirtualBox::i_saveSettings() with the UUID of the global registry
4993 * (VirtualBox::Data.uuidRegistry); this will save those media
4994 * which had been loaded from the global registry or have been
4995 * attached to a "legacy" machine which can't save its own registry;
4996 *
4997 * -- Machine::saveSettings() with the UUID of a machine, if a medium
4998 * has been attached to a machine created with VirtualBox 4.0 or later.
4999 *
5000 * Media which have only been temporarily opened without having been
5001 * attached to a machine have a NULL registry UUID and therefore don't
5002 * get saved.
5003 *
5004 * This locks the media tree. Throws HRESULT on errors!
5005 *
5006 * @param mediaRegistry Settings structure to fill.
5007 * @param uuidRegistry The UUID of the media registry; either a machine UUID
5008 * (if machine registry) or the UUID of the global registry.
5009 * @param strMachineFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
5010 */
5011void VirtualBox::i_saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
5012 const Guid &uuidRegistry,
5013 const Utf8Str &strMachineFolder)
5014{
5015 // lock all media for the following; use a write lock because we're
5016 // modifying the PendingMachineRenamesList, which is protected by this
5017 AutoWriteLock mediaLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5018
5019 // if a machine was renamed, then we'll need to refresh media paths
5020 if (m->llPendingMachineRenames.size())
5021 {
5022 // make a single list from the three media lists so we don't need three loops
5023 MediaList llAllMedia;
5024 // with hard disks, we must use the map, not the list, because the list only has base images
5025 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
5026 llAllMedia.push_back(it->second);
5027 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
5028 llAllMedia.push_back(*it);
5029 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
5030 llAllMedia.push_back(*it);
5031
5032 SaveMediaRegistriesDesc *pDesc = new SaveMediaRegistriesDesc();
5033 for (MediaList::iterator it = llAllMedia.begin();
5034 it != llAllMedia.end();
5035 ++it)
5036 {
5037 Medium *pMedium = *it;
5038 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
5039 it2 != m->llPendingMachineRenames.end();
5040 ++it2)
5041 {
5042 const Data::PendingMachineRename &pmr = *it2;
5043 HRESULT hrc = pMedium->i_updatePath(pmr.strConfigDirOld, pmr.strConfigDirNew);
5044 if (SUCCEEDED(hrc))
5045 {
5046 // Remember which medium objects has been changed,
5047 // to trigger saving their registries later.
5048 pDesc->llMedia.push_back(pMedium);
5049 } else if (hrc == VBOX_E_FILE_ERROR)
5050 /* nothing */;
5051 else
5052 AssertComRC(hrc);
5053 }
5054 }
5055 // done, don't do it again until we have more machine renames
5056 m->llPendingMachineRenames.clear();
5057
5058 if (pDesc->llMedia.size())
5059 {
5060 // Handle the media registry saving in a separate thread, to
5061 // avoid giant locking problems and passing up the list many
5062 // levels up to whoever triggered saveSettings, as there are
5063 // lots of places which would need to handle saving more settings.
5064 pDesc->pVirtualBox = this;
5065
5066 //the function createThread() takes ownership of pDesc
5067 //so there is no need to use delete operator for pDesc
5068 //after calling this function
5069 HRESULT hrc = pDesc->createThread();
5070 pDesc = NULL;
5071
5072 if (FAILED(hrc))
5073 {
5074 // failure means that settings aren't saved, but there isn't
5075 // much we can do besides avoiding memory leaks
5076 LogRelFunc(("Failed to create thread for saving media registries (%Rhr)\n", hrc));
5077 }
5078 }
5079 else
5080 delete pDesc;
5081 }
5082
5083 struct {
5084 MediaOList &llSource;
5085 settings::MediaList &llTarget;
5086 } s[] =
5087 {
5088 // hard disks
5089 { m->allHardDisks, mediaRegistry.llHardDisks },
5090 // CD/DVD images
5091 { m->allDVDImages, mediaRegistry.llDvdImages },
5092 // floppy images
5093 { m->allFloppyImages, mediaRegistry.llFloppyImages }
5094 };
5095
5096 for (size_t i = 0; i < RT_ELEMENTS(s); ++i)
5097 {
5098 MediaOList &llSource = s[i].llSource;
5099 settings::MediaList &llTarget = s[i].llTarget;
5100 llTarget.clear();
5101 for (MediaList::const_iterator it = llSource.begin();
5102 it != llSource.end();
5103 ++it)
5104 {
5105 Medium *pMedium = *it;
5106 AutoCaller autoCaller(pMedium);
5107 if (FAILED(autoCaller.hrc())) throw autoCaller.hrc();
5108 AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
5109
5110 if (pMedium->i_isInRegistry(uuidRegistry))
5111 {
5112 llTarget.push_back(settings::Medium::Empty);
5113 HRESULT hrc = pMedium->i_saveSettings(llTarget.back(), strMachineFolder); // this recurses into child hard disks
5114 if (FAILED(hrc))
5115 {
5116 llTarget.pop_back();
5117 throw hrc;
5118 }
5119 }
5120 }
5121 }
5122}
5123
5124/**
5125 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
5126 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
5127 * places internally when settings need saving.
5128 *
5129 * @note Caller must have locked the VirtualBox object for writing and must not hold any
5130 * other locks since this locks all kinds of member objects and trees temporarily,
5131 * which could cause conflicts.
5132 */
5133HRESULT VirtualBox::i_saveSettings()
5134{
5135 AutoCaller autoCaller(this);
5136 AssertComRCReturnRC(autoCaller.hrc());
5137
5138 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5139 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
5140
5141 i_unmarkRegistryModified(i_getGlobalRegistryId());
5142
5143 HRESULT hrc = S_OK;
5144
5145 try
5146 {
5147 // machines
5148 m->pMainConfigFile->llMachines.clear();
5149 {
5150 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5151 for (MachinesOList::iterator it = m->allMachines.begin();
5152 it != m->allMachines.end();
5153 ++it)
5154 {
5155 Machine *pMachine = *it;
5156 // save actual machine registry entry
5157 settings::MachineRegistryEntry mre;
5158 hrc = pMachine->i_saveRegistryEntry(mre);
5159 m->pMainConfigFile->llMachines.push_back(mre);
5160 }
5161 }
5162
5163 i_saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
5164 m->uuidMediaRegistry, // global media registry ID
5165 Utf8Str::Empty); // strMachineFolder
5166
5167 m->pMainConfigFile->llDhcpServers.clear();
5168 {
5169 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5170 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
5171 it != m->allDHCPServers.end();
5172 ++it)
5173 {
5174 settings::DHCPServer d;
5175 hrc = (*it)->i_saveSettings(d);
5176 if (FAILED(hrc)) throw hrc;
5177 m->pMainConfigFile->llDhcpServers.push_back(d);
5178 }
5179 }
5180
5181#ifdef VBOX_WITH_NAT_SERVICE
5182 /* Saving NAT Network configuration */
5183 m->pMainConfigFile->llNATNetworks.clear();
5184 {
5185 AutoReadLock natNetworkLock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5186 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
5187 it != m->allNATNetworks.end();
5188 ++it)
5189 {
5190 settings::NATNetwork n;
5191 hrc = (*it)->i_saveSettings(n);
5192 if (FAILED(hrc)) throw hrc;
5193 m->pMainConfigFile->llNATNetworks.push_back(n);
5194 }
5195 }
5196#endif
5197
5198#ifdef VBOX_WITH_VMNET
5199 m->pMainConfigFile->llHostOnlyNetworks.clear();
5200 {
5201 AutoReadLock hostOnlyNetworkLock(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5202 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
5203 it != m->allHostOnlyNetworks.end();
5204 ++it)
5205 {
5206 settings::HostOnlyNetwork n;
5207 hrc = (*it)->i_saveSettings(n);
5208 if (FAILED(hrc)) throw hrc;
5209 m->pMainConfigFile->llHostOnlyNetworks.push_back(n);
5210 }
5211 }
5212#endif /* VBOX_WITH_VMNET */
5213
5214#ifdef VBOX_WITH_CLOUD_NET
5215 m->pMainConfigFile->llCloudNetworks.clear();
5216 {
5217 AutoReadLock cloudNetworkLock(m->allCloudNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5218 for (CloudNetworksOList::const_iterator it = m->allCloudNetworks.begin();
5219 it != m->allCloudNetworks.end();
5220 ++it)
5221 {
5222 settings::CloudNetwork n;
5223 hrc = (*it)->i_saveSettings(n);
5224 if (FAILED(hrc)) throw hrc;
5225 m->pMainConfigFile->llCloudNetworks.push_back(n);
5226 }
5227 }
5228#endif /* VBOX_WITH_CLOUD_NET */
5229 // leave extra data alone, it's still in the config file
5230
5231 // host data (USB filters)
5232 hrc = m->pHost->i_saveSettings(m->pMainConfigFile->host);
5233 if (FAILED(hrc)) throw hrc;
5234
5235 hrc = m->pSystemProperties->i_saveSettings(m->pMainConfigFile->systemProperties);
5236 if (FAILED(hrc)) throw hrc;
5237
5238 // and write out the XML, still under the lock
5239 m->pMainConfigFile->write(m->strSettingsFilePath);
5240 }
5241 catch (HRESULT hrcXcpt)
5242 {
5243 /* we assume that error info is set by the thrower */
5244 hrc = hrcXcpt;
5245 }
5246 catch (...)
5247 {
5248 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
5249 }
5250
5251 return hrc;
5252}
5253
5254/**
5255 * Helper to register the machine.
5256 *
5257 * When called during VirtualBox startup, adds the given machine to the
5258 * collection of registered machines. Otherwise tries to mark the machine
5259 * as registered, and, if succeeded, adds it to the collection and
5260 * saves global settings.
5261 *
5262 * @note The caller must have added itself as a caller of the @a aMachine
5263 * object if calls this method not on VirtualBox startup.
5264 *
5265 * @param aMachine machine to register
5266 *
5267 * @note Locks objects!
5268 */
5269HRESULT VirtualBox::i_registerMachine(Machine *aMachine)
5270{
5271 ComAssertRet(aMachine, E_INVALIDARG);
5272
5273 AutoCaller autoCaller(this);
5274 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5275
5276 HRESULT hrc = S_OK;
5277
5278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5279
5280 {
5281 ComObjPtr<Machine> pMachine;
5282 hrc = i_findMachine(aMachine->i_getId(),
5283 true /* fPermitInaccessible */,
5284 false /* aDoSetError */,
5285 &pMachine);
5286 if (SUCCEEDED(hrc))
5287 {
5288 /* sanity */
5289 AutoLimitedCaller machCaller(pMachine);
5290 AssertComRC(machCaller.hrc());
5291
5292 return setError(E_INVALIDARG,
5293 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
5294 aMachine->i_getId().raw(),
5295 pMachine->i_getSettingsFileFull().c_str());
5296 }
5297
5298 ComAssertRet(hrc == VBOX_E_OBJECT_NOT_FOUND, hrc);
5299 hrc = S_OK;
5300 }
5301
5302 if (getObjectState().getState() != ObjectState::InInit)
5303 {
5304 hrc = aMachine->i_prepareRegister();
5305 if (FAILED(hrc)) return hrc;
5306 }
5307
5308 /* add to the collection of registered machines */
5309 m->allMachines.addChild(aMachine);
5310
5311 if (getObjectState().getState() != ObjectState::InInit)
5312 hrc = i_saveSettings();
5313
5314 return hrc;
5315}
5316
5317/**
5318 * Remembers the given medium object by storing it in either the global
5319 * medium registry or a machine one.
5320 *
5321 * @note Caller must hold the media tree lock for writing; in addition, this
5322 * locks @a pMedium for reading
5323 *
5324 * @param pMedium Medium object to remember.
5325 * @param ppMedium Actually stored medium object. Can be different if due
5326 * to an unavoidable race there was a duplicate Medium object
5327 * created.
5328 * @param mediaTreeLock Reference to the AutoWriteLock holding the media tree
5329 * lock, necessary to release it in the right spot.
5330 * @param fCalledFromMediumInit Flag whether this is called from Medium::init().
5331 * @return
5332 */
5333HRESULT VirtualBox::i_registerMedium(const ComObjPtr<Medium> &pMedium,
5334 ComObjPtr<Medium> *ppMedium,
5335 AutoWriteLock &mediaTreeLock,
5336 bool fCalledFromMediumInit)
5337{
5338 AssertReturn(pMedium != NULL, E_INVALIDARG);
5339 AssertReturn(ppMedium != NULL, E_INVALIDARG);
5340
5341 // caller must hold the media tree write lock
5342 Assert(i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5343
5344 AutoCaller autoCaller(this);
5345 AssertComRCReturnRC(autoCaller.hrc());
5346
5347 AutoCaller mediumCaller(pMedium);
5348 AssertComRCReturnRC(mediumCaller.hrc());
5349
5350 bool fAddToGlobalRegistry = false;
5351 const char *pszDevType = NULL;
5352 Guid regId;
5353 ObjectsList<Medium> *pall = NULL;
5354 DeviceType_T devType;
5355 {
5356 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5357 devType = pMedium->i_getDeviceType();
5358
5359 if (!pMedium->i_getFirstRegistryMachineId(regId))
5360 fAddToGlobalRegistry = true;
5361 }
5362 switch (devType)
5363 {
5364 case DeviceType_HardDisk:
5365 pall = &m->allHardDisks;
5366 pszDevType = tr("hard disk");
5367 break;
5368 case DeviceType_DVD:
5369 pszDevType = tr("DVD image");
5370 pall = &m->allDVDImages;
5371 break;
5372 case DeviceType_Floppy:
5373 pszDevType = tr("floppy image");
5374 pall = &m->allFloppyImages;
5375 break;
5376 default:
5377 AssertMsgFailedReturn(("invalid device type %d", devType), E_INVALIDARG);
5378 }
5379
5380 Guid id;
5381 Utf8Str strLocationFull;
5382 ComObjPtr<Medium> pParent;
5383 {
5384 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5385 id = pMedium->i_getId();
5386 strLocationFull = pMedium->i_getLocationFull();
5387 pParent = pMedium->i_getParent();
5388
5389 /*
5390 * If a separate thread has called Medium::close() for this medium at the same
5391 * time as this i_registerMedium() call then there is a window of opportunity in
5392 * Medium::i_close() where the media tree lock is dropped before calling
5393 * Medium::uninit() (which reacquires the lock) that we can end up here attempting
5394 * to register a medium which is in the process of being closed. In addition, if
5395 * this is a differencing medium and Medium::close() is in progress for one its
5396 * parent media then we are similarly operating on a media registry in flux. In
5397 * either case registering a medium just before calling Medium::uninit() will
5398 * lead to an inconsistent media registry so bail out here since Medium::close()
5399 * got to this medium (or one of its parents) first.
5400 */
5401 if (devType == DeviceType_HardDisk)
5402 {
5403 ComObjPtr<Medium> pTmpMedium = pMedium;
5404 while (pTmpMedium.isNotNull())
5405 {
5406 AutoCaller mediumAC(pTmpMedium);
5407 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
5408 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
5409
5410 if (pTmpMedium->i_isClosing())
5411 return setError(E_INVALIDARG,
5412 tr("Cannot register %s '%s' {%RTuuid} because it is in the process of being closed"),
5413 pszDevType,
5414 pTmpMedium->i_getLocationFull().c_str(),
5415 pTmpMedium->i_getId().raw());
5416
5417 pTmpMedium = pTmpMedium->i_getParent();
5418 }
5419 }
5420 }
5421
5422 HRESULT hrc;
5423
5424 Utf8Str strConflict;
5425 ComObjPtr<Medium> pDupMedium;
5426 hrc = i_checkMediaForConflicts(id, strLocationFull, strConflict, &pDupMedium);
5427 if (FAILED(hrc)) return hrc;
5428
5429 if (pDupMedium.isNull())
5430 {
5431 if (strConflict.length())
5432 return setError(E_INVALIDARG,
5433 tr("Cannot register the %s '%s' {%RTuuid} because a %s already exists"),
5434 pszDevType,
5435 strLocationFull.c_str(),
5436 id.raw(),
5437 strConflict.c_str(),
5438 m->strSettingsFilePath.c_str());
5439
5440 // add to the collection if it is a base medium
5441 if (pParent.isNull())
5442 pall->getList().push_back(pMedium);
5443
5444 // store all hard disks (even differencing images) in the map
5445 if (devType == DeviceType_HardDisk)
5446 m->mapHardDisks[id] = pMedium;
5447 }
5448
5449 /*
5450 * If we have been called from Medium::initFromSettings() then the Medium object's
5451 * AutoCaller status will be 'InInit' which means that when making the assigment to
5452 * ppMedium below the Medium object will not call Medium::uninit(). By excluding
5453 * this code path from releasing and reacquiring the media tree lock we avoid a
5454 * potential deadlock with other threads which may be operating on the
5455 * disks/DVDs/floppies in the VM's media registry at the same time such as
5456 * Machine::unregister().
5457 */
5458 if (!fCalledFromMediumInit)
5459 {
5460 // pMedium may be the last reference to the Medium object, and the
5461 // caller may have specified the same ComObjPtr as the output parameter.
5462 // In this case the assignment will uninit the object, and we must not
5463 // have a caller pending.
5464 mediumCaller.release();
5465 // release media tree lock, must not be held at uninit time.
5466 mediaTreeLock.release();
5467 // must not hold the media tree write lock any more
5468 Assert(!i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5469 }
5470
5471 *ppMedium = pDupMedium.isNull() ? pMedium : pDupMedium;
5472
5473 if (fAddToGlobalRegistry)
5474 {
5475 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5476 if ( fCalledFromMediumInit
5477 ? (*ppMedium)->i_addRegistryNoCallerCheck(m->uuidMediaRegistry)
5478 : (*ppMedium)->i_addRegistry(m->uuidMediaRegistry))
5479 i_markRegistryModified(m->uuidMediaRegistry);
5480 }
5481
5482 // Restore the initial lock state, so that no unexpected lock changes are
5483 // done by this method, which would need adjustments everywhere.
5484 if (!fCalledFromMediumInit)
5485 mediaTreeLock.acquire();
5486
5487 return hrc;
5488}
5489
5490/**
5491 * Removes the given medium from the respective registry.
5492 *
5493 * @param pMedium Hard disk object to remove.
5494 *
5495 * @note Caller must hold the media tree lock for writing; in addition, this locks @a pMedium for reading
5496 */
5497HRESULT VirtualBox::i_unregisterMedium(Medium *pMedium)
5498{
5499 AssertReturn(pMedium != NULL, E_INVALIDARG);
5500
5501 AutoCaller autoCaller(this);
5502 AssertComRCReturnRC(autoCaller.hrc());
5503
5504 AutoCaller mediumCaller(pMedium);
5505 AssertComRCReturnRC(mediumCaller.hrc());
5506
5507 // caller must hold the media tree write lock
5508 Assert(i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5509
5510 Guid id;
5511 ComObjPtr<Medium> pParent;
5512 DeviceType_T devType;
5513 {
5514 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5515 id = pMedium->i_getId();
5516 pParent = pMedium->i_getParent();
5517 devType = pMedium->i_getDeviceType();
5518 }
5519
5520 ObjectsList<Medium> *pall = NULL;
5521 switch (devType)
5522 {
5523 case DeviceType_HardDisk:
5524 pall = &m->allHardDisks;
5525 break;
5526 case DeviceType_DVD:
5527 pall = &m->allDVDImages;
5528 break;
5529 case DeviceType_Floppy:
5530 pall = &m->allFloppyImages;
5531 break;
5532 default:
5533 AssertMsgFailedReturn(("invalid device type %d", devType), E_INVALIDARG);
5534 }
5535
5536 // remove from the collection if it is a base medium
5537 if (pParent.isNull())
5538 pall->getList().remove(pMedium);
5539
5540 // remove all hard disks (even differencing images) from map
5541 if (devType == DeviceType_HardDisk)
5542 {
5543 size_t cnt = m->mapHardDisks.erase(id);
5544 Assert(cnt == 1);
5545 NOREF(cnt);
5546 }
5547
5548 return S_OK;
5549}
5550
5551/**
5552 * Unregisters all Medium objects which belong to the given machine registry.
5553 * Gets called from Machine::uninit() just before the machine object dies
5554 * and must only be called with a machine UUID as the registry ID.
5555 *
5556 * Locks the media tree.
5557 *
5558 * @param uuidMachine Medium registry ID (always a machine UUID)
5559 * @return
5560 */
5561HRESULT VirtualBox::i_unregisterMachineMedia(const Guid &uuidMachine)
5562{
5563 Assert(!uuidMachine.isZero() && uuidMachine.isValid());
5564
5565 LogFlowFuncEnter();
5566
5567 AutoCaller autoCaller(this);
5568 AssertComRCReturnRC(autoCaller.hrc());
5569
5570 MediaList llMedia2Close;
5571
5572 {
5573 AutoWriteLock tlock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5574
5575 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
5576 it != m->allHardDisks.getList().end();
5577 ++it)
5578 {
5579 ComObjPtr<Medium> pMedium = *it;
5580 AutoCaller medCaller(pMedium);
5581 if (FAILED(medCaller.hrc())) return medCaller.hrc();
5582 AutoReadLock medlock(pMedium COMMA_LOCKVAL_SRC_POS);
5583 Log(("Looking at medium %RTuuid\n", pMedium->i_getId().raw()));
5584
5585 /* If the medium is still in the registry then either some code is
5586 * seriously buggy (unregistering a VM removes it automatically),
5587 * or the reference to a Machine object is destroyed without ever
5588 * being registered. The second condition checks if a medium is
5589 * in no registry, which indicates (set by unregistering) that a
5590 * medium is not used by any other VM and thus can be closed. */
5591 Guid dummy;
5592 if ( pMedium->i_isInRegistry(uuidMachine)
5593 || !pMedium->i_getFirstRegistryMachineId(dummy))
5594 {
5595 /* Collect all medium objects into llMedia2Close,
5596 * in right order for closing. */
5597 MediaList llMediaTodo;
5598 llMediaTodo.push_back(pMedium);
5599
5600 while (llMediaTodo.size() > 0)
5601 {
5602 ComObjPtr<Medium> pCurrent = llMediaTodo.<