VirtualBox

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

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

Shared Clipboard/URI: Update.

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