VirtualBox

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

最後變更 在這個檔案從52559是 52546,由 vboxsync 提交於 11 年 前

VBox/Main: #1909: Independent QMTranslator implementation. Some fixes to Utf8Str (added number of characters to be copied when created from CBSTR without terminating zero), added swap method to RTCString for fast exchange between strings. Makefile for VBoxSVC updated in order to compile with QMTranslatorImpl.cpp

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

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