VirtualBox

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

Last change on this file since 79209 was 79209, checked in by vboxsync, 6 years ago

Main: bugref:6913: Fixed: No IMediumRegisteredEvent occured for snapshots' mediums during adding the existing VM

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette