VirtualBox

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

Last change on this file since 54971 was 54948, checked in by vboxsync, 10 years ago

Main/Medium+Snapshot: make all code recursing over trees (objects containing lists of child objects) use as little stack as possible, and establish safe depth limits to avoid crashes, plus related cleanups in related code areas

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

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