VirtualBox

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

Last change on this file since 48013 was 47597, checked in by vboxsync, 11 years ago

Main/VirtualBox: remove accidental test code checkin, breaking IVirtualBox.getVersion

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 162.6 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 47597 2013-08-07 16:38:35Z vboxsync $ */
2/** @file
3 * Implementation of IVirtualBox in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2006-2013 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#include <iprt/asm.h>
19#include <iprt/base64.h>
20#include <iprt/buildconfig.h>
21#include <iprt/cpp/utils.h>
22#include <iprt/dir.h>
23#include <iprt/env.h>
24#include <iprt/file.h>
25#include <iprt/path.h>
26#include <iprt/process.h>
27#include <iprt/rand.h>
28#include <iprt/sha.h>
29#include <iprt/string.h>
30#include <iprt/stream.h>
31#include <iprt/thread.h>
32#include <iprt/uuid.h>
33#include <iprt/cpp/xml.h>
34
35#include <VBox/com/com.h>
36#include <VBox/com/array.h>
37#include "VBox/com/EventQueue.h"
38#include "VBox/com/MultiResult.h"
39
40#include <VBox/err.h>
41#include <VBox/param.h>
42#include <VBox/settings.h>
43#include <VBox/version.h>
44
45#include <package-generated.h>
46
47#include <algorithm>
48#include <set>
49#include <vector>
50#include <memory> // for auto_ptr
51
52#include "VirtualBoxImpl.h"
53
54#include "Global.h"
55#include "MachineImpl.h"
56#include "MediumImpl.h"
57#include "SharedFolderImpl.h"
58#include "ProgressImpl.h"
59#include "ProgressProxyImpl.h"
60#include "HostImpl.h"
61#include "USBControllerImpl.h"
62#include "SystemPropertiesImpl.h"
63#include "GuestOSTypeImpl.h"
64#include "NetworkServiceRunner.h"
65#include "DHCPServerImpl.h"
66#include "NATNetworkImpl.h"
67#ifdef VBOX_WITH_RESOURCE_USAGE_API
68# include "PerformanceImpl.h"
69#endif /* VBOX_WITH_RESOURCE_USAGE_API */
70#include "EventImpl.h"
71#include "VBoxEvents.h"
72#ifdef VBOX_WITH_EXTPACK
73# include "ExtPackManagerImpl.h"
74#endif
75#include "AutostartDb.h"
76#include "ClientWatcher.h"
77
78#include "AutoCaller.h"
79#include "Logging.h"
80
81#ifdef RT_OS_WINDOWS
82# include "win/svchlp.h"
83# include "win/VBoxComEvents.h"
84#endif
85
86////////////////////////////////////////////////////////////////////////////////
87//
88// Definitions
89//
90////////////////////////////////////////////////////////////////////////////////
91
92#define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
93
94////////////////////////////////////////////////////////////////////////////////
95//
96// Global variables
97//
98////////////////////////////////////////////////////////////////////////////////
99
100// static
101Bstr VirtualBox::sVersion;
102
103// static
104Bstr VirtualBox::sVersionNormalized;
105
106// static
107ULONG VirtualBox::sRevision;
108
109// static
110Bstr VirtualBox::sPackageType;
111
112// static
113Bstr VirtualBox::sAPIVersion;
114
115////////////////////////////////////////////////////////////////////////////////
116//
117// CallbackEvent class
118//
119////////////////////////////////////////////////////////////////////////////////
120
121/**
122 * Abstract callback event class to asynchronously call VirtualBox callbacks
123 * on a dedicated event thread. Subclasses reimplement #handleCallback()
124 * to call appropriate IVirtualBoxCallback methods depending on the event
125 * to be dispatched.
126 *
127 * @note The VirtualBox instance passed to the constructor is strongly
128 * referenced, so that the VirtualBox singleton won't be released until the
129 * event gets handled by the event thread.
130 */
131class VirtualBox::CallbackEvent : public Event
132{
133public:
134
135 CallbackEvent(VirtualBox *aVirtualBox, VBoxEventType_T aWhat)
136 : mVirtualBox(aVirtualBox), mWhat(aWhat)
137 {
138 Assert(aVirtualBox);
139 }
140
141 void *handler();
142
143 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) = 0;
144
145private:
146
147 /**
148 * Note that this is a weak ref -- the CallbackEvent handler thread
149 * is bound to the lifetime of the VirtualBox instance, so it's safe.
150 */
151 VirtualBox *mVirtualBox;
152protected:
153 VBoxEventType_T mWhat;
154};
155
156////////////////////////////////////////////////////////////////////////////////
157//
158// VirtualBox private member data definition
159//
160////////////////////////////////////////////////////////////////////////////////
161
162typedef ObjectsList<Medium> MediaOList;
163typedef ObjectsList<GuestOSType> GuestOSTypesOList;
164typedef ObjectsList<SharedFolder> SharedFoldersOList;
165typedef ObjectsList<DHCPServer> DHCPServersOList;
166typedef ObjectsList<NATNetwork> NATNetworksOList;
167
168typedef std::map<Guid, ComPtr<IProgress> > ProgressMap;
169typedef std::map<Guid, ComObjPtr<Medium> > HardDiskMap;
170
171/**
172 * Main VirtualBox data structure.
173 * @note |const| members are persistent during lifetime so can be accessed
174 * without locking.
175 */
176struct VirtualBox::Data
177{
178 Data()
179 : pMainConfigFile(NULL),
180 uuidMediaRegistry("48024e5c-fdd9-470f-93af-ec29f7ea518c"),
181 uRegistryNeedsSaving(0),
182 lockMachines(LOCKCLASS_LISTOFMACHINES),
183 allMachines(lockMachines),
184 lockGuestOSTypes(LOCKCLASS_LISTOFOTHEROBJECTS),
185 allGuestOSTypes(lockGuestOSTypes),
186 lockMedia(LOCKCLASS_LISTOFMEDIA),
187 allHardDisks(lockMedia),
188 allDVDImages(lockMedia),
189 allFloppyImages(lockMedia),
190 lockSharedFolders(LOCKCLASS_LISTOFOTHEROBJECTS),
191 allSharedFolders(lockSharedFolders),
192 lockDHCPServers(LOCKCLASS_LISTOFOTHEROBJECTS),
193 allDHCPServers(lockDHCPServers),
194 lockNATNetworks(LOCKCLASS_LISTOFOTHEROBJECTS),
195 allNATNetworks(lockNATNetworks),
196 mtxProgressOperations(LOCKCLASS_PROGRESSLIST),
197 pClientWatcher(NULL),
198 threadAsyncEvent(NIL_RTTHREAD),
199 pAsyncEventQ(NULL),
200 pAutostartDb(NULL),
201 fSettingsCipherKeySet(false)
202 {
203 }
204
205 ~Data()
206 {
207 if (pMainConfigFile)
208 {
209 delete pMainConfigFile;
210 pMainConfigFile = NULL;
211 }
212 };
213
214 // const data members not requiring locking
215 const Utf8Str strHomeDir;
216
217 // VirtualBox main settings file
218 const Utf8Str strSettingsFilePath;
219 settings::MainConfigFile *pMainConfigFile;
220
221 // constant pseudo-machine ID for global media registry
222 const Guid uuidMediaRegistry;
223
224 // counter if global media registry needs saving, updated using atomic
225 // operations, without requiring any locks
226 uint64_t uRegistryNeedsSaving;
227
228 // const objects not requiring locking
229 const ComObjPtr<Host> pHost;
230 const ComObjPtr<SystemProperties> pSystemProperties;
231#ifdef VBOX_WITH_RESOURCE_USAGE_API
232 const ComObjPtr<PerformanceCollector> pPerformanceCollector;
233#endif /* VBOX_WITH_RESOURCE_USAGE_API */
234
235 // Each of the following lists use a particular lock handle that protects the
236 // list as a whole. As opposed to version 3.1 and earlier, these lists no
237 // longer need the main VirtualBox object lock, but only the respective list
238 // lock. In each case, the locking order is defined that the list must be
239 // requested before object locks of members of the lists (see the order definitions
240 // in AutoLock.h; e.g. LOCKCLASS_LISTOFMACHINES before LOCKCLASS_MACHINEOBJECT).
241 RWLockHandle lockMachines;
242 MachinesOList allMachines;
243
244 RWLockHandle lockGuestOSTypes;
245 GuestOSTypesOList allGuestOSTypes;
246
247 // All the media lists are protected by the following locking handle:
248 RWLockHandle lockMedia;
249 MediaOList allHardDisks, // base images only!
250 allDVDImages,
251 allFloppyImages;
252 // the hard disks map is an additional map sorted by UUID for quick lookup
253 // and contains ALL hard disks (base and differencing); it is protected by
254 // the same lock as the other media lists above
255 HardDiskMap mapHardDisks;
256
257 // list of pending machine renames (also protected by media tree lock;
258 // see VirtualBox::rememberMachineNameChangeForMedia())
259 struct PendingMachineRename
260 {
261 Utf8Str strConfigDirOld;
262 Utf8Str strConfigDirNew;
263 };
264 typedef std::list<PendingMachineRename> PendingMachineRenamesList;
265 PendingMachineRenamesList llPendingMachineRenames;
266
267 RWLockHandle lockSharedFolders;
268 SharedFoldersOList allSharedFolders;
269
270 RWLockHandle lockDHCPServers;
271 DHCPServersOList allDHCPServers;
272
273 RWLockHandle lockNATNetworks;
274 NATNetworksOList allNATNetworks;
275
276 RWLockHandle mtxProgressOperations;
277 ProgressMap mapProgressOperations;
278
279 ClientWatcher * const pClientWatcher;
280
281 // the following are data for the async event thread
282 const RTTHREAD threadAsyncEvent;
283 EventQueue * const pAsyncEventQ;
284 const ComObjPtr<EventSource> pEventSource;
285
286#ifdef VBOX_WITH_EXTPACK
287 /** The extension pack manager object lives here. */
288 const ComObjPtr<ExtPackManager> ptrExtPackManager;
289#endif
290
291 /** The global autostart database for the user. */
292 AutostartDb * const pAutostartDb;
293
294 /** Settings secret */
295 bool fSettingsCipherKeySet;
296 uint8_t SettingsCipherKey[RTSHA512_HASH_SIZE];
297};
298
299
300// constructor / destructor
301/////////////////////////////////////////////////////////////////////////////
302
303VirtualBox::VirtualBox()
304{}
305
306VirtualBox::~VirtualBox()
307{}
308
309HRESULT VirtualBox::FinalConstruct()
310{
311 LogFlowThisFunc(("\n"));
312
313 HRESULT rc = init();
314
315 BaseFinalConstruct();
316
317 return rc;
318}
319
320void VirtualBox::FinalRelease()
321{
322 LogFlowThisFunc(("\n"));
323
324 uninit();
325
326 BaseFinalRelease();
327}
328
329// public initializer/uninitializer for internal purposes only
330/////////////////////////////////////////////////////////////////////////////
331
332/**
333 * Initializes the VirtualBox object.
334 *
335 * @return COM result code
336 */
337HRESULT VirtualBox::init()
338{
339 /* Enclose the state transition NotReady->InInit->Ready */
340 AutoInitSpan autoInitSpan(this);
341 AssertReturn(autoInitSpan.isOk(), E_FAIL);
342
343 /* Locking this object for writing during init sounds a bit paradoxical,
344 * but in the current locking mess this avoids that some code gets a
345 * read lock and later calls code which wants the same write lock. */
346 AutoWriteLock lock(this COMMA_LOCKVAL_SRC_POS);
347
348 // allocate our instance data
349 m = new Data;
350
351 LogFlow(("===========================================================\n"));
352 LogFlowThisFuncEnter();
353
354 if (sVersion.isEmpty())
355 sVersion = RTBldCfgVersion();
356 if (sVersionNormalized.isEmpty())
357 {
358 Utf8Str tmp(RTBldCfgVersion());
359 if (tmp.endsWith(VBOX_BUILD_PUBLISHER))
360 tmp = tmp.substr(0, tmp.length() - strlen(VBOX_BUILD_PUBLISHER));
361 sVersionNormalized = tmp;
362 }
363 sRevision = RTBldCfgRevision();
364 if (sPackageType.isEmpty())
365 sPackageType = VBOX_PACKAGE_STRING;
366 if (sAPIVersion.isEmpty())
367 sAPIVersion = VBOX_API_VERSION_STRING;
368 LogFlowThisFunc(("Version: %ls, Package: %ls, API Version: %ls\n", sVersion.raw(), sPackageType.raw(), sAPIVersion.raw()));
369
370 /* Get the VirtualBox home directory. */
371 {
372 char szHomeDir[RTPATH_MAX];
373 int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir));
374 if (RT_FAILURE(vrc))
375 return setError(E_FAIL,
376 tr("Could not create the VirtualBox home directory '%s' (%Rrc)"),
377 szHomeDir, vrc);
378
379 unconst(m->strHomeDir) = szHomeDir;
380 }
381
382 /* compose the VirtualBox.xml file name */
383 unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s",
384 m->strHomeDir.c_str(),
385 RTPATH_DELIMITER,
386 VBOX_GLOBAL_SETTINGS_FILE);
387 HRESULT rc = S_OK;
388 bool fCreate = false;
389 try
390 {
391 // load and parse VirtualBox.xml; this will throw on XML or logic errors
392 try
393 {
394 m->pMainConfigFile = new settings::MainConfigFile(&m->strSettingsFilePath);
395 }
396 catch (xml::EIPRTFailure &e)
397 {
398 // this is thrown by the XML backend if the RTOpen() call fails;
399 // only if the main settings file does not exist, create it,
400 // if there's something more serious, then do fail!
401 if (e.rc() == VERR_FILE_NOT_FOUND)
402 fCreate = true;
403 else
404 throw;
405 }
406
407 if (fCreate)
408 m->pMainConfigFile = new settings::MainConfigFile(NULL);
409
410#ifdef VBOX_WITH_RESOURCE_USAGE_API
411 /* create the performance collector object BEFORE host */
412 unconst(m->pPerformanceCollector).createObject();
413 rc = m->pPerformanceCollector->init();
414 ComAssertComRCThrowRC(rc);
415#endif /* VBOX_WITH_RESOURCE_USAGE_API */
416
417 /* create the host object early, machines will need it */
418 unconst(m->pHost).createObject();
419 rc = m->pHost->init(this);
420 ComAssertComRCThrowRC(rc);
421
422 rc = m->pHost->loadSettings(m->pMainConfigFile->host);
423 if (FAILED(rc)) throw rc;
424
425 /*
426 * Create autostart database object early, because the system properties
427 * might need it.
428 */
429 unconst(m->pAutostartDb) = new AutostartDb;
430
431 /* create the system properties object, someone may need it too */
432 unconst(m->pSystemProperties).createObject();
433 rc = m->pSystemProperties->init(this);
434 ComAssertComRCThrowRC(rc);
435
436 rc = m->pSystemProperties->loadSettings(m->pMainConfigFile->systemProperties);
437 if (FAILED(rc)) throw rc;
438
439 /* guest OS type objects, needed by machines */
440 for (size_t i = 0; i < Global::cOSTypes; ++i)
441 {
442 ComObjPtr<GuestOSType> guestOSTypeObj;
443 rc = guestOSTypeObj.createObject();
444 if (SUCCEEDED(rc))
445 {
446 rc = guestOSTypeObj->init(Global::sOSTypes[i]);
447 if (SUCCEEDED(rc))
448 m->allGuestOSTypes.addChild(guestOSTypeObj);
449 }
450 ComAssertComRCThrowRC(rc);
451 }
452
453 /* all registered media, needed by machines */
454 if (FAILED(rc = initMedia(m->uuidMediaRegistry,
455 m->pMainConfigFile->mediaRegistry,
456 Utf8Str::Empty))) // const Utf8Str &machineFolder
457 throw rc;
458
459 /* machines */
460 if (FAILED(rc = initMachines()))
461 throw rc;
462
463#ifdef DEBUG
464 LogFlowThisFunc(("Dumping media backreferences\n"));
465 dumpAllBackRefs();
466#endif
467
468 /* net services - dhcp services */
469 for (settings::DHCPServersList::const_iterator it = m->pMainConfigFile->llDhcpServers.begin();
470 it != m->pMainConfigFile->llDhcpServers.end();
471 ++it)
472 {
473 const settings::DHCPServer &data = *it;
474
475 ComObjPtr<DHCPServer> pDhcpServer;
476 if (SUCCEEDED(rc = pDhcpServer.createObject()))
477 rc = pDhcpServer->init(this, data);
478 if (FAILED(rc)) throw rc;
479
480 rc = registerDHCPServer(pDhcpServer, false /* aSaveRegistry */);
481 if (FAILED(rc)) throw rc;
482 }
483
484 /* net services - nat networks */
485 for (settings::NATNetworksList::const_iterator it = m->pMainConfigFile->llNATNetworks.begin();
486 it != m->pMainConfigFile->llNATNetworks.end();
487 ++it)
488 {
489 const settings::NATNetwork &net = *it;
490
491 ComObjPtr<NATNetwork> pNATNetwork;
492 if (SUCCEEDED(rc = pNATNetwork.createObject()))
493 rc = pNATNetwork->init(this, net);
494 if (FAILED(rc)) throw rc;
495
496 rc = registerNATNetwork(pNATNetwork, false /* aSaveRegistry */);
497 if (FAILED(rc)) throw rc;
498 }
499
500 /* events */
501 if (SUCCEEDED(rc = unconst(m->pEventSource).createObject()))
502 rc = m->pEventSource->init(static_cast<IVirtualBox*>(this));
503 if (FAILED(rc)) throw rc;
504
505#ifdef VBOX_WITH_EXTPACK
506 /* extension manager */
507 rc = unconst(m->ptrExtPackManager).createObject();
508 if (SUCCEEDED(rc))
509 rc = m->ptrExtPackManager->initExtPackManager(this, VBOXEXTPACKCTX_PER_USER_DAEMON);
510 if (FAILED(rc))
511 throw rc;
512#endif
513 }
514 catch (HRESULT err)
515 {
516 /* we assume that error info is set by the thrower */
517 rc = err;
518 }
519 catch (...)
520 {
521 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
522 }
523
524 if (SUCCEEDED(rc))
525 {
526 /* set up client monitoring */
527 try
528 {
529 unconst(m->pClientWatcher) = new ClientWatcher(this);
530 if (!m->pClientWatcher->isReady())
531 {
532 delete m->pClientWatcher;
533 unconst(m->pClientWatcher) = NULL;
534 rc = E_FAIL;
535 }
536 }
537 catch (std::bad_alloc &)
538 {
539 rc = E_OUTOFMEMORY;
540 }
541 }
542
543 if (SUCCEEDED(rc))
544 {
545 try
546 {
547 /* start the async event handler thread */
548 int vrc = RTThreadCreate(&unconst(m->threadAsyncEvent),
549 AsyncEventHandler,
550 &unconst(m->pAsyncEventQ),
551 0,
552 RTTHREADTYPE_MAIN_WORKER,
553 RTTHREADFLAGS_WAITABLE,
554 "EventHandler");
555 ComAssertRCThrow(vrc, E_FAIL);
556
557 /* wait until the thread sets m->pAsyncEventQ */
558 RTThreadUserWait(m->threadAsyncEvent, RT_INDEFINITE_WAIT);
559 ComAssertThrow(m->pAsyncEventQ, E_FAIL);
560 }
561 catch (HRESULT aRC)
562 {
563 rc = aRC;
564 }
565 }
566
567 /* Confirm a successful initialization when it's the case */
568 if (SUCCEEDED(rc))
569 autoInitSpan.setSucceeded();
570
571#ifdef VBOX_WITH_EXTPACK
572 /* Let the extension packs have a go at things. */
573 if (SUCCEEDED(rc))
574 {
575 lock.release();
576 m->ptrExtPackManager->callAllVirtualBoxReadyHooks();
577 }
578#endif
579
580 LogFlowThisFunc(("rc=%08X\n", rc));
581 LogFlowThisFuncLeave();
582 LogFlow(("===========================================================\n"));
583 return rc;
584}
585
586HRESULT VirtualBox::initMachines()
587{
588 for (settings::MachinesRegistry::const_iterator it = m->pMainConfigFile->llMachines.begin();
589 it != m->pMainConfigFile->llMachines.end();
590 ++it)
591 {
592 HRESULT rc = S_OK;
593 const settings::MachineRegistryEntry &xmlMachine = *it;
594 Guid uuid = xmlMachine.uuid;
595
596 ComObjPtr<Machine> pMachine;
597 if (SUCCEEDED(rc = pMachine.createObject()))
598 {
599 rc = pMachine->initFromSettings(this,
600 xmlMachine.strSettingsFile,
601 &uuid);
602 if (SUCCEEDED(rc))
603 rc = registerMachine(pMachine);
604 if (FAILED(rc))
605 return rc;
606 }
607 }
608
609 return S_OK;
610}
611
612/**
613 * Loads a media registry from XML and adds the media contained therein to
614 * the global lists of known media.
615 *
616 * This now (4.0) gets called from two locations:
617 *
618 * -- VirtualBox::init(), to load the global media registry from VirtualBox.xml;
619 *
620 * -- Machine::loadMachineDataFromSettings(), to load the per-machine registry
621 * from machine XML, for machines created with VirtualBox 4.0 or later.
622 *
623 * In both cases, the media found are added to the global lists so the
624 * global arrays of media (including the GUI's virtual media manager)
625 * continue to work as before.
626 *
627 * @param uuidMachineRegistry The UUID of the media registry. This is either the
628 * transient UUID created at VirtualBox startup for the global registry or
629 * a machine ID.
630 * @param mediaRegistry The XML settings structure to load, either from VirtualBox.xml
631 * or a machine XML.
632 * @return
633 */
634HRESULT VirtualBox::initMedia(const Guid &uuidRegistry,
635 const settings::MediaRegistry mediaRegistry,
636 const Utf8Str &strMachineFolder)
637{
638 LogFlow(("VirtualBox::initMedia ENTERING, uuidRegistry=%s, strMachineFolder=%s\n",
639 uuidRegistry.toString().c_str(),
640 strMachineFolder.c_str()));
641
642 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
643
644 HRESULT rc = S_OK;
645 settings::MediaList::const_iterator it;
646 for (it = mediaRegistry.llHardDisks.begin();
647 it != mediaRegistry.llHardDisks.end();
648 ++it)
649 {
650 const settings::Medium &xmlHD = *it;
651
652 ComObjPtr<Medium> pHardDisk;
653 if (SUCCEEDED(rc = pHardDisk.createObject()))
654 rc = pHardDisk->init(this,
655 NULL, // parent
656 DeviceType_HardDisk,
657 uuidRegistry,
658 xmlHD, // XML data; this recurses to processes the children
659 strMachineFolder);
660 if (FAILED(rc)) return rc;
661
662 rc = registerMedium(pHardDisk, &pHardDisk, DeviceType_HardDisk);
663 if (FAILED(rc)) return rc;
664 }
665
666 for (it = mediaRegistry.llDvdImages.begin();
667 it != mediaRegistry.llDvdImages.end();
668 ++it)
669 {
670 const settings::Medium &xmlDvd = *it;
671
672 ComObjPtr<Medium> pImage;
673 if (SUCCEEDED(pImage.createObject()))
674 rc = pImage->init(this,
675 NULL,
676 DeviceType_DVD,
677 uuidRegistry,
678 xmlDvd,
679 strMachineFolder);
680 if (FAILED(rc)) return rc;
681
682 rc = registerMedium(pImage, &pImage, DeviceType_DVD);
683 if (FAILED(rc)) return rc;
684 }
685
686 for (it = mediaRegistry.llFloppyImages.begin();
687 it != mediaRegistry.llFloppyImages.end();
688 ++it)
689 {
690 const settings::Medium &xmlFloppy = *it;
691
692 ComObjPtr<Medium> pImage;
693 if (SUCCEEDED(pImage.createObject()))
694 rc = pImage->init(this,
695 NULL,
696 DeviceType_Floppy,
697 uuidRegistry,
698 xmlFloppy,
699 strMachineFolder);
700 if (FAILED(rc)) return rc;
701
702 rc = registerMedium(pImage, &pImage, DeviceType_Floppy);
703 if (FAILED(rc)) return rc;
704 }
705
706 LogFlow(("VirtualBox::initMedia LEAVING\n"));
707
708 return S_OK;
709}
710
711void VirtualBox::uninit()
712{
713 Assert(!m->uRegistryNeedsSaving);
714 if (m->uRegistryNeedsSaving)
715 saveSettings();
716
717 /* Enclose the state transition Ready->InUninit->NotReady */
718 AutoUninitSpan autoUninitSpan(this);
719 if (autoUninitSpan.uninitDone())
720 return;
721
722 LogFlow(("===========================================================\n"));
723 LogFlowThisFuncEnter();
724 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
725
726 /* tell all our child objects we've been uninitialized */
727
728 LogFlowThisFunc(("Uninitializing machines (%d)...\n", m->allMachines.size()));
729 if (m->pHost)
730 {
731 /* It is necessary to hold the VirtualBox and Host locks here because
732 we may have to uninitialize SessionMachines. */
733 AutoMultiWriteLock2 multilock(this, m->pHost COMMA_LOCKVAL_SRC_POS);
734 m->allMachines.uninitAll();
735 }
736 else
737 m->allMachines.uninitAll();
738 m->allFloppyImages.uninitAll();
739 m->allDVDImages.uninitAll();
740 m->allHardDisks.uninitAll();
741 m->allDHCPServers.uninitAll();
742
743 m->mapProgressOperations.clear();
744
745 m->allGuestOSTypes.uninitAll();
746
747 /* Note that we release singleton children after we've all other children.
748 * In some cases this is important because these other children may use
749 * some resources of the singletons which would prevent them from
750 * uninitializing (as for example, mSystemProperties which owns
751 * MediumFormat objects which Medium objects refer to) */
752 if (m->pSystemProperties)
753 {
754 m->pSystemProperties->uninit();
755 unconst(m->pSystemProperties).setNull();
756 }
757
758 if (m->pHost)
759 {
760 m->pHost->uninit();
761 unconst(m->pHost).setNull();
762 }
763
764#ifdef VBOX_WITH_RESOURCE_USAGE_API
765 if (m->pPerformanceCollector)
766 {
767 m->pPerformanceCollector->uninit();
768 unconst(m->pPerformanceCollector).setNull();
769 }
770#endif /* VBOX_WITH_RESOURCE_USAGE_API */
771
772 LogFlowThisFunc(("Terminating the async event handler...\n"));
773 if (m->threadAsyncEvent != NIL_RTTHREAD)
774 {
775 /* signal to exit the event loop */
776 if (RT_SUCCESS(m->pAsyncEventQ->interruptEventQueueProcessing()))
777 {
778 /*
779 * Wait for thread termination (only after we've successfully
780 * interrupted the event queue processing!)
781 */
782 int vrc = RTThreadWait(m->threadAsyncEvent, 60000, NULL);
783 if (RT_FAILURE(vrc))
784 LogWarningFunc(("RTThreadWait(%RTthrd) -> %Rrc\n",
785 m->threadAsyncEvent, vrc));
786 }
787 else
788 {
789 AssertMsgFailed(("interruptEventQueueProcessing() failed\n"));
790 RTThreadWait(m->threadAsyncEvent, 0, NULL);
791 }
792
793 unconst(m->threadAsyncEvent) = NIL_RTTHREAD;
794 unconst(m->pAsyncEventQ) = NULL;
795 }
796
797 LogFlowThisFunc(("Releasing event source...\n"));
798 if (m->pEventSource)
799 {
800 // we don't perform uninit() as it's possible that some pending event refers to this source
801 unconst(m->pEventSource).setNull();
802 }
803
804 LogFlowThisFunc(("Terminating the client watcher...\n"));
805 if (m->pClientWatcher)
806 {
807 delete m->pClientWatcher;
808 unconst(m->pClientWatcher) = NULL;
809 }
810
811 delete m->pAutostartDb;
812
813 // clean up our instance data
814 delete m;
815
816 /* Unload hard disk plugin backends. */
817 VDShutdown();
818
819 LogFlowThisFuncLeave();
820 LogFlow(("===========================================================\n"));
821}
822
823// IVirtualBox properties
824/////////////////////////////////////////////////////////////////////////////
825
826STDMETHODIMP VirtualBox::COMGETTER(Version)(BSTR *aVersion)
827{
828 CheckComArgNotNull(aVersion);
829
830 AutoCaller autoCaller(this);
831 if (FAILED(autoCaller.rc())) return autoCaller.rc();
832
833 sVersion.cloneTo(aVersion);
834 return S_OK;
835}
836
837STDMETHODIMP VirtualBox::COMGETTER(VersionNormalized)(BSTR *aVersionNormalized)
838{
839 CheckComArgNotNull(aVersionNormalized);
840
841 AutoCaller autoCaller(this);
842 if (FAILED(autoCaller.rc())) return autoCaller.rc();
843
844 sVersionNormalized.cloneTo(aVersionNormalized);
845 return S_OK;
846}
847
848STDMETHODIMP VirtualBox::COMGETTER(Revision)(ULONG *aRevision)
849{
850 CheckComArgNotNull(aRevision);
851
852 AutoCaller autoCaller(this);
853 if (FAILED(autoCaller.rc())) return autoCaller.rc();
854
855 *aRevision = sRevision;
856 return S_OK;
857}
858
859STDMETHODIMP VirtualBox::COMGETTER(PackageType)(BSTR *aPackageType)
860{
861 CheckComArgNotNull(aPackageType);
862
863 AutoCaller autoCaller(this);
864 if (FAILED(autoCaller.rc())) return autoCaller.rc();
865
866 sPackageType.cloneTo(aPackageType);
867 return S_OK;
868}
869
870STDMETHODIMP VirtualBox::COMGETTER(APIVersion)(BSTR *aAPIVersion)
871{
872 CheckComArgNotNull(aAPIVersion);
873
874 AutoCaller autoCaller(this);
875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
876
877 sAPIVersion.cloneTo(aAPIVersion);
878 return S_OK;
879}
880
881STDMETHODIMP VirtualBox::COMGETTER(HomeFolder)(BSTR *aHomeFolder)
882{
883 CheckComArgNotNull(aHomeFolder);
884
885 AutoCaller autoCaller(this);
886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
887
888 /* mHomeDir is const and doesn't need a lock */
889 m->strHomeDir.cloneTo(aHomeFolder);
890 return S_OK;
891}
892
893STDMETHODIMP VirtualBox::COMGETTER(SettingsFilePath)(BSTR *aSettingsFilePath)
894{
895 CheckComArgNotNull(aSettingsFilePath);
896
897 AutoCaller autoCaller(this);
898 if (FAILED(autoCaller.rc())) return autoCaller.rc();
899
900 /* mCfgFile.mName is const and doesn't need a lock */
901 m->strSettingsFilePath.cloneTo(aSettingsFilePath);
902 return S_OK;
903}
904
905STDMETHODIMP VirtualBox::COMGETTER(Host)(IHost **aHost)
906{
907 CheckComArgOutPointerValid(aHost);
908
909 AutoCaller autoCaller(this);
910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
911
912 /* mHost is const, no need to lock */
913 m->pHost.queryInterfaceTo(aHost);
914 return S_OK;
915}
916
917STDMETHODIMP
918VirtualBox::COMGETTER(SystemProperties)(ISystemProperties **aSystemProperties)
919{
920 CheckComArgOutPointerValid(aSystemProperties);
921
922 AutoCaller autoCaller(this);
923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
924
925 /* mSystemProperties is const, no need to lock */
926 m->pSystemProperties.queryInterfaceTo(aSystemProperties);
927 return S_OK;
928}
929
930STDMETHODIMP
931VirtualBox::COMGETTER(Machines)(ComSafeArrayOut(IMachine *, aMachines))
932{
933 CheckComArgOutSafeArrayPointerValid(aMachines);
934
935 AutoCaller autoCaller(this);
936 if (FAILED(autoCaller.rc())) return autoCaller.rc();
937
938 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
939 SafeIfaceArray<IMachine> machines(m->allMachines.getList());
940 machines.detachTo(ComSafeArrayOutArg(aMachines));
941
942 return S_OK;
943}
944
945STDMETHODIMP
946VirtualBox::COMGETTER(MachineGroups)(ComSafeArrayOut(BSTR, aMachineGroups))
947{
948 CheckComArgOutSafeArrayPointerValid(aMachineGroups);
949
950 AutoCaller autoCaller(this);
951 if (FAILED(autoCaller.rc())) return autoCaller.rc();
952
953 std::list<Bstr> allGroups;
954
955 /* get copy of all machine references, to avoid holding the list lock */
956 MachinesOList::MyList allMachines;
957 {
958 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
959 allMachines = m->allMachines.getList();
960 }
961 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
962 it != allMachines.end();
963 ++it)
964 {
965 const ComObjPtr<Machine> &pMachine = *it;
966 AutoCaller autoMachineCaller(pMachine);
967 if (FAILED(autoMachineCaller.rc()))
968 continue;
969 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
970
971 if (pMachine->isAccessible())
972 {
973 const StringsList &thisGroups = pMachine->getGroups();
974 for (StringsList::const_iterator it2 = thisGroups.begin();
975 it2 != thisGroups.end();
976 ++it2)
977 allGroups.push_back(*it2);
978 }
979 }
980
981 /* throw out any duplicates */
982 allGroups.sort();
983 allGroups.unique();
984 com::SafeArray<BSTR> machineGroups(allGroups.size());
985 size_t i = 0;
986 for (std::list<Bstr>::const_iterator it = allGroups.begin();
987 it != allGroups.end();
988 ++it, i++)
989 {
990 const Bstr &tmp = *it;
991 tmp.cloneTo(&machineGroups[i]);
992 }
993 machineGroups.detachTo(ComSafeArrayOutArg(aMachineGroups));
994
995 return S_OK;
996}
997
998STDMETHODIMP VirtualBox::COMGETTER(HardDisks)(ComSafeArrayOut(IMedium *, aHardDisks))
999{
1000 CheckComArgOutSafeArrayPointerValid(aHardDisks);
1001
1002 AutoCaller autoCaller(this);
1003 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1004
1005 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1006 SafeIfaceArray<IMedium> hardDisks(m->allHardDisks.getList());
1007 hardDisks.detachTo(ComSafeArrayOutArg(aHardDisks));
1008
1009 return S_OK;
1010}
1011
1012STDMETHODIMP VirtualBox::COMGETTER(DVDImages)(ComSafeArrayOut(IMedium *, aDVDImages))
1013{
1014 CheckComArgOutSafeArrayPointerValid(aDVDImages);
1015
1016 AutoCaller autoCaller(this);
1017 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1018
1019 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1020 SafeIfaceArray<IMedium> images(m->allDVDImages.getList());
1021 images.detachTo(ComSafeArrayOutArg(aDVDImages));
1022
1023 return S_OK;
1024}
1025
1026STDMETHODIMP VirtualBox::COMGETTER(FloppyImages)(ComSafeArrayOut(IMedium *, aFloppyImages))
1027{
1028 CheckComArgOutSafeArrayPointerValid(aFloppyImages);
1029
1030 AutoCaller autoCaller(this);
1031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1032
1033 AutoReadLock al(m->allFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1034 SafeIfaceArray<IMedium> images(m->allFloppyImages.getList());
1035 images.detachTo(ComSafeArrayOutArg(aFloppyImages));
1036
1037 return S_OK;
1038}
1039
1040STDMETHODIMP VirtualBox::COMGETTER(ProgressOperations)(ComSafeArrayOut(IProgress *, aOperations))
1041{
1042 CheckComArgOutPointerValid(aOperations);
1043
1044 AutoCaller autoCaller(this);
1045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1046
1047 /* protect mProgressOperations */
1048 AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1049 SafeIfaceArray<IProgress> progress(m->mapProgressOperations);
1050 progress.detachTo(ComSafeArrayOutArg(aOperations));
1051
1052 return S_OK;
1053}
1054
1055STDMETHODIMP VirtualBox::COMGETTER(GuestOSTypes)(ComSafeArrayOut(IGuestOSType *, aGuestOSTypes))
1056{
1057 CheckComArgOutSafeArrayPointerValid(aGuestOSTypes);
1058
1059 AutoCaller autoCaller(this);
1060 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1061
1062 AutoReadLock al(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1063 SafeIfaceArray<IGuestOSType> ostypes(m->allGuestOSTypes.getList());
1064 ostypes.detachTo(ComSafeArrayOutArg(aGuestOSTypes));
1065
1066 return S_OK;
1067}
1068
1069STDMETHODIMP VirtualBox::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
1070{
1071#ifndef RT_OS_WINDOWS
1072 NOREF(aSharedFoldersSize);
1073#endif /* RT_OS_WINDOWS */
1074
1075 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
1076
1077 AutoCaller autoCaller(this);
1078 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1079
1080 return setError(E_NOTIMPL, "Not yet implemented");
1081}
1082
1083STDMETHODIMP
1084VirtualBox::COMGETTER(PerformanceCollector)(IPerformanceCollector **aPerformanceCollector)
1085{
1086#ifdef VBOX_WITH_RESOURCE_USAGE_API
1087 CheckComArgOutPointerValid(aPerformanceCollector);
1088
1089 AutoCaller autoCaller(this);
1090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1091
1092 /* mPerformanceCollector is const, no need to lock */
1093 m->pPerformanceCollector.queryInterfaceTo(aPerformanceCollector);
1094
1095 return S_OK;
1096#else /* !VBOX_WITH_RESOURCE_USAGE_API */
1097 NOREF(aPerformanceCollector);
1098 ReturnComNotImplemented();
1099#endif /* !VBOX_WITH_RESOURCE_USAGE_API */
1100}
1101
1102STDMETHODIMP
1103VirtualBox::COMGETTER(DHCPServers)(ComSafeArrayOut(IDHCPServer *, aDHCPServers))
1104{
1105 CheckComArgOutSafeArrayPointerValid(aDHCPServers);
1106
1107 AutoCaller autoCaller(this);
1108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1109
1110 AutoReadLock al(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1111 SafeIfaceArray<IDHCPServer> svrs(m->allDHCPServers.getList());
1112 svrs.detachTo(ComSafeArrayOutArg(aDHCPServers));
1113
1114 return S_OK;
1115}
1116
1117
1118STDMETHODIMP
1119VirtualBox::COMGETTER(NATNetworks)(ComSafeArrayOut(INATNetwork *, aNATNetworks))
1120{
1121#ifdef VBOX_WITH_NAT_SERVICE
1122 CheckComArgOutSafeArrayPointerValid(aNATNetworks);
1123
1124 AutoCaller autoCaller(this);
1125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1126
1127 AutoReadLock al(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1128 SafeIfaceArray<INATNetwork> nets(m->allNATNetworks.getList());
1129 nets.detachTo(ComSafeArrayOutArg(aNATNetworks));
1130
1131 return S_OK;
1132#else
1133 NOREF(aNATNetworks);
1134# ifndef RT_OS_WINDOWS
1135 NOREF(aNATNetworksSize);
1136# endif
1137 return E_NOTIMPL;
1138#endif
1139}
1140
1141
1142STDMETHODIMP
1143VirtualBox::COMGETTER(EventSource)(IEventSource ** aEventSource)
1144{
1145 CheckComArgOutPointerValid(aEventSource);
1146
1147 AutoCaller autoCaller(this);
1148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1149
1150 /* event source is const, no need to lock */
1151 m->pEventSource.queryInterfaceTo(aEventSource);
1152
1153 return S_OK;
1154}
1155
1156STDMETHODIMP
1157VirtualBox::COMGETTER(ExtensionPackManager)(IExtPackManager **aExtPackManager)
1158{
1159 CheckComArgOutPointerValid(aExtPackManager);
1160
1161 AutoCaller autoCaller(this);
1162 HRESULT hrc = autoCaller.rc();
1163 if (SUCCEEDED(hrc))
1164 {
1165#ifdef VBOX_WITH_EXTPACK
1166 /* The extension pack manager is const, no need to lock. */
1167 hrc = m->ptrExtPackManager.queryInterfaceTo(aExtPackManager);
1168#else
1169 hrc = E_NOTIMPL;
1170#endif
1171 }
1172
1173 return hrc;
1174}
1175
1176STDMETHODIMP VirtualBox::COMGETTER(InternalNetworks)(ComSafeArrayOut(BSTR, aInternalNetworks))
1177{
1178 CheckComArgOutSafeArrayPointerValid(aInternalNetworks);
1179
1180 AutoCaller autoCaller(this);
1181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1182
1183 std::list<Bstr> allInternalNetworks;
1184
1185 /* get copy of all machine references, to avoid holding the list lock */
1186 MachinesOList::MyList allMachines;
1187 {
1188 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1189 allMachines = m->allMachines.getList();
1190 }
1191 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1192 it != allMachines.end();
1193 ++it)
1194 {
1195 const ComObjPtr<Machine> &pMachine = *it;
1196 AutoCaller autoMachineCaller(pMachine);
1197 if (FAILED(autoMachineCaller.rc()))
1198 continue;
1199 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1200
1201 if (pMachine->isAccessible())
1202 {
1203 uint32_t cNetworkAdapters = Global::getMaxNetworkAdapters(pMachine->getChipsetType());
1204 for (ULONG i = 0; i < cNetworkAdapters; i++)
1205 {
1206 ComPtr<INetworkAdapter> pNet;
1207 HRESULT rc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
1208 if (FAILED(rc) || pNet.isNull())
1209 continue;
1210 Bstr strInternalNetwork;
1211 rc = pNet->COMGETTER(InternalNetwork)(strInternalNetwork.asOutParam());
1212 if (FAILED(rc) || strInternalNetwork.isEmpty())
1213 continue;
1214
1215 allInternalNetworks.push_back(strInternalNetwork);
1216 }
1217 }
1218 }
1219
1220 /* throw out any duplicates */
1221 allInternalNetworks.sort();
1222 allInternalNetworks.unique();
1223 com::SafeArray<BSTR> internalNetworks(allInternalNetworks.size());
1224 size_t i = 0;
1225 for (std::list<Bstr>::const_iterator it = allInternalNetworks.begin();
1226 it != allInternalNetworks.end();
1227 ++it, i++)
1228 {
1229 const Bstr &tmp = *it;
1230 tmp.cloneTo(&internalNetworks[i]);
1231 }
1232 internalNetworks.detachTo(ComSafeArrayOutArg(aInternalNetworks));
1233
1234 return S_OK;
1235}
1236
1237STDMETHODIMP VirtualBox::COMGETTER(GenericNetworkDrivers)(ComSafeArrayOut(BSTR, aGenericNetworkDrivers))
1238{
1239 CheckComArgOutSafeArrayPointerValid(aGenericNetworkDrivers);
1240
1241 AutoCaller autoCaller(this);
1242 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1243
1244 std::list<Bstr> allGenericNetworkDrivers;
1245
1246 /* get copy of all machine references, to avoid holding the list lock */
1247 MachinesOList::MyList allMachines;
1248 {
1249 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1250 allMachines = m->allMachines.getList();
1251 }
1252 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1253 it != allMachines.end();
1254 ++it)
1255 {
1256 const ComObjPtr<Machine> &pMachine = *it;
1257 AutoCaller autoMachineCaller(pMachine);
1258 if (FAILED(autoMachineCaller.rc()))
1259 continue;
1260 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1261
1262 if (pMachine->isAccessible())
1263 {
1264 uint32_t cNetworkAdapters = Global::getMaxNetworkAdapters(pMachine->getChipsetType());
1265 for (ULONG i = 0; i < cNetworkAdapters; i++)
1266 {
1267 ComPtr<INetworkAdapter> pNet;
1268 HRESULT rc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
1269 if (FAILED(rc) || pNet.isNull())
1270 continue;
1271 Bstr strGenericNetworkDriver;
1272 rc = pNet->COMGETTER(GenericDriver)(strGenericNetworkDriver.asOutParam());
1273 if (FAILED(rc) || strGenericNetworkDriver.isEmpty())
1274 continue;
1275
1276 allGenericNetworkDrivers.push_back(strGenericNetworkDriver);
1277 }
1278 }
1279 }
1280
1281 /* throw out any duplicates */
1282 allGenericNetworkDrivers.sort();
1283 allGenericNetworkDrivers.unique();
1284 com::SafeArray<BSTR> genericNetworks(allGenericNetworkDrivers.size());
1285 size_t i = 0;
1286 for (std::list<Bstr>::const_iterator it = allGenericNetworkDrivers.begin();
1287 it != allGenericNetworkDrivers.end();
1288 ++it, i++)
1289 {
1290 const Bstr &tmp = *it;
1291 tmp.cloneTo(&genericNetworks[i]);
1292 }
1293 genericNetworks.detachTo(ComSafeArrayOutArg(aGenericNetworkDrivers));
1294
1295 return S_OK;
1296}
1297
1298STDMETHODIMP
1299VirtualBox::CheckFirmwarePresent(FirmwareType_T aFirmwareType,
1300 IN_BSTR aVersion,
1301 BSTR *aUrl,
1302 BSTR *aFile,
1303 BOOL *aResult)
1304{
1305 CheckComArgNotNull(aResult);
1306
1307 AutoCaller autoCaller(this);
1308 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1309
1310 NOREF(aVersion);
1311
1312 static const struct
1313 {
1314 FirmwareType_T type;
1315 const char* fileName;
1316 const char* url;
1317 }
1318 firmwareDesc[] =
1319 {
1320 {
1321 /* compiled-in firmware */
1322 FirmwareType_BIOS, NULL, NULL
1323 },
1324 {
1325 FirmwareType_EFI32, "VBoxEFI32.fd", "http://virtualbox.org/firmware/VBoxEFI32.fd"
1326 },
1327 {
1328 FirmwareType_EFI64, "VBoxEFI64.fd", "http://virtualbox.org/firmware/VBoxEFI64.fd"
1329 },
1330 {
1331 FirmwareType_EFIDUAL, "VBoxEFIDual.fd", "http://virtualbox.org/firmware/VBoxEFIDual.fd"
1332 }
1333 };
1334
1335 for (size_t i = 0; i < sizeof(firmwareDesc) / sizeof(firmwareDesc[0]); i++)
1336 {
1337 if (aFirmwareType != firmwareDesc[i].type)
1338 continue;
1339
1340 /* compiled-in firmware */
1341 if (firmwareDesc[i].fileName == NULL)
1342 {
1343 *aResult = TRUE;
1344 break;
1345 }
1346
1347 Utf8Str shortName, fullName;
1348
1349 shortName = Utf8StrFmt("Firmware%c%s",
1350 RTPATH_DELIMITER,
1351 firmwareDesc[i].fileName);
1352 int rc = calculateFullPath(shortName, fullName);
1353 AssertRCReturn(rc, rc);
1354 if (RTFileExists(fullName.c_str()))
1355 {
1356 *aResult = TRUE;
1357 if (aFile)
1358 Utf8Str(fullName).cloneTo(aFile);
1359 break;
1360 }
1361
1362 char pszVBoxPath[RTPATH_MAX];
1363 rc = RTPathExecDir(pszVBoxPath, RTPATH_MAX);
1364 AssertRCReturn(rc, rc);
1365 fullName = Utf8StrFmt("%s%c%s",
1366 pszVBoxPath,
1367 RTPATH_DELIMITER,
1368 firmwareDesc[i].fileName);
1369 if (RTFileExists(fullName.c_str()))
1370 {
1371 *aResult = TRUE;
1372 if (aFile)
1373 Utf8Str(fullName).cloneTo(aFile);
1374 break;
1375 }
1376
1377 /** @todo: account for version in the URL */
1378 if (aUrl != NULL)
1379 {
1380 Utf8Str strUrl(firmwareDesc[i].url);
1381 strUrl.cloneTo(aUrl);
1382 }
1383 *aResult = FALSE;
1384
1385 /* Assume single record per firmware type */
1386 break;
1387 }
1388
1389 return S_OK;
1390}
1391// IVirtualBox methods
1392/////////////////////////////////////////////////////////////////////////////
1393
1394/* Helper for VirtualBox::ComposeMachineFilename */
1395static void sanitiseMachineFilename(Utf8Str &aName);
1396
1397STDMETHODIMP VirtualBox::ComposeMachineFilename(IN_BSTR aName,
1398 IN_BSTR aGroup,
1399 IN_BSTR aCreateFlags,
1400 IN_BSTR aBaseFolder,
1401 BSTR *aFilename)
1402{
1403 LogFlowThisFuncEnter();
1404 LogFlowThisFunc(("aName=\"%ls\",aBaseFolder=\"%ls\"\n", aName, aBaseFolder));
1405
1406 CheckComArgStrNotEmptyOrNull(aName);
1407 CheckComArgOutPointerValid(aFilename);
1408
1409 AutoCaller autoCaller(this);
1410 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1411
1412 Utf8Str strCreateFlags(aCreateFlags);
1413 Guid id;
1414 bool fDirectoryIncludesUUID = false;
1415 if (!strCreateFlags.isEmpty())
1416 {
1417 const char *pcszNext = strCreateFlags.c_str();
1418 while (*pcszNext != '\0')
1419 {
1420 Utf8Str strFlag;
1421 const char *pcszComma = RTStrStr(pcszNext, ",");
1422 if (!pcszComma)
1423 strFlag = pcszNext;
1424 else
1425 strFlag = Utf8Str(pcszNext, pcszComma - pcszNext);
1426
1427 const char *pcszEqual = RTStrStr(strFlag.c_str(), "=");
1428 /* skip over everything which doesn't contain '=' */
1429 if (pcszEqual && pcszEqual != strFlag.c_str())
1430 {
1431 Utf8Str strKey(strFlag.c_str(), pcszEqual - strFlag.c_str());
1432 Utf8Str strValue(strFlag.c_str() + (pcszEqual - strFlag.c_str() + 1));
1433
1434 if (strKey == "UUID")
1435 id = strValue.c_str();
1436 else if (strKey == "directoryIncludesUUID")
1437 fDirectoryIncludesUUID = (strValue == "1");
1438 }
1439
1440 if (!pcszComma)
1441 pcszNext += strFlag.length();
1442 else
1443 pcszNext += strFlag.length() + 1;
1444 }
1445 }
1446
1447 if (id.isZero())
1448 fDirectoryIncludesUUID = false;
1449 else if (!id.isValid())
1450 {
1451 /* do something else */
1452 return setError(E_INVALIDARG,
1453 tr("'%ls' is not a valid Guid"),
1454 id.toStringCurly().c_str());
1455 }
1456
1457 Utf8Str strGroup(aGroup);
1458 if (strGroup.isEmpty())
1459 strGroup = "/";
1460 HRESULT rc = validateMachineGroup(strGroup, true);
1461 if (FAILED(rc))
1462 return rc;
1463
1464 /* Compose the settings file name using the following scheme:
1465 *
1466 * <base_folder><group>/<machine_name>/<machine_name>.xml
1467 *
1468 * If a non-null and non-empty base folder is specified, the default
1469 * machine folder will be used as a base folder.
1470 * We sanitise the machine name to a safe white list of characters before
1471 * using it.
1472 */
1473 Utf8Str strBase = aBaseFolder;
1474 Utf8Str strName = aName;
1475 Utf8Str strDirName(strName);
1476 if (fDirectoryIncludesUUID)
1477 strDirName += Utf8StrFmt(" (%RTuuid)", id.raw());
1478 sanitiseMachineFilename(strName);
1479 sanitiseMachineFilename(strDirName);
1480
1481 if (strBase.isEmpty())
1482 /* we use the non-full folder value below to keep the path relative */
1483 getDefaultMachineFolder(strBase);
1484
1485 calculateFullPath(strBase, strBase);
1486
1487 /* eliminate toplevel group to avoid // in the result */
1488 if (strGroup == "/")
1489 strGroup.setNull();
1490 Bstr bstrSettingsFile = BstrFmt("%s%s%c%s%c%s.vbox",
1491 strBase.c_str(),
1492 strGroup.c_str(),
1493 RTPATH_DELIMITER,
1494 strDirName.c_str(),
1495 RTPATH_DELIMITER,
1496 strName.c_str());
1497
1498 bstrSettingsFile.detachTo(aFilename);
1499
1500 return S_OK;
1501}
1502
1503/**
1504 * Remove characters from a machine file name which can be problematic on
1505 * particular systems.
1506 * @param strName The file name to sanitise.
1507 */
1508void sanitiseMachineFilename(Utf8Str &strName)
1509{
1510 /** Set of characters which should be safe for use in filenames: some basic
1511 * ASCII, Unicode from Latin-1 alphabetic to the end of Hangul. We try to
1512 * skip anything that could count as a control character in Windows or
1513 * *nix, or be otherwise difficult for shells to handle (I would have
1514 * preferred to remove the space and brackets too). We also remove all
1515 * characters which need UTF-16 surrogate pairs for Windows's benefit. */
1516 RTUNICP aCpSet[] =
1517 { ' ', ' ', '(', ')', '-', '.', '0', '9', 'A', 'Z', 'a', 'z', '_', '_',
1518 0xa0, 0xd7af, '\0' };
1519 char *pszName = strName.mutableRaw();
1520 int cReplacements = RTStrPurgeComplementSet(pszName, aCpSet, '_');
1521 Assert(cReplacements >= 0);
1522 NOREF(cReplacements);
1523 /* No leading dot or dash. */
1524 if (pszName[0] == '.' || pszName[0] == '-')
1525 pszName[0] = '_';
1526 /* No trailing dot. */
1527 if (pszName[strName.length() - 1] == '.')
1528 pszName[strName.length() - 1] = '_';
1529 /* Mangle leading and trailing spaces. */
1530 for (size_t i = 0; pszName[i] == ' '; ++i)
1531 pszName[i] = '_';
1532 for (size_t i = strName.length() - 1; i && pszName[i] == ' '; --i)
1533 pszName[i] = '_';
1534}
1535
1536#ifdef DEBUG
1537/** Simple unit test/operation examples for sanitiseMachineFilename(). */
1538static unsigned testSanitiseMachineFilename(void (*pfnPrintf)(const char *, ...))
1539{
1540 unsigned cErrors = 0;
1541
1542 /** Expected results of sanitising given file names. */
1543 static struct
1544 {
1545 /** The test file name to be sanitised (Utf-8). */
1546 const char *pcszIn;
1547 /** The expected sanitised output (Utf-8). */
1548 const char *pcszOutExpected;
1549 } aTest[] =
1550 {
1551 { "OS/2 2.1", "OS_2 2.1" },
1552 { "-!My VM!-", "__My VM_-" },
1553 { "\xF0\x90\x8C\xB0", "____" },
1554 { " My VM ", "__My VM__" },
1555 { ".My VM.", "_My VM_" },
1556 { "My VM", "My VM" }
1557 };
1558 for (unsigned i = 0; i < RT_ELEMENTS(aTest); ++i)
1559 {
1560 Utf8Str str(aTest[i].pcszIn);
1561 sanitiseMachineFilename(str);
1562 if (str.compare(aTest[i].pcszOutExpected))
1563 {
1564 ++cErrors;
1565 pfnPrintf("%s: line %d, expected %s, actual %s\n",
1566 __PRETTY_FUNCTION__, i, aTest[i].pcszOutExpected,
1567 str.c_str());
1568 }
1569 }
1570 return cErrors;
1571}
1572
1573/** @todo Proper testcase. */
1574/** @todo Do we have a better method of doing init functions? */
1575namespace
1576{
1577 class TestSanitiseMachineFilename
1578 {
1579 public:
1580 TestSanitiseMachineFilename(void)
1581 {
1582 Assert(!testSanitiseMachineFilename(RTAssertMsg2));
1583 }
1584 };
1585 TestSanitiseMachineFilename s_TestSanitiseMachineFilename;
1586}
1587#endif
1588
1589/** @note Locks mSystemProperties object for reading. */
1590STDMETHODIMP VirtualBox::CreateMachine(IN_BSTR aSettingsFile,
1591 IN_BSTR aName,
1592 ComSafeArrayIn(IN_BSTR, aGroups),
1593 IN_BSTR aOsTypeId,
1594 IN_BSTR aCreateFlags,
1595 IMachine **aMachine)
1596{
1597 LogFlowThisFuncEnter();
1598 LogFlowThisFunc(("aSettingsFile=\"%ls\", aName=\"%ls\", aOsTypeId =\"%ls\", aCreateFlags=\"%ls\"\n", aSettingsFile, aName, aOsTypeId, aCreateFlags));
1599
1600 CheckComArgStrNotEmptyOrNull(aName);
1601 /** @todo tighten checks on aId? */
1602 CheckComArgOutPointerValid(aMachine);
1603
1604 AutoCaller autoCaller(this);
1605 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1606
1607 StringsList llGroups;
1608 HRESULT rc = convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1609 if (FAILED(rc))
1610 return rc;
1611
1612 Utf8Str strCreateFlags(aCreateFlags);
1613 Guid id;
1614 bool fForceOverwrite = false;
1615 bool fDirectoryIncludesUUID = false;
1616 if (!strCreateFlags.isEmpty())
1617 {
1618 const char *pcszNext = strCreateFlags.c_str();
1619 while (*pcszNext != '\0')
1620 {
1621 Utf8Str strFlag;
1622 const char *pcszComma = RTStrStr(pcszNext, ",");
1623 if (!pcszComma)
1624 strFlag = pcszNext;
1625 else
1626 strFlag = Utf8Str(pcszNext, pcszComma - pcszNext);
1627
1628 const char *pcszEqual = RTStrStr(strFlag.c_str(), "=");
1629 /* skip over everything which doesn't contain '=' */
1630 if (pcszEqual && pcszEqual != strFlag.c_str())
1631 {
1632 Utf8Str strKey(strFlag.c_str(), pcszEqual - strFlag.c_str());
1633 Utf8Str strValue(strFlag.c_str() + (pcszEqual - strFlag.c_str() + 1));
1634
1635 if (strKey == "UUID")
1636 id = strValue.c_str();
1637 else if (strKey == "forceOverwrite")
1638 fForceOverwrite = (strValue == "1");
1639 else if (strKey == "directoryIncludesUUID")
1640 fDirectoryIncludesUUID = (strValue == "1");
1641 }
1642
1643 if (!pcszComma)
1644 pcszNext += strFlag.length();
1645 else
1646 pcszNext += strFlag.length() + 1;
1647 }
1648 }
1649 /* Create UUID if none was specified. */
1650 if (id.isZero())
1651 id.create();
1652 else if (!id.isValid())
1653 {
1654 /* do something else */
1655 return setError(E_INVALIDARG,
1656 tr("'%ls' is not a valid Guid"),
1657 id.toStringCurly().c_str());
1658 }
1659
1660 /* NULL settings file means compose automatically */
1661 Bstr bstrSettingsFile(aSettingsFile);
1662 if (bstrSettingsFile.isEmpty())
1663 {
1664 Utf8Str strNewCreateFlags(Utf8StrFmt("UUID=%RTuuid", id.raw()));
1665 if (fDirectoryIncludesUUID)
1666 strNewCreateFlags += ",directoryIncludesUUID=1";
1667
1668 rc = ComposeMachineFilename(aName,
1669 Bstr(llGroups.front()).raw(),
1670 Bstr(strNewCreateFlags).raw(),
1671 NULL /* aBaseFolder */,
1672 bstrSettingsFile.asOutParam());
1673 if (FAILED(rc)) return rc;
1674 }
1675
1676 /* create a new object */
1677 ComObjPtr<Machine> machine;
1678 rc = machine.createObject();
1679 if (FAILED(rc)) return rc;
1680
1681 GuestOSType *osType = NULL;
1682 rc = findGuestOSType(Bstr(aOsTypeId), osType);
1683 if (FAILED(rc)) return rc;
1684
1685 /* initialize the machine object */
1686 rc = machine->init(this,
1687 Utf8Str(bstrSettingsFile),
1688 Utf8Str(aName),
1689 llGroups,
1690 osType,
1691 id,
1692 fForceOverwrite,
1693 fDirectoryIncludesUUID);
1694 if (SUCCEEDED(rc))
1695 {
1696 /* set the return value */
1697 rc = machine.queryInterfaceTo(aMachine);
1698 AssertComRC(rc);
1699
1700#ifdef VBOX_WITH_EXTPACK
1701 /* call the extension pack hooks */
1702 m->ptrExtPackManager->callAllVmCreatedHooks(machine);
1703#endif
1704 }
1705
1706 LogFlowThisFuncLeave();
1707
1708 return rc;
1709}
1710
1711STDMETHODIMP VirtualBox::OpenMachine(IN_BSTR aSettingsFile,
1712 IMachine **aMachine)
1713{
1714 CheckComArgStrNotEmptyOrNull(aSettingsFile);
1715 CheckComArgOutPointerValid(aMachine);
1716
1717 AutoCaller autoCaller(this);
1718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1719
1720 HRESULT rc = E_FAIL;
1721
1722 /* create a new object */
1723 ComObjPtr<Machine> machine;
1724 rc = machine.createObject();
1725 if (SUCCEEDED(rc))
1726 {
1727 /* initialize the machine object */
1728 rc = machine->initFromSettings(this,
1729 aSettingsFile,
1730 NULL); /* const Guid *aId */
1731 if (SUCCEEDED(rc))
1732 {
1733 /* set the return value */
1734 rc = machine.queryInterfaceTo(aMachine);
1735 ComAssertComRC(rc);
1736 }
1737 }
1738
1739 return rc;
1740}
1741
1742/** @note Locks objects! */
1743STDMETHODIMP VirtualBox::RegisterMachine(IMachine *aMachine)
1744{
1745 CheckComArgNotNull(aMachine);
1746
1747 AutoCaller autoCaller(this);
1748 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1749
1750 HRESULT rc;
1751
1752 Bstr name;
1753 rc = aMachine->COMGETTER(Name)(name.asOutParam());
1754 if (FAILED(rc)) return rc;
1755
1756 /* We can safely cast child to Machine * here because only Machine
1757 * implementations of IMachine can be among our children. */
1758 Machine *pMachine = static_cast<Machine*>(aMachine);
1759
1760 AutoCaller machCaller(pMachine);
1761 ComAssertComRCRetRC(machCaller.rc());
1762
1763 rc = registerMachine(pMachine);
1764 /* fire an event */
1765 if (SUCCEEDED(rc))
1766 onMachineRegistered(pMachine->getId(), TRUE);
1767
1768 return rc;
1769}
1770
1771/** @note Locks this object for reading, then some machine objects for reading. */
1772STDMETHODIMP VirtualBox::FindMachine(IN_BSTR aNameOrId, IMachine **aMachine)
1773{
1774 LogFlowThisFuncEnter();
1775 LogFlowThisFunc(("aName=\"%ls\", aMachine={%p}\n", aNameOrId, aMachine));
1776
1777 CheckComArgStrNotEmptyOrNull(aNameOrId);
1778 CheckComArgOutPointerValid(aMachine);
1779
1780 AutoCaller autoCaller(this);
1781 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1782
1783 /* start with not found */
1784 HRESULT rc = S_OK;
1785 ComObjPtr<Machine> pMachineFound;
1786
1787 Guid id(aNameOrId);
1788 if (id.isValid() && !id.isZero())
1789
1790 rc = findMachine(id,
1791 true /* fPermitInaccessible */,
1792 true /* setError */,
1793 &pMachineFound);
1794 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
1795 else
1796 {
1797 Utf8Str strName(aNameOrId);
1798 rc = findMachineByName(aNameOrId,
1799 true /* setError */,
1800 &pMachineFound);
1801 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
1802 }
1803
1804 /* this will set (*machine) to NULL if machineObj is null */
1805 pMachineFound.queryInterfaceTo(aMachine);
1806
1807 LogFlowThisFunc(("aName=\"%ls\", aMachine=%p, rc=%08X\n", aNameOrId, *aMachine, rc));
1808 LogFlowThisFuncLeave();
1809
1810 return rc;
1811}
1812
1813STDMETHODIMP VirtualBox::GetMachinesByGroups(ComSafeArrayIn(IN_BSTR, aGroups), ComSafeArrayOut(IMachine *, aMachines))
1814{
1815 CheckComArgSafeArrayNotNull(aGroups);
1816 CheckComArgOutSafeArrayPointerValid(aMachines);
1817
1818 AutoCaller autoCaller(this);
1819 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1820
1821 StringsList llGroups;
1822 HRESULT rc = convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1823 if (FAILED(rc))
1824 return rc;
1825 /* we want to rely on sorted groups during compare, to save time */
1826 llGroups.sort();
1827
1828 /* get copy of all machine references, to avoid holding the list lock */
1829 MachinesOList::MyList allMachines;
1830 {
1831 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1832 allMachines = m->allMachines.getList();
1833 }
1834
1835 com::SafeIfaceArray<IMachine> saMachines;
1836 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1837 it != allMachines.end();
1838 ++it)
1839 {
1840 const ComObjPtr<Machine> &pMachine = *it;
1841 AutoCaller autoMachineCaller(pMachine);
1842 if (FAILED(autoMachineCaller.rc()))
1843 continue;
1844 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1845
1846 if (pMachine->isAccessible())
1847 {
1848 const StringsList &thisGroups = pMachine->getGroups();
1849 for (StringsList::const_iterator it2 = thisGroups.begin();
1850 it2 != thisGroups.end();
1851 ++it2)
1852 {
1853 const Utf8Str &group = *it2;
1854 bool fAppended = false;
1855 for (StringsList::const_iterator it3 = llGroups.begin();
1856 it3 != llGroups.end();
1857 ++it3)
1858 {
1859 int order = it3->compare(group);
1860 if (order == 0)
1861 {
1862 saMachines.push_back(pMachine);
1863 fAppended = true;
1864 break;
1865 }
1866 else if (order > 0)
1867 break;
1868 else
1869 continue;
1870 }
1871 /* avoid duplicates and save time */
1872 if (fAppended)
1873 break;
1874 }
1875 }
1876 }
1877
1878 saMachines.detachTo(ComSafeArrayOutArg(aMachines));
1879
1880 return S_OK;
1881}
1882
1883STDMETHODIMP VirtualBox::GetMachineStates(ComSafeArrayIn(IMachine *, aMachines), ComSafeArrayOut(MachineState_T, aStates))
1884{
1885 CheckComArgSafeArrayNotNull(aMachines);
1886 CheckComArgOutSafeArrayPointerValid(aStates);
1887
1888 com::SafeIfaceArray<IMachine> saMachines(ComSafeArrayInArg(aMachines));
1889 com::SafeArray<MachineState_T> saStates(saMachines.size());
1890 for (size_t i = 0; i < saMachines.size(); i++)
1891 {
1892 ComPtr<IMachine> pMachine = saMachines[i];
1893 MachineState_T state = MachineState_Null;
1894 if (!pMachine.isNull())
1895 {
1896 HRESULT rc = pMachine->COMGETTER(State)(&state);
1897 if (rc == E_ACCESSDENIED)
1898 rc = S_OK;
1899 AssertComRC(rc);
1900 }
1901 saStates[i] = state;
1902 }
1903 saStates.detachTo(ComSafeArrayOutArg(aStates));
1904
1905 return S_OK;
1906}
1907
1908STDMETHODIMP VirtualBox::CreateHardDisk(IN_BSTR aFormat,
1909 IN_BSTR aLocation,
1910 IMedium **aHardDisk)
1911{
1912 CheckComArgOutPointerValid(aHardDisk);
1913
1914 AutoCaller autoCaller(this);
1915 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1916
1917 /* we don't access non-const data members so no need to lock */
1918
1919 Utf8Str format(aFormat);
1920 if (format.isEmpty())
1921 getDefaultHardDiskFormat(format);
1922
1923 ComObjPtr<Medium> hardDisk;
1924 hardDisk.createObject();
1925 HRESULT rc = hardDisk->init(this,
1926 format,
1927 aLocation,
1928 Guid::Empty /* media registry: none yet */);
1929
1930 if (SUCCEEDED(rc))
1931 hardDisk.queryInterfaceTo(aHardDisk);
1932
1933 return rc;
1934}
1935
1936STDMETHODIMP VirtualBox::OpenMedium(IN_BSTR aLocation,
1937 DeviceType_T deviceType,
1938 AccessMode_T accessMode,
1939 BOOL fForceNewUuid,
1940 IMedium **aMedium)
1941{
1942 HRESULT rc = S_OK;
1943 CheckComArgStrNotEmptyOrNull(aLocation);
1944 CheckComArgOutPointerValid(aMedium);
1945
1946 AutoCaller autoCaller(this);
1947 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1948
1949 Guid id(aLocation);
1950 ComObjPtr<Medium> pMedium;
1951
1952 // have to get write lock as the whole find/update sequence must be done
1953 // in one critical section, otherwise there are races which can lead to
1954 // multiple Medium objects with the same content
1955 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1956
1957 // check if the device type is correct, and see if a medium for the
1958 // given path has already initialized; if so, return that
1959 switch (deviceType)
1960 {
1961 case DeviceType_HardDisk:
1962 if (id.isValid() && !id.isZero())
1963 rc = findHardDiskById(id, false /* setError */, &pMedium);
1964 else
1965 rc = findHardDiskByLocation(aLocation,
1966 false, /* aSetError */
1967 &pMedium);
1968 break;
1969
1970 case DeviceType_Floppy:
1971 case DeviceType_DVD:
1972 if (id.isValid() && !id.isZero())
1973 rc = findDVDOrFloppyImage(deviceType, &id, Utf8Str::Empty,
1974 false /* setError */, &pMedium);
1975 else
1976 rc = findDVDOrFloppyImage(deviceType, NULL, aLocation,
1977 false /* setError */, &pMedium);
1978
1979 // enforce read-only for DVDs even if caller specified ReadWrite
1980 if (deviceType == DeviceType_DVD)
1981 accessMode = AccessMode_ReadOnly;
1982 break;
1983
1984 default:
1985 return setError(E_INVALIDARG, "Device type must be HardDisk, DVD or Floppy %d", deviceType);
1986 }
1987
1988 if (pMedium.isNull())
1989 {
1990 pMedium.createObject();
1991 treeLock.release();
1992 rc = pMedium->init(this,
1993 aLocation,
1994 (accessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
1995 !!fForceNewUuid,
1996 deviceType);
1997 treeLock.acquire();
1998
1999 if (SUCCEEDED(rc))
2000 {
2001 rc = registerMedium(pMedium, &pMedium, deviceType);
2002
2003 treeLock.release();
2004
2005 /* Note that it's important to call uninit() on failure to register
2006 * because the differencing hard disk would have been already associated
2007 * with the parent and this association needs to be broken. */
2008
2009 if (FAILED(rc))
2010 {
2011 pMedium->uninit();
2012 rc = VBOX_E_OBJECT_NOT_FOUND;
2013 }
2014 }
2015 else
2016 rc = VBOX_E_OBJECT_NOT_FOUND;
2017 }
2018
2019 if (SUCCEEDED(rc))
2020 pMedium.queryInterfaceTo(aMedium);
2021
2022 return rc;
2023}
2024
2025
2026/** @note Locks this object for reading. */
2027STDMETHODIMP VirtualBox::GetGuestOSType(IN_BSTR aId, IGuestOSType **aType)
2028{
2029 CheckComArgNotNull(aType);
2030
2031 AutoCaller autoCaller(this);
2032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2033
2034 *aType = NULL;
2035
2036 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2037 for (GuestOSTypesOList::iterator it = m->allGuestOSTypes.begin();
2038 it != m->allGuestOSTypes.end();
2039 ++it)
2040 {
2041 const Bstr &typeId = (*it)->id();
2042 AssertMsg(!typeId.isEmpty(), ("ID must not be NULL"));
2043 if (typeId.compare(aId, Bstr::CaseInsensitive) == 0)
2044 {
2045 (*it).queryInterfaceTo(aType);
2046 break;
2047 }
2048 }
2049
2050 return (*aType) ? S_OK :
2051 setError(E_INVALIDARG,
2052 tr("'%ls' is not a valid Guest OS type"),
2053 aId);
2054}
2055
2056STDMETHODIMP VirtualBox::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath,
2057 BOOL /* aWritable */, BOOL /* aAutoMount */)
2058{
2059 CheckComArgStrNotEmptyOrNull(aName);
2060 CheckComArgStrNotEmptyOrNull(aHostPath);
2061
2062 AutoCaller autoCaller(this);
2063 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2064
2065 return setError(E_NOTIMPL, "Not yet implemented");
2066}
2067
2068STDMETHODIMP VirtualBox::RemoveSharedFolder(IN_BSTR aName)
2069{
2070 CheckComArgStrNotEmptyOrNull(aName);
2071
2072 AutoCaller autoCaller(this);
2073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2074
2075 return setError(E_NOTIMPL, "Not yet implemented");
2076}
2077
2078/**
2079 * @note Locks this object for reading.
2080 */
2081STDMETHODIMP VirtualBox::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
2082{
2083 using namespace settings;
2084
2085 CheckComArgOutSafeArrayPointerValid(aKeys);
2086
2087 AutoCaller autoCaller(this);
2088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2089
2090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2091
2092 com::SafeArray<BSTR> saKeys(m->pMainConfigFile->mapExtraDataItems.size());
2093 int i = 0;
2094 for (StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin();
2095 it != m->pMainConfigFile->mapExtraDataItems.end();
2096 ++it, ++i)
2097 {
2098 const Utf8Str &strName = it->first; // the key
2099 strName.cloneTo(&saKeys[i]);
2100 }
2101 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
2102
2103 return S_OK;
2104}
2105
2106/**
2107 * @note Locks this object for reading.
2108 */
2109STDMETHODIMP VirtualBox::GetExtraData(IN_BSTR aKey,
2110 BSTR *aValue)
2111{
2112 CheckComArgStrNotEmptyOrNull(aKey);
2113 CheckComArgNotNull(aValue);
2114
2115 AutoCaller autoCaller(this);
2116 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2117
2118 /* start with nothing found */
2119 Utf8Str strKey(aKey);
2120 Bstr bstrResult;
2121
2122 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
2123 if (it != m->pMainConfigFile->mapExtraDataItems.end())
2124 // found:
2125 bstrResult = it->second; // source is a Utf8Str
2126
2127 /* return the result to caller (may be empty) */
2128 bstrResult.cloneTo(aValue);
2129
2130 return S_OK;
2131}
2132
2133/**
2134 * @note Locks this object for writing.
2135 */
2136STDMETHODIMP VirtualBox::SetExtraData(IN_BSTR aKey,
2137 IN_BSTR aValue)
2138{
2139 CheckComArgStrNotEmptyOrNull(aKey);
2140
2141 AutoCaller autoCaller(this);
2142 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2143
2144 Utf8Str strKey(aKey);
2145 Utf8Str strValue(aValue);
2146 Utf8Str strOldValue; // empty
2147
2148 // locking note: we only hold the read lock briefly to look up the old value,
2149 // then release it and call the onExtraCanChange callbacks. There is a small
2150 // chance of a race insofar as the callback might be called twice if two callers
2151 // change the same key at the same time, but that's a much better solution
2152 // than the deadlock we had here before. The actual changing of the extradata
2153 // is then performed under the write lock and race-free.
2154
2155 // look up the old value first; if nothing has changed then we need not do anything
2156 {
2157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
2158 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
2159 if (it != m->pMainConfigFile->mapExtraDataItems.end())
2160 strOldValue = it->second;
2161 }
2162
2163 bool fChanged;
2164 if ((fChanged = (strOldValue != strValue)))
2165 {
2166 // ask for permission from all listeners outside the locks;
2167 // onExtraDataCanChange() only briefly requests the VirtualBox
2168 // lock to copy the list of callbacks to invoke
2169 Bstr error;
2170 Bstr bstrValue(aValue);
2171
2172 if (!onExtraDataCanChange(Guid::Empty, aKey, bstrValue.raw(), error))
2173 {
2174 const char *sep = error.isEmpty() ? "" : ": ";
2175 CBSTR err = error.raw();
2176 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
2177 sep, err));
2178 return setError(E_ACCESSDENIED,
2179 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
2180 aKey,
2181 bstrValue.raw(),
2182 sep,
2183 err);
2184 }
2185
2186 // data is changing and change not vetoed: then write it out under the lock
2187
2188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2189
2190 if (strValue.isEmpty())
2191 m->pMainConfigFile->mapExtraDataItems.erase(strKey);
2192 else
2193 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
2194 // creates a new key if needed
2195
2196 /* save settings on success */
2197 HRESULT rc = saveSettings();
2198 if (FAILED(rc)) return rc;
2199 }
2200
2201 // fire notification outside the lock
2202 if (fChanged)
2203 onExtraDataChange(Guid::Empty, aKey, aValue);
2204
2205 return S_OK;
2206}
2207
2208/**
2209 *
2210 */
2211STDMETHODIMP VirtualBox::SetSettingsSecret(IN_BSTR aValue)
2212{
2213 storeSettingsKey(aValue);
2214 decryptSettings();
2215 return S_OK;
2216}
2217
2218int VirtualBox::decryptMediumSettings(Medium *pMedium)
2219{
2220 Bstr bstrCipher;
2221 HRESULT hrc = pMedium->GetProperty(Bstr("InitiatorSecretEncrypted").raw(),
2222 bstrCipher.asOutParam());
2223 if (SUCCEEDED(hrc))
2224 {
2225 Utf8Str strPlaintext;
2226 int rc = decryptSetting(&strPlaintext, bstrCipher);
2227 if (RT_SUCCESS(rc))
2228 pMedium->setPropertyDirect("InitiatorSecret", strPlaintext);
2229 else
2230 return rc;
2231 }
2232 return VINF_SUCCESS;
2233}
2234
2235/**
2236 * Decrypt all encrypted settings.
2237 *
2238 * So far we only have encrypted iSCSI initiator secrets so we just go through
2239 * all hard disk mediums and determine the plain 'InitiatorSecret' from
2240 * 'InitiatorSecretEncrypted. The latter is stored as Base64 because medium
2241 * properties need to be null-terminated strings.
2242 */
2243int VirtualBox::decryptSettings()
2244{
2245 bool fFailure = false;
2246 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2247 for (MediaList::const_iterator mt = m->allHardDisks.begin();
2248 mt != m->allHardDisks.end();
2249 ++mt)
2250 {
2251 ComObjPtr<Medium> pMedium = *mt;
2252 AutoCaller medCaller(pMedium);
2253 if (FAILED(medCaller.rc()))
2254 continue;
2255 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
2256 int vrc = decryptMediumSettings(pMedium);
2257 if (RT_FAILURE(vrc))
2258 fFailure = true;
2259 }
2260 return fFailure ? VERR_INVALID_PARAMETER : VINF_SUCCESS;
2261}
2262
2263/**
2264 * Encode.
2265 *
2266 * @param aPlaintext plaintext to be encrypted
2267 * @param aCiphertext resulting ciphertext (base64-encoded)
2268 */
2269int VirtualBox::encryptSetting(const Utf8Str &aPlaintext, Utf8Str *aCiphertext)
2270{
2271 uint8_t abCiphertext[32];
2272 char szCipherBase64[128];
2273 size_t cchCipherBase64;
2274 int rc = encryptSettingBytes((uint8_t*)aPlaintext.c_str(), abCiphertext,
2275 aPlaintext.length()+1, sizeof(abCiphertext));
2276 if (RT_SUCCESS(rc))
2277 {
2278 rc = RTBase64Encode(abCiphertext, sizeof(abCiphertext),
2279 szCipherBase64, sizeof(szCipherBase64),
2280 &cchCipherBase64);
2281 if (RT_SUCCESS(rc))
2282 *aCiphertext = szCipherBase64;
2283 }
2284 return rc;
2285}
2286
2287/**
2288 * Decode.
2289 *
2290 * @param aPlaintext resulting plaintext
2291 * @param aCiphertext ciphertext (base64-encoded) to decrypt
2292 */
2293int VirtualBox::decryptSetting(Utf8Str *aPlaintext, const Utf8Str &aCiphertext)
2294{
2295 uint8_t abPlaintext[64];
2296 uint8_t abCiphertext[64];
2297 size_t cbCiphertext;
2298 int rc = RTBase64Decode(aCiphertext.c_str(),
2299 abCiphertext, sizeof(abCiphertext),
2300 &cbCiphertext, NULL);
2301 if (RT_SUCCESS(rc))
2302 {
2303 rc = decryptSettingBytes(abPlaintext, abCiphertext, cbCiphertext);
2304 if (RT_SUCCESS(rc))
2305 {
2306 for (unsigned i = 0; i < cbCiphertext; i++)
2307 {
2308 /* sanity check: null-terminated string? */
2309 if (abPlaintext[i] == '\0')
2310 {
2311 /* sanity check: valid UTF8 string? */
2312 if (RTStrIsValidEncoding((const char*)abPlaintext))
2313 {
2314 *aPlaintext = Utf8Str((const char*)abPlaintext);
2315 return VINF_SUCCESS;
2316 }
2317 }
2318 }
2319 rc = VERR_INVALID_MAGIC;
2320 }
2321 }
2322 return rc;
2323}
2324
2325/**
2326 * Encrypt secret bytes. Use the m->SettingsCipherKey as key.
2327 *
2328 * @param aPlaintext clear text to be encrypted
2329 * @param aCiphertext resulting encrypted text
2330 * @param aPlaintextSize size of the plaintext
2331 * @param aCiphertextSize size of the ciphertext
2332 */
2333int VirtualBox::encryptSettingBytes(const uint8_t *aPlaintext, uint8_t *aCiphertext,
2334 size_t aPlaintextSize, size_t aCiphertextSize) const
2335{
2336 unsigned i, j;
2337 uint8_t aBytes[64];
2338
2339 if (!m->fSettingsCipherKeySet)
2340 return VERR_INVALID_STATE;
2341
2342 if (aCiphertextSize > sizeof(aBytes))
2343 return VERR_BUFFER_OVERFLOW;
2344
2345 if (aCiphertextSize < 32)
2346 return VERR_INVALID_PARAMETER;
2347
2348 AssertCompile(sizeof(m->SettingsCipherKey) >= 32);
2349
2350 /* store the first 8 bytes of the cipherkey for verification */
2351 for (i = 0, j = 0; i < 8; i++, j++)
2352 aCiphertext[i] = m->SettingsCipherKey[j];
2353
2354 for (unsigned k = 0; k < aPlaintextSize && i < aCiphertextSize; i++, k++)
2355 {
2356 aCiphertext[i] = (aPlaintext[k] ^ m->SettingsCipherKey[j]);
2357 if (++j >= sizeof(m->SettingsCipherKey))
2358 j = 0;
2359 }
2360
2361 /* fill with random data to have a minimal length (salt) */
2362 if (i < aCiphertextSize)
2363 {
2364 RTRandBytes(aBytes, aCiphertextSize - i);
2365 for (int k = 0; i < aCiphertextSize; i++, k++)
2366 {
2367 aCiphertext[i] = aBytes[k] ^ m->SettingsCipherKey[j];
2368 if (++j >= sizeof(m->SettingsCipherKey))
2369 j = 0;
2370 }
2371 }
2372
2373 return VINF_SUCCESS;
2374}
2375
2376/**
2377 * Decrypt secret bytes. Use the m->SettingsCipherKey as key.
2378 *
2379 * @param aPlaintext resulting plaintext
2380 * @param aCiphertext ciphertext to be decrypted
2381 * @param aCiphertextSize size of the ciphertext == size of the plaintext
2382 */
2383int VirtualBox::decryptSettingBytes(uint8_t *aPlaintext,
2384 const uint8_t *aCiphertext, size_t aCiphertextSize) const
2385{
2386 unsigned i, j;
2387
2388 if (!m->fSettingsCipherKeySet)
2389 return VERR_INVALID_STATE;
2390
2391 if (aCiphertextSize < 32)
2392 return VERR_INVALID_PARAMETER;
2393
2394 /* key verification */
2395 for (i = 0, j = 0; i < 8; i++, j++)
2396 if (aCiphertext[i] != m->SettingsCipherKey[j])
2397 return VERR_INVALID_MAGIC;
2398
2399 /* poison */
2400 memset(aPlaintext, 0xff, aCiphertextSize);
2401 for (int k = 0; i < aCiphertextSize; i++, k++)
2402 {
2403 aPlaintext[k] = aCiphertext[i] ^ m->SettingsCipherKey[j];
2404 if (++j >= sizeof(m->SettingsCipherKey))
2405 j = 0;
2406 }
2407
2408 return VINF_SUCCESS;
2409}
2410
2411/**
2412 * Store a settings key.
2413 *
2414 * @param aKey the key to store
2415 */
2416void VirtualBox::storeSettingsKey(const Utf8Str &aKey)
2417{
2418 RTSha512(aKey.c_str(), aKey.length(), m->SettingsCipherKey);
2419 m->fSettingsCipherKeySet = true;
2420}
2421
2422// public methods only for internal purposes
2423/////////////////////////////////////////////////////////////////////////////
2424
2425#ifdef DEBUG
2426void VirtualBox::dumpAllBackRefs()
2427{
2428 {
2429 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2430 for (MediaList::const_iterator mt = m->allHardDisks.begin();
2431 mt != m->allHardDisks.end();
2432 ++mt)
2433 {
2434 ComObjPtr<Medium> pMedium = *mt;
2435 pMedium->dumpBackRefs();
2436 }
2437 }
2438 {
2439 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2440 for (MediaList::const_iterator mt = m->allDVDImages.begin();
2441 mt != m->allDVDImages.end();
2442 ++mt)
2443 {
2444 ComObjPtr<Medium> pMedium = *mt;
2445 pMedium->dumpBackRefs();
2446 }
2447 }
2448}
2449#endif
2450
2451/**
2452 * Posts an event to the event queue that is processed asynchronously
2453 * on a dedicated thread.
2454 *
2455 * Posting events to the dedicated event queue is useful to perform secondary
2456 * actions outside any object locks -- for example, to iterate over a list
2457 * of callbacks and inform them about some change caused by some object's
2458 * method call.
2459 *
2460 * @param event event to post; must have been allocated using |new|, will
2461 * be deleted automatically by the event thread after processing
2462 *
2463 * @note Doesn't lock any object.
2464 */
2465HRESULT VirtualBox::postEvent(Event *event)
2466{
2467 AssertReturn(event, E_FAIL);
2468
2469 HRESULT rc;
2470 AutoCaller autoCaller(this);
2471 if (SUCCEEDED((rc = autoCaller.rc())))
2472 {
2473 if (autoCaller.state() != Ready)
2474 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
2475 autoCaller.state()));
2476 // return S_OK
2477 else if ( (m->pAsyncEventQ)
2478 && (m->pAsyncEventQ->postEvent(event))
2479 )
2480 return S_OK;
2481 else
2482 rc = E_FAIL;
2483 }
2484
2485 // in any event of failure, we must clean up here, or we'll leak;
2486 // the caller has allocated the object using new()
2487 delete event;
2488 return rc;
2489}
2490
2491/**
2492 * Adds a progress to the global collection of pending operations.
2493 * Usually gets called upon progress object initialization.
2494 *
2495 * @param aProgress Operation to add to the collection.
2496 *
2497 * @note Doesn't lock objects.
2498 */
2499HRESULT VirtualBox::addProgress(IProgress *aProgress)
2500{
2501 CheckComArgNotNull(aProgress);
2502
2503 AutoCaller autoCaller(this);
2504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2505
2506 Bstr id;
2507 HRESULT rc = aProgress->COMGETTER(Id)(id.asOutParam());
2508 AssertComRCReturnRC(rc);
2509
2510 /* protect mProgressOperations */
2511 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2512
2513 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
2514 return S_OK;
2515}
2516
2517/**
2518 * Removes the progress from the global collection of pending operations.
2519 * Usually gets called upon progress completion.
2520 *
2521 * @param aId UUID of the progress operation to remove
2522 *
2523 * @note Doesn't lock objects.
2524 */
2525HRESULT VirtualBox::removeProgress(IN_GUID aId)
2526{
2527 AutoCaller autoCaller(this);
2528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2529
2530 ComPtr<IProgress> progress;
2531
2532 /* protect mProgressOperations */
2533 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2534
2535 size_t cnt = m->mapProgressOperations.erase(aId);
2536 Assert(cnt == 1);
2537 NOREF(cnt);
2538
2539 return S_OK;
2540}
2541
2542#ifdef RT_OS_WINDOWS
2543
2544struct StartSVCHelperClientData
2545{
2546 ComObjPtr<VirtualBox> that;
2547 ComObjPtr<Progress> progress;
2548 bool privileged;
2549 VirtualBox::SVCHelperClientFunc func;
2550 void *user;
2551};
2552
2553/**
2554 * Helper method that starts a worker thread that:
2555 * - creates a pipe communication channel using SVCHlpClient;
2556 * - starts an SVC Helper process that will inherit this channel;
2557 * - executes the supplied function by passing it the created SVCHlpClient
2558 * and opened instance to communicate to the Helper process and the given
2559 * Progress object.
2560 *
2561 * The user function is supposed to communicate to the helper process
2562 * using the \a aClient argument to do the requested job and optionally expose
2563 * the progress through the \a aProgress object. The user function should never
2564 * call notifyComplete() on it: this will be done automatically using the
2565 * result code returned by the function.
2566 *
2567 * Before the user function is started, the communication channel passed to
2568 * the \a aClient argument is fully set up, the function should start using
2569 * its write() and read() methods directly.
2570 *
2571 * The \a aVrc parameter of the user function may be used to return an error
2572 * code if it is related to communication errors (for example, returned by
2573 * the SVCHlpClient members when they fail). In this case, the correct error
2574 * message using this value will be reported to the caller. Note that the
2575 * value of \a aVrc is inspected only if the user function itself returns
2576 * success.
2577 *
2578 * If a failure happens anywhere before the user function would be normally
2579 * called, it will be called anyway in special "cleanup only" mode indicated
2580 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
2581 * all the function is supposed to do is to cleanup its aUser argument if
2582 * necessary (it's assumed that the ownership of this argument is passed to
2583 * the user function once #startSVCHelperClient() returns a success, thus
2584 * making it responsible for the cleanup).
2585 *
2586 * After the user function returns, the thread will send the SVCHlpMsg::Null
2587 * message to indicate a process termination.
2588 *
2589 * @param aPrivileged |true| to start the SVC Helper process as a privileged
2590 * user that can perform administrative tasks
2591 * @param aFunc user function to run
2592 * @param aUser argument to the user function
2593 * @param aProgress progress object that will track operation completion
2594 *
2595 * @note aPrivileged is currently ignored (due to some unsolved problems in
2596 * Vista) and the process will be started as a normal (unprivileged)
2597 * process.
2598 *
2599 * @note Doesn't lock anything.
2600 */
2601HRESULT VirtualBox::startSVCHelperClient(bool aPrivileged,
2602 SVCHelperClientFunc aFunc,
2603 void *aUser, Progress *aProgress)
2604{
2605 AssertReturn(aFunc, E_POINTER);
2606 AssertReturn(aProgress, E_POINTER);
2607
2608 AutoCaller autoCaller(this);
2609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2610
2611 /* create the SVCHelperClientThread() argument */
2612 std::auto_ptr <StartSVCHelperClientData>
2613 d(new StartSVCHelperClientData());
2614 AssertReturn(d.get(), E_OUTOFMEMORY);
2615
2616 d->that = this;
2617 d->progress = aProgress;
2618 d->privileged = aPrivileged;
2619 d->func = aFunc;
2620 d->user = aUser;
2621
2622 RTTHREAD tid = NIL_RTTHREAD;
2623 int vrc = RTThreadCreate(&tid, SVCHelperClientThread,
2624 static_cast <void *>(d.get()),
2625 0, RTTHREADTYPE_MAIN_WORKER,
2626 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2627 if (RT_FAILURE(vrc))
2628 return setError(E_FAIL, "Could not create SVCHelper thread (%Rrc)", vrc);
2629
2630 /* d is now owned by SVCHelperClientThread(), so release it */
2631 d.release();
2632
2633 return S_OK;
2634}
2635
2636/**
2637 * Worker thread for startSVCHelperClient().
2638 */
2639/* static */
2640DECLCALLBACK(int)
2641VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser)
2642{
2643 LogFlowFuncEnter();
2644
2645 std::auto_ptr<StartSVCHelperClientData>
2646 d(static_cast<StartSVCHelperClientData*>(aUser));
2647
2648 HRESULT rc = S_OK;
2649 bool userFuncCalled = false;
2650
2651 do
2652 {
2653 AssertBreakStmt(d.get(), rc = E_POINTER);
2654 AssertReturn(!d->progress.isNull(), E_POINTER);
2655
2656 /* protect VirtualBox from uninitialization */
2657 AutoCaller autoCaller(d->that);
2658 if (!autoCaller.isOk())
2659 {
2660 /* it's too late */
2661 rc = autoCaller.rc();
2662 break;
2663 }
2664
2665 int vrc = VINF_SUCCESS;
2666
2667 Guid id;
2668 id.create();
2669 SVCHlpClient client;
2670 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
2671 id.raw()).c_str());
2672 if (RT_FAILURE(vrc))
2673 {
2674 rc = d->that->setError(E_FAIL,
2675 tr("Could not create the communication channel (%Rrc)"), vrc);
2676 break;
2677 }
2678
2679 /* get the path to the executable */
2680 char exePathBuf[RTPATH_MAX];
2681 char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX);
2682 if (!exePath)
2683 {
2684 rc = d->that->setError(E_FAIL, tr("Cannot get executable name"));
2685 break;
2686 }
2687
2688 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().c_str());
2689
2690 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.c_str()));
2691
2692 RTPROCESS pid = NIL_RTPROCESS;
2693
2694 if (d->privileged)
2695 {
2696 /* Attempt to start a privileged process using the Run As dialog */
2697
2698 Bstr file = exePath;
2699 Bstr parameters = argsStr;
2700
2701 SHELLEXECUTEINFO shExecInfo;
2702
2703 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
2704
2705 shExecInfo.fMask = NULL;
2706 shExecInfo.hwnd = NULL;
2707 shExecInfo.lpVerb = L"runas";
2708 shExecInfo.lpFile = file.raw();
2709 shExecInfo.lpParameters = parameters.raw();
2710 shExecInfo.lpDirectory = NULL;
2711 shExecInfo.nShow = SW_NORMAL;
2712 shExecInfo.hInstApp = NULL;
2713
2714 if (!ShellExecuteEx(&shExecInfo))
2715 {
2716 int vrc2 = RTErrConvertFromWin32(GetLastError());
2717 /* hide excessive details in case of a frequent error
2718 * (pressing the Cancel button to close the Run As dialog) */
2719 if (vrc2 == VERR_CANCELLED)
2720 rc = d->that->setError(E_FAIL,
2721 tr("Operation canceled by the user"));
2722 else
2723 rc = d->that->setError(E_FAIL,
2724 tr("Could not launch a privileged process '%s' (%Rrc)"),
2725 exePath, vrc2);
2726 break;
2727 }
2728 }
2729 else
2730 {
2731 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2732 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
2733 if (RT_FAILURE(vrc))
2734 {
2735 rc = d->that->setError(E_FAIL,
2736 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2737 break;
2738 }
2739 }
2740
2741 /* wait for the client to connect */
2742 vrc = client.connect();
2743 if (RT_SUCCESS(vrc))
2744 {
2745 /* start the user supplied function */
2746 rc = d->func(&client, d->progress, d->user, &vrc);
2747 userFuncCalled = true;
2748 }
2749
2750 /* send the termination signal to the process anyway */
2751 {
2752 int vrc2 = client.write(SVCHlpMsg::Null);
2753 if (RT_SUCCESS(vrc))
2754 vrc = vrc2;
2755 }
2756
2757 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2758 {
2759 rc = d->that->setError(E_FAIL,
2760 tr("Could not operate the communication channel (%Rrc)"), vrc);
2761 break;
2762 }
2763 }
2764 while (0);
2765
2766 if (FAILED(rc) && !userFuncCalled)
2767 {
2768 /* call the user function in the "cleanup only" mode
2769 * to let it free resources passed to in aUser */
2770 d->func(NULL, NULL, d->user, NULL);
2771 }
2772
2773 d->progress->notifyComplete(rc);
2774
2775 LogFlowFuncLeave();
2776 return 0;
2777}
2778
2779#endif /* RT_OS_WINDOWS */
2780
2781/**
2782 * Sends a signal to the client watcher to rescan the set of machines
2783 * that have open sessions.
2784 *
2785 * @note Doesn't lock anything.
2786 */
2787void VirtualBox::updateClientWatcher()
2788{
2789 AutoCaller autoCaller(this);
2790 AssertComRCReturnVoid(autoCaller.rc());
2791
2792 AssertPtrReturnVoid(m->pClientWatcher);
2793 m->pClientWatcher->update();
2794}
2795
2796/**
2797 * Adds the given child process ID to the list of processes to be reaped.
2798 * This call should be followed by #updateClientWatcher() to take the effect.
2799 *
2800 * @note Doesn't lock anything.
2801 */
2802void VirtualBox::addProcessToReap(RTPROCESS pid)
2803{
2804 AutoCaller autoCaller(this);
2805 AssertComRCReturnVoid(autoCaller.rc());
2806
2807 AssertPtrReturnVoid(m->pClientWatcher);
2808 m->pClientWatcher->addProcess(pid);
2809}
2810
2811/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2812struct MachineEvent : public VirtualBox::CallbackEvent
2813{
2814 MachineEvent(VirtualBox *aVB, VBoxEventType_T aWhat, const Guid &aId, BOOL aBool)
2815 : CallbackEvent(aVB, aWhat), id(aId.toUtf16())
2816 , mBool(aBool)
2817 { }
2818
2819 MachineEvent(VirtualBox *aVB, VBoxEventType_T aWhat, const Guid &aId, MachineState_T aState)
2820 : CallbackEvent(aVB, aWhat), id(aId.toUtf16())
2821 , mState(aState)
2822 {}
2823
2824 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2825 {
2826 switch (mWhat)
2827 {
2828 case VBoxEventType_OnMachineDataChanged:
2829 aEvDesc.init(aSource, mWhat, id.raw(), mBool);
2830 break;
2831
2832 case VBoxEventType_OnMachineStateChanged:
2833 aEvDesc.init(aSource, mWhat, id.raw(), mState);
2834 break;
2835
2836 case VBoxEventType_OnMachineRegistered:
2837 aEvDesc.init(aSource, mWhat, id.raw(), mBool);
2838 break;
2839
2840 default:
2841 AssertFailedReturn(S_OK);
2842 }
2843 return S_OK;
2844 }
2845
2846 Bstr id;
2847 MachineState_T mState;
2848 BOOL mBool;
2849};
2850
2851/**
2852 * @note Doesn't lock any object.
2853 */
2854void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2855{
2856 postEvent(new MachineEvent(this, VBoxEventType_OnMachineStateChanged, aId, aState));
2857}
2858
2859/**
2860 * @note Doesn't lock any object.
2861 */
2862void VirtualBox::onMachineDataChange(const Guid &aId, BOOL aTemporary)
2863{
2864 postEvent(new MachineEvent(this, VBoxEventType_OnMachineDataChanged, aId, aTemporary));
2865}
2866
2867/**
2868 * @note Locks this object for reading.
2869 */
2870BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2871 Bstr &aError)
2872{
2873 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2874 aId.toString().c_str(), aKey, aValue));
2875
2876 AutoCaller autoCaller(this);
2877 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2878
2879 BOOL allowChange = TRUE;
2880 Bstr id = aId.toUtf16();
2881
2882 VBoxEventDesc evDesc;
2883 evDesc.init(m->pEventSource, VBoxEventType_OnExtraDataCanChange, id.raw(), aKey, aValue);
2884 BOOL fDelivered = evDesc.fire(3000); /* Wait up to 3 secs for delivery */
2885 //Assert(fDelivered);
2886 if (fDelivered)
2887 {
2888 ComPtr<IEvent> aEvent;
2889 evDesc.getEvent(aEvent.asOutParam());
2890 ComPtr<IExtraDataCanChangeEvent> aCanChangeEvent = aEvent;
2891 Assert(aCanChangeEvent);
2892 BOOL fVetoed = FALSE;
2893 aCanChangeEvent->IsVetoed(&fVetoed);
2894 allowChange = !fVetoed;
2895
2896 if (!allowChange)
2897 {
2898 SafeArray<BSTR> aVetos;
2899 aCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos));
2900 if (aVetos.size() > 0)
2901 aError = aVetos[0];
2902 }
2903 }
2904 else
2905 allowChange = TRUE;
2906
2907 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2908 return allowChange;
2909}
2910
2911/** Event for onExtraDataChange() */
2912struct ExtraDataEvent : public VirtualBox::CallbackEvent
2913{
2914 ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId,
2915 IN_BSTR aKey, IN_BSTR aVal)
2916 : CallbackEvent(aVB, VBoxEventType_OnExtraDataChanged)
2917 , machineId(aMachineId.toUtf16()), key(aKey), val(aVal)
2918 {}
2919
2920 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2921 {
2922 return aEvDesc.init(aSource, VBoxEventType_OnExtraDataChanged, machineId.raw(), key.raw(), val.raw());
2923 }
2924
2925 Bstr machineId, key, val;
2926};
2927
2928/**
2929 * @note Doesn't lock any object.
2930 */
2931void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2932{
2933 postEvent(new ExtraDataEvent(this, aId, aKey, aValue));
2934}
2935
2936/**
2937 * @note Doesn't lock any object.
2938 */
2939void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered)
2940{
2941 postEvent(new MachineEvent(this, VBoxEventType_OnMachineRegistered, aId, aRegistered));
2942}
2943
2944/** Event for onSessionStateChange() */
2945struct SessionEvent : public VirtualBox::CallbackEvent
2946{
2947 SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2948 : CallbackEvent(aVB, VBoxEventType_OnSessionStateChanged)
2949 , machineId(aMachineId.toUtf16()), sessionState(aState)
2950 {}
2951
2952 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2953 {
2954 return aEvDesc.init(aSource, VBoxEventType_OnSessionStateChanged, machineId.raw(), sessionState);
2955 }
2956 Bstr machineId;
2957 SessionState_T sessionState;
2958};
2959
2960/**
2961 * @note Doesn't lock any object.
2962 */
2963void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState)
2964{
2965 postEvent(new SessionEvent(this, aId, aState));
2966}
2967
2968/** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */
2969struct SnapshotEvent : public VirtualBox::CallbackEvent
2970{
2971 SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2972 VBoxEventType_T aWhat)
2973 : CallbackEvent(aVB, aWhat)
2974 , machineId(aMachineId), snapshotId(aSnapshotId)
2975 {}
2976
2977 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2978 {
2979 return aEvDesc.init(aSource, mWhat, machineId.toUtf16().raw(),
2980 snapshotId.toUtf16().raw());
2981 }
2982
2983 Guid machineId;
2984 Guid snapshotId;
2985};
2986
2987/**
2988 * @note Doesn't lock any object.
2989 */
2990void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
2991{
2992 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2993 VBoxEventType_OnSnapshotTaken));
2994}
2995
2996/**
2997 * @note Doesn't lock any object.
2998 */
2999void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
3000{
3001 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
3002 VBoxEventType_OnSnapshotDeleted));
3003}
3004
3005/**
3006 * @note Doesn't lock any object.
3007 */
3008void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
3009{
3010 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
3011 VBoxEventType_OnSnapshotChanged));
3012}
3013
3014/** Event for onGuestPropertyChange() */
3015struct GuestPropertyEvent : public VirtualBox::CallbackEvent
3016{
3017 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
3018 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
3019 : CallbackEvent(aVBox, VBoxEventType_OnGuestPropertyChanged),
3020 machineId(aMachineId),
3021 name(aName),
3022 value(aValue),
3023 flags(aFlags)
3024 {}
3025
3026 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
3027 {
3028 return aEvDesc.init(aSource, VBoxEventType_OnGuestPropertyChanged,
3029 machineId.toUtf16().raw(), name.raw(), value.raw(), flags.raw());
3030 }
3031
3032 Guid machineId;
3033 Bstr name, value, flags;
3034};
3035
3036/**
3037 * @note Doesn't lock any object.
3038 */
3039void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
3040 IN_BSTR aValue, IN_BSTR aFlags)
3041{
3042 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
3043}
3044
3045/**
3046 * @note Doesn't lock any object.
3047 */
3048void VirtualBox::onNatRedirectChange(const Guid &aMachineId, ULONG ulSlot, bool fRemove, IN_BSTR aName,
3049 NATProtocol_T aProto, IN_BSTR aHostIp, uint16_t aHostPort,
3050 IN_BSTR aGuestIp, uint16_t aGuestPort)
3051{
3052 fireNATRedirectEvent(m->pEventSource, aMachineId.toUtf16().raw(), ulSlot, fRemove, aName, aProto, aHostIp,
3053 aHostPort, aGuestIp, aGuestPort);
3054}
3055
3056void VirtualBox::onNATNetworkChange(IN_BSTR aName)
3057{
3058 fireNATNetworkChangedEvent(m->pEventSource, aName);
3059}
3060
3061void VirtualBox::onNATNetworkStartStop(IN_BSTR aName, BOOL fStart)
3062{
3063 fireNATNetworkStartStopEvent(m->pEventSource, aName, fStart);
3064}
3065void VirtualBox::onNATNetworkSetting(IN_BSTR aNetworkName, BOOL aEnabled,
3066 IN_BSTR aNetwork, IN_BSTR aGateway,
3067 BOOL aAdvertiseDefaultIpv6RouteEnabled,
3068 BOOL fNeedDhcpServer)
3069{
3070 fireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled,
3071 aNetwork, aGateway,
3072 aAdvertiseDefaultIpv6RouteEnabled, fNeedDhcpServer);
3073}
3074
3075void VirtualBox::onNATNetworkPortForward(IN_BSTR aNetworkName, BOOL create, BOOL fIpv6,
3076 IN_BSTR aRuleName, NATProtocol_T proto,
3077 IN_BSTR aHostIp, LONG aHostPort,
3078 IN_BSTR aGuestIp, LONG aGuestPort)
3079{
3080 fireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create,
3081 fIpv6, aRuleName, proto,
3082 aHostIp, aHostPort,
3083 aGuestIp, aGuestPort);
3084}
3085
3086/**
3087 * @note Locks this object for reading.
3088 */
3089ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
3090{
3091 ComObjPtr<GuestOSType> type;
3092 AutoCaller autoCaller(this);
3093 AssertComRCReturn(autoCaller.rc(), type);
3094
3095 /* unknown type must always be the first */
3096 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
3097
3098 return m->allGuestOSTypes.front();
3099}
3100
3101/**
3102 * Returns the list of opened machines (machines having direct sessions opened
3103 * by client processes) and optionally the list of direct session controls.
3104 *
3105 * @param aMachines Where to put opened machines (will be empty if none).
3106 * @param aControls Where to put direct session controls (optional).
3107 *
3108 * @note The returned lists contain smart pointers. So, clear it as soon as
3109 * it becomes no more necessary to release instances.
3110 *
3111 * @note It can be possible that a session machine from the list has been
3112 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
3113 * when accessing unprotected data directly.
3114 *
3115 * @note Locks objects for reading.
3116 */
3117void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
3118 InternalControlList *aControls /*= NULL*/)
3119{
3120 AutoCaller autoCaller(this);
3121 AssertComRCReturnVoid(autoCaller.rc());
3122
3123 aMachines.clear();
3124 if (aControls)
3125 aControls->clear();
3126
3127 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3128
3129 for (MachinesOList::iterator it = m->allMachines.begin();
3130 it != m->allMachines.end();
3131 ++it)
3132 {
3133 ComObjPtr<SessionMachine> sm;
3134 ComPtr<IInternalSessionControl> ctl;
3135 if ((*it)->isSessionOpen(sm, &ctl))
3136 {
3137 aMachines.push_back(sm);
3138 if (aControls)
3139 aControls->push_back(ctl);
3140 }
3141 }
3142}
3143
3144/**
3145 * Gets a reference to the machine list. This is the real thing, not a copy,
3146 * so bad things will happen if the caller doesn't hold the necessary lock.
3147 *
3148 * @returns reference to machine list
3149 *
3150 * @note Caller must hold the VirtualBox object lock at least for reading.
3151 */
3152VirtualBox::MachinesOList &VirtualBox::getMachinesList(void)
3153{
3154 return m->allMachines;
3155}
3156
3157/**
3158 * Searches for a machine object with the given ID in the collection
3159 * of registered machines.
3160 *
3161 * @param aId Machine UUID to look for.
3162 * @param aPermitInaccessible If true, inaccessible machines will be found;
3163 * if false, this will fail if the given machine is inaccessible.
3164 * @param aSetError If true, set errorinfo if the machine is not found.
3165 * @param aMachine Returned machine, if found.
3166 * @return
3167 */
3168HRESULT VirtualBox::findMachine(const Guid &aId,
3169 bool fPermitInaccessible,
3170 bool aSetError,
3171 ComObjPtr<Machine> *aMachine /* = NULL */)
3172{
3173 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
3174
3175 AutoCaller autoCaller(this);
3176 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3177
3178 {
3179 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3180
3181 for (MachinesOList::iterator it = m->allMachines.begin();
3182 it != m->allMachines.end();
3183 ++it)
3184 {
3185 ComObjPtr<Machine> pMachine = *it;
3186
3187 if (!fPermitInaccessible)
3188 {
3189 // skip inaccessible machines
3190 AutoCaller machCaller(pMachine);
3191 if (FAILED(machCaller.rc()))
3192 continue;
3193 }
3194
3195 if (pMachine->getId() == aId)
3196 {
3197 rc = S_OK;
3198 if (aMachine)
3199 *aMachine = pMachine;
3200 break;
3201 }
3202 }
3203 }
3204
3205 if (aSetError && FAILED(rc))
3206 rc = setError(rc,
3207 tr("Could not find a registered machine with UUID {%RTuuid}"),
3208 aId.raw());
3209
3210 return rc;
3211}
3212
3213/**
3214 * Searches for a machine object with the given name or location in the
3215 * collection of registered machines.
3216 *
3217 * @param aName Machine name or location to look for.
3218 * @param aSetError If true, set errorinfo if the machine is not found.
3219 * @param aMachine Returned machine, if found.
3220 * @return
3221 */
3222HRESULT VirtualBox::findMachineByName(const Utf8Str &aName, bool aSetError,
3223 ComObjPtr<Machine> *aMachine /* = NULL */)
3224{
3225 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
3226
3227 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3228 for (MachinesOList::iterator it = m->allMachines.begin();
3229 it != m->allMachines.end();
3230 ++it)
3231 {
3232 ComObjPtr<Machine> &pMachine = *it;
3233 AutoCaller machCaller(pMachine);
3234 if (machCaller.rc())
3235 continue; // we can't ask inaccessible machines for their names
3236
3237 AutoReadLock machLock(pMachine COMMA_LOCKVAL_SRC_POS);
3238 if (pMachine->getName() == aName)
3239 {
3240 rc = S_OK;
3241 if (aMachine)
3242 *aMachine = pMachine;
3243 break;
3244 }
3245 if (!RTPathCompare(pMachine->getSettingsFileFull().c_str(), aName.c_str()))
3246 {
3247 rc = S_OK;
3248 if (aMachine)
3249 *aMachine = pMachine;
3250 break;
3251 }
3252 }
3253
3254 if (aSetError && FAILED(rc))
3255 rc = setError(rc,
3256 tr("Could not find a registered machine named '%s'"), aName.c_str());
3257
3258 return rc;
3259}
3260
3261static HRESULT validateMachineGroupHelper(const Utf8Str &aGroup, bool fPrimary, VirtualBox *pVirtualBox)
3262{
3263 /* empty strings are invalid */
3264 if (aGroup.isEmpty())
3265 return E_INVALIDARG;
3266 /* the toplevel group is valid */
3267 if (aGroup == "/")
3268 return S_OK;
3269 /* any other strings of length 1 are invalid */
3270 if (aGroup.length() == 1)
3271 return E_INVALIDARG;
3272 /* must start with a slash */
3273 if (aGroup.c_str()[0] != '/')
3274 return E_INVALIDARG;
3275 /* must not end with a slash */
3276 if (aGroup.c_str()[aGroup.length() - 1] == '/')
3277 return E_INVALIDARG;
3278 /* check the group components */
3279 const char *pStr = aGroup.c_str() + 1; /* first char is /, skip it */
3280 while (pStr)
3281 {
3282 char *pSlash = RTStrStr(pStr, "/");
3283 if (pSlash)
3284 {
3285 /* no empty components (or // sequences in other words) */
3286 if (pSlash == pStr)
3287 return E_INVALIDARG;
3288 /* check if the machine name rules are violated, because that means
3289 * the group components are too close to the limits. */
3290 Utf8Str tmp((const char *)pStr, (size_t)(pSlash - pStr));
3291 Utf8Str tmp2(tmp);
3292 sanitiseMachineFilename(tmp);
3293 if (tmp != tmp2)
3294 return E_INVALIDARG;
3295 if (fPrimary)
3296 {
3297 HRESULT rc = pVirtualBox->findMachineByName(tmp,
3298 false /* aSetError */);
3299 if (SUCCEEDED(rc))
3300 return VBOX_E_VM_ERROR;
3301 }
3302 pStr = pSlash + 1;
3303 }
3304 else
3305 {
3306 /* check if the machine name rules are violated, because that means
3307 * the group components is too close to the limits. */
3308 Utf8Str tmp(pStr);
3309 Utf8Str tmp2(tmp);
3310 sanitiseMachineFilename(tmp);
3311 if (tmp != tmp2)
3312 return E_INVALIDARG;
3313 pStr = NULL;
3314 }
3315 }
3316 return S_OK;
3317}
3318
3319/**
3320 * Validates a machine group.
3321 *
3322 * @param aMachineGroup Machine group.
3323 * @param fPrimary Set if this is the primary group.
3324 *
3325 * @return S_OK or E_INVALIDARG
3326 */
3327HRESULT VirtualBox::validateMachineGroup(const Utf8Str &aGroup, bool fPrimary)
3328{
3329 HRESULT rc = validateMachineGroupHelper(aGroup, fPrimary, this);
3330 if (FAILED(rc))
3331 {
3332 if (rc == VBOX_E_VM_ERROR)
3333 rc = setError(E_INVALIDARG,
3334 tr("Machine group '%s' conflicts with a virtual machine name"),
3335 aGroup.c_str());
3336 else
3337 rc = setError(rc,
3338 tr("Invalid machine group '%s'"),
3339 aGroup.c_str());
3340 }
3341 return rc;
3342}
3343
3344/**
3345 * Takes a list of machine groups, and sanitizes/validates it.
3346 *
3347 * @param aMachineGroups Safearray with the machine groups.
3348 * @param pllMachineGroups Pointer to list of strings for the result.
3349 *
3350 * @return S_OK or E_INVALIDARG
3351 */
3352HRESULT VirtualBox::convertMachineGroups(ComSafeArrayIn(IN_BSTR, aMachineGroups), StringsList *pllMachineGroups)
3353{
3354 pllMachineGroups->clear();
3355 if (aMachineGroups)
3356 {
3357 com::SafeArray<IN_BSTR> machineGroups(ComSafeArrayInArg(aMachineGroups));
3358 for (size_t i = 0; i < machineGroups.size(); i++)
3359 {
3360 Utf8Str group(machineGroups[i]);
3361 if (group.length() == 0)
3362 group = "/";
3363
3364 HRESULT rc = validateMachineGroup(group, i == 0);
3365 if (FAILED(rc))
3366 return rc;
3367
3368 /* no duplicates please */
3369 if ( find(pllMachineGroups->begin(), pllMachineGroups->end(), group)
3370 == pllMachineGroups->end())
3371 pllMachineGroups->push_back(group);
3372 }
3373 if (pllMachineGroups->size() == 0)
3374 pllMachineGroups->push_back("/");
3375 }
3376 else
3377 pllMachineGroups->push_back("/");
3378
3379 return S_OK;
3380}
3381
3382/**
3383 * Searches for a Medium object with the given ID in the list of registered
3384 * hard disks.
3385 *
3386 * @param aId ID of the hard disk. Must not be empty.
3387 * @param aSetError If @c true , the appropriate error info is set in case
3388 * when the hard disk is not found.
3389 * @param aHardDisk Where to store the found hard disk object (can be NULL).
3390 *
3391 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
3392 *
3393 * @note Locks the media tree for reading.
3394 */
3395HRESULT VirtualBox::findHardDiskById(const Guid &id,
3396 bool aSetError,
3397 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
3398{
3399 AssertReturn(!id.isZero(), E_INVALIDARG);
3400
3401 // we use the hard disks map, but it is protected by the
3402 // hard disk _list_ lock handle
3403 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3404
3405 HardDiskMap::const_iterator it = m->mapHardDisks.find(id);
3406 if (it != m->mapHardDisks.end())
3407 {
3408 if (aHardDisk)
3409 *aHardDisk = (*it).second;
3410 return S_OK;
3411 }
3412
3413 if (aSetError)
3414 return setError(VBOX_E_OBJECT_NOT_FOUND,
3415 tr("Could not find an open hard disk with UUID {%RTuuid}"),
3416 id.raw());
3417
3418 return VBOX_E_OBJECT_NOT_FOUND;
3419}
3420
3421/**
3422 * Searches for a Medium object with the given ID or location in the list of
3423 * registered hard disks. If both ID and location are specified, the first
3424 * object that matches either of them (not necessarily both) is returned.
3425 *
3426 * @param aLocation Full location specification. Must not be empty.
3427 * @param aSetError If @c true , the appropriate error info is set in case
3428 * when the hard disk is not found.
3429 * @param aHardDisk Where to store the found hard disk object (can be NULL).
3430 *
3431 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
3432 *
3433 * @note Locks the media tree for reading.
3434 */
3435HRESULT VirtualBox::findHardDiskByLocation(const Utf8Str &strLocation,
3436 bool aSetError,
3437 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
3438{
3439 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
3440
3441 // we use the hard disks map, but it is protected by the
3442 // hard disk _list_ lock handle
3443 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3444
3445 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
3446 it != m->mapHardDisks.end();
3447 ++it)
3448 {
3449 const ComObjPtr<Medium> &pHD = (*it).second;
3450
3451 AutoCaller autoCaller(pHD);
3452 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3453 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
3454
3455 Utf8Str strLocationFull = pHD->getLocationFull();
3456
3457 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
3458 {
3459 if (aHardDisk)
3460 *aHardDisk = pHD;
3461 return S_OK;
3462 }
3463 }
3464
3465 if (aSetError)
3466 return setError(VBOX_E_OBJECT_NOT_FOUND,
3467 tr("Could not find an open hard disk with location '%s'"),
3468 strLocation.c_str());
3469
3470 return VBOX_E_OBJECT_NOT_FOUND;
3471}
3472
3473/**
3474 * Searches for a Medium object with the given ID or location in the list of
3475 * registered DVD or floppy images, depending on the @a mediumType argument.
3476 * If both ID and file path are specified, the first object that matches either
3477 * of them (not necessarily both) is returned.
3478 *
3479 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
3480 * @param aId ID of the image file (unused when NULL).
3481 * @param aLocation Full path to the image file (unused when NULL).
3482 * @param aSetError If @c true, the appropriate error info is set in case when
3483 * the image is not found.
3484 * @param aImage Where to store the found image object (can be NULL).
3485 *
3486 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
3487 *
3488 * @note Locks the media tree for reading.
3489 */
3490HRESULT VirtualBox::findDVDOrFloppyImage(DeviceType_T mediumType,
3491 const Guid *aId,
3492 const Utf8Str &aLocation,
3493 bool aSetError,
3494 ComObjPtr<Medium> *aImage /* = NULL */)
3495{
3496 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
3497
3498 Utf8Str location;
3499 if (!aLocation.isEmpty())
3500 {
3501 int vrc = calculateFullPath(aLocation, location);
3502 if (RT_FAILURE(vrc))
3503 return setError(VBOX_E_FILE_ERROR,
3504 tr("Invalid image file location '%s' (%Rrc)"),
3505 aLocation.c_str(),
3506 vrc);
3507 }
3508
3509 MediaOList *pMediaList;
3510
3511 switch (mediumType)
3512 {
3513 case DeviceType_DVD:
3514 pMediaList = &m->allDVDImages;
3515 break;
3516
3517 case DeviceType_Floppy:
3518 pMediaList = &m->allFloppyImages;
3519 break;
3520
3521 default:
3522 return E_INVALIDARG;
3523 }
3524
3525 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
3526
3527 bool found = false;
3528
3529 for (MediaList::const_iterator it = pMediaList->begin();
3530 it != pMediaList->end();
3531 ++it)
3532 {
3533 // no AutoCaller, registered image life time is bound to this
3534 Medium *pMedium = *it;
3535 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
3536 const Utf8Str &strLocationFull = pMedium->getLocationFull();
3537
3538 found = ( aId
3539 && pMedium->getId() == *aId)
3540 || ( !aLocation.isEmpty()
3541 && RTPathCompare(location.c_str(),
3542 strLocationFull.c_str()) == 0);
3543 if (found)
3544 {
3545 if (pMedium->getDeviceType() != mediumType)
3546 {
3547 if (mediumType == DeviceType_DVD)
3548 return setError(E_INVALIDARG,
3549 "Cannot mount DVD medium '%s' as floppy", strLocationFull.c_str());
3550 else
3551 return setError(E_INVALIDARG,
3552 "Cannot mount floppy medium '%s' as DVD", strLocationFull.c_str());
3553 }
3554
3555 if (aImage)
3556 *aImage = pMedium;
3557 break;
3558 }
3559 }
3560
3561 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3562
3563 if (aSetError && !found)
3564 {
3565 if (aId)
3566 setError(rc,
3567 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
3568 aId->raw(),
3569 m->strSettingsFilePath.c_str());
3570 else
3571 setError(rc,
3572 tr("Could not find an image file with location '%s' in the media registry ('%s')"),
3573 aLocation.c_str(),
3574 m->strSettingsFilePath.c_str());
3575 }
3576
3577 return rc;
3578}
3579
3580/**
3581 * Searches for an IMedium object that represents the given UUID.
3582 *
3583 * If the UUID is empty (indicating an empty drive), this sets pMedium
3584 * to NULL and returns S_OK.
3585 *
3586 * If the UUID refers to a host drive of the given device type, this
3587 * sets pMedium to the object from the list in IHost and returns S_OK.
3588 *
3589 * If the UUID is an image file, this sets pMedium to the object that
3590 * findDVDOrFloppyImage() returned.
3591 *
3592 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
3593 *
3594 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
3595 * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
3596 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
3597 * @param pMedium out: IMedium object found.
3598 * @return
3599 */
3600HRESULT VirtualBox::findRemoveableMedium(DeviceType_T mediumType,
3601 const Guid &uuid,
3602 bool fRefresh,
3603 bool aSetError,
3604 ComObjPtr<Medium> &pMedium)
3605{
3606 if (uuid.isZero())
3607 {
3608 // that's easy
3609 pMedium.setNull();
3610 return S_OK;
3611 }
3612 else if (!uuid.isValid())
3613 {
3614 /* handling of case invalid GUID */
3615 return setError(VBOX_E_OBJECT_NOT_FOUND,
3616 tr("Guid '%ls' is invalid"),
3617 uuid.toString().c_str());
3618 }
3619
3620 // first search for host drive with that UUID
3621 HRESULT rc = m->pHost->findHostDriveById(mediumType,
3622 uuid,
3623 fRefresh,
3624 pMedium);
3625 if (rc == VBOX_E_OBJECT_NOT_FOUND)
3626 // then search for an image with that UUID
3627 rc = findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, aSetError, &pMedium);
3628
3629 return rc;
3630}
3631
3632HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
3633 GuestOSType*& pGuestOSType)
3634{
3635 /* Look for a GuestOSType object */
3636 AssertMsg(m->allGuestOSTypes.size() != 0,
3637 ("Guest OS types array must be filled"));
3638
3639 if (bstrOSType.isEmpty())
3640 {
3641 pGuestOSType = NULL;
3642 return S_OK;
3643 }
3644
3645 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3646 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
3647 it != m->allGuestOSTypes.end();
3648 ++it)
3649 {
3650 if ((*it)->id() == bstrOSType)
3651 {
3652 pGuestOSType = *it;
3653 return S_OK;
3654 }
3655 }
3656
3657 return setError(VBOX_E_OBJECT_NOT_FOUND,
3658 tr("Guest OS type '%ls' is invalid"),
3659 bstrOSType.raw());
3660}
3661
3662/**
3663 * Returns the constant pseudo-machine UUID that is used to identify the
3664 * global media registry.
3665 *
3666 * Starting with VirtualBox 4.0 each medium remembers in its instance data
3667 * in which media registry it is saved (if any): this can either be a machine
3668 * UUID, if it's in a per-machine media registry, or this global ID.
3669 *
3670 * This UUID is only used to identify the VirtualBox object while VirtualBox
3671 * is running. It is a compile-time constant and not saved anywhere.
3672 *
3673 * @return
3674 */
3675const Guid& VirtualBox::getGlobalRegistryId() const
3676{
3677 return m->uuidMediaRegistry;
3678}
3679
3680const ComObjPtr<Host>& VirtualBox::host() const
3681{
3682 return m->pHost;
3683}
3684
3685SystemProperties* VirtualBox::getSystemProperties() const
3686{
3687 return m->pSystemProperties;
3688}
3689
3690#ifdef VBOX_WITH_EXTPACK
3691/**
3692 * Getter that SystemProperties and others can use to talk to the extension
3693 * pack manager.
3694 */
3695ExtPackManager* VirtualBox::getExtPackManager() const
3696{
3697 return m->ptrExtPackManager;
3698}
3699#endif
3700
3701/**
3702 * Getter that machines can talk to the autostart database.
3703 */
3704AutostartDb* VirtualBox::getAutostartDb() const
3705{
3706 return m->pAutostartDb;
3707}
3708
3709#ifdef VBOX_WITH_RESOURCE_USAGE_API
3710const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
3711{
3712 return m->pPerformanceCollector;
3713}
3714#endif /* VBOX_WITH_RESOURCE_USAGE_API */
3715
3716/**
3717 * Returns the default machine folder from the system properties
3718 * with proper locking.
3719 * @return
3720 */
3721void VirtualBox::getDefaultMachineFolder(Utf8Str &str) const
3722{
3723 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3724 str = m->pSystemProperties->m->strDefaultMachineFolder;
3725}
3726
3727/**
3728 * Returns the default hard disk format from the system properties
3729 * with proper locking.
3730 * @return
3731 */
3732void VirtualBox::getDefaultHardDiskFormat(Utf8Str &str) const
3733{
3734 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3735 str = m->pSystemProperties->m->strDefaultHardDiskFormat;
3736}
3737
3738const Utf8Str& VirtualBox::homeDir() const
3739{
3740 return m->strHomeDir;
3741}
3742
3743/**
3744 * Calculates the absolute path of the given path taking the VirtualBox home
3745 * directory as the current directory.
3746 *
3747 * @param aPath Path to calculate the absolute path for.
3748 * @param aResult Where to put the result (used only on success, can be the
3749 * same Utf8Str instance as passed in @a aPath).
3750 * @return IPRT result.
3751 *
3752 * @note Doesn't lock any object.
3753 */
3754int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
3755{
3756 AutoCaller autoCaller(this);
3757 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
3758
3759 /* no need to lock since mHomeDir is const */
3760
3761 char folder[RTPATH_MAX];
3762 int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
3763 strPath.c_str(),
3764 folder,
3765 sizeof(folder));
3766 if (RT_SUCCESS(vrc))
3767 aResult = folder;
3768
3769 return vrc;
3770}
3771
3772/**
3773 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
3774 * if it is a subdirectory thereof, or simply copying it otherwise.
3775 *
3776 * @param strSource Path to evalue and copy.
3777 * @param strTarget Buffer to receive target path.
3778 */
3779void VirtualBox::copyPathRelativeToConfig(const Utf8Str &strSource,
3780 Utf8Str &strTarget)
3781{
3782 AutoCaller autoCaller(this);
3783 AssertComRCReturnVoid(autoCaller.rc());
3784
3785 // no need to lock since mHomeDir is const
3786
3787 // use strTarget as a temporary buffer to hold the machine settings dir
3788 strTarget = m->strHomeDir;
3789 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
3790 // is relative: then append what's left
3791 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
3792 else
3793 // is not relative: then overwrite
3794 strTarget = strSource;
3795}
3796
3797// private methods
3798/////////////////////////////////////////////////////////////////////////////
3799
3800/**
3801 * Checks if there is a hard disk, DVD or floppy image with the given ID or
3802 * location already registered.
3803 *
3804 * On return, sets @a aConflict to the string describing the conflicting medium,
3805 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3806 * either case. A failure is unexpected.
3807 *
3808 * @param aId UUID to check.
3809 * @param aLocation Location to check.
3810 * @param aConflict Where to return parameters of the conflicting medium.
3811 * @param ppMedium Medium reference in case this is simply a duplicate.
3812 *
3813 * @note Locks the media tree and media objects for reading.
3814 */
3815HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId,
3816 const Utf8Str &aLocation,
3817 Utf8Str &aConflict,
3818 ComObjPtr<Medium> *ppMedium)
3819{
3820 AssertReturn(!aId.isZero() && !aLocation.isEmpty(), E_FAIL);
3821 AssertReturn(ppMedium, E_INVALIDARG);
3822
3823 aConflict.setNull();
3824 ppMedium->setNull();
3825
3826 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3827
3828 HRESULT rc = S_OK;
3829
3830 ComObjPtr<Medium> pMediumFound;
3831 const char *pcszType = NULL;
3832
3833 if (aId.isValid() && !aId.isZero())
3834 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
3835 if (FAILED(rc) && !aLocation.isEmpty())
3836 rc = findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
3837 if (SUCCEEDED(rc))
3838 pcszType = tr("hard disk");
3839
3840 if (!pcszType)
3841 {
3842 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
3843 if (SUCCEEDED(rc))
3844 pcszType = tr("CD/DVD image");
3845 }
3846
3847 if (!pcszType)
3848 {
3849 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
3850 if (SUCCEEDED(rc))
3851 pcszType = tr("floppy image");
3852 }
3853
3854 if (pcszType && pMediumFound)
3855 {
3856 /* Note: no AutoCaller since bound to this */
3857 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
3858
3859 Utf8Str strLocFound = pMediumFound->getLocationFull();
3860 Guid idFound = pMediumFound->getId();
3861
3862 if ( (RTPathCompare(strLocFound.c_str(), aLocation.c_str()) == 0)
3863 && (idFound == aId)
3864 )
3865 *ppMedium = pMediumFound;
3866
3867 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
3868 pcszType,
3869 strLocFound.c_str(),
3870 idFound.raw());
3871 }
3872
3873 return S_OK;
3874}
3875
3876/**
3877 * Checks whether the given UUID is already in use by one medium for the
3878 * given device type.
3879 *
3880 * @returns true if the UUID is already in use
3881 * fale otherwise
3882 * @param aId The UUID to check.
3883 * @param deviceType The device type the UUID is going to be checked for
3884 * conflicts.
3885 */
3886bool VirtualBox::isMediaUuidInUse(const Guid &aId, DeviceType_T deviceType)
3887{
3888 /* A zero UUID is invalid here, always claim that it is already used. */
3889 AssertReturn(!aId.isZero(), true);
3890
3891 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3892
3893 HRESULT rc = S_OK;
3894 bool fInUse = false;
3895
3896 ComObjPtr<Medium> pMediumFound;
3897
3898 switch (deviceType)
3899 {
3900 case DeviceType_HardDisk:
3901 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
3902 break;
3903 case DeviceType_DVD:
3904 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
3905 break;
3906 case DeviceType_Floppy:
3907 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
3908 break;
3909 default:
3910 AssertMsgFailed(("Invalid device type %d\n", deviceType));
3911 }
3912
3913 if (SUCCEEDED(rc) && pMediumFound)
3914 fInUse = true;
3915
3916 return fInUse;
3917}
3918
3919/**
3920 * Called from Machine::prepareSaveSettings() when it has detected
3921 * that a machine has been renamed. Such renames will require
3922 * updating the global media registry during the
3923 * VirtualBox::saveSettings() that follows later.
3924*
3925 * When a machine is renamed, there may well be media (in particular,
3926 * diff images for snapshots) in the global registry that will need
3927 * to have their paths updated. Before 3.2, Machine::saveSettings
3928 * used to call VirtualBox::saveSettings implicitly, which was both
3929 * unintuitive and caused locking order problems. Now, we remember
3930 * such pending name changes with this method so that
3931 * VirtualBox::saveSettings() can process them properly.
3932 */
3933void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3934 const Utf8Str &strNewConfigDir)
3935{
3936 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3937
3938 Data::PendingMachineRename pmr;
3939 pmr.strConfigDirOld = strOldConfigDir;
3940 pmr.strConfigDirNew = strNewConfigDir;
3941 m->llPendingMachineRenames.push_back(pmr);
3942}
3943
3944struct SaveMediaRegistriesDesc
3945{
3946 MediaList llMedia;
3947 ComObjPtr<VirtualBox> pVirtualBox;
3948};
3949
3950static int fntSaveMediaRegistries(RTTHREAD ThreadSelf, void *pvUser)
3951{
3952 NOREF(ThreadSelf);
3953 SaveMediaRegistriesDesc *pDesc = (SaveMediaRegistriesDesc *)pvUser;
3954 if (!pDesc)
3955 {
3956 LogRelFunc(("Thread for saving media registries lacks parameters\n"));
3957 return VERR_INVALID_PARAMETER;
3958 }
3959
3960 for (MediaList::const_iterator it = pDesc->llMedia.begin();
3961 it != pDesc->llMedia.end();
3962 ++it)
3963 {
3964 Medium *pMedium = *it;
3965 pMedium->markRegistriesModified();
3966 }
3967
3968 pDesc->pVirtualBox->saveModifiedRegistries();
3969
3970 pDesc->llMedia.clear();
3971 pDesc->pVirtualBox.setNull();
3972 delete pDesc;
3973
3974 return VINF_SUCCESS;
3975}
3976
3977/**
3978 * Goes through all known media (hard disks, floppies and DVDs) and saves
3979 * those into the given settings::MediaRegistry structures whose registry
3980 * ID match the given UUID.
3981 *
3982 * Before actually writing to the structures, all media paths (not just the
3983 * ones for the given registry) are updated if machines have been renamed
3984 * since the last call.
3985 *
3986 * This gets called from two contexts:
3987 *
3988 * -- VirtualBox::saveSettings() with the UUID of the global registry
3989 * (VirtualBox::Data.uuidRegistry); this will save those media
3990 * which had been loaded from the global registry or have been
3991 * attached to a "legacy" machine which can't save its own registry;
3992 *
3993 * -- Machine::saveSettings() with the UUID of a machine, if a medium
3994 * has been attached to a machine created with VirtualBox 4.0 or later.
3995 *
3996 * Media which have only been temporarily opened without having been
3997 * attached to a machine have a NULL registry UUID and therefore don't
3998 * get saved.
3999 *
4000 * This locks the media tree. Throws HRESULT on errors!
4001 *
4002 * @param mediaRegistry Settings structure to fill.
4003 * @param uuidRegistry The UUID of the media registry; either a machine UUID (if machine registry) or the UUID of the global registry.
4004 * @param strMachineFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
4005 */
4006void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
4007 const Guid &uuidRegistry,
4008 const Utf8Str &strMachineFolder)
4009{
4010 // lock all media for the following; use a write lock because we're
4011 // modifying the PendingMachineRenamesList, which is protected by this
4012 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4013
4014 // if a machine was renamed, then we'll need to refresh media paths
4015 if (m->llPendingMachineRenames.size())
4016 {
4017 // make a single list from the three media lists so we don't need three loops
4018 MediaList llAllMedia;
4019 // with hard disks, we must use the map, not the list, because the list only has base images
4020 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
4021 llAllMedia.push_back(it->second);
4022 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
4023 llAllMedia.push_back(*it);
4024 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
4025 llAllMedia.push_back(*it);
4026
4027 SaveMediaRegistriesDesc *pDesc = new SaveMediaRegistriesDesc();
4028 for (MediaList::iterator it = llAllMedia.begin();
4029 it != llAllMedia.end();
4030 ++it)
4031 {
4032 Medium *pMedium = *it;
4033 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
4034 it2 != m->llPendingMachineRenames.end();
4035 ++it2)
4036 {
4037 const Data::PendingMachineRename &pmr = *it2;
4038 HRESULT rc = pMedium->updatePath(pmr.strConfigDirOld,
4039 pmr.strConfigDirNew);
4040 if (SUCCEEDED(rc))
4041 {
4042 // Remember which medium objects has been changed,
4043 // to trigger saving their registries later.
4044 pDesc->llMedia.push_back(pMedium);
4045 } else if (rc == VBOX_E_FILE_ERROR)
4046 /* nothing */;
4047 else
4048 AssertComRC(rc);
4049 }
4050 }
4051 // done, don't do it again until we have more machine renames
4052 m->llPendingMachineRenames.clear();
4053
4054 if (pDesc->llMedia.size())
4055 {
4056 // Handle the media registry saving in a separate thread, to
4057 // avoid giant locking problems and passing up the list many
4058 // levels up to whoever triggered saveSettings, as there are
4059 // lots of places which would need to handle saving more settings.
4060 pDesc->pVirtualBox = this;
4061 int vrc = RTThreadCreate(NULL,
4062 fntSaveMediaRegistries,
4063 (void *)pDesc,
4064 0, // cbStack (default)
4065 RTTHREADTYPE_MAIN_WORKER,
4066 0, // flags
4067 "SaveMediaReg");
4068 ComAssertRC(vrc);
4069 // failure means that settings aren't saved, but there isn't
4070 // much we can do besides avoiding memory leaks
4071 if (RT_FAILURE(vrc))
4072 {
4073 LogRelFunc(("Failed to create thread for saving media registries (%Rrc)\n", vrc));
4074 delete pDesc;
4075 }
4076 }
4077 else
4078 delete pDesc;
4079 }
4080
4081 struct {
4082 MediaOList &llSource;
4083 settings::MediaList &llTarget;
4084 } s[] =
4085 {
4086 // hard disks
4087 { m->allHardDisks, mediaRegistry.llHardDisks },
4088 // CD/DVD images
4089 { m->allDVDImages, mediaRegistry.llDvdImages },
4090 // floppy images
4091 { m->allFloppyImages, mediaRegistry.llFloppyImages }
4092 };
4093
4094 HRESULT rc;
4095
4096 for (size_t i = 0; i < RT_ELEMENTS(s); ++i)
4097 {
4098 MediaOList &llSource = s[i].llSource;
4099 settings::MediaList &llTarget = s[i].llTarget;
4100 llTarget.clear();
4101 for (MediaList::const_iterator it = llSource.begin();
4102 it != llSource.end();
4103 ++it)
4104 {
4105 Medium *pMedium = *it;
4106 AutoCaller autoCaller(pMedium);
4107 if (FAILED(autoCaller.rc())) throw autoCaller.rc();
4108 AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
4109
4110 if (pMedium->isInRegistry(uuidRegistry))
4111 {
4112 settings::Medium med;
4113 rc = pMedium->saveSettings(med, strMachineFolder); // this recurses into child hard disks
4114 if (FAILED(rc)) throw rc;
4115 llTarget.push_back(med);
4116 }
4117 }
4118 }
4119}
4120
4121/**
4122 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
4123 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
4124 * places internally when settings need saving.
4125 *
4126 * @note Caller must have locked the VirtualBox object for writing and must not hold any
4127 * other locks since this locks all kinds of member objects and trees temporarily,
4128 * which could cause conflicts.
4129 */
4130HRESULT VirtualBox::saveSettings()
4131{
4132 AutoCaller autoCaller(this);
4133 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4134
4135 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
4136 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
4137
4138 HRESULT rc = S_OK;
4139
4140 try
4141 {
4142 // machines
4143 m->pMainConfigFile->llMachines.clear();
4144 {
4145 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4146 for (MachinesOList::iterator it = m->allMachines.begin();
4147 it != m->allMachines.end();
4148 ++it)
4149 {
4150 Machine *pMachine = *it;
4151 // save actual machine registry entry
4152 settings::MachineRegistryEntry mre;
4153 rc = pMachine->saveRegistryEntry(mre);
4154 m->pMainConfigFile->llMachines.push_back(mre);
4155 }
4156 }
4157
4158 saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
4159 m->uuidMediaRegistry, // global media registry ID
4160 Utf8Str::Empty); // strMachineFolder
4161
4162 m->pMainConfigFile->llDhcpServers.clear();
4163 {
4164 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4165 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4166 it != m->allDHCPServers.end();
4167 ++it)
4168 {
4169 settings::DHCPServer d;
4170 rc = (*it)->saveSettings(d);
4171 if (FAILED(rc)) throw rc;
4172 m->pMainConfigFile->llDhcpServers.push_back(d);
4173 }
4174 }
4175
4176#ifdef VBOX_WITH_NAT_SERVICE
4177 /* Saving NAT Network configuration */
4178 m->pMainConfigFile->llNATNetworks.clear();
4179 {
4180 AutoReadLock natNetworkLock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4181 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
4182 it != m->allNATNetworks.end();
4183 ++it)
4184 {
4185 settings::NATNetwork n;
4186 rc = (*it)->saveSettings(n);
4187 if (FAILED(rc)) throw rc;
4188 m->pMainConfigFile->llNATNetworks.push_back(n);
4189 }
4190 }
4191#endif
4192
4193 // leave extra data alone, it's still in the config file
4194
4195 // host data (USB filters)
4196 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
4197 if (FAILED(rc)) throw rc;
4198
4199 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
4200 if (FAILED(rc)) throw rc;
4201
4202 // and write out the XML, still under the lock
4203 m->pMainConfigFile->write(m->strSettingsFilePath);
4204 }
4205 catch (HRESULT err)
4206 {
4207 /* we assume that error info is set by the thrower */
4208 rc = err;
4209 }
4210 catch (...)
4211 {
4212 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
4213 }
4214
4215 return rc;
4216}
4217
4218/**
4219 * Helper to register the machine.
4220 *
4221 * When called during VirtualBox startup, adds the given machine to the
4222 * collection of registered machines. Otherwise tries to mark the machine
4223 * as registered, and, if succeeded, adds it to the collection and
4224 * saves global settings.
4225 *
4226 * @note The caller must have added itself as a caller of the @a aMachine
4227 * object if calls this method not on VirtualBox startup.
4228 *
4229 * @param aMachine machine to register
4230 *
4231 * @note Locks objects!
4232 */
4233HRESULT VirtualBox::registerMachine(Machine *aMachine)
4234{
4235 ComAssertRet(aMachine, E_INVALIDARG);
4236
4237 AutoCaller autoCaller(this);
4238 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4239
4240 HRESULT rc = S_OK;
4241
4242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4243
4244 {
4245 ComObjPtr<Machine> pMachine;
4246 rc = findMachine(aMachine->getId(),
4247 true /* fPermitInaccessible */,
4248 false /* aDoSetError */,
4249 &pMachine);
4250 if (SUCCEEDED(rc))
4251 {
4252 /* sanity */
4253 AutoLimitedCaller machCaller(pMachine);
4254 AssertComRC(machCaller.rc());
4255
4256 return setError(E_INVALIDARG,
4257 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
4258 aMachine->getId().raw(),
4259 pMachine->getSettingsFileFull().c_str());
4260 }
4261
4262 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
4263 rc = S_OK;
4264 }
4265
4266 if (autoCaller.state() != InInit)
4267 {
4268 rc = aMachine->prepareRegister();
4269 if (FAILED(rc)) return rc;
4270 }
4271
4272 /* add to the collection of registered machines */
4273 m->allMachines.addChild(aMachine);
4274
4275 if (autoCaller.state() != InInit)
4276 rc = saveSettings();
4277
4278 return rc;
4279}
4280
4281/**
4282 * Remembers the given medium object by storing it in either the global
4283 * medium registry or a machine one.
4284 *
4285 * @note Caller must hold the media tree lock for writing; in addition, this
4286 * locks @a pMedium for reading
4287 *
4288 * @param pMedium Medium object to remember.
4289 * @param ppMedium Actually stored medium object. Can be different if due
4290 * to an unavoidable race there was a duplicate Medium object
4291 * created.
4292 * @param argType Either DeviceType_HardDisk, DeviceType_DVD or DeviceType_Floppy.
4293 * @return
4294 */
4295HRESULT VirtualBox::registerMedium(const ComObjPtr<Medium> &pMedium,
4296 ComObjPtr<Medium> *ppMedium,
4297 DeviceType_T argType)
4298{
4299 AssertReturn(pMedium != NULL, E_INVALIDARG);
4300 AssertReturn(ppMedium != NULL, E_INVALIDARG);
4301
4302 AutoCaller autoCaller(this);
4303 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4304
4305 AutoCaller mediumCaller(pMedium);
4306 AssertComRCReturn(mediumCaller.rc(), mediumCaller.rc());
4307
4308 const char *pszDevType = NULL;
4309 ObjectsList<Medium> *pall = NULL;
4310 switch (argType)
4311 {
4312 case DeviceType_HardDisk:
4313 pall = &m->allHardDisks;
4314 pszDevType = tr("hard disk");
4315 break;
4316 case DeviceType_DVD:
4317 pszDevType = tr("DVD image");
4318 pall = &m->allDVDImages;
4319 break;
4320 case DeviceType_Floppy:
4321 pszDevType = tr("floppy image");
4322 pall = &m->allFloppyImages;
4323 break;
4324 default:
4325 AssertMsgFailedReturn(("invalid device type %d", argType), E_INVALIDARG);
4326 }
4327
4328 // caller must hold the media tree write lock
4329 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4330
4331 Guid id;
4332 Utf8Str strLocationFull;
4333 ComObjPtr<Medium> pParent;
4334 {
4335 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4336 id = pMedium->getId();
4337 strLocationFull = pMedium->getLocationFull();
4338 pParent = pMedium->getParent();
4339 }
4340
4341 HRESULT rc;
4342
4343 Utf8Str strConflict;
4344 ComObjPtr<Medium> pDupMedium;
4345 rc = checkMediaForConflicts(id,
4346 strLocationFull,
4347 strConflict,
4348 &pDupMedium);
4349 if (FAILED(rc)) return rc;
4350
4351 if (pDupMedium.isNull())
4352 {
4353 if (strConflict.length())
4354 return setError(E_INVALIDARG,
4355 tr("Cannot register the %s '%s' {%RTuuid} because a %s already exists"),
4356 pszDevType,
4357 strLocationFull.c_str(),
4358 id.raw(),
4359 strConflict.c_str(),
4360 m->strSettingsFilePath.c_str());
4361
4362 // add to the collection if it is a base medium
4363 if (pParent.isNull())
4364 pall->getList().push_back(pMedium);
4365
4366 // store all hard disks (even differencing images) in the map
4367 if (argType == DeviceType_HardDisk)
4368 m->mapHardDisks[id] = pMedium;
4369
4370 *ppMedium = pMedium;
4371 }
4372 else
4373 {
4374 // pMedium may be the last reference to the Medium object, and the
4375 // caller may have specified the same ComObjPtr as the output parameter.
4376 // In this case the assignment will uninit the object, and we must not
4377 // have a caller pending.
4378 mediumCaller.release();
4379 *ppMedium = pDupMedium;
4380 }
4381
4382 return rc;
4383}
4384
4385/**
4386 * Removes the given medium from the respective registry.
4387 *
4388 * @param pMedium Hard disk object to remove.
4389 *
4390 * @note Caller must hold the media tree lock for writing; in addition, this locks @a pMedium for reading
4391 */
4392HRESULT VirtualBox::unregisterMedium(Medium *pMedium)
4393{
4394 AssertReturn(pMedium != NULL, E_INVALIDARG);
4395
4396 AutoCaller autoCaller(this);
4397 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4398
4399 AutoCaller mediumCaller(pMedium);
4400 AssertComRCReturn(mediumCaller.rc(), mediumCaller.rc());
4401
4402 // caller must hold the media tree write lock
4403 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4404
4405 Guid id;
4406 ComObjPtr<Medium> pParent;
4407 DeviceType_T devType;
4408 {
4409 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4410 id = pMedium->getId();
4411 pParent = pMedium->getParent();
4412 devType = pMedium->getDeviceType();
4413 }
4414
4415 ObjectsList<Medium> *pall = NULL;
4416 switch (devType)
4417 {
4418 case DeviceType_HardDisk:
4419 pall = &m->allHardDisks;
4420 break;
4421 case DeviceType_DVD:
4422 pall = &m->allDVDImages;
4423 break;
4424 case DeviceType_Floppy:
4425 pall = &m->allFloppyImages;
4426 break;
4427 default:
4428 AssertMsgFailedReturn(("invalid device type %d", devType), E_INVALIDARG);
4429 }
4430
4431 // remove from the collection if it is a base medium
4432 if (pParent.isNull())
4433 pall->getList().remove(pMedium);
4434
4435 // remove all hard disks (even differencing images) from map
4436 if (devType == DeviceType_HardDisk)
4437 {
4438 size_t cnt = m->mapHardDisks.erase(id);
4439 Assert(cnt == 1);
4440 NOREF(cnt);
4441 }
4442
4443 return S_OK;
4444}
4445
4446/**
4447 * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
4448 * with children appearing before their parents.
4449 * @param llMedia
4450 * @param pMedium
4451 */
4452void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
4453{
4454 // recurse first, then add ourselves; this way children end up on the
4455 // list before their parents
4456
4457 const MediaList &llChildren = pMedium->getChildren();
4458 for (MediaList::const_iterator it = llChildren.begin();
4459 it != llChildren.end();
4460 ++it)
4461 {
4462 Medium *pChild = *it;
4463 pushMediumToListWithChildren(llMedia, pChild);
4464 }
4465
4466 Log(("Pushing medium %RTuuid\n", pMedium->getId().raw()));
4467 llMedia.push_back(pMedium);
4468}
4469
4470/**
4471 * Unregisters all Medium objects which belong to the given machine registry.
4472 * Gets called from Machine::uninit() just before the machine object dies
4473 * and must only be called with a machine UUID as the registry ID.
4474 *
4475 * Locks the media tree.
4476 *
4477 * @param uuidMachine Medium registry ID (always a machine UUID)
4478 * @return
4479 */
4480HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine)
4481{
4482 Assert(!uuidMachine.isZero() && uuidMachine.isValid());
4483
4484 LogFlowFuncEnter();
4485
4486 AutoCaller autoCaller(this);
4487 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4488
4489 MediaList llMedia2Close;
4490
4491 {
4492 AutoWriteLock tlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4493
4494 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
4495 it != m->allHardDisks.getList().end();
4496 ++it)
4497 {
4498 ComObjPtr<Medium> pMedium = *it;
4499 AutoCaller medCaller(pMedium);
4500 if (FAILED(medCaller.rc())) return medCaller.rc();
4501 AutoReadLock medlock(pMedium COMMA_LOCKVAL_SRC_POS);
4502
4503 if (pMedium->isInRegistry(uuidMachine))
4504 // recursively with children first
4505 pushMediumToListWithChildren(llMedia2Close, pMedium);
4506 }
4507 }
4508
4509 for (MediaList::iterator it = llMedia2Close.begin();
4510 it != llMedia2Close.end();
4511 ++it)
4512 {
4513 ComObjPtr<Medium> pMedium = *it;
4514 Log(("Closing medium %RTuuid\n", pMedium->getId().raw()));
4515 AutoCaller mac(pMedium);
4516 pMedium->close(mac);
4517 }
4518
4519 LogFlowFuncLeave();
4520
4521 return S_OK;
4522}
4523
4524/**
4525 * Removes the given machine object from the internal list of registered machines.
4526 * Called from Machine::Unregister().
4527 * @param pMachine
4528 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
4529 * @return
4530 */
4531HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
4532 const Guid &id)
4533{
4534 // remove from the collection of registered machines
4535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4536 m->allMachines.removeChild(pMachine);
4537 // save the global registry
4538 HRESULT rc = saveSettings();
4539 alock.release();
4540
4541 /*
4542 * Now go over all known media and checks if they were registered in the
4543 * media registry of the given machine. Each such medium is then moved to
4544 * a different media registry to make sure it doesn't get lost since its
4545 * media registry is about to go away.
4546 *
4547 * This fixes the following use case: Image A.vdi of machine A is also used
4548 * by machine B, but registered in the media registry of machine A. If machine
4549 * A is deleted, A.vdi must be moved to the registry of B, or else B will
4550 * become inaccessible.
4551 */
4552 {
4553 AutoReadLock tlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4554 // iterate over the list of *base* images
4555 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
4556 it != m->allHardDisks.getList().end();
4557 ++it)
4558 {
4559 ComObjPtr<Medium> &pMedium = *it;
4560 AutoCaller medCaller(pMedium);
4561 if (FAILED(medCaller.rc())) return medCaller.rc();
4562 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
4563
4564 if (pMedium->removeRegistry(id, true /* fRecurse */))
4565 {
4566 // machine ID was found in base medium's registry list:
4567 // move this base image and all its children to another registry then
4568 // 1) first, find a better registry to add things to
4569 const Guid *puuidBetter = pMedium->getAnyMachineBackref();
4570 if (puuidBetter)
4571 {
4572 // 2) better registry found: then use that
4573 pMedium->addRegistry(*puuidBetter, true /* fRecurse */);
4574 // 3) and make sure the registry is saved below
4575 mlock.release();
4576 tlock.release();
4577 markRegistryModified(*puuidBetter);
4578 tlock.acquire();
4579 mlock.release();
4580 }
4581 }
4582 }
4583 }
4584
4585 saveModifiedRegistries();
4586
4587 /* fire an event */
4588 onMachineRegistered(id, FALSE);
4589
4590 return rc;
4591}
4592
4593/**
4594 * Marks the registry for @a uuid as modified, so that it's saved in a later
4595 * call to saveModifiedRegistries().
4596 *
4597 * @param uuid
4598 */
4599void VirtualBox::markRegistryModified(const Guid &uuid)
4600{
4601 if (uuid == getGlobalRegistryId())
4602 ASMAtomicIncU64(&m->uRegistryNeedsSaving);
4603 else
4604 {
4605 ComObjPtr<Machine> pMachine;
4606 HRESULT rc = findMachine(uuid,
4607 false /* fPermitInaccessible */,
4608 false /* aSetError */,
4609 &pMachine);
4610 if (SUCCEEDED(rc))
4611 {
4612 AutoCaller machineCaller(pMachine);
4613 if (SUCCEEDED(machineCaller.rc()))
4614 ASMAtomicIncU64(&pMachine->uRegistryNeedsSaving);
4615 }
4616 }
4617}
4618
4619/**
4620 * Saves all settings files according to the modified flags in the Machine
4621 * objects and in the VirtualBox object.
4622 *
4623 * This locks machines and the VirtualBox object as necessary, so better not
4624 * hold any locks before calling this.
4625 *
4626 * @return
4627 */
4628void VirtualBox::saveModifiedRegistries()
4629{
4630 HRESULT rc = S_OK;
4631 bool fNeedsGlobalSettings = false;
4632 uint64_t uOld;
4633
4634 {
4635 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4636 for (MachinesOList::iterator it = m->allMachines.begin();
4637 it != m->allMachines.end();
4638 ++it)
4639 {
4640 const ComObjPtr<Machine> &pMachine = *it;
4641
4642 for (;;)
4643 {
4644 uOld = ASMAtomicReadU64(&pMachine->uRegistryNeedsSaving);
4645 if (!uOld)
4646 break;
4647 if (ASMAtomicCmpXchgU64(&pMachine->uRegistryNeedsSaving, 0, uOld))
4648 break;
4649 ASMNopPause();
4650 }
4651 if (uOld)
4652 {
4653 AutoCaller autoCaller(pMachine);
4654 if (FAILED(autoCaller.rc()))
4655 continue;
4656 /* object is already dead, no point in saving settings */
4657 if (autoCaller.state() != Ready)
4658 continue;
4659 AutoWriteLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
4660 rc = pMachine->saveSettings(&fNeedsGlobalSettings,
4661 Machine::SaveS_Force); // caller said save, so stop arguing
4662 }
4663 }
4664 }
4665
4666 for (;;)
4667 {
4668 uOld = ASMAtomicReadU64(&m->uRegistryNeedsSaving);
4669 if (!uOld)
4670 break;
4671 if (ASMAtomicCmpXchgU64(&m->uRegistryNeedsSaving, 0, uOld))
4672 break;
4673 ASMNopPause();
4674 }
4675 if (uOld || fNeedsGlobalSettings)
4676 {
4677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4678 rc = saveSettings();
4679 }
4680 NOREF(rc); /* XXX */
4681}
4682
4683
4684/* static */
4685const Bstr &VirtualBox::getVersionNormalized()
4686{
4687 return sVersionNormalized;
4688}
4689
4690/**
4691 * Checks if the path to the specified file exists, according to the path
4692 * information present in the file name. Optionally the path is created.
4693 *
4694 * Note that the given file name must contain the full path otherwise the
4695 * extracted relative path will be created based on the current working
4696 * directory which is normally unknown.
4697 *
4698 * @param aFileName Full file name which path is checked/created.
4699 * @param aCreate Flag if the path should be created if it doesn't exist.
4700 *
4701 * @return Extended error information on failure to check/create the path.
4702 */
4703/* static */
4704HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName, bool fCreate)
4705{
4706 Utf8Str strDir(strFileName);
4707 strDir.stripFilename();
4708 if (!RTDirExists(strDir.c_str()))
4709 {
4710 if (fCreate)
4711 {
4712 int vrc = RTDirCreateFullPath(strDir.c_str(), 0700);
4713 if (RT_FAILURE(vrc))
4714 return setErrorStatic(VBOX_E_IPRT_ERROR,
4715 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
4716 strDir.c_str(),
4717 vrc));
4718 }
4719 else
4720 return setErrorStatic(VBOX_E_IPRT_ERROR,
4721 Utf8StrFmt(tr("Directory '%s' does not exist"),
4722 strDir.c_str()));
4723 }
4724
4725 return S_OK;
4726}
4727
4728const Utf8Str& VirtualBox::settingsFilePath()
4729{
4730 return m->strSettingsFilePath;
4731}
4732
4733/**
4734 * Returns the lock handle which protects the media trees (hard disks,
4735 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
4736 * are no longer protected by the VirtualBox lock, but by this more
4737 * specialized lock. Mind the locking order: always request this lock
4738 * after the VirtualBox object lock but before the locks of the media
4739 * objects contained in these lists. See AutoLock.h.
4740 */
4741RWLockHandle& VirtualBox::getMediaTreeLockHandle()
4742{
4743 return m->lockMedia;
4744}
4745
4746/**
4747 * Thread function that handles custom events posted using #postEvent().
4748 */
4749// static
4750DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4751{
4752 LogFlowFuncEnter();
4753
4754 AssertReturn(pvUser, VERR_INVALID_POINTER);
4755
4756 HRESULT hr = com::Initialize();
4757 if (FAILED(hr))
4758 return VERR_COM_UNEXPECTED;
4759
4760 int rc = VINF_SUCCESS;
4761
4762 try
4763 {
4764 /* Create an event queue for the current thread. */
4765 EventQueue *pEventQueue = new EventQueue();
4766 AssertPtr(pEventQueue);
4767
4768 /* Return the queue to the one who created this thread. */
4769 *(static_cast <EventQueue **>(pvUser)) = pEventQueue;
4770
4771 /* signal that we're ready. */
4772 RTThreadUserSignal(thread);
4773
4774 /*
4775 * In case of spurious wakeups causing VERR_TIMEOUTs and/or other return codes
4776 * we must not stop processing events and delete the pEventQueue object. This must
4777 * be done ONLY when we stop this loop via interruptEventQueueProcessing().
4778 * See @bugref{5724}.
4779 */
4780 for (;;)
4781 {
4782 rc = pEventQueue->processEventQueue(RT_INDEFINITE_WAIT);
4783 if (rc == VERR_INTERRUPTED)
4784 {
4785 LogFlow(("Event queue processing ended with rc=%Rrc\n", rc));
4786 rc = VINF_SUCCESS; /* Set success when exiting. */
4787 break;
4788 }
4789 }
4790
4791 delete pEventQueue;
4792 }
4793 catch (std::bad_alloc &ba)
4794 {
4795 rc = VERR_NO_MEMORY;
4796 NOREF(ba);
4797 }
4798
4799 com::Shutdown();
4800
4801 LogFlowFuncLeaveRC(rc);
4802 return rc;
4803}
4804
4805
4806////////////////////////////////////////////////////////////////////////////////
4807
4808/**
4809 * Takes the current list of registered callbacks of the managed VirtualBox
4810 * instance, and calls #handleCallback() for every callback item from the
4811 * list, passing the item as an argument.
4812 *
4813 * @note Locks the managed VirtualBox object for reading but leaves the lock
4814 * before iterating over callbacks and calling their methods.
4815 */
4816void *VirtualBox::CallbackEvent::handler()
4817{
4818 if (!mVirtualBox)
4819 return NULL;
4820
4821 AutoCaller autoCaller(mVirtualBox);
4822 if (!autoCaller.isOk())
4823 {
4824 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4825 autoCaller.state()));
4826 /* We don't need mVirtualBox any more, so release it */
4827 mVirtualBox = NULL;
4828 return NULL;
4829 }
4830
4831 {
4832 VBoxEventDesc evDesc;
4833 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4834
4835 evDesc.fire(/* don't wait for delivery */0);
4836 }
4837
4838 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4839 return NULL;
4840}
4841
4842//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4843//{
4844// return E_NOTIMPL;
4845//}
4846
4847STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4848{
4849 CheckComArgStrNotEmptyOrNull(aName);
4850 CheckComArgNotNull(aServer);
4851
4852 AutoCaller autoCaller(this);
4853 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4854
4855 ComObjPtr<DHCPServer> dhcpServer;
4856 dhcpServer.createObject();
4857 HRESULT rc = dhcpServer->init(this, aName);
4858 if (FAILED(rc)) return rc;
4859
4860 rc = registerDHCPServer(dhcpServer, true);
4861 if (FAILED(rc)) return rc;
4862
4863 dhcpServer.queryInterfaceTo(aServer);
4864
4865 return rc;
4866}
4867
4868STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4869{
4870 CheckComArgStrNotEmptyOrNull(aName);
4871 CheckComArgNotNull(aServer);
4872
4873 AutoCaller autoCaller(this);
4874 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4875
4876 HRESULT rc;
4877 Bstr bstr;
4878 ComPtr<DHCPServer> found;
4879
4880 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4881
4882 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4883 it != m->allDHCPServers.end();
4884 ++it)
4885 {
4886 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4887 if (FAILED(rc)) return rc;
4888
4889 if (bstr == aName)
4890 {
4891 found = *it;
4892 break;
4893 }
4894 }
4895
4896 if (!found)
4897 return E_INVALIDARG;
4898
4899 return found.queryInterfaceTo(aServer);
4900}
4901
4902STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4903{
4904 CheckComArgNotNull(aServer);
4905
4906 AutoCaller autoCaller(this);
4907 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4908
4909 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4910
4911 return rc;
4912}
4913
4914/**
4915 * Remembers the given DHCP server in the settings.
4916 *
4917 * @param aDHCPServer DHCP server object to remember.
4918 * @param aSaveSettings @c true to save settings to disk (default).
4919 *
4920 * When @a aSaveSettings is @c true, this operation may fail because of the
4921 * failed #saveSettings() method it calls. In this case, the dhcp server object
4922 * will not be remembered. It is therefore the responsibility of the caller to
4923 * call this method as the last step of some action that requires registration
4924 * in order to make sure that only fully functional dhcp server objects get
4925 * registered.
4926 *
4927 * @note Locks this object for writing and @a aDHCPServer for reading.
4928 */
4929HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4930 bool aSaveSettings /*= true*/)
4931{
4932 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4933
4934 AutoCaller autoCaller(this);
4935 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4936
4937 AutoCaller dhcpServerCaller(aDHCPServer);
4938 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4939
4940 Bstr name;
4941 HRESULT rc;
4942 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4943 if (FAILED(rc)) return rc;
4944
4945 ComPtr<IDHCPServer> existing;
4946 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
4947 if (SUCCEEDED(rc))
4948 return E_INVALIDARG;
4949
4950 rc = S_OK;
4951
4952 m->allDHCPServers.addChild(aDHCPServer);
4953
4954 if (aSaveSettings)
4955 {
4956 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4957 rc = saveSettings();
4958 vboxLock.release();
4959
4960 if (FAILED(rc))
4961 unregisterDHCPServer(aDHCPServer, false /* aSaveSettings */);
4962 }
4963
4964 return rc;
4965}
4966
4967/**
4968 * Removes the given DHCP server from the settings.
4969 *
4970 * @param aDHCPServer DHCP server object to remove.
4971 * @param aSaveSettings @c true to save settings to disk (default).
4972 *
4973 * When @a aSaveSettings is @c true, this operation may fail because of the
4974 * failed #saveSettings() method it calls. In this case, the DHCP server
4975 * will NOT be removed from the settingsi when this method returns.
4976 *
4977 * @note Locks this object for writing.
4978 */
4979HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4980 bool aSaveSettings /*= true*/)
4981{
4982 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4983
4984 AutoCaller autoCaller(this);
4985 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4986
4987 AutoCaller dhcpServerCaller(aDHCPServer);
4988 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4989
4990 m->allDHCPServers.removeChild(aDHCPServer);
4991
4992 HRESULT rc = S_OK;
4993
4994 if (aSaveSettings)
4995 {
4996 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4997 rc = saveSettings();
4998 vboxLock.release();
4999
5000 if (FAILED(rc))
5001 registerDHCPServer(aDHCPServer, false /* aSaveSettings */);
5002 }
5003
5004 return rc;
5005}
5006
5007
5008/**
5009 * NAT Network
5010 */
5011
5012STDMETHODIMP VirtualBox::CreateNATNetwork(IN_BSTR aName, INATNetwork ** aNatNetwork)
5013{
5014#ifdef VBOX_WITH_NAT_SERVICE
5015 CheckComArgStrNotEmptyOrNull(aName);
5016 CheckComArgNotNull(aNatNetwork);
5017
5018 AutoCaller autoCaller(this);
5019 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5020
5021 ComObjPtr<NATNetwork> natNetwork;
5022 natNetwork.createObject();
5023 HRESULT rc = natNetwork->init(this, aName);
5024 if (FAILED(rc)) return rc;
5025
5026 rc = registerNATNetwork(natNetwork, true);
5027 if (FAILED(rc)) return rc;
5028
5029 natNetwork.queryInterfaceTo(aNatNetwork);
5030
5031 fireNATNetworkCreationDeletionEvent(m->pEventSource, aName, TRUE);
5032 return rc;
5033#else
5034 NOREF(aName);
5035 NOREF(aNatNetwork);
5036 return E_NOTIMPL;
5037#endif
5038}
5039
5040STDMETHODIMP VirtualBox::FindNATNetworkByName(IN_BSTR aName, INATNetwork ** aNetwork)
5041{
5042#ifdef VBOX_WITH_NAT_SERVICE
5043 CheckComArgStrNotEmptyOrNull(aName);
5044 CheckComArgNotNull(aNetwork);
5045
5046 AutoCaller autoCaller(this);
5047 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5048
5049 HRESULT rc;
5050 Bstr bstr;
5051 ComPtr<NATNetwork> found;
5052
5053 AutoReadLock alock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5054
5055 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
5056 it != m->allNATNetworks.end();
5057 ++it)
5058 {
5059 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
5060 if (FAILED(rc)) return rc;
5061
5062 if (bstr == aName)
5063 {
5064 found = *it;
5065 break;
5066 }
5067 }
5068
5069 if (!found)
5070 return E_INVALIDARG;
5071
5072 return found.queryInterfaceTo(aNetwork);
5073#else
5074 NOREF(aName);
5075 NOREF(aNetwork);
5076 return E_NOTIMPL;
5077#endif
5078}
5079
5080STDMETHODIMP VirtualBox::RemoveNATNetwork(INATNetwork * aNetwork)
5081{
5082#ifdef VBOX_WITH_NAT_SERVICE
5083 CheckComArgNotNull(aNetwork);
5084
5085 AutoCaller autoCaller(this);
5086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5087 Bstr name;
5088 HRESULT rc;
5089 NATNetwork *network = static_cast<NATNetwork *>(aNetwork);
5090 rc = network->COMGETTER(NetworkName)(name.asOutParam());
5091 rc = unregisterNATNetwork(network, true);
5092 fireNATNetworkCreationDeletionEvent(m->pEventSource, name.raw(), FALSE);
5093 return rc;
5094#else
5095 NOREF(aNetwork);
5096 return E_NOTIMPL;
5097#endif
5098
5099}
5100/**
5101 * Remembers the given NAT network in the settings.
5102 *
5103 * @param aNATNetwork NAT Network object to remember.
5104 * @param aSaveSettings @c true to save settings to disk (default).
5105 *
5106 *
5107 * @note Locks this object for writing and @a aNATNetwork for reading.
5108 */
5109HRESULT VirtualBox::registerNATNetwork(NATNetwork *aNATNetwork,
5110 bool aSaveSettings /*= true*/)
5111{
5112#ifdef VBOX_WITH_NAT_SERVICE
5113 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
5114
5115 AutoCaller autoCaller(this);
5116 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5117
5118 AutoCaller natNetworkCaller(aNATNetwork);
5119 AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc());
5120
5121 Bstr name;
5122 HRESULT rc;
5123 rc = aNATNetwork->COMGETTER(NetworkName)(name.asOutParam());
5124 if (FAILED(rc)) return rc;
5125
5126 ComPtr<INATNetwork> existing;
5127 rc = FindNATNetworkByName(name.raw(), existing.asOutParam());
5128 if (SUCCEEDED(rc))
5129 return E_INVALIDARG;
5130
5131 rc = S_OK;
5132
5133 m->allNATNetworks.addChild(aNATNetwork);
5134
5135 if (aSaveSettings)
5136 {
5137 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
5138 rc = saveSettings();
5139 vboxLock.release();
5140
5141 if (FAILED(rc))
5142 unregisterNATNetwork(aNATNetwork, false /* aSaveSettings */);
5143 }
5144
5145 return rc;
5146#else
5147 NOREF(aNATNetwork);
5148 NOREF(aSaveSettings);
5149 /* No panic please (silently ignore) */
5150 return S_OK;
5151#endif
5152}
5153
5154/**
5155 * Removes the given NAT network from the settings.
5156 *
5157 * @param aNATNetwork NAT network object to remove.
5158 * @param aSaveSettings @c true to save settings to disk (default).
5159 *
5160 * When @a aSaveSettings is @c true, this operation may fail because of the
5161 * failed #saveSettings() method it calls. In this case, the DHCP server
5162 * will NOT be removed from the settingsi when this method returns.
5163 *
5164 * @note Locks this object for writing.
5165 */
5166HRESULT VirtualBox::unregisterNATNetwork(NATNetwork *aNATNetwork,
5167 bool aSaveSettings /*= true*/)
5168{
5169#ifdef VBOX_WITH_NAT_SERVICE
5170 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
5171
5172 AutoCaller autoCaller(this);
5173 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5174
5175 AutoCaller natNetworkCaller(aNATNetwork);
5176 AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc());
5177
5178 m->allNATNetworks.removeChild(aNATNetwork);
5179
5180 HRESULT rc = S_OK;
5181
5182 if (aSaveSettings)
5183 {
5184 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
5185 rc = saveSettings();
5186 vboxLock.release();
5187
5188 if (FAILED(rc))
5189 registerNATNetwork(aNATNetwork, false /* aSaveSettings */);
5190 }
5191
5192 return rc;
5193#else
5194 NOREF(aNATNetwork);
5195 NOREF(aSaveSettings);
5196 return E_NOTIMPL;
5197#endif
5198}
5199/* 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