VirtualBox

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

Last change on this file since 88956 was 87949, checked in by vboxsync, 4 years ago

NAT/Net: Refuse to modify NATNetwork when an instance of it is
running. Use existing infrastructure that keeps track of the number
of VMs that use any given natnet (though it's exposes to natnet in a
somewhat haphazard way). bugref:9909.

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

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