VirtualBox

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

Last change on this file since 48431 was 48297, checked in by vboxsync, 11 years ago

Main/Medium: redesign API level medium locking, needed conversions from MediaList to MediumLockLists in several places, forced cleanups elsewhere, too
Main/Token: introduced token objects for controlling the unlocking, will be used as a general concept in the future
Main/Snapshot: snapshot deletion needed significant cleanups as it was still using many shortcuts, directly calling the API to lock media instead of using lock lists. Now much better, and the online snapshot deletion is also a lot cleaner as it no longer passes unnecessary parameters around which are already known in the machine/snapshot code
Main/MediumLock: small improvements, now has a mode which skips locking already locked media, needed by the Snapshot code where we have overlapping lock lists and have to update the original one instead
Main/Console+Session+Machine: follow-up changes for the online snapshot merging parameter passing simplification, plus an unrelated lock order violation fix in Machine which happens only for inaccessible machines
Main/testcase: update correspondingly
doc: update SDK reference

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 163.0 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 48297 2013-09-05 09:57:44Z 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 machines list. As opposed
4735 * to version 3.1 and earlier, these lists are no longer protected by the
4736 * VirtualBox lock, but by this more specialized lock. Mind the locking
4737 * order: always request this lock after the VirtualBox object lock but
4738 * before the locks of any machine object. See AutoLock.h.
4739 */
4740RWLockHandle& VirtualBox::getMachinesListLockHandle()
4741{
4742 return m->lockMachines;
4743}
4744
4745/**
4746 * Returns the lock handle which protects the media trees (hard disks,
4747 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
4748 * are no longer protected by the VirtualBox lock, but by this more
4749 * specialized lock. Mind the locking order: always request this lock
4750 * after the VirtualBox object lock but before the locks of the media
4751 * objects contained in these lists. See AutoLock.h.
4752 */
4753RWLockHandle& VirtualBox::getMediaTreeLockHandle()
4754{
4755 return m->lockMedia;
4756}
4757
4758/**
4759 * Thread function that handles custom events posted using #postEvent().
4760 */
4761// static
4762DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4763{
4764 LogFlowFuncEnter();
4765
4766 AssertReturn(pvUser, VERR_INVALID_POINTER);
4767
4768 HRESULT hr = com::Initialize();
4769 if (FAILED(hr))
4770 return VERR_COM_UNEXPECTED;
4771
4772 int rc = VINF_SUCCESS;
4773
4774 try
4775 {
4776 /* Create an event queue for the current thread. */
4777 EventQueue *pEventQueue = new EventQueue();
4778 AssertPtr(pEventQueue);
4779
4780 /* Return the queue to the one who created this thread. */
4781 *(static_cast <EventQueue **>(pvUser)) = pEventQueue;
4782
4783 /* signal that we're ready. */
4784 RTThreadUserSignal(thread);
4785
4786 /*
4787 * In case of spurious wakeups causing VERR_TIMEOUTs and/or other return codes
4788 * we must not stop processing events and delete the pEventQueue object. This must
4789 * be done ONLY when we stop this loop via interruptEventQueueProcessing().
4790 * See @bugref{5724}.
4791 */
4792 for (;;)
4793 {
4794 rc = pEventQueue->processEventQueue(RT_INDEFINITE_WAIT);
4795 if (rc == VERR_INTERRUPTED)
4796 {
4797 LogFlow(("Event queue processing ended with rc=%Rrc\n", rc));
4798 rc = VINF_SUCCESS; /* Set success when exiting. */
4799 break;
4800 }
4801 }
4802
4803 delete pEventQueue;
4804 }
4805 catch (std::bad_alloc &ba)
4806 {
4807 rc = VERR_NO_MEMORY;
4808 NOREF(ba);
4809 }
4810
4811 com::Shutdown();
4812
4813 LogFlowFuncLeaveRC(rc);
4814 return rc;
4815}
4816
4817
4818////////////////////////////////////////////////////////////////////////////////
4819
4820/**
4821 * Takes the current list of registered callbacks of the managed VirtualBox
4822 * instance, and calls #handleCallback() for every callback item from the
4823 * list, passing the item as an argument.
4824 *
4825 * @note Locks the managed VirtualBox object for reading but leaves the lock
4826 * before iterating over callbacks and calling their methods.
4827 */
4828void *VirtualBox::CallbackEvent::handler()
4829{
4830 if (!mVirtualBox)
4831 return NULL;
4832
4833 AutoCaller autoCaller(mVirtualBox);
4834 if (!autoCaller.isOk())
4835 {
4836 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4837 autoCaller.state()));
4838 /* We don't need mVirtualBox any more, so release it */
4839 mVirtualBox = NULL;
4840 return NULL;
4841 }
4842
4843 {
4844 VBoxEventDesc evDesc;
4845 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4846
4847 evDesc.fire(/* don't wait for delivery */0);
4848 }
4849
4850 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4851 return NULL;
4852}
4853
4854//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4855//{
4856// return E_NOTIMPL;
4857//}
4858
4859STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4860{
4861 CheckComArgStrNotEmptyOrNull(aName);
4862 CheckComArgNotNull(aServer);
4863
4864 AutoCaller autoCaller(this);
4865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4866
4867 ComObjPtr<DHCPServer> dhcpServer;
4868 dhcpServer.createObject();
4869 HRESULT rc = dhcpServer->init(this, aName);
4870 if (FAILED(rc)) return rc;
4871
4872 rc = registerDHCPServer(dhcpServer, true);
4873 if (FAILED(rc)) return rc;
4874
4875 dhcpServer.queryInterfaceTo(aServer);
4876
4877 return rc;
4878}
4879
4880STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4881{
4882 CheckComArgStrNotEmptyOrNull(aName);
4883 CheckComArgNotNull(aServer);
4884
4885 AutoCaller autoCaller(this);
4886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4887
4888 HRESULT rc;
4889 Bstr bstr;
4890 ComPtr<DHCPServer> found;
4891
4892 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4893
4894 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4895 it != m->allDHCPServers.end();
4896 ++it)
4897 {
4898 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4899 if (FAILED(rc)) return rc;
4900
4901 if (bstr == aName)
4902 {
4903 found = *it;
4904 break;
4905 }
4906 }
4907
4908 if (!found)
4909 return E_INVALIDARG;
4910
4911 return found.queryInterfaceTo(aServer);
4912}
4913
4914STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4915{
4916 CheckComArgNotNull(aServer);
4917
4918 AutoCaller autoCaller(this);
4919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4920
4921 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4922
4923 return rc;
4924}
4925
4926/**
4927 * Remembers the given DHCP server in the settings.
4928 *
4929 * @param aDHCPServer DHCP server object to remember.
4930 * @param aSaveSettings @c true to save settings to disk (default).
4931 *
4932 * When @a aSaveSettings is @c true, this operation may fail because of the
4933 * failed #saveSettings() method it calls. In this case, the dhcp server object
4934 * will not be remembered. It is therefore the responsibility of the caller to
4935 * call this method as the last step of some action that requires registration
4936 * in order to make sure that only fully functional dhcp server objects get
4937 * registered.
4938 *
4939 * @note Locks this object for writing and @a aDHCPServer for reading.
4940 */
4941HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4942 bool aSaveSettings /*= true*/)
4943{
4944 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4945
4946 AutoCaller autoCaller(this);
4947 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4948
4949 AutoCaller dhcpServerCaller(aDHCPServer);
4950 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4951
4952 Bstr name;
4953 HRESULT rc;
4954 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4955 if (FAILED(rc)) return rc;
4956
4957 ComPtr<IDHCPServer> existing;
4958 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
4959 if (SUCCEEDED(rc))
4960 return E_INVALIDARG;
4961
4962 rc = S_OK;
4963
4964 m->allDHCPServers.addChild(aDHCPServer);
4965
4966 if (aSaveSettings)
4967 {
4968 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4969 rc = saveSettings();
4970 vboxLock.release();
4971
4972 if (FAILED(rc))
4973 unregisterDHCPServer(aDHCPServer, false /* aSaveSettings */);
4974 }
4975
4976 return rc;
4977}
4978
4979/**
4980 * Removes the given DHCP server from the settings.
4981 *
4982 * @param aDHCPServer DHCP server object to remove.
4983 * @param aSaveSettings @c true to save settings to disk (default).
4984 *
4985 * When @a aSaveSettings is @c true, this operation may fail because of the
4986 * failed #saveSettings() method it calls. In this case, the DHCP server
4987 * will NOT be removed from the settingsi when this method returns.
4988 *
4989 * @note Locks this object for writing.
4990 */
4991HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4992 bool aSaveSettings /*= true*/)
4993{
4994 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4995
4996 AutoCaller autoCaller(this);
4997 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4998
4999 AutoCaller dhcpServerCaller(aDHCPServer);
5000 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
5001
5002 m->allDHCPServers.removeChild(aDHCPServer);
5003
5004 HRESULT rc = S_OK;
5005
5006 if (aSaveSettings)
5007 {
5008 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
5009 rc = saveSettings();
5010 vboxLock.release();
5011
5012 if (FAILED(rc))
5013 registerDHCPServer(aDHCPServer, false /* aSaveSettings */);
5014 }
5015
5016 return rc;
5017}
5018
5019
5020/**
5021 * NAT Network
5022 */
5023
5024STDMETHODIMP VirtualBox::CreateNATNetwork(IN_BSTR aName, INATNetwork ** aNatNetwork)
5025{
5026#ifdef VBOX_WITH_NAT_SERVICE
5027 CheckComArgStrNotEmptyOrNull(aName);
5028 CheckComArgNotNull(aNatNetwork);
5029
5030 AutoCaller autoCaller(this);
5031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5032
5033 ComObjPtr<NATNetwork> natNetwork;
5034 natNetwork.createObject();
5035 HRESULT rc = natNetwork->init(this, aName);
5036 if (FAILED(rc)) return rc;
5037
5038 rc = registerNATNetwork(natNetwork, true);
5039 if (FAILED(rc)) return rc;
5040
5041 natNetwork.queryInterfaceTo(aNatNetwork);
5042
5043 fireNATNetworkCreationDeletionEvent(m->pEventSource, aName, TRUE);
5044 return rc;
5045#else
5046 NOREF(aName);
5047 NOREF(aNatNetwork);
5048 return E_NOTIMPL;
5049#endif
5050}
5051
5052STDMETHODIMP VirtualBox::FindNATNetworkByName(IN_BSTR aName, INATNetwork ** aNetwork)
5053{
5054#ifdef VBOX_WITH_NAT_SERVICE
5055 CheckComArgStrNotEmptyOrNull(aName);
5056 CheckComArgNotNull(aNetwork);
5057
5058 AutoCaller autoCaller(this);
5059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5060
5061 HRESULT rc;
5062 Bstr bstr;
5063 ComPtr<NATNetwork> found;
5064
5065 AutoReadLock alock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5066
5067 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
5068 it != m->allNATNetworks.end();
5069 ++it)
5070 {
5071 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
5072 if (FAILED(rc)) return rc;
5073
5074 if (bstr == aName)
5075 {
5076 found = *it;
5077 break;
5078 }
5079 }
5080
5081 if (!found)
5082 return E_INVALIDARG;
5083
5084 return found.queryInterfaceTo(aNetwork);
5085#else
5086 NOREF(aName);
5087 NOREF(aNetwork);
5088 return E_NOTIMPL;
5089#endif
5090}
5091
5092STDMETHODIMP VirtualBox::RemoveNATNetwork(INATNetwork * aNetwork)
5093{
5094#ifdef VBOX_WITH_NAT_SERVICE
5095 CheckComArgNotNull(aNetwork);
5096
5097 AutoCaller autoCaller(this);
5098 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5099 Bstr name;
5100 HRESULT rc;
5101 NATNetwork *network = static_cast<NATNetwork *>(aNetwork);
5102 rc = network->COMGETTER(NetworkName)(name.asOutParam());
5103 rc = unregisterNATNetwork(network, true);
5104 fireNATNetworkCreationDeletionEvent(m->pEventSource, name.raw(), FALSE);
5105 return rc;
5106#else
5107 NOREF(aNetwork);
5108 return E_NOTIMPL;
5109#endif
5110
5111}
5112/**
5113 * Remembers the given NAT network in the settings.
5114 *
5115 * @param aNATNetwork NAT Network object to remember.
5116 * @param aSaveSettings @c true to save settings to disk (default).
5117 *
5118 *
5119 * @note Locks this object for writing and @a aNATNetwork for reading.
5120 */
5121HRESULT VirtualBox::registerNATNetwork(NATNetwork *aNATNetwork,
5122 bool aSaveSettings /*= true*/)
5123{
5124#ifdef VBOX_WITH_NAT_SERVICE
5125 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
5126
5127 AutoCaller autoCaller(this);
5128 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5129
5130 AutoCaller natNetworkCaller(aNATNetwork);
5131 AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc());
5132
5133 Bstr name;
5134 HRESULT rc;
5135 rc = aNATNetwork->COMGETTER(NetworkName)(name.asOutParam());
5136 if (FAILED(rc)) return rc;
5137
5138 ComPtr<INATNetwork> existing;
5139 rc = FindNATNetworkByName(name.raw(), existing.asOutParam());
5140 if (SUCCEEDED(rc))
5141 return E_INVALIDARG;
5142
5143 rc = S_OK;
5144
5145 m->allNATNetworks.addChild(aNATNetwork);
5146
5147 if (aSaveSettings)
5148 {
5149 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
5150 rc = saveSettings();
5151 vboxLock.release();
5152
5153 if (FAILED(rc))
5154 unregisterNATNetwork(aNATNetwork, false /* aSaveSettings */);
5155 }
5156
5157 return rc;
5158#else
5159 NOREF(aNATNetwork);
5160 NOREF(aSaveSettings);
5161 /* No panic please (silently ignore) */
5162 return S_OK;
5163#endif
5164}
5165
5166/**
5167 * Removes the given NAT network from the settings.
5168 *
5169 * @param aNATNetwork NAT network object to remove.
5170 * @param aSaveSettings @c true to save settings to disk (default).
5171 *
5172 * When @a aSaveSettings is @c true, this operation may fail because of the
5173 * failed #saveSettings() method it calls. In this case, the DHCP server
5174 * will NOT be removed from the settingsi when this method returns.
5175 *
5176 * @note Locks this object for writing.
5177 */
5178HRESULT VirtualBox::unregisterNATNetwork(NATNetwork *aNATNetwork,
5179 bool aSaveSettings /*= true*/)
5180{
5181#ifdef VBOX_WITH_NAT_SERVICE
5182 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
5183
5184 AutoCaller autoCaller(this);
5185 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5186
5187 AutoCaller natNetworkCaller(aNATNetwork);
5188 AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc());
5189
5190 m->allNATNetworks.removeChild(aNATNetwork);
5191
5192 HRESULT rc = S_OK;
5193
5194 if (aSaveSettings)
5195 {
5196 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
5197 rc = saveSettings();
5198 vboxLock.release();
5199
5200 if (FAILED(rc))
5201 registerNATNetwork(aNATNetwork, false /* aSaveSettings */);
5202 }
5203
5204 return rc;
5205#else
5206 NOREF(aNATNetwork);
5207 NOREF(aSaveSettings);
5208 return E_NOTIMPL;
5209#endif
5210}
5211/* 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