VirtualBox

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

Last change on this file since 73768 was 73716, checked in by vboxsync, 6 years ago

Main/CloudProviderManager+CloudProvider+CloudProfile: Introduce CloudProfile as separate interface, and do a big cleanup. Adding synchronization and incomplete support for moving to an extension pack. Updated VBoxManage to list providers and touched up the GUI code slightly to deal with the changed interfaces.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use