VirtualBox

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

Last change on this file since 73092 was 73003, checked in by vboxsync, 7 years ago

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

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

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