VirtualBox

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

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

Main: bugref:6913: Fixed compilation errors

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

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