VirtualBox

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

Last change on this file since 107597 was 107588, checked in by vboxsync, 3 weeks ago

Main,Puel: Made the object tracker code a compile time option (VBOX_WITH_MAIN_OBJECT_TRACKER) and disabled it since it causes VBoxSVC to crash during uninit. It is also badly in need of a review + style-cleanup (did some). Making it optional eliminates the need for Puel to include the object tracker code which it won't need (neither does VBoxC/Client.dll). bugref:10806

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 232.4 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 107588 2025-01-09 11:04:40Z vboxsync $ */
2/** @file
3 * Implementation of IVirtualBox in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_VIRTUALBOX
29#include <iprt/asm.h>
30#include <iprt/base64.h>
31#include <iprt/buildconfig.h>
32#include <iprt/cpp/utils.h>
33#include <iprt/dir.h>
34#include <iprt/env.h>
35#include <iprt/file.h>
36#include <iprt/path.h>
37#include <iprt/process.h>
38#include <iprt/rand.h>
39#include <iprt/sha.h>
40#include <iprt/string.h>
41#include <iprt/stream.h>
42#include <iprt/system.h>
43#include <iprt/thread.h>
44#include <iprt/uuid.h>
45#include <iprt/cpp/xml.h>
46#include <iprt/ctype.h>
47
48#include <VBox/com/com.h>
49#include <VBox/com/array.h>
50#include "VBox/com/EventQueue.h"
51#include "VBox/com/MultiResult.h"
52
53#include <VBox/err.h>
54#include <VBox/param.h>
55#include <VBox/settings.h>
56#include <VBox/sup.h>
57#include <VBox/version.h>
58
59#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
60# include <VBox/GuestHost/SharedClipboard-transfers.h>
61#endif
62
63#include <package-generated.h>
64
65#include <algorithm>
66#include <set>
67#include <vector>
68#include <memory> // for auto_ptr
69
70#include "VirtualBoxImpl.h"
71
72#include "Global.h"
73#include "MachineImpl.h"
74#include "MediumImpl.h"
75#include "SharedFolderImpl.h"
76#include "ProgressImpl.h"
77#include "HostImpl.h"
78#include "USBControllerImpl.h"
79#include "SystemPropertiesImpl.h"
80#include "GuestOSTypeImpl.h"
81#include "NetworkServiceRunner.h"
82#include "DHCPServerImpl.h"
83#include "NATNetworkImpl.h"
84#ifdef VBOX_WITH_VMNET
85#include "HostOnlyNetworkImpl.h"
86#endif /* VBOX_WITH_VMNET */
87#ifdef VBOX_WITH_CLOUD_NET
88#include "CloudNetworkImpl.h"
89#endif /* VBOX_WITH_CLOUD_NET */
90#ifdef VBOX_WITH_RESOURCE_USAGE_API
91# include "PerformanceImpl.h"
92#endif /* VBOX_WITH_RESOURCE_USAGE_API */
93#ifdef VBOX_WITH_UPDATE_AGENT
94# include "UpdateAgentImpl.h"
95#endif
96#include "EventImpl.h"
97#ifdef VBOX_WITH_EXTPACK
98# include "ExtPackManagerImpl.h"
99#endif
100#ifdef VBOX_WITH_UNATTENDED
101# include "UnattendedImpl.h"
102#endif
103#include "AutostartDb.h"
104#include "ClientWatcher.h"
105#include "AutoCaller.h"
106#include "LoggingNew.h"
107#include "CloudProviderManagerImpl.h"
108#include "ThreadTask.h"
109#include "VBoxEvents.h"
110#ifdef VBOX_WITH_MAIN_OBJECT_TRACKER
111# include "ObjectsTracker.h"
112#endif
113
114#include <QMTranslator.h>
115
116#ifdef RT_OS_WINDOWS
117# include "win/svchlp.h"
118# include "tchar.h"
119#endif
120
121
122////////////////////////////////////////////////////////////////////////////////
123//
124// Definitions
125//
126////////////////////////////////////////////////////////////////////////////////
127
128#define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
129
130////////////////////////////////////////////////////////////////////////////////
131//
132// Global variables
133//
134////////////////////////////////////////////////////////////////////////////////
135
136#ifdef VBOX_WITH_MAIN_OBJECT_TRACKER
137extern TrackedObjectsCollector gTrackedObjectsCollector;
138#endif
139
140// static
141com::Utf8Str VirtualBox::sVersion;
142
143// static
144com::Utf8Str VirtualBox::sVersionNormalized;
145
146// static
147ULONG VirtualBox::sRevision;
148
149// static
150com::Utf8Str VirtualBox::sPackageType;
151
152// static
153com::Utf8Str VirtualBox::sAPIVersion;
154
155// static
156std::map<com::Utf8Str, int> VirtualBox::sNatNetworkNameToRefCount;
157
158// static leaked (todo: find better place to free it.)
159RWLockHandle *VirtualBox::spMtxNatNetworkNameToRefCountLock;
160
161
162#if 0 /* obsoleted by AsyncEvent */
163////////////////////////////////////////////////////////////////////////////////
164//
165// CallbackEvent class
166//
167////////////////////////////////////////////////////////////////////////////////
168
169/**
170 * Abstract callback event class to asynchronously call VirtualBox callbacks
171 * on a dedicated event thread. Subclasses reimplement #prepareEventDesc()
172 * to initialize the event depending on the event to be dispatched.
173 *
174 * @note The VirtualBox instance passed to the constructor is strongly
175 * referenced, so that the VirtualBox singleton won't be released until the
176 * event gets handled by the event thread.
177 */
178class VirtualBox::CallbackEvent : public Event
179{
180public:
181
182 CallbackEvent(VirtualBox *aVirtualBox, VBoxEventType_T aWhat)
183 : mVirtualBox(aVirtualBox), mWhat(aWhat)
184 {
185 Assert(aVirtualBox);
186 }
187
188 void *handler();
189
190 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) = 0;
191
192private:
193
194 /**
195 * Note that this is a weak ref -- the CallbackEvent handler thread
196 * is bound to the lifetime of the VirtualBox instance, so it's safe.
197 */
198 VirtualBox *mVirtualBox;
199protected:
200 VBoxEventType_T mWhat;
201};
202#endif
203
204////////////////////////////////////////////////////////////////////////////////
205//
206// AsyncEvent class
207//
208////////////////////////////////////////////////////////////////////////////////
209
210/**
211 * For firing off an event on asynchronously on an event thread.
212 */
213class VirtualBox::AsyncEvent : public Event
214{
215public:
216 AsyncEvent(VirtualBox *a_pVirtualBox, ComPtr<IEvent> const &a_rEvent)
217 : mVirtualBox(a_pVirtualBox), mEvent(a_rEvent)
218 {
219 Assert(a_pVirtualBox);
220 }
221
222 void *handler() RT_OVERRIDE;
223
224private:
225 /**
226 * @note This is a weak ref -- the CallbackEvent handler thread is bound to the
227 * lifetime of the VirtualBox instance, so it's safe.
228 */
229 VirtualBox *mVirtualBox;
230 /** The event. */
231 ComPtr<IEvent> mEvent;
232};
233
234////////////////////////////////////////////////////////////////////////////////
235//
236// VirtualBox private member data definition
237//
238////////////////////////////////////////////////////////////////////////////////
239
240#if defined(RT_OS_WINDOWS) && defined(VBOXSVC_WITH_CLIENT_WATCHER)
241/**
242 * Client process watcher data.
243 */
244class WatchedClientProcess
245{
246public:
247 WatchedClientProcess(RTPROCESS a_pid, HANDLE a_hProcess) RT_NOEXCEPT
248 : m_pid(a_pid)
249 , m_cRefs(1)
250 , m_hProcess(a_hProcess)
251 {
252 }
253
254 ~WatchedClientProcess()
255 {
256 if (m_hProcess != NULL)
257 {
258 ::CloseHandle(m_hProcess);
259 m_hProcess = NULL;
260 }
261 m_pid = NIL_RTPROCESS;
262 }
263
264 /** The client PID. */
265 RTPROCESS m_pid;
266 /** Number of references to this structure. */
267 uint32_t volatile m_cRefs;
268 /** Handle of the client process.
269 * Ideally, we've got full query privileges, but we'll settle for waiting. */
270 HANDLE m_hProcess;
271};
272typedef std::map<RTPROCESS, WatchedClientProcess *> WatchedClientProcessMap;
273#endif
274
275typedef ObjectsList<Medium> MediaOList;
276typedef ObjectsList<GuestOSType> GuestOSTypesOList;
277typedef ObjectsList<SharedFolder> SharedFoldersOList;
278typedef ObjectsList<DHCPServer> DHCPServersOList;
279typedef ObjectsList<NATNetwork> NATNetworksOList;
280#ifdef VBOX_WITH_VMNET
281typedef ObjectsList<HostOnlyNetwork> HostOnlyNetworksOList;
282#endif /* VBOX_WITH_VMNET */
283#ifdef VBOX_WITH_CLOUD_NET
284typedef ObjectsList<CloudNetwork> CloudNetworksOList;
285#endif /* VBOX_WITH_CLOUD_NET */
286
287typedef std::map<Guid, ComPtr<IProgress> > ProgressMap;
288typedef std::map<Guid, ComObjPtr<Medium> > HardDiskMap;
289
290/**
291 * Main VirtualBox data structure.
292 * @note |const| members are persistent during lifetime so can be accessed
293 * without locking.
294 */
295struct VirtualBox::Data
296{
297 Data()
298 : pMainConfigFile(NULL)
299 , uuidMediaRegistry("48024e5c-fdd9-470f-93af-ec29f7ea518c")
300 , uRegistryNeedsSaving(0)
301 , lockMachines(LOCKCLASS_LISTOFMACHINES, "Machines")
302 , allMachines(lockMachines)
303 , lockGuestOSTypes(LOCKCLASS_LISTOFOTHEROBJECTS, "GuestOSTypes")
304 , allGuestOSTypes(lockGuestOSTypes)
305 , lockMedia(LOCKCLASS_LISTOFMEDIA, "Media")
306 , allHardDisks(lockMedia)
307 , allDVDImages(lockMedia)
308 , allFloppyImages(lockMedia)
309 , lockSharedFolders(LOCKCLASS_LISTOFOTHEROBJECTS, "SharedFolders")
310 , allSharedFolders(lockSharedFolders)
311 , lockDHCPServers(LOCKCLASS_LISTOFOTHEROBJECTS, "DHCPServers")
312 , allDHCPServers(lockDHCPServers)
313 , lockNATNetworks(LOCKCLASS_LISTOFOTHEROBJECTS, "NATNetworks")
314 , allNATNetworks(lockNATNetworks)
315#ifdef VBOX_WITH_VMNET
316 , lockHostOnlyNetworks(LOCKCLASS_LISTOFOTHEROBJECTS, "HostOnlyNetworks")
317 , allHostOnlyNetworks(lockHostOnlyNetworks)
318#endif /* VBOX_WITH_VMNET */
319#ifdef VBOX_WITH_CLOUD_NET
320 , lockCloudNetworks(LOCKCLASS_LISTOFOTHEROBJECTS, "CloudNetworks")
321 , allCloudNetworks(lockCloudNetworks)
322#endif /* VBOX_WITH_CLOUD_NET */
323 , mtxProgressOperations(LOCKCLASS_PROGRESSLIST, "ProgressOperations")
324 , pClientWatcher(NULL)
325 , threadAsyncEvent(NIL_RTTHREAD)
326 , pAsyncEventQ(NULL)
327 , pAutostartDb(NULL)
328 , fSettingsCipherKeySet(false)
329#ifdef VBOX_WITH_MAIN_NLS
330 , pVBoxTranslator(NULL)
331 , pTrComponent(NULL)
332#endif
333#if defined(RT_OS_WINDOWS) && defined(VBOXSVC_WITH_CLIENT_WATCHER)
334 , fWatcherIsReliable(RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
335#endif
336 , hLdrModCrypto(NIL_RTLDRMOD)
337 , cRefsCrypto(0)
338 , pCryptoIf(NULL)
339#ifdef VBOX_WITH_MAIN_OBJECT_TRACKER
340 , objectTrackerTask(NULL)
341#endif
342 {
343#if defined(RT_OS_WINDOWS) && defined(VBOXSVC_WITH_CLIENT_WATCHER)
344 RTCritSectRwInit(&WatcherCritSect);
345#endif
346 }
347
348 ~Data()
349 {
350 if (pMainConfigFile)
351 {
352 delete pMainConfigFile;
353 pMainConfigFile = NULL;
354 }
355 };
356
357 // const data members not requiring locking
358 const Utf8Str strHomeDir;
359
360 // VirtualBox main settings file
361 const Utf8Str strSettingsFilePath;
362 settings::MainConfigFile *pMainConfigFile;
363
364 // constant pseudo-machine ID for global media registry
365 const Guid uuidMediaRegistry;
366
367 // counter if global media registry needs saving, updated using atomic
368 // operations, without requiring any locks
369 uint64_t uRegistryNeedsSaving;
370
371 // const objects not requiring locking
372 const ComObjPtr<Host> pHost;
373 const ComObjPtr<SystemProperties> pSystemProperties;
374#ifdef VBOX_WITH_RESOURCE_USAGE_API
375 const ComObjPtr<PerformanceCollector> pPerformanceCollector;
376#endif /* VBOX_WITH_RESOURCE_USAGE_API */
377
378 // Each of the following lists use a particular lock handle that protects the
379 // list as a whole. As opposed to version 3.1 and earlier, these lists no
380 // longer need the main VirtualBox object lock, but only the respective list
381 // lock. In each case, the locking order is defined that the list must be
382 // requested before object locks of members of the lists (see the order definitions
383 // in AutoLock.h; e.g. LOCKCLASS_LISTOFMACHINES before LOCKCLASS_MACHINEOBJECT).
384 RWLockHandle lockMachines;
385 MachinesOList allMachines;
386
387 RWLockHandle lockGuestOSTypes;
388 GuestOSTypesOList allGuestOSTypes;
389
390 // All the media lists are protected by the following locking handle:
391 RWLockHandle lockMedia;
392 MediaOList allHardDisks, // base images only!
393 allDVDImages,
394 allFloppyImages;
395 // the hard disks map is an additional map sorted by UUID for quick lookup
396 // and contains ALL hard disks (base and differencing); it is protected by
397 // the same lock as the other media lists above
398 HardDiskMap mapHardDisks;
399
400 // list of pending machine renames (also protected by media tree lock;
401 // see VirtualBox::rememberMachineNameChangeForMedia())
402 struct PendingMachineRename
403 {
404 Utf8Str strConfigDirOld;
405 Utf8Str strConfigDirNew;
406 };
407 typedef std::list<PendingMachineRename> PendingMachineRenamesList;
408 PendingMachineRenamesList llPendingMachineRenames;
409
410 RWLockHandle lockSharedFolders;
411 SharedFoldersOList allSharedFolders;
412
413 RWLockHandle lockDHCPServers;
414 DHCPServersOList allDHCPServers;
415
416 RWLockHandle lockNATNetworks;
417 NATNetworksOList allNATNetworks;
418
419#ifdef VBOX_WITH_VMNET
420 RWLockHandle lockHostOnlyNetworks;
421 HostOnlyNetworksOList allHostOnlyNetworks;
422#endif /* VBOX_WITH_VMNET */
423#ifdef VBOX_WITH_CLOUD_NET
424 RWLockHandle lockCloudNetworks;
425 CloudNetworksOList allCloudNetworks;
426#endif /* VBOX_WITH_CLOUD_NET */
427
428 RWLockHandle mtxProgressOperations;
429 ProgressMap mapProgressOperations;
430
431 ClientWatcher * const pClientWatcher;
432
433 // the following are data for the async event thread
434 const RTTHREAD threadAsyncEvent;
435 EventQueue * const pAsyncEventQ;
436 const ComObjPtr<EventSource> pEventSource;
437
438#ifdef VBOX_WITH_EXTPACK
439 /** The extension pack manager object lives here. */
440 const ComObjPtr<ExtPackManager> ptrExtPackManager;
441#endif
442
443 /** The reference to the cloud provider manager singleton. */
444 const ComObjPtr<CloudProviderManager> pCloudProviderManager;
445
446 /** The global autostart database for the user. */
447 AutostartDb * const pAutostartDb;
448
449 /** Settings secret */
450 bool fSettingsCipherKeySet;
451 uint8_t SettingsCipherKey[RTSHA512_HASH_SIZE];
452#ifdef VBOX_WITH_MAIN_NLS
453 VirtualBoxTranslator *pVBoxTranslator;
454 PTRCOMPONENT pTrComponent;
455#endif
456#if defined(RT_OS_WINDOWS) && defined(VBOXSVC_WITH_CLIENT_WATCHER)
457 /** Critical section protecting WatchedProcesses. */
458 RTCRITSECTRW WatcherCritSect;
459 /** Map of processes being watched, key is the PID. */
460 WatchedClientProcessMap WatchedProcesses;
461 /** Set if the watcher is reliable, otherwise cleared.
462 * The watcher goes unreliable when we run out of memory, fail open a client
463 * process, or if the watcher thread gets messed up. */
464 bool fWatcherIsReliable;
465#endif
466
467 /** @name Members related to the cryptographic support interface.
468 * @{ */
469 /** The loaded module handle if loaded. */
470 RTLDRMOD hLdrModCrypto;
471 /** Reference counter tracking how many users of the cryptographic support
472 * are there currently. */
473 volatile uint32_t cRefsCrypto;
474 /** Pointer to the cryptographic support interface. */
475 PCVBOXCRYPTOIF pCryptoIf;
476 /** Critical section protecting the module handle. */
477 RTCRITSECT CritSectModCrypto;
478 /** @} */
479
480#ifdef VBOX_WITH_MAIN_OBJECT_TRACKER
481 /** The tracked object collector (better if it'll be a singleton) */
482 ObjectTracker *objectTrackerTask;
483#endif
484};
485
486
487/**
488 * VirtualBox firmware descriptor.
489 */
490typedef struct VBOXFWDESC
491{
492 FirmwareType_T enmType;
493 bool fBuiltIn;
494 const char *pszFileName;
495 const char *pszUrl;
496} VBoxFwDesc;
497typedef const VBOXFWDESC *PVBOXFWDESC;
498
499
500// constructor / destructor
501/////////////////////////////////////////////////////////////////////////////
502
503DEFINE_EMPTY_CTOR_DTOR(VirtualBox)
504
505HRESULT VirtualBox::FinalConstruct()
506{
507 LogRelFlowThisFuncEnter();
508 LogRel(("VirtualBox: object creation starts\n"));
509
510 BaseFinalConstruct();
511
512 HRESULT hrc = init();
513
514 LogRelFlowThisFuncLeave();
515 LogRel(("VirtualBox: object created\n"));
516
517 return hrc;
518}
519
520void VirtualBox::FinalRelease()
521{
522 LogRelFlowThisFuncEnter();
523 LogRel(("VirtualBox: object deletion starts\n"));
524
525 uninit();
526
527 BaseFinalRelease();
528
529 LogRel(("VirtualBox: object deleted\n"));
530 LogRelFlowThisFuncLeave();
531}
532
533// public initializer/uninitializer for internal purposes only
534/////////////////////////////////////////////////////////////////////////////
535
536/**
537 * Initializes the VirtualBox object.
538 *
539 * @return COM result code
540 */
541HRESULT VirtualBox::init()
542{
543 LogRelFlowThisFuncEnter();
544 /* Enclose the state transition NotReady->InInit->Ready */
545 AutoInitSpan autoInitSpan(this);
546 AssertReturn(autoInitSpan.isOk(), E_FAIL);
547
548 /* Locking this object for writing during init sounds a bit paradoxical,
549 * but in the current locking mess this avoids that some code gets a
550 * read lock and later calls code which wants the same write lock. */
551 AutoWriteLock lock(this COMMA_LOCKVAL_SRC_POS);
552
553 // allocate our instance data
554 m = new Data;
555
556 LogFlow(("===========================================================\n"));
557 LogFlowThisFuncEnter();
558
559 if (sVersion.isEmpty())
560 sVersion = RTBldCfgVersion();
561 if (sVersionNormalized.isEmpty())
562 {
563 Utf8Str tmp(RTBldCfgVersion());
564 if (tmp.endsWith(VBOX_BUILD_PUBLISHER))
565 tmp = tmp.substr(0, tmp.length() - strlen(VBOX_BUILD_PUBLISHER));
566 sVersionNormalized = tmp;
567 }
568 sRevision = RTBldCfgRevision();
569 if (sPackageType.isEmpty())
570 sPackageType = VBOX_PACKAGE_STRING;
571 if (sAPIVersion.isEmpty())
572 sAPIVersion = VBOX_API_VERSION_STRING;
573 if (!spMtxNatNetworkNameToRefCountLock)
574 spMtxNatNetworkNameToRefCountLock = new RWLockHandle(LOCKCLASS_VIRTUALBOXOBJECT, "spMtxNatNetworkNameToRefCountLock");
575
576 LogFlowThisFunc(("Version: %s, Package: %s, API Version: %s\n", sVersion.c_str(), sPackageType.c_str(), sAPIVersion.c_str()));
577
578#ifdef VBOX_WITH_MAIN_OBJECT_TRACKER
579 /* Try to start Object tracker thread as earlier as possible (same code in VirtualBoxClientImpl.cpp). */
580 {
581 int vrc = VERR_GENERAL_FAILURE;
582 if (gTrackedObjectsCollector.init())
583 {
584 LogRel(("Starting the Object tracker thread\n"));
585 try
586 {
587 m->objectTrackerTask = new ObjectTracker();
588 if (m->objectTrackerTask->init()) // some init procedure - bird: some comment!
589 vrc = m->objectTrackerTask->createThread();
590 }
591 catch (...)
592 {
593 LogRel(("Exception during starting the Object tracker thread\n"));
594 if (m->objectTrackerTask)
595 {
596 delete m->objectTrackerTask;
597 m->objectTrackerTask = NULL;
598 }
599 vrc = VERR_INVALID_STATE;
600 }
601 }
602 if (RT_SUCCESS(vrc))
603 LogRel(("Successfully started the Object tracker thread\n"));
604 else
605 LogRel(("Failed to start the Object tracker thread (%Rrc)\n", vrc));
606 }
607#endif
608
609 /* Important: DO NOT USE any kind of "early return" (except the single
610 * one above, checking the init span success) in this method. It is vital
611 * for correct error handling that it has only one point of return, which
612 * does all the magic on COM to signal object creation success and
613 * reporting the error later for every API method. COM translates any
614 * unsuccessful object creation to REGDB_E_CLASSNOTREG errors or similar
615 * unhelpful ones which cause us a lot of grief with troubleshooting. */
616
617 HRESULT hrc = S_OK;
618 bool fCreate = false;
619 try
620 {
621 /* Create the event source early as we may fire async event during settings loading (media). */
622 hrc = unconst(m->pEventSource).createObject();
623 if (FAILED(hrc)) throw hrc;
624 hrc = m->pEventSource->init();
625 if (FAILED(hrc)) throw hrc;
626
627
628 /* Get the VirtualBox home directory. */
629 {
630 char szHomeDir[RTPATH_MAX];
631 int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir));
632 if (RT_FAILURE(vrc))
633 throw setErrorBoth(E_FAIL, vrc,
634 tr("Could not create the VirtualBox home directory '%s' (%Rrc)"),
635 szHomeDir, vrc);
636
637 unconst(m->strHomeDir) = szHomeDir;
638 }
639
640 LogRel(("Home directory: '%s'\n", m->strHomeDir.c_str()));
641
642 i_reportDriverVersions();
643
644 /* Create the critical section protecting the cryptographic module handle. */
645 {
646 int vrc = RTCritSectInit(&m->CritSectModCrypto);
647 if (RT_FAILURE(vrc))
648 throw setErrorBoth(E_FAIL, vrc,
649 tr("Could not create the cryptographic module critical section (%Rrc)"),
650 vrc);
651
652 }
653
654 /* compose the VirtualBox.xml file name */
655 unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s",
656 m->strHomeDir.c_str(),
657 RTPATH_DELIMITER,
658 VBOX_GLOBAL_SETTINGS_FILE);
659 // load and parse VirtualBox.xml; this will throw on XML or logic errors
660 try
661 {
662 m->pMainConfigFile = new settings::MainConfigFile(&m->strSettingsFilePath);
663 }
664 catch (xml::EIPRTFailure &e)
665 {
666 // this is thrown by the XML backend if the RTOpen() call fails;
667 // only if the main settings file does not exist, create it,
668 // if there's something more serious, then do fail!
669 if (e.getStatus() == VERR_FILE_NOT_FOUND)
670 fCreate = true;
671 else
672 throw;
673 }
674
675 if (fCreate)
676 m->pMainConfigFile = new settings::MainConfigFile(NULL);
677
678#ifdef VBOX_WITH_RESOURCE_USAGE_API
679 /* create the performance collector object BEFORE host */
680 unconst(m->pPerformanceCollector).createObject();
681 hrc = m->pPerformanceCollector->init();
682 ComAssertComRCThrowRC(hrc);
683#endif /* VBOX_WITH_RESOURCE_USAGE_API */
684
685 /* create the host object early, machines will need it */
686 unconst(m->pHost).createObject();
687 hrc = m->pHost->init(this);
688 ComAssertComRCThrowRC(hrc);
689
690 hrc = m->pHost->i_loadSettings(m->pMainConfigFile->host);
691 if (FAILED(hrc)) throw hrc;
692
693 /*
694 * Create autostart database object early, because the system properties
695 * might need it.
696 */
697 unconst(m->pAutostartDb) = new AutostartDb;
698
699 /* create the system properties object, someone may need it too */
700 hrc = unconst(m->pSystemProperties).createObject();
701 if (SUCCEEDED(hrc))
702 hrc = m->pSystemProperties->init(this);
703 ComAssertComRCThrowRC(hrc);
704
705 hrc = m->pSystemProperties->i_loadSettings(m->pMainConfigFile->systemProperties);
706 if (FAILED(hrc)) throw hrc;
707#ifdef VBOX_WITH_MAIN_NLS
708 m->pVBoxTranslator = VirtualBoxTranslator::instance();
709 /* Do not throw an exception on language errors.
710 * Just do not use translation. */
711 if (m->pVBoxTranslator)
712 {
713
714 char szNlsPath[RTPATH_MAX];
715 int vrc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
716 if (RT_SUCCESS(vrc))
717 vrc = RTPathAppend(szNlsPath, sizeof(szNlsPath), "nls" RTPATH_SLASH_STR "VirtualBoxAPI");
718
719 if (RT_SUCCESS(vrc))
720 {
721 vrc = m->pVBoxTranslator->registerTranslation(szNlsPath, true, &m->pTrComponent);
722 if (RT_SUCCESS(vrc))
723 {
724 com::Utf8Str strLocale;
725 HRESULT hrc2 = m->pSystemProperties->getLanguageId(strLocale);
726 if (SUCCEEDED(hrc2))
727 {
728 vrc = m->pVBoxTranslator->i_loadLanguage(strLocale.c_str());
729 if (RT_FAILURE(vrc))
730 {
731 hrc2 = Global::vboxStatusCodeToCOM(vrc);
732 LogRel(("Load language failed (%Rhrc).\n", hrc2));
733 }
734 }
735 else
736 {
737 LogRel(("Getting language settings failed (%Rhrc).\n", hrc2));
738 m->pVBoxTranslator->release();
739 m->pVBoxTranslator = NULL;
740 m->pTrComponent = NULL;
741 }
742 }
743 else
744 {
745 HRESULT hrc2 = Global::vboxStatusCodeToCOM(vrc);
746 LogRel(("Register translation failed (%Rhrc).\n", hrc2));
747 m->pVBoxTranslator->release();
748 m->pVBoxTranslator = NULL;
749 m->pTrComponent = NULL;
750 }
751 }
752 else
753 {
754 HRESULT hrc2 = Global::vboxStatusCodeToCOM(vrc);
755 LogRel(("Path constructing failed (%Rhrc).\n", hrc2));
756 m->pVBoxTranslator->release();
757 m->pVBoxTranslator = NULL;
758 m->pTrComponent = NULL;
759 }
760 }
761 else
762 LogRel(("Translator creation failed.\n"));
763#endif
764
765#ifdef VBOX_WITH_EXTPACK
766 /*
767 * Initialize extension pack manager before system properties because
768 * it is required for the VD plugins.
769 */
770 hrc = unconst(m->ptrExtPackManager).createObject();
771 if (SUCCEEDED(hrc))
772 hrc = m->ptrExtPackManager->initExtPackManager(this, VBOXEXTPACKCTX_PER_USER_DAEMON);
773 if (FAILED(hrc))
774 throw hrc;
775#endif
776 /* guest OS type objects, needed by machines */
777 for (size_t i = 0; i < Global::cOSTypes; ++i)
778 {
779 ComObjPtr<GuestOSType> guestOSTypeObj;
780 hrc = guestOSTypeObj.createObject();
781 if (SUCCEEDED(hrc))
782 {
783 hrc = guestOSTypeObj->init(Global::sOSTypes[i]);
784 if (SUCCEEDED(hrc))
785 m->allGuestOSTypes.addChild(guestOSTypeObj);
786 }
787 ComAssertComRCThrowRC(hrc);
788 }
789
790 /* all registered media, needed by machines */
791 if (FAILED(hrc = initMedia(m->uuidMediaRegistry,
792 m->pMainConfigFile->mediaRegistry,
793 Utf8Str::Empty))) // const Utf8Str &machineFolder
794 throw hrc;
795
796 /* machines */
797 if (FAILED(hrc = initMachines()))
798 throw hrc;
799
800#ifdef DEBUG
801 LogFlowThisFunc(("Dumping media backreferences\n"));
802 i_dumpAllBackRefs();
803#endif
804
805 /* net services - dhcp services */
806 for (settings::DHCPServersList::const_iterator it = m->pMainConfigFile->llDhcpServers.begin();
807 it != m->pMainConfigFile->llDhcpServers.end();
808 ++it)
809 {
810 const settings::DHCPServer &data = *it;
811
812 ComObjPtr<DHCPServer> pDhcpServer;
813 if (SUCCEEDED(hrc = pDhcpServer.createObject()))
814 hrc = pDhcpServer->init(this, data);
815 if (FAILED(hrc)) throw hrc;
816
817 hrc = i_registerDHCPServer(pDhcpServer, false /* aSaveRegistry */);
818 if (FAILED(hrc)) throw hrc;
819 }
820
821 /* net services - nat networks */
822 for (settings::NATNetworksList::const_iterator it = m->pMainConfigFile->llNATNetworks.begin();
823 it != m->pMainConfigFile->llNATNetworks.end();
824 ++it)
825 {
826 const settings::NATNetwork &net = *it;
827
828 ComObjPtr<NATNetwork> pNATNetwork;
829 hrc = pNATNetwork.createObject();
830 AssertComRCThrowRC(hrc);
831 hrc = pNATNetwork->init(this, "");
832 AssertComRCThrowRC(hrc);
833 hrc = pNATNetwork->i_loadSettings(net);
834 AssertComRCThrowRC(hrc);
835 hrc = i_registerNATNetwork(pNATNetwork, false /* aSaveRegistry */);
836 AssertComRCThrowRC(hrc);
837 }
838
839#ifdef VBOX_WITH_VMNET
840 /* host-only networks */
841 for (settings::HostOnlyNetworksList::const_iterator it = m->pMainConfigFile->llHostOnlyNetworks.begin();
842 it != m->pMainConfigFile->llHostOnlyNetworks.end();
843 ++it)
844 {
845 ComObjPtr<HostOnlyNetwork> pHostOnlyNetwork;
846 hrc = pHostOnlyNetwork.createObject();
847 AssertComRCThrowRC(hrc);
848 hrc = pHostOnlyNetwork->init(this, "TODO???");
849 AssertComRCThrowRC(hrc);
850 hrc = pHostOnlyNetwork->i_loadSettings(*it);
851 AssertComRCThrowRC(hrc);
852 m->allHostOnlyNetworks.addChild(pHostOnlyNetwork);
853 AssertComRCThrowRC(hrc);
854 }
855#endif /* VBOX_WITH_VMNET */
856
857#ifdef VBOX_WITH_CLOUD_NET
858 /* net services - cloud networks */
859 for (settings::CloudNetworksList::const_iterator it = m->pMainConfigFile->llCloudNetworks.begin();
860 it != m->pMainConfigFile->llCloudNetworks.end();
861 ++it)
862 {
863 ComObjPtr<CloudNetwork> pCloudNetwork;
864 hrc = pCloudNetwork.createObject();
865 AssertComRCThrowRC(hrc);
866 hrc = pCloudNetwork->init(this, "");
867 AssertComRCThrowRC(hrc);
868 hrc = pCloudNetwork->i_loadSettings(*it);
869 AssertComRCThrowRC(hrc);
870 m->allCloudNetworks.addChild(pCloudNetwork);
871 AssertComRCThrowRC(hrc);
872 }
873#endif /* VBOX_WITH_CLOUD_NET */
874
875 /* cloud provider manager */
876 hrc = unconst(m->pCloudProviderManager).createObject();
877 if (SUCCEEDED(hrc))
878 hrc = m->pCloudProviderManager->init(this);
879 ComAssertComRCThrowRC(hrc);
880 if (FAILED(hrc)) throw hrc;
881 }
882 catch (HRESULT err)
883 {
884 /* we assume that error info is set by the thrower */
885 hrc = err;
886 }
887 catch (...)
888 {
889 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
890 }
891
892 if (SUCCEEDED(hrc))
893 {
894 /* set up client monitoring */
895 try
896 {
897 unconst(m->pClientWatcher) = new ClientWatcher(this);
898 if (!m->pClientWatcher->isReady())
899 {
900 delete m->pClientWatcher;
901 unconst(m->pClientWatcher) = NULL;
902 hrc = E_FAIL;
903 }
904 }
905 catch (std::bad_alloc &)
906 {
907 hrc = E_OUTOFMEMORY;
908 }
909 }
910
911 if (SUCCEEDED(hrc))
912 {
913 try
914 {
915 /* start the async event handler thread */
916 int vrc = RTThreadCreate(&unconst(m->threadAsyncEvent),
917 AsyncEventHandler,
918 &unconst(m->pAsyncEventQ),
919 0,
920 RTTHREADTYPE_MAIN_WORKER,
921 RTTHREADFLAGS_WAITABLE,
922 "EventHandler");
923 ComAssertRCThrow(vrc, E_FAIL);
924
925 /* wait until the thread sets m->pAsyncEventQ */
926 RTThreadUserWait(m->threadAsyncEvent, RT_INDEFINITE_WAIT);
927 ComAssertThrow(m->pAsyncEventQ, E_FAIL);
928 }
929 catch (HRESULT hrcXcpt)
930 {
931 hrc = hrcXcpt;
932 }
933 }
934
935#ifdef VBOX_WITH_EXTPACK
936 /* Let the extension packs have a go at things. */
937 if (SUCCEEDED(hrc))
938 {
939 lock.release();
940 m->ptrExtPackManager->i_callAllVirtualBoxReadyHooks();
941 }
942#endif
943
944 /* Confirm a successful initialization when it's the case. Must be last,
945 * as on failure it will uninitialize the object. */
946 if (SUCCEEDED(hrc))
947 autoInitSpan.setSucceeded();
948 else
949 autoInitSpan.setFailed(hrc);
950
951 LogFlowThisFunc(("hrc=%Rhrc\n", hrc));
952 LogFlowThisFuncLeave();
953 LogFlow(("===========================================================\n"));
954 /* Unconditionally return success, because the error return is delayed to
955 * the attribute/method calls through the InitFailed object state. */
956 return S_OK;
957}
958
959HRESULT VirtualBox::initMachines()
960{
961 for (settings::MachinesRegistry::const_iterator it = m->pMainConfigFile->llMachines.begin();
962 it != m->pMainConfigFile->llMachines.end();
963 ++it)
964 {
965 HRESULT hrc = S_OK;
966 const settings::MachineRegistryEntry &xmlMachine = *it;
967 Guid uuid = xmlMachine.uuid;
968
969 /* Check if machine record has valid parameters. */
970 if (xmlMachine.strSettingsFile.isEmpty() || uuid.isZero())
971 {
972 LogRel(("Skipped invalid machine record.\n"));
973 continue;
974 }
975
976 ComObjPtr<Machine> pMachine;
977 com::Utf8Str strPassword;
978 if (SUCCEEDED(hrc = pMachine.createObject()))
979 {
980 hrc = pMachine->initFromSettings(this, xmlMachine.strSettingsFile, &uuid, strPassword);
981 if (SUCCEEDED(hrc))
982 hrc = i_registerMachine(pMachine);
983 if (FAILED(hrc))
984 return hrc;
985 }
986 }
987
988 return S_OK;
989}
990
991/**
992 * Loads a media registry from XML and adds the media contained therein to
993 * the global lists of known media.
994 *
995 * This now (4.0) gets called from two locations:
996 *
997 * -- VirtualBox::init(), to load the global media registry from VirtualBox.xml;
998 *
999 * -- Machine::loadMachineDataFromSettings(), to load the per-machine registry
1000 * from machine XML, for machines created with VirtualBox 4.0 or later.
1001 *
1002 * In both cases, the media found are added to the global lists so the
1003 * global arrays of media (including the GUI's virtual media manager)
1004 * continue to work as before.
1005 *
1006 * @param uuidRegistry The UUID of the media registry. This is either the
1007 * transient UUID created at VirtualBox startup for the global registry or
1008 * a machine ID.
1009 * @param mediaRegistry The XML settings structure to load, either from VirtualBox.xml
1010 * or a machine XML.
1011 * @param strMachineFolder The folder of the machine.
1012 * @return
1013 */
1014HRESULT VirtualBox::initMedia(const Guid &uuidRegistry,
1015 const settings::MediaRegistry &mediaRegistry,
1016 const Utf8Str &strMachineFolder)
1017{
1018 LogFlow(("VirtualBox::initMedia ENTERING, uuidRegistry=%s, strMachineFolder=%s\n",
1019 uuidRegistry.toString().c_str(),
1020 strMachineFolder.c_str()));
1021
1022 AutoWriteLock treeLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1023
1024 // the order of notification is critical for GUI, so use std::list<std::pair> instead of map
1025 std::list<std::pair<Guid, DeviceType_T> > uIdsForNotify;
1026
1027 HRESULT hrc = S_OK;
1028 settings::MediaList::const_iterator it;
1029 for (it = mediaRegistry.llHardDisks.begin();
1030 it != mediaRegistry.llHardDisks.end();
1031 ++it)
1032 {
1033 const settings::Medium &xmlHD = *it;
1034
1035 hrc = Medium::initFromSettings(this,
1036 DeviceType_HardDisk,
1037 uuidRegistry,
1038 strMachineFolder,
1039 xmlHD,
1040 treeLock,
1041 uIdsForNotify);
1042 if (FAILED(hrc)) return hrc;
1043 }
1044
1045 for (it = mediaRegistry.llDvdImages.begin();
1046 it != mediaRegistry.llDvdImages.end();
1047 ++it)
1048 {
1049 const settings::Medium &xmlDvd = *it;
1050
1051 hrc = Medium::initFromSettings(this,
1052 DeviceType_DVD,
1053 uuidRegistry,
1054 strMachineFolder,
1055 xmlDvd,
1056 treeLock,
1057 uIdsForNotify);
1058 if (FAILED(hrc)) return hrc;
1059 }
1060
1061 for (it = mediaRegistry.llFloppyImages.begin();
1062 it != mediaRegistry.llFloppyImages.end();
1063 ++it)
1064 {
1065 const settings::Medium &xmlFloppy = *it;
1066
1067 hrc = Medium::initFromSettings(this,
1068 DeviceType_Floppy,
1069 uuidRegistry,
1070 strMachineFolder,
1071 xmlFloppy,
1072 treeLock,
1073 uIdsForNotify);
1074 if (FAILED(hrc)) return hrc;
1075 }
1076
1077 for (std::list<std::pair<Guid, DeviceType_T> >::const_iterator itItem = uIdsForNotify.begin();
1078 itItem != uIdsForNotify.end();
1079 ++itItem)
1080 {
1081 i_onMediumRegistered(itItem->first, itItem->second, TRUE);
1082 }
1083
1084 LogFlow(("VirtualBox::initMedia LEAVING\n"));
1085
1086 return S_OK;
1087}
1088
1089void VirtualBox::uninit()
1090{
1091 /* Must be done outside the AutoUninitSpan, as it expects AutoCaller to
1092 * be successful. This needs additional checks to protect against double
1093 * uninit, as then the pointer is NULL. */
1094 if (RT_VALID_PTR(m))
1095 {
1096 Assert(!m->uRegistryNeedsSaving);
1097 if (m->uRegistryNeedsSaving)
1098 i_saveSettings();
1099 }
1100
1101 /* Enclose the state transition Ready->InUninit->NotReady */
1102 AutoUninitSpan autoUninitSpan(this);
1103 if (autoUninitSpan.uninitDone())
1104 return;
1105
1106 LogFlow(("===========================================================\n"));
1107 LogFlowThisFuncEnter();
1108 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
1109
1110 /* tell all our child objects we've been uninitialized */
1111
1112 LogFlowThisFunc(("Uninitializing machines (%d)...\n", m->allMachines.size()));
1113 if (m->pHost)
1114 {
1115 /* It is necessary to hold the VirtualBox and Host locks here because
1116 we may have to uninitialize SessionMachines. */
1117 AutoMultiWriteLock2 multilock(this, m->pHost COMMA_LOCKVAL_SRC_POS);
1118 m->allMachines.uninitAll();
1119 }
1120 else
1121 m->allMachines.uninitAll();
1122
1123 m->allFloppyImages.uninitAll();
1124 m->allDVDImages.uninitAll();
1125 m->allHardDisks.uninitAll();
1126 m->allDHCPServers.uninitAll();
1127 m->allGuestOSTypes.uninitAll();
1128
1129 /* Note that we release singleton children after we've all other children.
1130 * In some cases this is important because these other children may use
1131 * some resources of the singletons which would prevent them from
1132 * uninitializing (as for example, mSystemProperties which owns
1133 * MediumFormat objects which Medium objects refer to) */
1134 if (m->pCloudProviderManager)
1135 {
1136 m->pCloudProviderManager->uninit();
1137 unconst(m->pCloudProviderManager).setNull();
1138 }
1139
1140 if (m->pSystemProperties)
1141 {
1142 m->pSystemProperties->uninit();
1143 unconst(m->pSystemProperties).setNull();
1144 }
1145
1146 if (m->pHost)
1147 {
1148 m->pHost->uninit();
1149 unconst(m->pHost).setNull();
1150 }
1151
1152#ifdef VBOX_WITH_RESOURCE_USAGE_API
1153 if (m->pPerformanceCollector)
1154 {
1155 m->pPerformanceCollector->uninit();
1156 unconst(m->pPerformanceCollector).setNull();
1157 }
1158#endif /* VBOX_WITH_RESOURCE_USAGE_API */
1159
1160 /*
1161 * Unload the cryptographic module if loaded before the extension
1162 * pack manager is torn down.
1163 */
1164 Assert(!m->cRefsCrypto);
1165 if (m->hLdrModCrypto != NIL_RTLDRMOD)
1166 {
1167 m->pCryptoIf = NULL;
1168
1169 int vrc = RTLdrClose(m->hLdrModCrypto);
1170 AssertRC(vrc);
1171 m->hLdrModCrypto = NIL_RTLDRMOD;
1172 }
1173
1174 RTCritSectDelete(&m->CritSectModCrypto);
1175
1176 m->mapProgressOperations.clear();
1177
1178#ifdef VBOX_WITH_MAIN_OBJECT_TRACKER
1179 /*
1180 * Call gTrackedObjectsCollector uninitialization before ExtPackManager uninitialization!!!
1181 * Otherwise, this results in an error when releasing resources (in ComPtr::cleanup).
1182 */
1183 if (m->objectTrackerTask)
1184 {
1185 LogRel(("BACKEND: Terminating the object tracker...\n"));
1186 m->objectTrackerTask->finish();//set the termination flag in the thread
1187 delete m->objectTrackerTask;//waiting the thread termination is going in the m_objectTrackerTask destructor
1188 gTrackedObjectsCollector.uninit();
1189 }
1190#endif
1191
1192#ifdef VBOX_WITH_EXTPACK
1193 if (m->ptrExtPackManager)
1194 {
1195 m->ptrExtPackManager->uninit();
1196 unconst(m->ptrExtPackManager).setNull();
1197 }
1198#endif
1199
1200 LogFlowThisFunc(("Terminating the async event handler...\n"));
1201 if (m->threadAsyncEvent != NIL_RTTHREAD)
1202 {
1203 /* signal to exit the event loop */
1204 if (RT_SUCCESS(m->pAsyncEventQ->interruptEventQueueProcessing()))
1205 {
1206 /*
1207 * Wait for thread termination (only after we've successfully
1208 * interrupted the event queue processing!)
1209 */
1210 int vrc = RTThreadWait(m->threadAsyncEvent, 60000, NULL);
1211 if (RT_FAILURE(vrc))
1212 Log1WarningFunc(("RTThreadWait(%RTthrd) -> %Rrc\n", m->threadAsyncEvent, vrc));
1213 }
1214 else
1215 {
1216 AssertMsgFailed(("interruptEventQueueProcessing() failed\n"));
1217 RTThreadWait(m->threadAsyncEvent, 0, NULL);
1218 }
1219
1220 unconst(m->threadAsyncEvent) = NIL_RTTHREAD;
1221 unconst(m->pAsyncEventQ) = NULL;
1222 }
1223
1224 LogFlowThisFunc(("Releasing event source...\n"));
1225 if (m->pEventSource)
1226 {
1227 // Must uninit the event source here, because it makes no sense that
1228 // it survives longer than the base object. If someone gets an event
1229 // with such an event source then that's life and it has to be dealt
1230 // with appropriately on the API client side.
1231 m->pEventSource->uninit();
1232 unconst(m->pEventSource).setNull();
1233 }
1234
1235 LogFlowThisFunc(("Terminating the client watcher...\n"));
1236 if (m->pClientWatcher)
1237 {
1238 delete m->pClientWatcher;
1239 unconst(m->pClientWatcher) = NULL;
1240 }
1241
1242 delete m->pAutostartDb;
1243#ifdef VBOX_WITH_MAIN_NLS
1244 if (m->pVBoxTranslator)
1245 m->pVBoxTranslator->release();
1246#endif
1247 // clean up our instance data
1248 delete m;
1249 m = NULL;
1250
1251 /* Unload hard disk plugin backends. */
1252 VDShutdown();
1253
1254 LogFlowThisFuncLeave();
1255 LogFlow(("===========================================================\n"));
1256}
1257
1258// Wrapped IVirtualBox properties
1259/////////////////////////////////////////////////////////////////////////////
1260HRESULT VirtualBox::getVersion(com::Utf8Str &aVersion)
1261{
1262 aVersion = sVersion;
1263 return S_OK;
1264}
1265
1266HRESULT VirtualBox::getVersionNormalized(com::Utf8Str &aVersionNormalized)
1267{
1268 aVersionNormalized = sVersionNormalized;
1269 return S_OK;
1270}
1271
1272HRESULT VirtualBox::getRevision(ULONG *aRevision)
1273{
1274 *aRevision = sRevision;
1275 return S_OK;
1276}
1277
1278HRESULT VirtualBox::getPackageType(com::Utf8Str &aPackageType)
1279{
1280 aPackageType = sPackageType;
1281 return S_OK;
1282}
1283
1284HRESULT VirtualBox::getAPIVersion(com::Utf8Str &aAPIVersion)
1285{
1286 aAPIVersion = sAPIVersion;
1287 return S_OK;
1288}
1289
1290HRESULT VirtualBox::getAPIRevision(LONG64 *aAPIRevision)
1291{
1292 AssertCompile(VBOX_VERSION_MAJOR < 128 && VBOX_VERSION_MAJOR > 0);
1293 AssertCompile((uint64_t)VBOX_VERSION_MINOR < 256);
1294 uint64_t uRevision = ((uint64_t)VBOX_VERSION_MAJOR << 56)
1295 | ((uint64_t)VBOX_VERSION_MINOR << 48)
1296 | ((uint64_t)VBOX_VERSION_BUILD << 40);
1297
1298 /** @todo This needs to be the same in OSE and non-OSE, preferrably
1299 * only changing when actual API changes happens. */
1300 uRevision |= 1;
1301
1302 *aAPIRevision = (LONG64)uRevision;
1303
1304 return S_OK;
1305}
1306
1307HRESULT VirtualBox::getHomeFolder(com::Utf8Str &aHomeFolder)
1308{
1309 /* mHomeDir is const and doesn't need a lock */
1310 aHomeFolder = m->strHomeDir;
1311 return S_OK;
1312}
1313
1314HRESULT VirtualBox::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
1315{
1316 /* mCfgFile.mName is const and doesn't need a lock */
1317 aSettingsFilePath = m->strSettingsFilePath;
1318 return S_OK;
1319}
1320
1321HRESULT VirtualBox::getHost(ComPtr<IHost> &aHost)
1322{
1323 /* mHost is const, no need to lock */
1324 m->pHost.queryInterfaceTo(aHost.asOutParam());
1325 return S_OK;
1326}
1327
1328HRESULT VirtualBox::getPlatformProperties(PlatformArchitecture_T platformArchitecture,
1329 ComPtr<IPlatformProperties> &aPlatformProperties)
1330{
1331 ComObjPtr<PlatformProperties> platformProperties;
1332 HRESULT hrc = platformProperties.createObject();
1333 AssertComRCReturn(hrc, hrc);
1334
1335 hrc = platformProperties->init(this);
1336 AssertComRCReturn(hrc, hrc);
1337
1338 hrc = platformProperties->i_setArchitecture(platformArchitecture);
1339 AssertComRCReturn(hrc, hrc);
1340
1341 return platformProperties.queryInterfaceTo(aPlatformProperties.asOutParam());
1342}
1343
1344HRESULT VirtualBox::getSystemProperties(ComPtr<ISystemProperties> &aSystemProperties)
1345{
1346 /* mSystemProperties is const, no need to lock */
1347 m->pSystemProperties.queryInterfaceTo(aSystemProperties.asOutParam());
1348 return S_OK;
1349}
1350
1351HRESULT VirtualBox::getMachines(std::vector<ComPtr<IMachine> > &aMachines)
1352{
1353 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1354 aMachines.resize(m->allMachines.size());
1355 size_t i = 0;
1356 for (MachinesOList::const_iterator it= m->allMachines.begin();
1357 it!= m->allMachines.end(); ++it, ++i)
1358 (*it).queryInterfaceTo(aMachines[i].asOutParam());
1359 return S_OK;
1360}
1361
1362HRESULT VirtualBox::getMachineGroups(std::vector<com::Utf8Str> &aMachineGroups)
1363{
1364 std::list<com::Utf8Str> allGroups;
1365
1366 /* get copy of all machine references, to avoid holding the list lock */
1367 MachinesOList::MyList allMachines;
1368 {
1369 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1370 allMachines = m->allMachines.getList();
1371 }
1372 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1373 it != allMachines.end();
1374 ++it)
1375 {
1376 const ComObjPtr<Machine> &pMachine = *it;
1377 AutoCaller autoMachineCaller(pMachine);
1378 if (FAILED(autoMachineCaller.hrc()))
1379 continue;
1380 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1381
1382 if (pMachine->i_isAccessible())
1383 {
1384 const StringsList &thisGroups = pMachine->i_getGroups();
1385 for (StringsList::const_iterator it2 = thisGroups.begin();
1386 it2 != thisGroups.end(); ++it2)
1387 allGroups.push_back(*it2);
1388 }
1389 }
1390
1391 /* throw out any duplicates */
1392 allGroups.sort();
1393 allGroups.unique();
1394 aMachineGroups.resize(allGroups.size());
1395 size_t i = 0;
1396 for (std::list<com::Utf8Str>::const_iterator it = allGroups.begin();
1397 it != allGroups.end(); ++it, ++i)
1398 aMachineGroups[i] = (*it);
1399 return S_OK;
1400}
1401
1402HRESULT VirtualBox::getHardDisks(std::vector<ComPtr<IMedium> > &aHardDisks)
1403{
1404 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1405 aHardDisks.resize(m->allHardDisks.size());
1406 size_t i = 0;
1407 for (MediaOList::const_iterator it = m->allHardDisks.begin();
1408 it != m->allHardDisks.end(); ++it, ++i)
1409 (*it).queryInterfaceTo(aHardDisks[i].asOutParam());
1410 return S_OK;
1411}
1412
1413HRESULT VirtualBox::getDVDImages(std::vector<ComPtr<IMedium> > &aDVDImages)
1414{
1415 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1416 aDVDImages.resize(m->allDVDImages.size());
1417 size_t i = 0;
1418 for (MediaOList::const_iterator it = m->allDVDImages.begin();
1419 it!= m->allDVDImages.end(); ++it, ++i)
1420 (*it).queryInterfaceTo(aDVDImages[i].asOutParam());
1421 return S_OK;
1422}
1423
1424HRESULT VirtualBox::getFloppyImages(std::vector<ComPtr<IMedium> > &aFloppyImages)
1425{
1426 AutoReadLock al(m->allFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1427 aFloppyImages.resize(m->allFloppyImages.size());
1428 size_t i = 0;
1429 for (MediaOList::const_iterator it = m->allFloppyImages.begin();
1430 it != m->allFloppyImages.end(); ++it, ++i)
1431 (*it).queryInterfaceTo(aFloppyImages[i].asOutParam());
1432 return S_OK;
1433}
1434
1435HRESULT VirtualBox::getProgressOperations(std::vector<ComPtr<IProgress> > &aProgressOperations)
1436{
1437 /* protect mProgressOperations */
1438 AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1439 ProgressMap pmap(m->mapProgressOperations);
1440 /* Can release lock now. The following code works on a copy of the map. */
1441 safeLock.release();
1442 aProgressOperations.resize(pmap.size());
1443 size_t i = 0;
1444 for (ProgressMap::iterator it = pmap.begin(); it != pmap.end(); ++it, ++i)
1445 it->second.queryInterfaceTo(aProgressOperations[i].asOutParam());
1446 return S_OK;
1447}
1448
1449
1450/**
1451 * Returns all supported guest OS types for one ore more platform architecture(s).
1452 *
1453 * @returns HRESULT
1454 * @param aArchitectures Platform architectures to return supported guest OS types for.
1455 * If empty, all supported guest OS for all platform architectures will be returned.
1456 * @param aGuestOSTypes Where to return the supported guest OS types.
1457 * Will be empty if none supported.
1458 */
1459HRESULT VirtualBox::i_getSupportedGuestOSTypes(std::vector<PlatformArchitecture_T> aArchitectures,
1460 std::vector<ComPtr<IGuestOSType> > &aGuestOSTypes)
1461{
1462 AutoReadLock al(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1463
1464 aGuestOSTypes.clear();
1465
1466 /** @todo We might want to redo this at some point, to better group / hash this. */
1467
1468 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin(); it != m->allGuestOSTypes.end(); ++it)
1469 {
1470 bool fFound = false;
1471 if (aArchitectures.size() == 0) /* If empty, we add all types we have. */
1472 fFound = true;
1473 else
1474 {
1475 for (size_t i = 0; i < aArchitectures.size(); i++)
1476 {
1477 if (aArchitectures[i] == (*it)->i_platformArchitecture())
1478 {
1479 fFound = true;
1480 break;
1481 }
1482 }
1483 }
1484
1485 if (fFound)
1486 {
1487 ComPtr<IGuestOSType> osType;
1488 (*it).queryInterfaceTo(osType.asOutParam());
1489 aGuestOSTypes.push_back(osType);
1490 }
1491 }
1492
1493 return S_OK;
1494}
1495
1496HRESULT VirtualBox::getGuestOSTypes(std::vector<ComPtr<IGuestOSType> > &aGuestOSTypes)
1497{
1498 std::vector<PlatformArchitecture_T> vecArchitectures; /* Stays empty to return all guest OS types. */
1499 return VirtualBox::i_getSupportedGuestOSTypes(vecArchitectures, aGuestOSTypes);
1500}
1501
1502HRESULT VirtualBox::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
1503{
1504 NOREF(aSharedFolders);
1505
1506 return setError(E_NOTIMPL, tr("Not yet implemented"));
1507}
1508
1509HRESULT VirtualBox::getPerformanceCollector(ComPtr<IPerformanceCollector> &aPerformanceCollector)
1510{
1511#ifdef VBOX_WITH_RESOURCE_USAGE_API
1512 /* mPerformanceCollector is const, no need to lock */
1513 m->pPerformanceCollector.queryInterfaceTo(aPerformanceCollector.asOutParam());
1514
1515 return S_OK;
1516#else /* !VBOX_WITH_RESOURCE_USAGE_API */
1517 NOREF(aPerformanceCollector);
1518 ReturnComNotImplemented();
1519#endif /* !VBOX_WITH_RESOURCE_USAGE_API */
1520}
1521
1522HRESULT VirtualBox::getDHCPServers(std::vector<ComPtr<IDHCPServer> > &aDHCPServers)
1523{
1524 AutoReadLock al(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1525 aDHCPServers.resize(m->allDHCPServers.size());
1526 size_t i = 0;
1527 for (DHCPServersOList::const_iterator it= m->allDHCPServers.begin();
1528 it!= m->allDHCPServers.end(); ++it, ++i)
1529 (*it).queryInterfaceTo(aDHCPServers[i].asOutParam());
1530 return S_OK;
1531}
1532
1533
1534HRESULT VirtualBox::getNATNetworks(std::vector<ComPtr<INATNetwork> > &aNATNetworks)
1535{
1536#ifdef VBOX_WITH_NAT_SERVICE
1537 AutoReadLock al(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1538 aNATNetworks.resize(m->allNATNetworks.size());
1539 size_t i = 0;
1540 for (NATNetworksOList::const_iterator it= m->allNATNetworks.begin();
1541 it!= m->allNATNetworks.end(); ++it, ++i)
1542 (*it).queryInterfaceTo(aNATNetworks[i].asOutParam());
1543 return S_OK;
1544#else
1545 NOREF(aNATNetworks);
1546 return E_NOTIMPL;
1547#endif
1548}
1549
1550HRESULT VirtualBox::getEventSource(ComPtr<IEventSource> &aEventSource)
1551{
1552 /* event source is const, no need to lock */
1553 m->pEventSource.queryInterfaceTo(aEventSource.asOutParam());
1554 return S_OK;
1555}
1556
1557HRESULT VirtualBox::getExtensionPackManager(ComPtr<IExtPackManager> &aExtensionPackManager)
1558{
1559 HRESULT hrc = S_OK;
1560#ifdef VBOX_WITH_EXTPACK
1561 /* The extension pack manager is const, no need to lock. */
1562 hrc = m->ptrExtPackManager.queryInterfaceTo(aExtensionPackManager.asOutParam());
1563#else
1564 hrc = E_NOTIMPL;
1565 NOREF(aExtensionPackManager);
1566#endif
1567 return hrc;
1568}
1569
1570/**
1571 * Host Only Network
1572 */
1573HRESULT VirtualBox::createHostOnlyNetwork(const com::Utf8Str &aNetworkName,
1574 ComPtr<IHostOnlyNetwork> &aNetwork)
1575{
1576#ifdef VBOX_WITH_VMNET
1577 ComObjPtr<HostOnlyNetwork> HostOnlyNetwork;
1578 HostOnlyNetwork.createObject();
1579 HRESULT hrc = HostOnlyNetwork->init(this, aNetworkName);
1580 if (FAILED(hrc)) return hrc;
1581
1582 m->allHostOnlyNetworks.addChild(HostOnlyNetwork);
1583
1584 {
1585 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1586 hrc = i_saveSettings();
1587 vboxLock.release();
1588
1589 if (FAILED(hrc))
1590 m->allHostOnlyNetworks.removeChild(HostOnlyNetwork);
1591 else
1592 HostOnlyNetwork.queryInterfaceTo(aNetwork.asOutParam());
1593 }
1594
1595 return hrc;
1596#else /* !VBOX_WITH_VMNET */
1597 NOREF(aNetworkName);
1598 NOREF(aNetwork);
1599 return E_NOTIMPL;
1600#endif /* !VBOX_WITH_VMNET */
1601}
1602
1603HRESULT VirtualBox::findHostOnlyNetworkByName(const com::Utf8Str &aNetworkName,
1604 ComPtr<IHostOnlyNetwork> &aNetwork)
1605{
1606#ifdef VBOX_WITH_VMNET
1607 Bstr bstrNameToFind(aNetworkName);
1608
1609 AutoReadLock alock(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1610
1611 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
1612 it != m->allHostOnlyNetworks.end();
1613 ++it)
1614 {
1615 Bstr bstrHostOnlyNetworkName;
1616 HRESULT hrc = (*it)->COMGETTER(NetworkName)(bstrHostOnlyNetworkName.asOutParam());
1617 if (FAILED(hrc)) return hrc;
1618
1619 if (bstrHostOnlyNetworkName == bstrNameToFind)
1620 {
1621 it->queryInterfaceTo(aNetwork.asOutParam());
1622 return S_OK;
1623 }
1624 }
1625 return VBOX_E_OBJECT_NOT_FOUND;
1626#else /* !VBOX_WITH_VMNET */
1627 NOREF(aNetworkName);
1628 NOREF(aNetwork);
1629 return E_NOTIMPL;
1630#endif /* !VBOX_WITH_VMNET */
1631}
1632
1633HRESULT VirtualBox::findHostOnlyNetworkById(const com::Guid &aId,
1634 ComPtr<IHostOnlyNetwork> &aNetwork)
1635{
1636#ifdef VBOX_WITH_VMNET
1637 ComObjPtr<HostOnlyNetwork> network;
1638 AutoReadLock alock(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1639
1640 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
1641 it != m->allHostOnlyNetworks.end();
1642 ++it)
1643 {
1644 Bstr bstrHostOnlyNetworkId;
1645 HRESULT hrc = (*it)->COMGETTER(Id)(bstrHostOnlyNetworkId.asOutParam());
1646 if (FAILED(hrc)) return hrc;
1647
1648 if (Guid(bstrHostOnlyNetworkId) == aId)
1649 {
1650 it->queryInterfaceTo(aNetwork.asOutParam());;
1651 return S_OK;
1652 }
1653 }
1654 return VBOX_E_OBJECT_NOT_FOUND;
1655#else /* !VBOX_WITH_VMNET */
1656 NOREF(aId);
1657 NOREF(aNetwork);
1658 return E_NOTIMPL;
1659#endif /* !VBOX_WITH_VMNET */
1660}
1661
1662HRESULT VirtualBox::removeHostOnlyNetwork(const ComPtr<IHostOnlyNetwork> &aNetwork)
1663{
1664#ifdef VBOX_WITH_VMNET
1665 Bstr name;
1666 HRESULT hrc = aNetwork->COMGETTER(NetworkName)(name.asOutParam());
1667 if (FAILED(hrc))
1668 return hrc;
1669 IHostOnlyNetwork *p = aNetwork;
1670 HostOnlyNetwork *network = static_cast<HostOnlyNetwork *>(p);
1671
1672 AutoCaller autoCaller(this);
1673 AssertComRCReturnRC(autoCaller.hrc());
1674
1675 AutoCaller HostOnlyNetworkCaller(network);
1676 AssertComRCReturnRC(HostOnlyNetworkCaller.hrc());
1677
1678 m->allHostOnlyNetworks.removeChild(network);
1679
1680 {
1681 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1682 hrc = i_saveSettings();
1683 vboxLock.release();
1684
1685 if (FAILED(hrc))
1686 m->allHostOnlyNetworks.addChild(network);
1687 }
1688 return hrc;
1689#else /* !VBOX_WITH_VMNET */
1690 NOREF(aNetwork);
1691 return E_NOTIMPL;
1692#endif /* !VBOX_WITH_VMNET */
1693}
1694
1695HRESULT VirtualBox::getHostOnlyNetworks(std::vector<ComPtr<IHostOnlyNetwork> > &aHostOnlyNetworks)
1696{
1697#ifdef VBOX_WITH_VMNET
1698 AutoReadLock al(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1699 aHostOnlyNetworks.resize(m->allHostOnlyNetworks.size());
1700 size_t i = 0;
1701 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
1702 it != m->allHostOnlyNetworks.end(); ++it)
1703 (*it).queryInterfaceTo(aHostOnlyNetworks[i++].asOutParam());
1704 return S_OK;
1705#else /* !VBOX_WITH_VMNET */
1706 NOREF(aHostOnlyNetworks);
1707 return E_NOTIMPL;
1708#endif /* !VBOX_WITH_VMNET */
1709}
1710
1711
1712HRESULT VirtualBox::getInternalNetworks(std::vector<com::Utf8Str> &aInternalNetworks)
1713{
1714 std::list<com::Utf8Str> allInternalNetworks;
1715
1716 /* get copy of all machine references, to avoid holding the list lock */
1717 MachinesOList::MyList allMachines;
1718 {
1719 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1720 allMachines = m->allMachines.getList();
1721 }
1722 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1723 it != allMachines.end(); ++it)
1724 {
1725 const ComObjPtr<Machine> &pMachine = *it;
1726 AutoCaller autoMachineCaller(pMachine);
1727 if (FAILED(autoMachineCaller.hrc()))
1728 continue;
1729 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1730
1731 if (pMachine->i_isAccessible())
1732 {
1733 ChipsetType_T enmChipsetType;
1734 HRESULT hrc = pMachine->i_getPlatform()->getChipsetType(&enmChipsetType);
1735 ComAssertComRC(hrc);
1736
1737 uint32_t const cNetworkAdapters = PlatformProperties::s_getMaxNetworkAdapters(enmChipsetType);
1738 for (ULONG i = 0; i < cNetworkAdapters; i++)
1739 {
1740 ComPtr<INetworkAdapter> pNet;
1741 hrc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
1742 if (FAILED(hrc) || pNet.isNull())
1743 continue;
1744 Bstr strInternalNetwork;
1745 hrc = pNet->COMGETTER(InternalNetwork)(strInternalNetwork.asOutParam());
1746 if (FAILED(hrc) || strInternalNetwork.isEmpty())
1747 continue;
1748
1749 allInternalNetworks.push_back(Utf8Str(strInternalNetwork));
1750 }
1751 }
1752 }
1753
1754 /* throw out any duplicates */
1755 allInternalNetworks.sort();
1756 allInternalNetworks.unique();
1757 size_t i = 0;
1758 aInternalNetworks.resize(allInternalNetworks.size());
1759 for (std::list<com::Utf8Str>::const_iterator it = allInternalNetworks.begin();
1760 it != allInternalNetworks.end();
1761 ++it, ++i)
1762 aInternalNetworks[i] = *it;
1763 return S_OK;
1764}
1765
1766HRESULT VirtualBox::getGenericNetworkDrivers(std::vector<com::Utf8Str> &aGenericNetworkDrivers)
1767{
1768 std::list<com::Utf8Str> allGenericNetworkDrivers;
1769
1770 /* get copy of all machine references, to avoid holding the list lock */
1771 MachinesOList::MyList allMachines;
1772 {
1773 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1774 allMachines = m->allMachines.getList();
1775 }
1776 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
1777 it != allMachines.end();
1778 ++it)
1779 {
1780 const ComObjPtr<Machine> &pMachine = *it;
1781 AutoCaller autoMachineCaller(pMachine);
1782 if (FAILED(autoMachineCaller.hrc()))
1783 continue;
1784 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
1785
1786 if (pMachine->i_isAccessible())
1787 {
1788 ChipsetType_T enmChipsetType;
1789 HRESULT hrc = pMachine->i_getPlatform()->getChipsetType(&enmChipsetType);
1790 ComAssertComRC(hrc);
1791
1792 uint32_t const cNetworkAdapters = PlatformProperties::s_getMaxNetworkAdapters(enmChipsetType);
1793 for (ULONG i = 0; i < cNetworkAdapters; i++)
1794 {
1795 ComPtr<INetworkAdapter> pNet;
1796 hrc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());
1797 if (FAILED(hrc) || pNet.isNull())
1798 continue;
1799 Bstr strGenericNetworkDriver;
1800 hrc = pNet->COMGETTER(GenericDriver)(strGenericNetworkDriver.asOutParam());
1801 if (FAILED(hrc) || strGenericNetworkDriver.isEmpty())
1802 continue;
1803
1804 allGenericNetworkDrivers.push_back(Utf8Str(strGenericNetworkDriver).c_str());
1805 }
1806 }
1807 }
1808
1809 /* throw out any duplicates */
1810 allGenericNetworkDrivers.sort();
1811 allGenericNetworkDrivers.unique();
1812 aGenericNetworkDrivers.resize(allGenericNetworkDrivers.size());
1813 size_t i = 0;
1814 for (std::list<com::Utf8Str>::const_iterator it = allGenericNetworkDrivers.begin();
1815 it != allGenericNetworkDrivers.end(); ++it, ++i)
1816 aGenericNetworkDrivers[i] = *it;
1817
1818 return S_OK;
1819}
1820
1821/**
1822 * Cloud Network
1823 */
1824#ifdef VBOX_WITH_CLOUD_NET
1825HRESULT VirtualBox::i_findCloudNetworkByName(const com::Utf8Str &aNetworkName,
1826 ComObjPtr<CloudNetwork> *aNetwork)
1827{
1828 ComPtr<CloudNetwork> found;
1829 Bstr bstrNameToFind(aNetworkName);
1830
1831 AutoReadLock alock(m->allCloudNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1832
1833 for (CloudNetworksOList::const_iterator it = m->allCloudNetworks.begin();
1834 it != m->allCloudNetworks.end();
1835 ++it)
1836 {
1837 Bstr bstrCloudNetworkName;
1838 HRESULT hrc = (*it)->COMGETTER(NetworkName)(bstrCloudNetworkName.asOutParam());
1839 if (FAILED(hrc)) return hrc;
1840
1841 if (bstrCloudNetworkName == bstrNameToFind)
1842 {
1843 *aNetwork = *it;
1844 return S_OK;
1845 }
1846 }
1847 return VBOX_E_OBJECT_NOT_FOUND;
1848}
1849#endif /* VBOX_WITH_CLOUD_NET */
1850
1851HRESULT VirtualBox::createCloudNetwork(const com::Utf8Str &aNetworkName,
1852 ComPtr<ICloudNetwork> &aNetwork)
1853{
1854#ifdef VBOX_WITH_CLOUD_NET
1855 ComObjPtr<CloudNetwork> cloudNetwork;
1856 cloudNetwork.createObject();
1857 HRESULT hrc = cloudNetwork->init(this, aNetworkName);
1858 if (FAILED(hrc)) return hrc;
1859
1860 m->allCloudNetworks.addChild(cloudNetwork);
1861
1862 {
1863 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1864 hrc = i_saveSettings();
1865 vboxLock.release();
1866
1867 if (FAILED(hrc))
1868 m->allCloudNetworks.removeChild(cloudNetwork);
1869 else
1870 cloudNetwork.queryInterfaceTo(aNetwork.asOutParam());
1871 }
1872
1873 return hrc;
1874#else /* !VBOX_WITH_CLOUD_NET */
1875 NOREF(aNetworkName);
1876 NOREF(aNetwork);
1877 return E_NOTIMPL;
1878#endif /* !VBOX_WITH_CLOUD_NET */
1879}
1880
1881HRESULT VirtualBox::findCloudNetworkByName(const com::Utf8Str &aNetworkName,
1882 ComPtr<ICloudNetwork> &aNetwork)
1883{
1884#ifdef VBOX_WITH_CLOUD_NET
1885 ComObjPtr<CloudNetwork> network;
1886 HRESULT hrc = i_findCloudNetworkByName(aNetworkName, &network);
1887 if (SUCCEEDED(hrc))
1888 network.queryInterfaceTo(aNetwork.asOutParam());
1889 return hrc;
1890#else /* !VBOX_WITH_CLOUD_NET */
1891 NOREF(aNetworkName);
1892 NOREF(aNetwork);
1893 return E_NOTIMPL;
1894#endif /* !VBOX_WITH_CLOUD_NET */
1895}
1896
1897HRESULT VirtualBox::removeCloudNetwork(const ComPtr<ICloudNetwork> &aNetwork)
1898{
1899#ifdef VBOX_WITH_CLOUD_NET
1900 Bstr name;
1901 HRESULT hrc = aNetwork->COMGETTER(NetworkName)(name.asOutParam());
1902 if (FAILED(hrc))
1903 return hrc;
1904 ICloudNetwork *p = aNetwork;
1905 CloudNetwork *network = static_cast<CloudNetwork *>(p);
1906
1907 AutoCaller autoCaller(this);
1908 AssertComRCReturnRC(autoCaller.hrc());
1909
1910 AutoCaller cloudNetworkCaller(network);
1911 AssertComRCReturnRC(cloudNetworkCaller.hrc());
1912
1913 m->allCloudNetworks.removeChild(network);
1914
1915 {
1916 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
1917 hrc = i_saveSettings();
1918 vboxLock.release();
1919
1920 if (FAILED(hrc))
1921 m->allCloudNetworks.addChild(network);
1922 }
1923 return hrc;
1924#else /* !VBOX_WITH_CLOUD_NET */
1925 NOREF(aNetwork);
1926 return E_NOTIMPL;
1927#endif /* !VBOX_WITH_CLOUD_NET */
1928}
1929
1930HRESULT VirtualBox::getCloudNetworks(std::vector<ComPtr<ICloudNetwork> > &aCloudNetworks)
1931{
1932#ifdef VBOX_WITH_CLOUD_NET
1933 AutoReadLock al(m->allCloudNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1934 aCloudNetworks.resize(m->allCloudNetworks.size());
1935 size_t i = 0;
1936 for (CloudNetworksOList::const_iterator it = m->allCloudNetworks.begin();
1937 it != m->allCloudNetworks.end(); ++it)
1938 (*it).queryInterfaceTo(aCloudNetworks[i++].asOutParam());
1939 return S_OK;
1940#else /* !VBOX_WITH_CLOUD_NET */
1941 NOREF(aCloudNetworks);
1942 return E_NOTIMPL;
1943#endif /* !VBOX_WITH_CLOUD_NET */
1944}
1945
1946#ifdef VBOX_WITH_CLOUD_NET
1947HRESULT VirtualBox::i_getEventSource(ComPtr<IEventSource>& aSource)
1948{
1949 m->pEventSource.queryInterfaceTo(aSource.asOutParam());
1950 return S_OK;
1951}
1952#endif /* VBOX_WITH_CLOUD_NET */
1953
1954HRESULT VirtualBox::getCloudProviderManager(ComPtr<ICloudProviderManager> &aCloudProviderManager)
1955{
1956 HRESULT hrc = m->pCloudProviderManager.queryInterfaceTo(aCloudProviderManager.asOutParam());
1957 return hrc;
1958}
1959
1960HRESULT VirtualBox::checkFirmwarePresent(PlatformArchitecture_T aPlatformArchitecture,
1961 FirmwareType_T aFirmwareType,
1962 const com::Utf8Str &aVersion,
1963 com::Utf8Str &aUrl,
1964 com::Utf8Str &aFile,
1965 BOOL *aResult)
1966{
1967 NOREF(aVersion);
1968
1969 static const VBOXFWDESC s_aFwDescX86[] =
1970 {
1971 { FirmwareType_BIOS, true, NULL, NULL },
1972#ifdef VBOX_WITH_EFI_IN_DD2
1973 { FirmwareType_EFI32, true, "VBoxEFI-x86.fd", NULL },
1974 { FirmwareType_EFI64, true, "VBoxEFI-amd64.fd", NULL },
1975 { FirmwareType_EFIDUAL, true, "VBoxEFIDual.fd", NULL },
1976#else /* Note! These links does not work! */
1977 { FirmwareType_EFI32, false, "VBoxEFI-x86.fd", "http://virtualbox.org/firmware/VBoxEFI-x86.fd" },
1978 { FirmwareType_EFI64, false, "VBoxEFI-amd64.fd", "http://virtualbox.org/firmware/VBoxEFI-amd64.fd" },
1979 { FirmwareType_EFIDUAL, false, "VBoxEFIDual.fd", "http://virtualbox.org/firmware/VBoxEFIDual.fd" },
1980#endif
1981 };
1982
1983 static const VBOXFWDESC s_aFwDescArm[] =
1984 {
1985#ifdef VBOX_WITH_EFI_IN_DD2
1986 { FirmwareType_EFI32, true, "VBoxEFI-arm32.fd", NULL },
1987 { FirmwareType_EFI64, true, "VBoxEFI-arm64.fd", NULL },
1988 #else /* Note! These links does not work! */
1989 { FirmwareType_EFI32, false, "VBoxEFI-arm32.fd", "http://virtualbox.org/firmware/VBoxEFI-arm32.fd" },
1990 { FirmwareType_EFI64, false, "VBoxEFI-arm64.fd", "http://virtualbox.org/firmware/VBoxEFI-arm64.fd" },
1991#endif
1992 };
1993
1994 PVBOXFWDESC pFwDesc = NULL;
1995 uint32_t cFwDesc = 0;
1996 if (aPlatformArchitecture == PlatformArchitecture_x86)
1997 {
1998 pFwDesc = &s_aFwDescX86[0];
1999 cFwDesc = RT_ELEMENTS(s_aFwDescX86);
2000 }
2001 else if (aPlatformArchitecture == PlatformArchitecture_ARM)
2002 {
2003 pFwDesc = &s_aFwDescArm[0];
2004 cFwDesc = RT_ELEMENTS(s_aFwDescArm);
2005 }
2006 else
2007 return E_INVALIDARG;
2008
2009 for (size_t i = 0; i < cFwDesc; i++)
2010 {
2011 if (aFirmwareType != pFwDesc->enmType)
2012 {
2013 pFwDesc++;
2014 continue;
2015 }
2016
2017 /* compiled-in firmware */
2018 if (pFwDesc->fBuiltIn)
2019 {
2020 aFile = pFwDesc->pszFileName;
2021 *aResult = TRUE;
2022 break;
2023 }
2024
2025 Utf8Str fullName;
2026 Utf8StrFmt shortName("Firmware%c%s", RTPATH_DELIMITER, pFwDesc->pszFileName);
2027 int vrc = i_calculateFullPath(shortName, fullName);
2028 AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
2029 if (RTFileExists(fullName.c_str()))
2030 {
2031 *aResult = TRUE;
2032 aFile = fullName;
2033 break;
2034 }
2035
2036 char szVBoxPath[RTPATH_MAX];
2037 vrc = RTPathExecDir(szVBoxPath, RTPATH_MAX);
2038 AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
2039 vrc = RTPathAppend(szVBoxPath, sizeof(szVBoxPath), pFwDesc->pszFileName);
2040 AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
2041 if (RTFileExists(szVBoxPath))
2042 {
2043 *aResult = TRUE;
2044 aFile = szVBoxPath;
2045 break;
2046 }
2047
2048 /** @todo account for version in the URL */
2049 aUrl = pFwDesc->pszUrl;
2050 *aResult = FALSE;
2051
2052 /* Assume single record per firmware type */
2053 break;
2054 }
2055
2056 return S_OK;
2057}
2058
2059/**
2060 * Walk the list of GuestOSType objects and return a list of all known guest
2061 * OS families.
2062 *
2063 * @param aOSFamilies Where to store the list of guest OS families.
2064 *
2065 * @note Locks the guest OS types list for reading.
2066 */
2067HRESULT VirtualBox::getGuestOSFamilies(std::vector<com::Utf8Str> &aOSFamilies)
2068{
2069 std::list<com::Utf8Str> allOSFamilies;
2070
2071 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2072
2073 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
2074 it != m->allGuestOSTypes.end(); ++it)
2075 {
2076 const Utf8Str &familyId = (*it)->i_familyId();
2077 AssertMsg(!familyId.isEmpty(), ("familfyId must not be NULL"));
2078 allOSFamilies.push_back(familyId);
2079 }
2080
2081 /* throw out any duplicates */
2082 allOSFamilies.sort();
2083 allOSFamilies.unique();
2084
2085 aOSFamilies.resize(allOSFamilies.size());
2086 size_t i = 0;
2087 for (std::list<com::Utf8Str>::const_iterator it = allOSFamilies.begin();
2088 it != allOSFamilies.end(); ++it, ++i)
2089 aOSFamilies[i] = (*it);
2090
2091 return S_OK;
2092}
2093
2094// Wrapped IVirtualBox methods
2095/////////////////////////////////////////////////////////////////////////////
2096
2097/* Helper for VirtualBox::ComposeMachineFilename */
2098static void sanitiseMachineFilename(Utf8Str &aName);
2099
2100HRESULT VirtualBox::composeMachineFilename(const com::Utf8Str &aName,
2101 const com::Utf8Str &aGroup,
2102 const com::Utf8Str &aCreateFlags,
2103 const com::Utf8Str &aBaseFolder,
2104 com::Utf8Str &aFile)
2105{
2106 if (RT_UNLIKELY(aName.isEmpty()))
2107 return setError(E_INVALIDARG, tr("Machine name is invalid, must not be empty"));
2108
2109 Utf8Str strBase = aBaseFolder;
2110 Utf8Str strName = aName;
2111
2112 LogFlowThisFunc(("aName=\"%s\",aBaseFolder=\"%s\"\n", strName.c_str(), strBase.c_str()));
2113
2114 com::Guid id;
2115 bool fDirectoryIncludesUUID = false;
2116 if (!aCreateFlags.isEmpty())
2117 {
2118 size_t uPos = 0;
2119 com::Utf8Str strKey;
2120 com::Utf8Str strValue;
2121 while ((uPos = aCreateFlags.parseKeyValue(strKey, strValue, uPos)) != com::Utf8Str::npos)
2122 {
2123 if (strKey == "UUID")
2124 id = strValue.c_str();
2125 else if (strKey == "directoryIncludesUUID")
2126 fDirectoryIncludesUUID = (strValue == "1");
2127 }
2128 }
2129
2130 if (id.isZero())
2131 fDirectoryIncludesUUID = false;
2132 else if (!id.isValid())
2133 {
2134 /* do something else */
2135 return setError(E_INVALIDARG,
2136 tr("'%s' is not a valid Guid"),
2137 id.toStringCurly().c_str());
2138 }
2139
2140 Utf8Str strGroup(aGroup);
2141 if (strGroup.isEmpty())
2142 strGroup = "/";
2143 HRESULT hrc = i_validateMachineGroup(strGroup, true);
2144 if (FAILED(hrc))
2145 return hrc;
2146
2147 /* Compose the settings file name using the following scheme:
2148 *
2149 * <base_folder><group>/<machine_name>/<machine_name>.xml
2150 *
2151 * If a non-null and non-empty base folder is specified, the default
2152 * machine folder will be used as a base folder.
2153 * We sanitise the machine name to a safe white list of characters before
2154 * using it.
2155 */
2156 Utf8Str strDirName(strName);
2157 if (fDirectoryIncludesUUID)
2158 strDirName += Utf8StrFmt(" (%RTuuid)", id.raw());
2159 sanitiseMachineFilename(strName);
2160 sanitiseMachineFilename(strDirName);
2161
2162 if (strBase.isEmpty())
2163 /* we use the non-full folder value below to keep the path relative */
2164 i_getDefaultMachineFolder(strBase);
2165
2166 i_calculateFullPath(strBase, strBase);
2167
2168 /* eliminate toplevel group to avoid // in the result */
2169 if (strGroup == "/")
2170 strGroup.setNull();
2171 aFile = com::Utf8StrFmt("%s%s%c%s%c%s.vbox",
2172 strBase.c_str(),
2173 strGroup.c_str(),
2174 RTPATH_DELIMITER,
2175 strDirName.c_str(),
2176 RTPATH_DELIMITER,
2177 strName.c_str());
2178 return S_OK;
2179}
2180
2181/**
2182 * Remove characters from a machine file name which can be problematic on
2183 * particular systems.
2184 * @param strName The file name to sanitise.
2185 */
2186void sanitiseMachineFilename(Utf8Str &strName)
2187{
2188 if (strName.isEmpty())
2189 return;
2190
2191 /* Set of characters which should be safe for use in filenames: some basic
2192 * ASCII, Unicode from Latin-1 alphabetic to the end of Hangul. We try to
2193 * skip anything that could count as a control character in Windows or
2194 * *nix, or be otherwise difficult for shells to handle (I would have
2195 * preferred to remove the space and brackets too). We also remove all
2196 * characters which need UTF-16 surrogate pairs for Windows's benefit.
2197 */
2198 static RTUNICP const s_uszValidRangePairs[] =
2199 {
2200 ' ', ' ',
2201 '(', ')',
2202 '-', '.',
2203 '0', '9',
2204 'A', 'Z',
2205 'a', 'z',
2206 '_', '_',
2207 0xa0, 0xd7af,
2208 '\0'
2209 };
2210
2211 char *pszName = strName.mutableRaw();
2212 ssize_t cReplacements = RTStrPurgeComplementSet(pszName, s_uszValidRangePairs, '_');
2213 Assert(cReplacements >= 0);
2214 NOREF(cReplacements);
2215
2216 /* No leading dot or dash. */
2217 if (pszName[0] == '.' || pszName[0] == '-')
2218 pszName[0] = '_';
2219
2220 /* No trailing dot. */
2221 if (pszName[strName.length() - 1] == '.')
2222 pszName[strName.length() - 1] = '_';
2223
2224 /* Mangle leading and trailing spaces. */
2225 for (size_t i = 0; pszName[i] == ' '; ++i)
2226 pszName[i] = '_';
2227 for (size_t i = strName.length() - 1; i && pszName[i] == ' '; --i)
2228 pszName[i] = '_';
2229}
2230
2231#ifdef DEBUG
2232typedef DECLCALLBACKTYPE(void, FNTESTPRINTF,(const char *, ...));
2233/** Simple unit test/operation examples for sanitiseMachineFilename(). */
2234static unsigned testSanitiseMachineFilename(FNTESTPRINTF *pfnPrintf)
2235{
2236 unsigned cErrors = 0;
2237
2238 /** Expected results of sanitising given file names. */
2239 static struct
2240 {
2241 /** The test file name to be sanitised (Utf-8). */
2242 const char *pcszIn;
2243 /** The expected sanitised output (Utf-8). */
2244 const char *pcszOutExpected;
2245 } aTest[] =
2246 {
2247 { "OS/2 2.1", "OS_2 2.1" },
2248 { "-!My VM!-", "__My VM_-" },
2249 { "\xF0\x90\x8C\xB0", "____" },
2250 { " My VM ", "__My VM__" },
2251 { ".My VM.", "_My VM_" },
2252 { "My VM", "My VM" }
2253 };
2254 for (unsigned i = 0; i < RT_ELEMENTS(aTest); ++i)
2255 {
2256 Utf8Str str(aTest[i].pcszIn);
2257 sanitiseMachineFilename(str);
2258 if (str.compare(aTest[i].pcszOutExpected))
2259 {
2260 ++cErrors;
2261 pfnPrintf("%s: line %d, expected %s, actual %s\n",
2262 __PRETTY_FUNCTION__, i, aTest[i].pcszOutExpected,
2263 str.c_str());
2264 }
2265 }
2266 return cErrors;
2267}
2268
2269/** @todo Proper testcase. */
2270/** @todo Do we have a better method of doing init functions? */
2271namespace
2272{
2273 class TestSanitiseMachineFilename
2274 {
2275 public:
2276 TestSanitiseMachineFilename(void)
2277 {
2278 Assert(!testSanitiseMachineFilename(RTAssertMsg2));
2279 }
2280 };
2281 TestSanitiseMachineFilename s_TestSanitiseMachineFilename;
2282}
2283#endif
2284
2285/** @note Locks mSystemProperties object for reading. */
2286HRESULT VirtualBox::createMachine(const com::Utf8Str &aSettingsFile,
2287 const com::Utf8Str &aName,
2288 PlatformArchitecture_T aArchitecture,
2289 const std::vector<com::Utf8Str> &aGroups,
2290 const com::Utf8Str &aOsTypeId,
2291 const com::Utf8Str &aFlags,
2292 const com::Utf8Str &aCipher,
2293 const com::Utf8Str &aPasswordId,
2294 const com::Utf8Str &aPassword,
2295 ComPtr<IMachine> &aMachine)
2296{
2297 if (aArchitecture == PlatformArchitecture_None)
2298 return setError(E_INVALIDARG, tr("'Must specify a valid platform architecture"));
2299
2300 LogFlowThisFuncEnter();
2301 LogFlowThisFunc(("aSettingsFile=\"%s\", aName=\"%s\", aArchitecture=%#x, aOsTypeId =\"%s\", aCreateFlags=\"%s\"\n",
2302 aSettingsFile.c_str(), aName.c_str(), aArchitecture, aOsTypeId.c_str(), aFlags.c_str()));
2303
2304#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
2305 if (aArchitecture != PlatformArchitecture_x86)/* x86 hosts only allows creating x86 VMs for now. */
2306 return setError(VBOX_E_PLATFORM_ARCH_NOT_SUPPORTED, tr("'Creating VMs for platform architecture %s not supported on %s"),
2307 Global::stringifyPlatformArchitecture(aArchitecture),
2308 Global::stringifyPlatformArchitecture(PlatformArchitecture_x86));
2309#endif
2310
2311 StringsList llGroups;
2312 HRESULT hrc = i_convertMachineGroups(aGroups, &llGroups);
2313 if (FAILED(hrc))
2314 return hrc;
2315
2316 /** @todo r=bird: Would be good to rewrite this parsing using offset into
2317 * aFlags and drop all the C pointers, strchr, misguided RTStrStr and
2318 * tedious copying of substrings. */
2319 Utf8Str strCreateFlags(aFlags); /** @todo r=bird: WTF is the point of this copy? */
2320 Guid id;
2321 bool fForceOverwrite = false;
2322 bool fDirectoryIncludesUUID = false;
2323 if (!strCreateFlags.isEmpty())
2324 {
2325 const char *pcszNext = strCreateFlags.c_str();
2326 while (*pcszNext != '\0')
2327 {
2328 Utf8Str strFlag;
2329 const char *pcszComma = strchr(pcszNext, ','); /*clueless version: RTStrStr(pcszNext, ","); */
2330 if (!pcszComma)
2331 strFlag = pcszNext;
2332 else
2333 strFlag.assign(pcszNext, (size_t)(pcszComma - pcszNext));
2334
2335 const char *pcszEqual = strchr(strFlag.c_str(), '='); /* more cluelessness: RTStrStr(strFlag.c_str(), "="); */
2336 /* skip over everything which doesn't contain '=' */
2337 if (pcszEqual && pcszEqual != strFlag.c_str())
2338 {
2339 Utf8Str strKey(strFlag.c_str(), (size_t)(pcszEqual - strFlag.c_str()));
2340 Utf8Str strValue(strFlag.c_str() + (pcszEqual - strFlag.c_str() + 1));
2341
2342 if (strKey == "UUID")
2343 id = strValue.c_str();
2344 else if (strKey == "forceOverwrite")
2345 fForceOverwrite = (strValue == "1");
2346 else if (strKey == "directoryIncludesUUID")
2347 fDirectoryIncludesUUID = (strValue == "1");
2348 }
2349
2350 if (!pcszComma)
2351 pcszNext += strFlag.length(); /* you can just 'break' out here... */
2352 else
2353 pcszNext += strFlag.length() + 1;
2354 }
2355 }
2356
2357 /* Create UUID if none was specified. */
2358 if (id.isZero())
2359 id.create();
2360 else if (!id.isValid())
2361 {
2362 /* do something else */
2363 return setError(E_INVALIDARG, tr("'%s' is not a valid Guid"), id.toStringCurly().c_str());
2364 }
2365
2366 /* NULL settings file means compose automatically */
2367 Utf8Str strSettingsFile(aSettingsFile);
2368 if (strSettingsFile.isEmpty())
2369 {
2370 Utf8Str strNewCreateFlags(Utf8StrFmt("UUID=%RTuuid", id.raw()));
2371 if (fDirectoryIncludesUUID)
2372 strNewCreateFlags += ",directoryIncludesUUID=1";
2373
2374 com::Utf8Str blstr;
2375 hrc = composeMachineFilename(aName,
2376 llGroups.front(),
2377 strNewCreateFlags,
2378 blstr /* aBaseFolder */,
2379 strSettingsFile);
2380 if (FAILED(hrc)) return hrc;
2381 }
2382
2383 /* create a new object */
2384 ComObjPtr<Machine> machine;
2385 hrc = machine.createObject();
2386 if (FAILED(hrc)) return hrc;
2387
2388 ComObjPtr<GuestOSType> osType;
2389 if (!aOsTypeId.isEmpty())
2390 i_findGuestOSType(aOsTypeId, osType);
2391
2392 /* initialize the machine object */
2393 hrc = machine->init(this,
2394 strSettingsFile,
2395 aName,
2396 aArchitecture,
2397 llGroups,
2398 aOsTypeId,
2399 osType,
2400 id,
2401 fForceOverwrite,
2402 fDirectoryIncludesUUID,
2403 aCipher,
2404 aPasswordId,
2405 aPassword);
2406 if (SUCCEEDED(hrc))
2407 {
2408 /* set the return value */
2409 machine.queryInterfaceTo(aMachine.asOutParam());
2410 AssertComRC(hrc);
2411
2412#ifdef VBOX_WITH_EXTPACK
2413 /* call the extension pack hooks */
2414 m->ptrExtPackManager->i_callAllVmCreatedHooks(machine);
2415#endif
2416 }
2417
2418 LogFlowThisFuncLeave();
2419
2420 return hrc;
2421}
2422
2423HRESULT VirtualBox::openMachine(const com::Utf8Str &aSettingsFile,
2424 const com::Utf8Str &aPassword,
2425 ComPtr<IMachine> &aMachine)
2426{
2427 /* create a new object */
2428 ComObjPtr<Machine> machine;
2429 HRESULT hrc = machine.createObject();
2430 if (SUCCEEDED(hrc))
2431 {
2432 /* initialize the machine object */
2433 hrc = machine->initFromSettings(this, aSettingsFile, NULL /* const Guid *aId */, aPassword);
2434 if (SUCCEEDED(hrc))
2435 {
2436 /* set the return value */
2437 machine.queryInterfaceTo(aMachine.asOutParam());
2438 ComAssertComRC(hrc);
2439 }
2440 }
2441
2442 return hrc;
2443}
2444
2445/** @note Locks objects! */
2446HRESULT VirtualBox::registerMachine(const ComPtr<IMachine> &aMachine)
2447{
2448 Bstr name;
2449 HRESULT hrc = aMachine->COMGETTER(Name)(name.asOutParam());
2450 if (FAILED(hrc)) return hrc;
2451
2452 /* We can safely cast child to Machine * here because only Machine
2453 * implementations of IMachine can be among our children. */
2454 IMachine *aM = aMachine;
2455 Machine *pMachine = static_cast<Machine*>(aM);
2456
2457 AutoCaller machCaller(pMachine);
2458 ComAssertComRCRetRC(machCaller.hrc());
2459
2460 hrc = i_registerMachine(pMachine);
2461 /* fire an event */
2462 if (SUCCEEDED(hrc))
2463 i_onMachineRegistered(pMachine->i_getId(), TRUE);
2464
2465 return hrc;
2466}
2467
2468/** @note Locks this object for reading, then some machine objects for reading. */
2469HRESULT VirtualBox::findMachine(const com::Utf8Str &aSettingsFile,
2470 ComPtr<IMachine> &aMachine)
2471{
2472 LogFlowThisFuncEnter();
2473 LogFlowThisFunc(("aSettingsFile=\"%s\", aMachine={%p}\n", aSettingsFile.c_str(), &aMachine));
2474
2475 /* start with not found */
2476 HRESULT hrc = S_OK;
2477 ComObjPtr<Machine> pMachineFound;
2478
2479 Guid id(aSettingsFile);
2480 Utf8Str strFile(aSettingsFile);
2481 if (id.isValid() && !id.isZero())
2482 hrc = i_findMachine(id,
2483 true /* fPermitInaccessible */,
2484 true /* setError */,
2485 &pMachineFound);
2486 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
2487 else
2488 {
2489 hrc = i_findMachineByName(strFile,
2490 true /* setError */,
2491 &pMachineFound);
2492 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
2493 }
2494
2495 /* this will set (*machine) to NULL if machineObj is null */
2496 pMachineFound.queryInterfaceTo(aMachine.asOutParam());
2497
2498 LogFlowThisFunc(("aName=\"%s\", aMachine=%p, hrc=%08X\n", aSettingsFile.c_str(), &aMachine, hrc));
2499 LogFlowThisFuncLeave();
2500
2501 return hrc;
2502}
2503
2504HRESULT VirtualBox::getMachinesByGroups(const std::vector<com::Utf8Str> &aGroups,
2505 std::vector<ComPtr<IMachine> > &aMachines)
2506{
2507 StringsList llGroups;
2508 HRESULT hrc = i_convertMachineGroups(aGroups, &llGroups);
2509 if (FAILED(hrc))
2510 return hrc;
2511
2512 /* we want to rely on sorted groups during compare, to save time */
2513 llGroups.sort();
2514
2515 /* get copy of all machine references, to avoid holding the list lock */
2516 MachinesOList::MyList allMachines;
2517 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2518 allMachines = m->allMachines.getList();
2519
2520 std::vector<ComObjPtr<IMachine> > saMachines;
2521 saMachines.resize(0);
2522 for (MachinesOList::MyList::const_iterator it = allMachines.begin();
2523 it != allMachines.end();
2524 ++it)
2525 {
2526 const ComObjPtr<Machine> &pMachine = *it;
2527 AutoCaller autoMachineCaller(pMachine);
2528 if (FAILED(autoMachineCaller.hrc()))
2529 continue;
2530 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
2531
2532 if (pMachine->i_isAccessible())
2533 {
2534 const StringsList &thisGroups = pMachine->i_getGroups();
2535 for (StringsList::const_iterator it2 = thisGroups.begin();
2536 it2 != thisGroups.end();
2537 ++it2)
2538 {
2539 const Utf8Str &group = *it2;
2540 bool fAppended = false;
2541 for (StringsList::const_iterator it3 = llGroups.begin();
2542 it3 != llGroups.end();
2543 ++it3)
2544 {
2545 int order = it3->compare(group);
2546 if (order == 0)
2547 {
2548 saMachines.push_back(static_cast<IMachine *>(pMachine));
2549 fAppended = true;
2550 break;
2551 }
2552 else if (order > 0)
2553 break;
2554 else
2555 continue;
2556 }
2557 /* avoid duplicates and save time */
2558 if (fAppended)
2559 break;
2560 }
2561 }
2562 }
2563 aMachines.resize(saMachines.size());
2564 size_t i = 0;
2565 for(i = 0; i < saMachines.size(); ++i)
2566 saMachines[i].queryInterfaceTo(aMachines[i].asOutParam());
2567
2568 return S_OK;
2569}
2570
2571HRESULT VirtualBox::getMachineStates(const std::vector<ComPtr<IMachine> > &aMachines,
2572 std::vector<MachineState_T> &aStates)
2573{
2574 com::SafeIfaceArray<IMachine> saMachines(aMachines);
2575 aStates.resize(aMachines.size());
2576 for (size_t i = 0; i < saMachines.size(); i++)
2577 {
2578 ComPtr<IMachine> pMachine = saMachines[i];
2579 MachineState_T state = MachineState_Null;
2580 if (!pMachine.isNull())
2581 {
2582 HRESULT hrc = pMachine->COMGETTER(State)(&state);
2583 if (hrc == E_ACCESSDENIED)
2584 hrc = S_OK;
2585 AssertComRC(hrc);
2586 }
2587 aStates[i] = state;
2588 }
2589 return S_OK;
2590}
2591
2592HRESULT VirtualBox::createUnattendedInstaller(ComPtr<IUnattended> &aUnattended)
2593{
2594#ifdef VBOX_WITH_UNATTENDED
2595 ComObjPtr<Unattended> ptrUnattended;
2596 HRESULT hrc = ptrUnattended.createObject();
2597 if (SUCCEEDED(hrc))
2598 {
2599 AutoReadLock wlock(this COMMA_LOCKVAL_SRC_POS);
2600 hrc = ptrUnattended->initUnattended(this);
2601 if (SUCCEEDED(hrc))
2602 hrc = ptrUnattended.queryInterfaceTo(aUnattended.asOutParam());
2603 }
2604 return hrc;
2605#else
2606 NOREF(aUnattended);
2607 return E_NOTIMPL;
2608#endif
2609}
2610
2611HRESULT VirtualBox::createMedium(const com::Utf8Str &aFormat,
2612 const com::Utf8Str &aLocation,
2613 AccessMode_T aAccessMode,
2614 DeviceType_T aDeviceType,
2615 ComPtr<IMedium> &aMedium)
2616{
2617 NOREF(aAccessMode); /**< @todo r=klaus make use of access mode */
2618
2619 HRESULT hrc = S_OK;
2620
2621 ComObjPtr<Medium> medium;
2622 medium.createObject();
2623 com::Utf8Str format = aFormat;
2624
2625 switch (aDeviceType)
2626 {
2627 case DeviceType_HardDisk:
2628 {
2629
2630 /* we don't access non-const data members so no need to lock */
2631 if (format.isEmpty())
2632 i_getDefaultHardDiskFormat(format);
2633
2634 hrc = medium->init(this,
2635 format,
2636 aLocation,
2637 Guid::Empty /* media registry: none yet */,
2638 aDeviceType);
2639 }
2640 break;
2641
2642 case DeviceType_DVD:
2643 case DeviceType_Floppy:
2644 {
2645
2646 if (format.isEmpty())
2647 return setError(E_INVALIDARG, tr("Format must be Valid Type%s"), format.c_str());
2648
2649#if 0 /* unused */
2650 // enforce read-only for DVDs even if caller specified ReadWrite
2651 if (aDeviceType == DeviceType_DVD)
2652 aAccessMode = AccessMode_ReadOnly;
2653#endif
2654
2655 hrc = medium->init(this,
2656 format,
2657 aLocation,
2658 Guid::Empty /* media registry: none yet */,
2659 aDeviceType);
2660
2661 }
2662 break;
2663
2664 default:
2665 return setError(E_INVALIDARG, tr("Device type must be HardDisk, DVD or Floppy %d"), aDeviceType);
2666 }
2667
2668 if (SUCCEEDED(hrc))
2669 {
2670 medium.queryInterfaceTo(aMedium.asOutParam());
2671 com::Guid uMediumId = medium->i_getId();
2672 if (uMediumId.isValid() && !uMediumId.isZero())
2673 i_onMediumRegistered(uMediumId, medium->i_getDeviceType(), TRUE);
2674 }
2675
2676 return hrc;
2677}
2678
2679HRESULT VirtualBox::openMedium(const com::Utf8Str &aLocation,
2680 DeviceType_T aDeviceType,
2681 AccessMode_T aAccessMode,
2682 BOOL aForceNewUuid,
2683 ComPtr<IMedium> &aMedium)
2684{
2685 HRESULT hrc = S_OK;
2686 Guid id(aLocation);
2687 ComObjPtr<Medium> pMedium;
2688
2689 // have to get write lock as the whole find/update sequence must be done
2690 // in one critical section, otherwise there are races which can lead to
2691 // multiple Medium objects with the same content
2692 AutoWriteLock treeLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2693
2694 // check if the device type is correct, and see if a medium for the
2695 // given path has already initialized; if so, return that
2696 switch (aDeviceType)
2697 {
2698 case DeviceType_HardDisk:
2699 if (id.isValid() && !id.isZero())
2700 hrc = i_findHardDiskById(id, false /* setError */, &pMedium);
2701 else
2702 hrc = i_findHardDiskByLocation(aLocation, false, /* aSetError */ &pMedium);
2703 break;
2704
2705 case DeviceType_Floppy:
2706 case DeviceType_DVD:
2707 if (id.isValid() && !id.isZero())
2708 hrc = i_findDVDOrFloppyImage(aDeviceType, &id, Utf8Str::Empty, false /* setError */, &pMedium);
2709 else
2710 hrc = i_findDVDOrFloppyImage(aDeviceType, NULL, aLocation, false /* setError */, &pMedium);
2711
2712 // enforce read-only for DVDs even if caller specified ReadWrite
2713 if (aDeviceType == DeviceType_DVD)
2714 aAccessMode = AccessMode_ReadOnly;
2715 break;
2716
2717 default:
2718 return setError(E_INVALIDARG, tr("Device type must be HardDisk, DVD or Floppy %d"), aDeviceType);
2719 }
2720
2721 bool fMediumRegistered = false;
2722 if (pMedium.isNull())
2723 {
2724 pMedium.createObject();
2725 treeLock.release();
2726 hrc = pMedium->init(this,
2727 aLocation,
2728 (aAccessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
2729 !!aForceNewUuid,
2730 aDeviceType);
2731 treeLock.acquire();
2732
2733 if (SUCCEEDED(hrc))
2734 {
2735 hrc = i_registerMedium(pMedium, &pMedium, treeLock);
2736
2737 treeLock.release();
2738
2739 /* Note that it's important to call uninit() on failure to register
2740 * because the differencing hard disk would have been already associated
2741 * with the parent and this association needs to be broken. */
2742
2743 if (FAILED(hrc))
2744 {
2745 pMedium->uninit();
2746 hrc = VBOX_E_OBJECT_NOT_FOUND;
2747 }
2748 else
2749 fMediumRegistered = true;
2750 }
2751 else if (hrc != VBOX_E_INVALID_OBJECT_STATE)
2752 hrc = VBOX_E_OBJECT_NOT_FOUND;
2753 }
2754
2755 if (SUCCEEDED(hrc))
2756 {
2757 pMedium.queryInterfaceTo(aMedium.asOutParam());
2758 if (fMediumRegistered)
2759 i_onMediumRegistered(pMedium->i_getId(), pMedium->i_getDeviceType() ,TRUE);
2760 }
2761
2762 return hrc;
2763}
2764
2765
2766/** @note Locks this object for reading. */
2767HRESULT VirtualBox::getGuestOSType(const com::Utf8Str &aId,
2768 ComPtr<IGuestOSType> &aType)
2769{
2770 ComObjPtr<GuestOSType> pType;
2771 HRESULT hrc = i_findGuestOSType(aId, pType);
2772 pType.queryInterfaceTo(aType.asOutParam());
2773 return hrc;
2774}
2775
2776HRESULT VirtualBox::createSharedFolder(const com::Utf8Str &aName,
2777 const com::Utf8Str &aHostPath,
2778 BOOL aWritable,
2779 BOOL aAutomount,
2780 const com::Utf8Str &aAutoMountPoint)
2781{
2782 NOREF(aName);
2783 NOREF(aHostPath);
2784 NOREF(aWritable);
2785 NOREF(aAutomount);
2786 NOREF(aAutoMountPoint);
2787
2788 return setError(E_NOTIMPL, tr("Not yet implemented"));
2789}
2790
2791HRESULT VirtualBox::removeSharedFolder(const com::Utf8Str &aName)
2792{
2793 NOREF(aName);
2794 return setError(E_NOTIMPL, tr("Not yet implemented"));
2795}
2796
2797/**
2798 * @note Locks this object for reading.
2799 */
2800HRESULT VirtualBox::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
2801{
2802 using namespace settings;
2803
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 aKeys.resize(m->pMainConfigFile->mapExtraDataItems.size());
2807 size_t i = 0;
2808 for (StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin();
2809 it != m->pMainConfigFile->mapExtraDataItems.end(); ++it, ++i)
2810 aKeys[i] = it->first;
2811
2812 return S_OK;
2813}
2814
2815/**
2816 * @note Locks this object for reading.
2817 */
2818HRESULT VirtualBox::getExtraData(const com::Utf8Str &aKey,
2819 com::Utf8Str &aValue)
2820{
2821 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(aKey);
2822 if (it != m->pMainConfigFile->mapExtraDataItems.end())
2823 // found:
2824 aValue = it->second; // source is a Utf8Str
2825
2826 /* return the result to caller (may be empty) */
2827
2828 return S_OK;
2829}
2830
2831/**
2832 * @note Locks this object for writing.
2833 */
2834HRESULT VirtualBox::setExtraData(const com::Utf8Str &aKey,
2835 const com::Utf8Str &aValue)
2836{
2837 Utf8Str strKey(aKey);
2838 Utf8Str strValue(aValue);
2839 Utf8Str strOldValue; // empty
2840 HRESULT hrc = S_OK;
2841
2842 /* Because control characters in aKey have caused problems in the settings
2843 * they are rejected unless the key should be deleted. */
2844 if (!strValue.isEmpty())
2845 {
2846 for (size_t i = 0; i < strKey.length(); ++i)
2847 {
2848 char ch = strKey[i];
2849 if (RTLocCIsCntrl(ch))
2850 return E_INVALIDARG;
2851 }
2852 }
2853
2854 // locking note: we only hold the read lock briefly to look up the old value,
2855 // then release it and call the onExtraCanChange callbacks. There is a small
2856 // chance of a race insofar as the callback might be called twice if two callers
2857 // change the same key at the same time, but that's a much better solution
2858 // than the deadlock we had here before. The actual changing of the extradata
2859 // is then performed under the write lock and race-free.
2860
2861 // look up the old value first; if nothing has changed then we need not do anything
2862 {
2863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
2864 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
2865 if (it != m->pMainConfigFile->mapExtraDataItems.end())
2866 strOldValue = it->second;
2867 }
2868
2869 bool fChanged;
2870 if ((fChanged = (strOldValue != strValue)))
2871 {
2872 // ask for permission from all listeners outside the locks;
2873 // onExtraDataCanChange() only briefly requests the VirtualBox
2874 // lock to copy the list of callbacks to invoke
2875 Bstr error;
2876
2877 if (!i_onExtraDataCanChange(Guid::Empty, Bstr(aKey).raw(), Bstr(aValue).raw(), error))
2878 {
2879 const char *sep = error.isEmpty() ? "" : ": ";
2880 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
2881 return setError(E_ACCESSDENIED,
2882 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
2883 strKey.c_str(),
2884 strValue.c_str(),
2885 sep,
2886 error.raw());
2887 }
2888
2889 // data is changing and change not vetoed: then write it out under the lock
2890
2891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 if (strValue.isEmpty())
2894 m->pMainConfigFile->mapExtraDataItems.erase(strKey);
2895 else
2896 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
2897 // creates a new key if needed
2898
2899 /* save settings on success */
2900 hrc = i_saveSettings();
2901 if (FAILED(hrc)) return hrc;
2902 }
2903
2904 // fire notification outside the lock
2905 if (fChanged)
2906 i_onExtraDataChanged(Guid::Empty, Bstr(aKey).raw(), Bstr(aValue).raw());
2907
2908 return hrc;
2909}
2910
2911/**
2912 *
2913 */
2914HRESULT VirtualBox::setSettingsSecret(const com::Utf8Str &aPassword)
2915{
2916 i_storeSettingsKey(aPassword);
2917 i_decryptSettings();
2918 return S_OK;
2919}
2920
2921int VirtualBox::i_decryptMediumSettings(Medium *pMedium)
2922{
2923 Bstr bstrCipher;
2924 HRESULT hrc = pMedium->GetProperty(Bstr("InitiatorSecretEncrypted").raw(),
2925 bstrCipher.asOutParam());
2926 if (SUCCEEDED(hrc))
2927 {
2928 Utf8Str strPlaintext;
2929 int vrc = i_decryptSetting(&strPlaintext, bstrCipher);
2930 if (RT_SUCCESS(vrc))
2931 pMedium->i_setPropertyDirect("InitiatorSecret", strPlaintext);
2932 else
2933 return vrc;
2934 }
2935 return VINF_SUCCESS;
2936}
2937
2938/**
2939 * Decrypt all encrypted settings.
2940 *
2941 * So far we only have encrypted iSCSI initiator secrets so we just go through
2942 * all hard disk media and determine the plain 'InitiatorSecret' from
2943 * 'InitiatorSecretEncrypted. The latter is stored as Base64 because medium
2944 * properties need to be null-terminated strings.
2945 */
2946int VirtualBox::i_decryptSettings()
2947{
2948 bool fFailure = false;
2949 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2950 for (MediaList::const_iterator mt = m->allHardDisks.begin();
2951 mt != m->allHardDisks.end();
2952 ++mt)
2953 {
2954 ComObjPtr<Medium> pMedium = *mt;
2955 AutoCaller medCaller(pMedium);
2956 if (FAILED(medCaller.hrc()))
2957 continue;
2958 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
2959 int vrc = i_decryptMediumSettings(pMedium);
2960 if (RT_FAILURE(vrc))
2961 fFailure = true;
2962 }
2963 if (!fFailure)
2964 {
2965 for (MediaList::const_iterator mt = m->allHardDisks.begin();
2966 mt != m->allHardDisks.end();
2967 ++mt)
2968 {
2969 i_onMediumConfigChanged(*mt);
2970 }
2971 }
2972 return fFailure ? VERR_INVALID_PARAMETER : VINF_SUCCESS;
2973}
2974
2975/**
2976 * Encode.
2977 *
2978 * @param aPlaintext plaintext to be encrypted
2979 * @param aCiphertext resulting ciphertext (base64-encoded)
2980 */
2981int VirtualBox::i_encryptSetting(const Utf8Str &aPlaintext, Utf8Str *aCiphertext)
2982{
2983 uint8_t abCiphertext[32];
2984 char szCipherBase64[128];
2985 size_t cchCipherBase64;
2986 int vrc = i_encryptSettingBytes((uint8_t*)aPlaintext.c_str(), abCiphertext, aPlaintext.length()+1, sizeof(abCiphertext));
2987 if (RT_SUCCESS(vrc))
2988 {
2989 vrc = RTBase64Encode(abCiphertext, sizeof(abCiphertext), szCipherBase64, sizeof(szCipherBase64), &cchCipherBase64);
2990 if (RT_SUCCESS(vrc))
2991 *aCiphertext = szCipherBase64;
2992 }
2993 return vrc;
2994}
2995
2996/**
2997 * Decode.
2998 *
2999 * @param aPlaintext resulting plaintext
3000 * @param aCiphertext ciphertext (base64-encoded) to decrypt
3001 */
3002int VirtualBox::i_decryptSetting(Utf8Str *aPlaintext, const Utf8Str &aCiphertext)
3003{
3004 uint8_t abPlaintext[64];
3005 uint8_t abCiphertext[64];
3006 size_t cbCiphertext;
3007 int vrc = RTBase64Decode(aCiphertext.c_str(),
3008 abCiphertext, sizeof(abCiphertext),
3009 &cbCiphertext, NULL);
3010 if (RT_SUCCESS(vrc))
3011 {
3012 vrc = i_decryptSettingBytes(abPlaintext, abCiphertext, cbCiphertext);
3013 if (RT_SUCCESS(vrc))
3014 {
3015 for (unsigned i = 0; i < cbCiphertext; i++)
3016 {
3017 /* sanity check: null-terminated string? */
3018 if (abPlaintext[i] == '\0')
3019 {
3020 /* sanity check: valid UTF8 string? */
3021 if (RTStrIsValidEncoding((const char*)abPlaintext))
3022 {
3023 *aPlaintext = Utf8Str((const char*)abPlaintext);
3024 return VINF_SUCCESS;
3025 }
3026 }
3027 }
3028 vrc = VERR_INVALID_MAGIC;
3029 }
3030 }
3031 return vrc;
3032}
3033
3034/**
3035 * Encrypt secret bytes. Use the m->SettingsCipherKey as key.
3036 *
3037 * @param aPlaintext clear text to be encrypted
3038 * @param aCiphertext resulting encrypted text
3039 * @param aPlaintextSize size of the plaintext
3040 * @param aCiphertextSize size of the ciphertext
3041 */
3042int VirtualBox::i_encryptSettingBytes(const uint8_t *aPlaintext, uint8_t *aCiphertext,
3043 size_t aPlaintextSize, size_t aCiphertextSize) const
3044{
3045 unsigned i, j;
3046 uint8_t aBytes[64];
3047
3048 if (!m->fSettingsCipherKeySet)
3049 return VERR_INVALID_STATE;
3050
3051 if (aCiphertextSize > sizeof(aBytes))
3052 return VERR_BUFFER_OVERFLOW;
3053
3054 if (aCiphertextSize < 32)
3055 return VERR_INVALID_PARAMETER;
3056
3057 AssertCompile(sizeof(m->SettingsCipherKey) >= 32);
3058
3059 /* store the first 8 bytes of the cipherkey for verification */
3060 for (i = 0, j = 0; i < 8; i++, j++)
3061 aCiphertext[i] = m->SettingsCipherKey[j];
3062
3063 for (unsigned k = 0; k < aPlaintextSize && i < aCiphertextSize; i++, k++)
3064 {
3065 aCiphertext[i] = (aPlaintext[k] ^ m->SettingsCipherKey[j]);
3066 if (++j >= sizeof(m->SettingsCipherKey))
3067 j = 0;
3068 }
3069
3070 /* fill with random data to have a minimal length (salt) */
3071 if (i < aCiphertextSize)
3072 {
3073 RTRandBytes(aBytes, aCiphertextSize - i);
3074 for (int k = 0; i < aCiphertextSize; i++, k++)
3075 {
3076 aCiphertext[i] = aBytes[k] ^ m->SettingsCipherKey[j];
3077 if (++j >= sizeof(m->SettingsCipherKey))
3078 j = 0;
3079 }
3080 }
3081
3082 return VINF_SUCCESS;
3083}
3084
3085/**
3086 * Decrypt secret bytes. Use the m->SettingsCipherKey as key.
3087 *
3088 * @param aPlaintext resulting plaintext
3089 * @param aCiphertext ciphertext to be decrypted
3090 * @param aCiphertextSize size of the ciphertext == size of the plaintext
3091 */
3092int VirtualBox::i_decryptSettingBytes(uint8_t *aPlaintext,
3093 const uint8_t *aCiphertext, size_t aCiphertextSize) const
3094{
3095 unsigned i, j;
3096
3097 if (!m->fSettingsCipherKeySet)
3098 return VERR_INVALID_STATE;
3099
3100 if (aCiphertextSize < 32)
3101 return VERR_INVALID_PARAMETER;
3102
3103 /* key verification */
3104 for (i = 0, j = 0; i < 8; i++, j++)
3105 if (aCiphertext[i] != m->SettingsCipherKey[j])
3106 return VERR_INVALID_MAGIC;
3107
3108 /* poison */
3109 memset(aPlaintext, 0xff, aCiphertextSize);
3110 for (int k = 0; i < aCiphertextSize; i++, k++)
3111 {
3112 aPlaintext[k] = aCiphertext[i] ^ m->SettingsCipherKey[j];
3113 if (++j >= sizeof(m->SettingsCipherKey))
3114 j = 0;
3115 }
3116
3117 return VINF_SUCCESS;
3118}
3119
3120/**
3121 * Store a settings key.
3122 *
3123 * @param aKey the key to store
3124 */
3125void VirtualBox::i_storeSettingsKey(const Utf8Str &aKey)
3126{
3127 RTSha512(aKey.c_str(), aKey.length(), m->SettingsCipherKey);
3128 m->fSettingsCipherKeySet = true;
3129}
3130
3131// public methods only for internal purposes
3132/////////////////////////////////////////////////////////////////////////////
3133
3134#ifdef DEBUG
3135void VirtualBox::i_dumpAllBackRefs()
3136{
3137 {
3138 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3139 for (MediaList::const_iterator mt = m->allHardDisks.begin();
3140 mt != m->allHardDisks.end();
3141 ++mt)
3142 {
3143 ComObjPtr<Medium> pMedium = *mt;
3144 pMedium->i_dumpBackRefs();
3145 }
3146 }
3147 {
3148 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3149 for (MediaList::const_iterator mt = m->allDVDImages.begin();
3150 mt != m->allDVDImages.end();
3151 ++mt)
3152 {
3153 ComObjPtr<Medium> pMedium = *mt;
3154 pMedium->i_dumpBackRefs();
3155 }
3156 }
3157}
3158#endif
3159
3160/**
3161 * Posts an event to the event queue that is processed asynchronously
3162 * on a dedicated thread.
3163 *
3164 * Posting events to the dedicated event queue is useful to perform secondary
3165 * actions outside any object locks -- for example, to iterate over a list
3166 * of callbacks and inform them about some change caused by some object's
3167 * method call.
3168 *
3169 * @param event event to post; must have been allocated using |new|, will
3170 * be deleted automatically by the event thread after processing
3171 *
3172 * @note Doesn't lock any object.
3173 */
3174HRESULT VirtualBox::i_postEvent(Event *event)
3175{
3176 AssertReturn(event, E_FAIL);
3177
3178 HRESULT hrc;
3179 AutoCaller autoCaller(this);
3180 if (SUCCEEDED((hrc = autoCaller.hrc())))
3181 {
3182 if (getObjectState().getState() != ObjectState::Ready)
3183 Log1WarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
3184 getObjectState().getState()));
3185 // return S_OK
3186 else if ( (m->pAsyncEventQ)
3187 && (m->pAsyncEventQ->postEvent(event))
3188 )
3189 return S_OK;
3190 else
3191 hrc = E_FAIL;
3192 }
3193
3194 // in any event of failure, we must clean up here, or we'll leak;
3195 // the caller has allocated the object using new()
3196 delete event;
3197 return hrc;
3198}
3199
3200/**
3201 * Adds a progress to the global collection of pending operations.
3202 * Usually gets called upon progress object initialization.
3203 *
3204 * @param aProgress Operation to add to the collection.
3205 *
3206 * @note Doesn't lock objects.
3207 */
3208HRESULT VirtualBox::i_addProgress(IProgress *aProgress)
3209{
3210 CheckComArgNotNull(aProgress);
3211
3212 AutoCaller autoCaller(this);
3213 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3214
3215 Bstr id;
3216 HRESULT hrc = aProgress->COMGETTER(Id)(id.asOutParam());
3217 AssertComRCReturnRC(hrc);
3218
3219 /* protect mProgressOperations */
3220 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
3221
3222 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
3223 return S_OK;
3224}
3225
3226/**
3227 * Removes the progress from the global collection of pending operations.
3228 * Usually gets called upon progress completion.
3229 *
3230 * @param aId UUID of the progress operation to remove
3231 *
3232 * @note Doesn't lock objects.
3233 */
3234HRESULT VirtualBox::i_removeProgress(IN_GUID aId)
3235{
3236 AutoCaller autoCaller(this);
3237 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3238
3239 ComPtr<IProgress> progress;
3240
3241 /* protect mProgressOperations */
3242 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
3243
3244 size_t cnt = m->mapProgressOperations.erase(aId);
3245 Assert(cnt == 1);
3246 NOREF(cnt);
3247
3248 return S_OK;
3249}
3250
3251#ifdef RT_OS_WINDOWS
3252
3253class StartSVCHelperClientData : public ThreadTask
3254{
3255public:
3256 StartSVCHelperClientData()
3257 {
3258 LogFlowFuncEnter();
3259 m_strTaskName = "SVCHelper";
3260 threadVoidData = NULL;
3261 initialized = false;
3262 privileged = false;
3263 func = NULL;
3264 user = NULL;
3265 }
3266
3267 virtual ~StartSVCHelperClientData()
3268 {
3269 LogFlowFuncEnter();
3270 if (threadVoidData!=NULL)
3271 {
3272 delete threadVoidData;
3273 threadVoidData=NULL;
3274 }
3275 };
3276
3277 void handler()
3278 {
3279 VirtualBox::i_SVCHelperClientThreadTask(this);
3280 }
3281
3282 const ComPtr<Progress>& GetProgressObject() const {return progress;}
3283
3284 bool init(VirtualBox* aVbox,
3285 Progress* aProgress,
3286 bool aPrivileged,
3287 VirtualBox::PFN_SVC_HELPER_CLIENT_T aFunc,
3288 void *aUser)
3289 {
3290 LogFlowFuncEnter();
3291 that = aVbox;
3292 progress = aProgress;
3293 privileged = aPrivileged;
3294 func = aFunc;
3295 user = aUser;
3296
3297 initThreadVoidData();
3298
3299 initialized = true;
3300
3301 return initialized;
3302 }
3303
3304 bool isOk() const{ return initialized;}
3305
3306 bool initialized;
3307 ComObjPtr<VirtualBox> that;
3308 ComObjPtr<Progress> progress;
3309 bool privileged;
3310 VirtualBox::PFN_SVC_HELPER_CLIENT_T func;
3311 void *user;
3312 ThreadVoidData *threadVoidData;
3313
3314private:
3315 bool initThreadVoidData()
3316 {
3317 LogFlowFuncEnter();
3318 threadVoidData = static_cast<ThreadVoidData*>(user);
3319 return true;
3320 }
3321};
3322
3323/**
3324 * Helper method that starts a worker thread that:
3325 * - creates a pipe communication channel using SVCHlpClient;
3326 * - starts an SVC Helper process that will inherit this channel;
3327 * - executes the supplied function by passing it the created SVCHlpClient
3328 * and opened instance to communicate to the Helper process and the given
3329 * Progress object.
3330 *
3331 * The user function is supposed to communicate to the helper process
3332 * using the \a aClient argument to do the requested job and optionally expose
3333 * the progress through the \a aProgress object. The user function should never
3334 * call notifyComplete() on it: this will be done automatically using the
3335 * result code returned by the function.
3336 *
3337 * Before the user function is started, the communication channel passed to
3338 * the \a aClient argument is fully set up, the function should start using
3339 * its write() and read() methods directly.
3340 *
3341 * The \a aVrc parameter of the user function may be used to return an error
3342 * code if it is related to communication errors (for example, returned by
3343 * the SVCHlpClient members when they fail). In this case, the correct error
3344 * message using this value will be reported to the caller. Note that the
3345 * value of \a aVrc is inspected only if the user function itself returns
3346 * success.
3347 *
3348 * If a failure happens anywhere before the user function would be normally
3349 * called, it will be called anyway in special "cleanup only" mode indicated
3350 * by \a aClient, \a aProgress and \a aVrc arguments set to NULL. In this mode,
3351 * all the function is supposed to do is to cleanup its aUser argument if
3352 * necessary (it's assumed that the ownership of this argument is passed to
3353 * the user function once #startSVCHelperClient() returns a success, thus
3354 * making it responsible for the cleanup).
3355 *
3356 * After the user function returns, the thread will send the SVCHlpMsg::Null
3357 * message to indicate a process termination.
3358 *
3359 * @param aPrivileged |true| to start the SVC Helper process as a privileged
3360 * user that can perform administrative tasks
3361 * @param aFunc user function to run
3362 * @param aUser argument to the user function
3363 * @param aProgress progress object that will track operation completion
3364 *
3365 * @note aPrivileged is currently ignored (due to some unsolved problems in
3366 * Vista) and the process will be started as a normal (unprivileged)
3367 * process.
3368 *
3369 * @note Doesn't lock anything.
3370 */
3371HRESULT VirtualBox::i_startSVCHelperClient(bool aPrivileged,
3372 PFN_SVC_HELPER_CLIENT_T aFunc,
3373 void *aUser, Progress *aProgress)
3374{
3375 LogFlowFuncEnter();
3376 AssertReturn(aFunc, E_POINTER);
3377 AssertReturn(aProgress, E_POINTER);
3378
3379 AutoCaller autoCaller(this);
3380 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3381
3382 /* create the i_SVCHelperClientThreadTask() argument */
3383
3384 HRESULT hrc = S_OK;
3385 StartSVCHelperClientData *pTask = NULL;
3386 try
3387 {
3388 pTask = new StartSVCHelperClientData();
3389
3390 pTask->init(this, aProgress, aPrivileged, aFunc, aUser);
3391
3392 if (!pTask->isOk())
3393 {
3394 delete pTask;
3395 LogRel(("Could not init StartSVCHelperClientData object \n"));
3396 throw E_FAIL;
3397 }
3398
3399 //this function delete pTask in case of exceptions, so there is no need in the call of delete operator
3400 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_WORKER);
3401
3402 }
3403 catch(std::bad_alloc &)
3404 {
3405 hrc = setError(E_OUTOFMEMORY);
3406 }
3407 catch(...)
3408 {
3409 LogRel(("Could not create thread for StartSVCHelperClientData \n"));
3410 hrc = E_FAIL;
3411 }
3412
3413 return hrc;
3414}
3415
3416/**
3417 * Worker thread for startSVCHelperClient().
3418 */
3419/* static */
3420void VirtualBox::i_SVCHelperClientThreadTask(StartSVCHelperClientData *pTask)
3421{
3422 LogFlowFuncEnter();
3423 HRESULT hrc = S_OK;
3424 bool userFuncCalled = false;
3425
3426 do
3427 {
3428 AssertBreakStmt(pTask, hrc = E_POINTER);
3429 AssertReturnVoid(!pTask->progress.isNull());
3430
3431 /* protect VirtualBox from uninitialization */
3432 AutoCaller autoCaller(pTask->that);
3433 if (!autoCaller.isOk())
3434 {
3435 /* it's too late */
3436 hrc = autoCaller.hrc();
3437 break;
3438 }
3439
3440 int vrc = VINF_SUCCESS;
3441
3442 Guid id;
3443 id.create();
3444 SVCHlpClient client;
3445 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
3446 id.raw()).c_str());
3447 if (RT_FAILURE(vrc))
3448 {
3449 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not create the communication channel (%Rrc)"), vrc);
3450 break;
3451 }
3452
3453 /* get the path to the executable */
3454 char exePathBuf[RTPATH_MAX];
3455 char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX);
3456 if (!exePath)
3457 {
3458 hrc = pTask->that->setError(E_FAIL, tr("Cannot get executable name"));
3459 break;
3460 }
3461
3462 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().c_str());
3463
3464 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.c_str()));
3465
3466 RTPROCESS pid = NIL_RTPROCESS;
3467
3468 if (pTask->privileged)
3469 {
3470 /* Attempt to start a privileged process using the Run As dialog */
3471
3472 Bstr file = exePath;
3473 Bstr parameters = argsStr;
3474
3475 SHELLEXECUTEINFO shExecInfo;
3476
3477 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
3478
3479 shExecInfo.fMask = NULL;
3480 shExecInfo.hwnd = NULL;
3481 shExecInfo.lpVerb = L"runas";
3482 shExecInfo.lpFile = file.raw();
3483 shExecInfo.lpParameters = parameters.raw();
3484 shExecInfo.lpDirectory = NULL;
3485 shExecInfo.nShow = SW_NORMAL;
3486 shExecInfo.hInstApp = NULL;
3487
3488 if (!ShellExecuteEx(&shExecInfo))
3489 {
3490 int vrc2 = RTErrConvertFromWin32(GetLastError());
3491 /* hide excessive details in case of a frequent error
3492 * (pressing the Cancel button to close the Run As dialog) */
3493 if (vrc2 == VERR_CANCELLED)
3494 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Operation canceled by the user"));
3495 else
3496 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not launch a privileged process '%s' (%Rrc)"), exePath, vrc2);
3497 break;
3498 }
3499 }
3500 else
3501 {
3502 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
3503 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
3504 if (RT_FAILURE(vrc))
3505 {
3506 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
3507 break;
3508 }
3509 }
3510
3511 /* wait for the client to connect */
3512 vrc = client.connect();
3513 if (RT_SUCCESS(vrc))
3514 {
3515 /* start the user supplied function */
3516 hrc = pTask->func(&client, pTask->progress, pTask->user, &vrc);
3517 userFuncCalled = true;
3518 }
3519
3520 /* send the termination signal to the process anyway */
3521 {
3522 int vrc2 = client.write(SVCHlpMsg::Null);
3523 if (RT_SUCCESS(vrc))
3524 vrc = vrc2;
3525 }
3526
3527 if (SUCCEEDED(hrc) && RT_FAILURE(vrc))
3528 {
3529 hrc = pTask->that->setErrorBoth(E_FAIL, vrc, tr("Could not operate the communication channel (%Rrc)"), vrc);
3530 break;
3531 }
3532 }
3533 while (0);
3534
3535 if (FAILED(hrc) && !userFuncCalled)
3536 {
3537 /* call the user function in the "cleanup only" mode
3538 * to let it free resources passed to in aUser */
3539 pTask->func(NULL, NULL, pTask->user, NULL);
3540 }
3541
3542 pTask->progress->i_notifyComplete(hrc);
3543
3544 LogFlowFuncLeave();
3545}
3546
3547#endif /* RT_OS_WINDOWS */
3548
3549/**
3550 * Sends a signal to the client watcher to rescan the set of machines
3551 * that have open sessions.
3552 *
3553 * @note Doesn't lock anything.
3554 */
3555void VirtualBox::i_updateClientWatcher()
3556{
3557 AutoCaller autoCaller(this);
3558 AssertComRCReturnVoid(autoCaller.hrc());
3559
3560 AssertPtrReturnVoid(m->pClientWatcher);
3561 m->pClientWatcher->update();
3562}
3563
3564/**
3565 * Adds the given child process ID to the list of processes to be reaped.
3566 * This call should be followed by #i_updateClientWatcher() to take the effect.
3567 *
3568 * @note Doesn't lock anything.
3569 */
3570void VirtualBox::i_addProcessToReap(RTPROCESS pid)
3571{
3572 AutoCaller autoCaller(this);
3573 AssertComRCReturnVoid(autoCaller.hrc());
3574
3575 AssertPtrReturnVoid(m->pClientWatcher);
3576 m->pClientWatcher->addProcess(pid);
3577}
3578
3579/**
3580 * VD plugin load
3581 */
3582int VirtualBox::i_loadVDPlugin(const char *pszPluginLibrary)
3583{
3584 return m->pSystemProperties->i_loadVDPlugin(pszPluginLibrary);
3585}
3586
3587/**
3588 * VD plugin unload
3589 */
3590int VirtualBox::i_unloadVDPlugin(const char *pszPluginLibrary)
3591{
3592 return m->pSystemProperties->i_unloadVDPlugin(pszPluginLibrary);
3593}
3594
3595/**
3596 * @note Doesn't lock any object.
3597 */
3598void VirtualBox::i_onMediumRegistered(const Guid &aMediumId, const DeviceType_T aDevType, const BOOL aRegistered)
3599{
3600 ComPtr<IEvent> ptrEvent;
3601 HRESULT hrc = ::CreateMediumRegisteredEvent(ptrEvent.asOutParam(), m->pEventSource,
3602 aMediumId.toString(), aDevType, aRegistered);
3603 AssertComRCReturnVoid(hrc);
3604 i_postEvent(new AsyncEvent(this, ptrEvent));
3605}
3606
3607void VirtualBox::i_onMediumConfigChanged(IMedium *aMedium)
3608{
3609 ComPtr<IEvent> ptrEvent;
3610 HRESULT hrc = ::CreateMediumConfigChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aMedium);
3611 AssertComRCReturnVoid(hrc);
3612 i_postEvent(new AsyncEvent(this, ptrEvent));
3613}
3614
3615void VirtualBox::i_onMediumChanged(IMediumAttachment *aMediumAttachment)
3616{
3617 ComPtr<IEvent> ptrEvent;
3618 HRESULT hrc = ::CreateMediumChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aMediumAttachment);
3619 AssertComRCReturnVoid(hrc);
3620 i_postEvent(new AsyncEvent(this, ptrEvent));
3621}
3622
3623/**
3624 * @note Doesn't lock any object.
3625 */
3626void VirtualBox::i_onStorageControllerChanged(const Guid &aMachineId, const com::Utf8Str &aControllerName)
3627{
3628 ComPtr<IEvent> ptrEvent;
3629 HRESULT hrc = ::CreateStorageControllerChangedEvent(ptrEvent.asOutParam(), m->pEventSource,
3630 aMachineId.toString(), aControllerName);
3631 AssertComRCReturnVoid(hrc);
3632 i_postEvent(new AsyncEvent(this, ptrEvent));
3633}
3634
3635void VirtualBox::i_onStorageDeviceChanged(IMediumAttachment *aStorageDevice, const BOOL fRemoved, const BOOL fSilent)
3636{
3637 ComPtr<IEvent> ptrEvent;
3638 HRESULT hrc = ::CreateStorageDeviceChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aStorageDevice, fRemoved, fSilent);
3639 AssertComRCReturnVoid(hrc);
3640 i_postEvent(new AsyncEvent(this, ptrEvent));
3641}
3642
3643/**
3644 * @note Doesn't lock any object.
3645 */
3646void VirtualBox::i_onMachineStateChanged(const Guid &aId, MachineState_T aState)
3647{
3648 ComPtr<IEvent> ptrEvent;
3649 HRESULT hrc = ::CreateMachineStateChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aState);
3650 AssertComRCReturnVoid(hrc);
3651 i_postEvent(new AsyncEvent(this, ptrEvent));
3652}
3653
3654/**
3655 * @note Doesn't lock any object.
3656 */
3657void VirtualBox::i_onMachineDataChanged(const Guid &aId, BOOL aTemporary)
3658{
3659 ComPtr<IEvent> ptrEvent;
3660 HRESULT hrc = ::CreateMachineDataChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aTemporary);
3661 AssertComRCReturnVoid(hrc);
3662 i_postEvent(new AsyncEvent(this, ptrEvent));
3663}
3664
3665/**
3666 * @note Doesn't lock any object.
3667 */
3668void VirtualBox::i_onMachineGroupsChanged(const Guid &aId)
3669{
3670 ComPtr<IEvent> ptrEvent;
3671 HRESULT hrc = ::CreateMachineGroupsChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), FALSE /*aDummy*/);
3672 AssertComRCReturnVoid(hrc);
3673 i_postEvent(new AsyncEvent(this, ptrEvent));
3674}
3675
3676/**
3677 * @note Locks this object for reading.
3678 */
3679BOOL VirtualBox::i_onExtraDataCanChange(const Guid &aId, const Utf8Str &aKey, const Utf8Str &aValue, Bstr &aError)
3680{
3681 LogFlowThisFunc(("machine={%RTuuid} aKey={%s} aValue={%s}\n", aId.raw(), aKey.c_str(), aValue.c_str()));
3682
3683 AutoCaller autoCaller(this);
3684 AssertComRCReturn(autoCaller.hrc(), FALSE);
3685
3686 ComPtr<IEvent> ptrEvent;
3687 HRESULT hrc = ::CreateExtraDataCanChangeEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aKey, aValue);
3688 AssertComRCReturn(hrc, TRUE);
3689
3690 VBoxEventDesc EvtDesc(ptrEvent, m->pEventSource);
3691 BOOL fDelivered = EvtDesc.fire(3000); /* Wait up to 3 secs for delivery */
3692 //Assert(fDelivered);
3693 BOOL fAllowChange = TRUE;
3694 if (fDelivered)
3695 {
3696 ComPtr<IExtraDataCanChangeEvent> ptrCanChangeEvent = ptrEvent;
3697 Assert(ptrCanChangeEvent);
3698
3699 BOOL fVetoed = FALSE;
3700 ptrCanChangeEvent->IsVetoed(&fVetoed);
3701 fAllowChange = !fVetoed;
3702
3703 if (!fAllowChange)
3704 {
3705 SafeArray<BSTR> aVetos;
3706 ptrCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos));
3707 if (aVetos.size() > 0)
3708 aError = aVetos[0];
3709 }
3710 }
3711
3712 LogFlowThisFunc(("fAllowChange=%RTbool\n", fAllowChange));
3713 return fAllowChange;
3714}
3715
3716/**
3717 * @note Doesn't lock any object.
3718 */
3719void VirtualBox::i_onExtraDataChanged(const Guid &aId, const Utf8Str &aKey, const Utf8Str &aValue)
3720{
3721 ComPtr<IEvent> ptrEvent;
3722 HRESULT hrc = ::CreateExtraDataChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aKey, aValue);
3723 AssertComRCReturnVoid(hrc);
3724 i_postEvent(new AsyncEvent(this, ptrEvent));
3725}
3726
3727/**
3728 * @note Doesn't lock any object.
3729 */
3730void VirtualBox::i_onMachineRegistered(const Guid &aId, BOOL aRegistered)
3731{
3732 ComPtr<IEvent> ptrEvent;
3733 HRESULT hrc = ::CreateMachineRegisteredEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aRegistered);
3734 AssertComRCReturnVoid(hrc);
3735 i_postEvent(new AsyncEvent(this, ptrEvent));
3736}
3737
3738/**
3739 * @note Doesn't lock any object.
3740 */
3741void VirtualBox::i_onSessionStateChanged(const Guid &aId, SessionState_T aState)
3742{
3743 ComPtr<IEvent> ptrEvent;
3744 HRESULT hrc = ::CreateSessionStateChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aId.toString(), aState);
3745 AssertComRCReturnVoid(hrc);
3746 i_postEvent(new AsyncEvent(this, ptrEvent));
3747}
3748
3749/**
3750 * @note Doesn't lock any object.
3751 */
3752void VirtualBox::i_onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
3753{
3754 ComPtr<IEvent> ptrEvent;
3755 HRESULT hrc = ::CreateSnapshotTakenEvent(ptrEvent.asOutParam(), m->pEventSource,
3756 aMachineId.toString(), aSnapshotId.toString());
3757 AssertComRCReturnVoid(hrc);
3758 i_postEvent(new AsyncEvent(this, ptrEvent));
3759}
3760
3761/**
3762 * @note Doesn't lock any object.
3763 */
3764void VirtualBox::i_onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
3765{
3766 ComPtr<IEvent> ptrEvent;
3767 HRESULT hrc = ::CreateSnapshotDeletedEvent(ptrEvent.asOutParam(), m->pEventSource,
3768 aMachineId.toString(), aSnapshotId.toString());
3769 AssertComRCReturnVoid(hrc);
3770 i_postEvent(new AsyncEvent(this, ptrEvent));
3771}
3772
3773/**
3774 * @note Doesn't lock any object.
3775 */
3776void VirtualBox::i_onSnapshotRestored(const Guid &aMachineId, const Guid &aSnapshotId)
3777{
3778 ComPtr<IEvent> ptrEvent;
3779 HRESULT hrc = ::CreateSnapshotRestoredEvent(ptrEvent.asOutParam(), m->pEventSource,
3780 aMachineId.toString(), aSnapshotId.toString());
3781 AssertComRCReturnVoid(hrc);
3782 i_postEvent(new AsyncEvent(this, ptrEvent));
3783}
3784
3785/**
3786 * @note Doesn't lock any object.
3787 */
3788void VirtualBox::i_onSnapshotChanged(const Guid &aMachineId, const Guid &aSnapshotId)
3789{
3790 ComPtr<IEvent> ptrEvent;
3791 HRESULT hrc = ::CreateSnapshotChangedEvent(ptrEvent.asOutParam(), m->pEventSource,
3792 aMachineId.toString(), aSnapshotId.toString());
3793 AssertComRCReturnVoid(hrc);
3794 i_postEvent(new AsyncEvent(this, ptrEvent));
3795}
3796
3797/**
3798 * @note Doesn't lock any object.
3799 */
3800void VirtualBox::i_onGuestPropertyChanged(const Guid &aMachineId, const Utf8Str &aName, const Utf8Str &aValue,
3801 const Utf8Str &aFlags, const BOOL fWasDeleted)
3802{
3803 ComPtr<IEvent> ptrEvent;
3804 HRESULT hrc = ::CreateGuestPropertyChangedEvent(ptrEvent.asOutParam(), m->pEventSource,
3805 aMachineId.toString(), aName, aValue, aFlags, fWasDeleted);
3806 AssertComRCReturnVoid(hrc);
3807 i_postEvent(new AsyncEvent(this, ptrEvent));
3808}
3809
3810/**
3811 * @note Doesn't lock any object.
3812 */
3813void VirtualBox::i_onNatRedirectChanged(const Guid &aMachineId, ULONG ulSlot, bool fRemove, const Utf8Str &aName,
3814 NATProtocol_T aProto, const Utf8Str &aHostIp, uint16_t aHostPort,
3815 const Utf8Str &aGuestIp, uint16_t aGuestPort)
3816{
3817 ::FireNATRedirectEvent(m->pEventSource, aMachineId.toString(), ulSlot, fRemove, aName, aProto, aHostIp,
3818 aHostPort, aGuestIp, aGuestPort);
3819}
3820
3821/** @todo Unused!! */
3822void VirtualBox::i_onNATNetworkChanged(const Utf8Str &aName)
3823{
3824 ::FireNATNetworkChangedEvent(m->pEventSource, aName);
3825}
3826
3827void VirtualBox::i_onNATNetworkStartStop(const Utf8Str &aName, BOOL fStart)
3828{
3829 ::FireNATNetworkStartStopEvent(m->pEventSource, aName, fStart);
3830}
3831
3832void VirtualBox::i_onNATNetworkSetting(const Utf8Str &aNetworkName, BOOL aEnabled,
3833 const Utf8Str &aNetwork, const Utf8Str &aGateway,
3834 BOOL aAdvertiseDefaultIpv6RouteEnabled,
3835 BOOL fNeedDhcpServer)
3836{
3837 ::FireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled, aNetwork, aGateway,
3838 aAdvertiseDefaultIpv6RouteEnabled, fNeedDhcpServer);
3839}
3840
3841void VirtualBox::i_onNATNetworkPortForward(const Utf8Str &aNetworkName, BOOL create, BOOL fIpv6,
3842 const Utf8Str &aRuleName, NATProtocol_T proto,
3843 const Utf8Str &aHostIp, LONG aHostPort,
3844 const Utf8Str &aGuestIp, LONG aGuestPort)
3845{
3846 ::FireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create, fIpv6, aRuleName, proto,
3847 aHostIp, aHostPort, aGuestIp, aGuestPort);
3848}
3849
3850
3851void VirtualBox::i_onHostNameResolutionConfigurationChange()
3852{
3853 if (m->pEventSource)
3854 ::FireHostNameResolutionConfigurationChangeEvent(m->pEventSource);
3855}
3856
3857
3858int VirtualBox::i_natNetworkRefInc(const Utf8Str &aNetworkName)
3859{
3860 AutoWriteLock safeLock(*spMtxNatNetworkNameToRefCountLock COMMA_LOCKVAL_SRC_POS);
3861
3862 if (!sNatNetworkNameToRefCount[aNetworkName])
3863 {
3864 ComPtr<INATNetwork> nat;
3865 HRESULT hrc = findNATNetworkByName(aNetworkName, nat);
3866 if (FAILED(hrc)) return -1;
3867
3868 hrc = nat->Start();
3869 if (SUCCEEDED(hrc))
3870 LogRel(("Started NAT network '%s'\n", aNetworkName.c_str()));
3871 else
3872 LogRel(("Error %Rhrc starting NAT network '%s'\n", hrc, aNetworkName.c_str()));
3873 AssertComRCReturn(hrc, -1);
3874 }
3875
3876 sNatNetworkNameToRefCount[aNetworkName]++;
3877
3878 return sNatNetworkNameToRefCount[aNetworkName];
3879}
3880
3881
3882int VirtualBox::i_natNetworkRefDec(const Utf8Str &aNetworkName)
3883{
3884 AutoWriteLock safeLock(*spMtxNatNetworkNameToRefCountLock COMMA_LOCKVAL_SRC_POS);
3885
3886 if (!sNatNetworkNameToRefCount[aNetworkName])
3887 return 0;
3888
3889 sNatNetworkNameToRefCount[aNetworkName]--;
3890
3891 if (!sNatNetworkNameToRefCount[aNetworkName])
3892 {
3893 ComPtr<INATNetwork> nat;
3894 HRESULT hrc = findNATNetworkByName(aNetworkName, nat);
3895 if (FAILED(hrc)) return -1;
3896
3897 hrc = nat->Stop();
3898 if (SUCCEEDED(hrc))
3899 LogRel(("Stopped NAT network '%s'\n", aNetworkName.c_str()));
3900 else
3901 LogRel(("Error %Rhrc stopping NAT network '%s'\n", hrc, aNetworkName.c_str()));
3902 AssertComRCReturn(hrc, -1);
3903 }
3904
3905 return sNatNetworkNameToRefCount[aNetworkName];
3906}
3907
3908
3909/*
3910 * Export this to NATNetwork so that its setters can refuse to change
3911 * essential network settings when an VBoxNatNet instance is running.
3912 */
3913RWLockHandle *VirtualBox::i_getNatNetLock() const
3914{
3915 return spMtxNatNetworkNameToRefCountLock;
3916}
3917
3918
3919/*
3920 * Export this to NATNetwork so that its setters can refuse to change
3921 * essential network settings when an VBoxNatNet instance is running.
3922 * The caller is expected to hold a read lock on i_getNatNetLock().
3923 */
3924bool VirtualBox::i_isNatNetStarted(const Utf8Str &aNetworkName) const
3925{
3926 return sNatNetworkNameToRefCount[aNetworkName] > 0;
3927}
3928
3929
3930void VirtualBox::i_onCloudProviderListChanged(BOOL aRegistered)
3931{
3932 ::FireCloudProviderListChangedEvent(m->pEventSource, aRegistered);
3933}
3934
3935
3936void VirtualBox::i_onCloudProviderRegistered(const Utf8Str &aProviderId, BOOL aRegistered)
3937{
3938 ::FireCloudProviderRegisteredEvent(m->pEventSource, aProviderId, aRegistered);
3939}
3940
3941
3942void VirtualBox::i_onCloudProviderUninstall(const Utf8Str &aProviderId)
3943{
3944 HRESULT hrc;
3945
3946 ComPtr<IEvent> pEvent;
3947 hrc = CreateCloudProviderUninstallEvent(pEvent.asOutParam(),
3948 m->pEventSource, aProviderId);
3949 if (FAILED(hrc))
3950 return;
3951
3952 BOOL fDelivered = FALSE;
3953 hrc = m->pEventSource->FireEvent(pEvent, /* :timeout */ 10000, &fDelivered);
3954 if (FAILED(hrc))
3955 return;
3956}
3957
3958void VirtualBox::i_onLanguageChanged(const Utf8Str &aLanguageId)
3959{
3960 ComPtr<IEvent> ptrEvent;
3961 HRESULT hrc = ::CreateLanguageChangedEvent(ptrEvent.asOutParam(), m->pEventSource, aLanguageId);
3962 AssertComRCReturnVoid(hrc);
3963 i_postEvent(new AsyncEvent(this, ptrEvent));
3964}
3965
3966void VirtualBox::i_onProgressCreated(const Guid &aId, BOOL aCreated)
3967{
3968 ::FireProgressCreatedEvent(m->pEventSource, aId.toString(), aCreated);
3969}
3970
3971#ifdef VBOX_WITH_UPDATE_AGENT
3972/**
3973 * @note Doesn't lock any object.
3974 */
3975void VirtualBox::i_onUpdateAgentAvailable(IUpdateAgent *aAgent,
3976 const Utf8Str &aVer, UpdateChannel_T aChannel, UpdateSeverity_T aSev,
3977 const Utf8Str &aDownloadURL, const Utf8Str &aWebURL, const Utf8Str &aReleaseNotes)
3978{
3979 ::FireUpdateAgentAvailableEvent(m->pEventSource, aAgent, aVer, aChannel, aSev,
3980 aDownloadURL, aWebURL, aReleaseNotes);
3981}
3982
3983/**
3984 * @note Doesn't lock any object.
3985 */
3986void VirtualBox::i_onUpdateAgentError(IUpdateAgent *aAgent, const Utf8Str &aErrMsg, LONG aRc)
3987{
3988 ::FireUpdateAgentErrorEvent(m->pEventSource, aAgent, aErrMsg, aRc);
3989}
3990
3991/**
3992 * @note Doesn't lock any object.
3993 */
3994void VirtualBox::i_onUpdateAgentStateChanged(IUpdateAgent *aAgent, UpdateState_T aState)
3995{
3996 ::FireUpdateAgentStateChangedEvent(m->pEventSource, aAgent, aState);
3997}
3998
3999/**
4000 * @note Doesn't lock any object.
4001 */
4002void VirtualBox::i_onUpdateAgentSettingsChanged(IUpdateAgent *aAgent, const Utf8Str &aAttributeHint)
4003{
4004 ::FireUpdateAgentSettingsChangedEvent(m->pEventSource, aAgent, aAttributeHint);
4005}
4006#endif /* VBOX_WITH_UPDATE_AGENT */
4007
4008#ifdef VBOX_WITH_EXTPACK
4009void VirtualBox::i_onExtPackInstalled(const Utf8Str &aExtPackName)
4010{
4011 ::FireExtPackInstalledEvent(m->pEventSource, aExtPackName);
4012}
4013
4014void VirtualBox::i_onExtPackUninstalled(const Utf8Str &aExtPackName)
4015{
4016 ::FireExtPackUninstalledEvent(m->pEventSource, aExtPackName);
4017}
4018#endif
4019
4020/**
4021 * @note Locks the list of other objects for reading.
4022 */
4023ComObjPtr<GuestOSType> VirtualBox::i_getUnknownOSType()
4024{
4025 ComObjPtr<GuestOSType> type;
4026
4027 /* unknown type must always be the first */
4028 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
4029
4030 return m->allGuestOSTypes.front();
4031}
4032
4033/**
4034 * Returns the list of opened machines (machines having VM sessions opened,
4035 * ignoring other sessions) and optionally the list of direct session controls.
4036 *
4037 * @param aMachines Where to put opened machines (will be empty if none).
4038 * @param aControls Where to put direct session controls (optional).
4039 *
4040 * @note The returned lists contain smart pointers. So, clear it as soon as
4041 * it becomes no more necessary to release instances.
4042 *
4043 * @note It can be possible that a session machine from the list has been
4044 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
4045 * when accessing unprotected data directly.
4046 *
4047 * @note Locks objects for reading.
4048 */
4049void VirtualBox::i_getOpenedMachines(SessionMachinesList &aMachines,
4050 InternalControlList *aControls /*= NULL*/)
4051{
4052 AutoCaller autoCaller(this);
4053 AssertComRCReturnVoid(autoCaller.hrc());
4054
4055 aMachines.clear();
4056 if (aControls)
4057 aControls->clear();
4058
4059 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4060
4061 for (MachinesOList::iterator it = m->allMachines.begin();
4062 it != m->allMachines.end();
4063 ++it)
4064 {
4065 ComObjPtr<SessionMachine> sm;
4066 ComPtr<IInternalSessionControl> ctl;
4067 if ((*it)->i_isSessionOpenVM(sm, &ctl))
4068 {
4069 aMachines.push_back(sm);
4070 if (aControls)
4071 aControls->push_back(ctl);
4072 }
4073 }
4074}
4075
4076/**
4077 * Gets a reference to the machine list. This is the real thing, not a copy,
4078 * so bad things will happen if the caller doesn't hold the necessary lock.
4079 *
4080 * @returns reference to machine list
4081 *
4082 * @note Caller must hold the VirtualBox object lock at least for reading.
4083 */
4084VirtualBox::MachinesOList &VirtualBox::i_getMachinesList(void)
4085{
4086 return m->allMachines;
4087}
4088
4089/**
4090 * Searches for a machine object with the given ID in the collection
4091 * of registered machines.
4092 *
4093 * @param aId Machine UUID to look for.
4094 * @param fPermitInaccessible If true, inaccessible machines will be found;
4095 * if false, this will fail if the given machine is inaccessible.
4096 * @param aSetError If true, set errorinfo if the machine is not found.
4097 * @param aMachine Returned machine, if found.
4098 * @return
4099 */
4100HRESULT VirtualBox::i_findMachine(const Guid &aId,
4101 bool fPermitInaccessible,
4102 bool aSetError,
4103 ComObjPtr<Machine> *aMachine /* = NULL */)
4104{
4105 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
4106
4107 AutoCaller autoCaller(this);
4108 AssertComRCReturnRC(autoCaller.hrc());
4109
4110 {
4111 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4112
4113 for (MachinesOList::iterator it = m->allMachines.begin();
4114 it != m->allMachines.end();
4115 ++it)
4116 {
4117 ComObjPtr<Machine> pMachine = *it;
4118
4119 if (!fPermitInaccessible)
4120 {
4121 // skip inaccessible machines
4122 AutoCaller machCaller(pMachine);
4123 if (FAILED(machCaller.hrc()))
4124 continue;
4125 }
4126
4127 if (pMachine->i_getId() == aId)
4128 {
4129 hrc = S_OK;
4130 if (aMachine)
4131 *aMachine = pMachine;
4132 break;
4133 }
4134 }
4135 }
4136
4137 if (aSetError && FAILED(hrc))
4138 hrc = setError(hrc, tr("Could not find a registered machine with UUID {%RTuuid}"), aId.raw());
4139
4140 return hrc;
4141}
4142
4143/**
4144 * Searches for a machine object with the given name or location in the
4145 * collection of registered machines.
4146 *
4147 * @param aName Machine name or location to look for.
4148 * @param aSetError If true, set errorinfo if the machine is not found.
4149 * @param aMachine Returned machine, if found.
4150 * @return
4151 */
4152HRESULT VirtualBox::i_findMachineByName(const Utf8Str &aName,
4153 bool aSetError,
4154 ComObjPtr<Machine> *aMachine /* = NULL */)
4155{
4156 HRESULT hrc = VBOX_E_OBJECT_NOT_FOUND;
4157
4158 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4159 for (MachinesOList::iterator it = m->allMachines.begin();
4160 it != m->allMachines.end();
4161 ++it)
4162 {
4163 ComObjPtr<Machine> &pMachine = *it;
4164 AutoCaller machCaller(pMachine);
4165 if (!machCaller.isOk())
4166 continue; // we can't ask inaccessible machines for their names
4167
4168 AutoReadLock machLock(pMachine COMMA_LOCKVAL_SRC_POS);
4169 if (pMachine->i_getName() == aName)
4170 {
4171 hrc = S_OK;
4172 if (aMachine)
4173 *aMachine = pMachine;
4174 break;
4175 }
4176 if (!RTPathCompare(pMachine->i_getSettingsFileFull().c_str(), aName.c_str()))
4177 {
4178 hrc = S_OK;
4179 if (aMachine)
4180 *aMachine = pMachine;
4181 break;
4182 }
4183 }
4184
4185 if (aSetError && FAILED(hrc))
4186 hrc = setError(hrc, tr("Could not find a registered machine named '%s'"), aName.c_str());
4187
4188 return hrc;
4189}
4190
4191static HRESULT i_validateMachineGroupHelper(const Utf8Str &aGroup, bool fPrimary, VirtualBox *pVirtualBox)
4192{
4193 /* empty strings are invalid */
4194 if (aGroup.isEmpty())
4195 return E_INVALIDARG;
4196 /* the toplevel group is valid */
4197 if (aGroup == "/")
4198 return S_OK;
4199 /* any other strings of length 1 are invalid */
4200 if (aGroup.length() == 1)
4201 return E_INVALIDARG;
4202 /* must start with a slash */
4203 if (aGroup.c_str()[0] != '/')
4204 return E_INVALIDARG;
4205 /* must not end with a slash */
4206 if (aGroup.c_str()[aGroup.length() - 1] == '/')
4207 return E_INVALIDARG;
4208 /* check the group components */
4209 const char *pStr = aGroup.c_str() + 1; /* first char is /, skip it */
4210 while (pStr)
4211 {
4212 char *pSlash = RTStrStr(pStr, "/");
4213 if (pSlash)
4214 {
4215 /* no empty components (or // sequences in other words) */
4216 if (pSlash == pStr)
4217 return E_INVALIDARG;
4218 /* check if the machine name rules are violated, because that means
4219 * the group components are too close to the limits. */
4220 Utf8Str tmp((const char *)pStr, (size_t)(pSlash - pStr));
4221 Utf8Str tmp2(tmp);
4222 sanitiseMachineFilename(tmp);
4223 if (tmp != tmp2)
4224 return E_INVALIDARG;
4225 if (fPrimary)
4226 {
4227 HRESULT hrc = pVirtualBox->i_findMachineByName(tmp, false /* aSetError */);
4228 if (SUCCEEDED(hrc))
4229 return VBOX_E_VM_ERROR;
4230 }
4231 pStr = pSlash + 1;
4232 }
4233 else
4234 {
4235 /* check if the machine name rules are violated, because that means
4236 * the group components is too close to the limits. */
4237 Utf8Str tmp(pStr);
4238 Utf8Str tmp2(tmp);
4239 sanitiseMachineFilename(tmp);
4240 if (tmp != tmp2)
4241 return E_INVALIDARG;
4242 pStr = NULL;
4243 }
4244 }
4245 return S_OK;
4246}
4247
4248/**
4249 * Validates a machine group.
4250 *
4251 * @param aGroup Machine group.
4252 * @param fPrimary Set if this is the primary group.
4253 *
4254 * @return S_OK or E_INVALIDARG
4255 */
4256HRESULT VirtualBox::i_validateMachineGroup(const Utf8Str &aGroup, bool fPrimary)
4257{
4258 HRESULT hrc = i_validateMachineGroupHelper(aGroup, fPrimary, this);
4259 if (FAILED(hrc))
4260 {
4261 if (hrc == VBOX_E_VM_ERROR)
4262 hrc = setError(E_INVALIDARG, tr("Machine group '%s' conflicts with a virtual machine name"), aGroup.c_str());
4263 else
4264 hrc = setError(hrc, tr("Invalid machine group '%s'"), aGroup.c_str());
4265 }
4266 return hrc;
4267}
4268
4269/**
4270 * Takes a list of machine groups, and sanitizes/validates it.
4271 *
4272 * @param aMachineGroups Array with the machine groups.
4273 * @param pllMachineGroups Pointer to list of strings for the result.
4274 *
4275 * @return S_OK or E_INVALIDARG
4276 */
4277HRESULT VirtualBox::i_convertMachineGroups(const std::vector<com::Utf8Str> aMachineGroups, StringsList *pllMachineGroups)
4278{
4279 pllMachineGroups->clear();
4280 if (aMachineGroups.size())
4281 {
4282 for (size_t i = 0; i < aMachineGroups.size(); i++)
4283 {
4284 Utf8Str group(aMachineGroups[i]);
4285 if (group.length() == 0)
4286 group = "/";
4287
4288 HRESULT hrc = i_validateMachineGroup(group, i == 0);
4289 if (FAILED(hrc))
4290 return hrc;
4291
4292 /* no duplicates please */
4293 if ( find(pllMachineGroups->begin(), pllMachineGroups->end(), group)
4294 == pllMachineGroups->end())
4295 pllMachineGroups->push_back(group);
4296 }
4297 if (pllMachineGroups->size() == 0)
4298 pllMachineGroups->push_back("/");
4299 }
4300 else
4301 pllMachineGroups->push_back("/");
4302
4303 return S_OK;
4304}
4305
4306/**
4307 * Searches for a Medium object with the given ID in the list of registered
4308 * hard disks.
4309 *
4310 * @param aId ID of the hard disk. Must not be empty.
4311 * @param aSetError If @c true , the appropriate error info is set in case
4312 * when the hard disk is not found.
4313 * @param aHardDisk Where to store the found hard disk object (can be NULL).
4314 *
4315 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
4316 *
4317 * @note Locks the media tree for reading.
4318 */
4319HRESULT VirtualBox::i_findHardDiskById(const Guid &aId,
4320 bool aSetError,
4321 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
4322{
4323 AssertReturn(!aId.isZero(), E_INVALIDARG);
4324
4325 // we use the hard disks map, but it is protected by the
4326 // hard disk _list_ lock handle
4327 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4328
4329 HardDiskMap::const_iterator it = m->mapHardDisks.find(aId);
4330 if (it != m->mapHardDisks.end())
4331 {
4332 if (aHardDisk)
4333 *aHardDisk = (*it).second;
4334 return S_OK;
4335 }
4336
4337 if (aSetError)
4338 return setError(VBOX_E_OBJECT_NOT_FOUND,
4339 tr("Could not find an open hard disk with UUID {%RTuuid}"),
4340 aId.raw());
4341
4342 return VBOX_E_OBJECT_NOT_FOUND;
4343}
4344
4345/**
4346 * Searches for a Medium object with the given ID or location in the list of
4347 * registered hard disks. If both ID and location are specified, the first
4348 * object that matches either of them (not necessarily both) is returned.
4349 *
4350 * @param strLocation Full location specification. Must not be empty.
4351 * @param aSetError If @c true , the appropriate error info is set in case
4352 * when the hard disk is not found.
4353 * @param aHardDisk Where to store the found hard disk object (can be NULL).
4354 *
4355 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
4356 *
4357 * @note Locks the media tree for reading.
4358 */
4359HRESULT VirtualBox::i_findHardDiskByLocation(const Utf8Str &strLocation,
4360 bool aSetError,
4361 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
4362{
4363 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
4364
4365 // we use the hard disks map, but it is protected by the
4366 // hard disk _list_ lock handle
4367 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4368
4369 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
4370 it != m->mapHardDisks.end();
4371 ++it)
4372 {
4373 const ComObjPtr<Medium> &pHD = (*it).second;
4374
4375 AutoCaller autoCaller(pHD);
4376 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
4377 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
4378
4379 Utf8Str strLocationFull = pHD->i_getLocationFull();
4380
4381 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
4382 {
4383 if (aHardDisk)
4384 *aHardDisk = pHD;
4385 return S_OK;
4386 }
4387 }
4388
4389 if (aSetError)
4390 return setError(VBOX_E_OBJECT_NOT_FOUND,
4391 tr("Could not find an open hard disk with location '%s'"),
4392 strLocation.c_str());
4393
4394 return VBOX_E_OBJECT_NOT_FOUND;
4395}
4396
4397/**
4398 * Searches for a Medium object with the given ID or location in the list of
4399 * registered DVD or floppy images, depending on the @a mediumType argument.
4400 * If both ID and file path are specified, the first object that matches either
4401 * of them (not necessarily both) is returned.
4402 *
4403 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
4404 * @param aId ID of the image file (unused when NULL).
4405 * @param aLocation Full path to the image file (unused when NULL).
4406 * @param aSetError If @c true, the appropriate error info is set in case when
4407 * the image is not found.
4408 * @param aImage Where to store the found image object (can be NULL).
4409 *
4410 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
4411 *
4412 * @note Locks the media tree for reading.
4413 */
4414HRESULT VirtualBox::i_findDVDOrFloppyImage(DeviceType_T mediumType,
4415 const Guid *aId,
4416 const Utf8Str &aLocation,
4417 bool aSetError,
4418 ComObjPtr<Medium> *aImage /* = NULL */)
4419{
4420 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
4421
4422 Utf8Str location;
4423 if (!aLocation.isEmpty())
4424 {
4425 int vrc = i_calculateFullPath(aLocation, location);
4426 if (RT_FAILURE(vrc))
4427 return setError(VBOX_E_FILE_ERROR,
4428 tr("Invalid image file location '%s' (%Rrc)"),
4429 aLocation.c_str(),
4430 vrc);
4431 }
4432
4433 MediaOList *pMediaList;
4434
4435 switch (mediumType)
4436 {
4437 case DeviceType_DVD:
4438 pMediaList = &m->allDVDImages;
4439 break;
4440
4441 case DeviceType_Floppy:
4442 pMediaList = &m->allFloppyImages;
4443 break;
4444
4445 default:
4446 return E_INVALIDARG;
4447 }
4448
4449 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
4450
4451 bool found = false;
4452
4453 for (MediaList::const_iterator it = pMediaList->begin();
4454 it != pMediaList->end();
4455 ++it)
4456 {
4457 // no AutoCaller, registered image life time is bound to this
4458 Medium *pMedium = *it;
4459 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
4460 const Utf8Str &strLocationFull = pMedium->i_getLocationFull();
4461
4462 found = ( aId
4463 && pMedium->i_getId() == *aId)
4464 || ( !aLocation.isEmpty()
4465 && RTPathCompare(location.c_str(),
4466 strLocationFull.c_str()) == 0);
4467 if (found)
4468 {
4469 if (pMedium->i_getDeviceType() != mediumType)
4470 {
4471 if (mediumType == DeviceType_DVD)
4472 return setError(E_INVALIDARG,
4473 tr("Cannot mount DVD medium '%s' as floppy"), strLocationFull.c_str());
4474 else
4475 return setError(E_INVALIDARG,
4476 tr("Cannot mount floppy medium '%s' as DVD"), strLocationFull.c_str());
4477 }
4478
4479 if (aImage)
4480 *aImage = pMedium;
4481 break;
4482 }
4483 }
4484
4485 HRESULT hrc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
4486
4487 if (aSetError && !found)
4488 {
4489 if (aId)
4490 setError(hrc,
4491 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
4492 aId->raw(),
4493 m->strSettingsFilePath.c_str());
4494 else
4495 setError(hrc,
4496 tr("Could not find an image file with location '%s' in the media registry ('%s')"),
4497 aLocation.c_str(),
4498 m->strSettingsFilePath.c_str());
4499 }
4500
4501 return hrc;
4502}
4503
4504/**
4505 * Searches for an IMedium object that represents the given UUID.
4506 *
4507 * If the UUID is empty (indicating an empty drive), this sets pMedium
4508 * to NULL and returns S_OK.
4509 *
4510 * If the UUID refers to a host drive of the given device type, this
4511 * sets pMedium to the object from the list in IHost and returns S_OK.
4512 *
4513 * If the UUID is an image file, this sets pMedium to the object that
4514 * findDVDOrFloppyImage() returned.
4515 *
4516 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
4517 *
4518 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
4519 * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
4520 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
4521 * @param aSetError
4522 * @param pMedium out: IMedium object found.
4523 * @return
4524 */
4525HRESULT VirtualBox::i_findRemoveableMedium(DeviceType_T mediumType,
4526 const Guid &uuid,
4527 bool fRefresh,
4528 bool aSetError,
4529 ComObjPtr<Medium> &pMedium)
4530{
4531 if (uuid.isZero())
4532 {
4533 // that's easy
4534 pMedium.setNull();
4535 return S_OK;
4536 }
4537 else if (!uuid.isValid())
4538 {
4539 /* handling of case invalid GUID */
4540 return setError(VBOX_E_OBJECT_NOT_FOUND,
4541 tr("Guid '%s' is invalid"),
4542 uuid.toString().c_str());
4543 }
4544
4545 // first search for host drive with that UUID
4546 HRESULT hrc = m->pHost->i_findHostDriveById(mediumType, uuid, fRefresh, pMedium);
4547 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
4548 // then search for an image with that UUID
4549 hrc = i_findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, aSetError, &pMedium);
4550
4551 return hrc;
4552}
4553
4554/* Look for a GuestOSType object */
4555HRESULT VirtualBox::i_findGuestOSType(const Utf8Str &strOSType,
4556 ComObjPtr<GuestOSType> &guestOSType)
4557{
4558 guestOSType.setNull();
4559
4560 AssertMsg(m->allGuestOSTypes.size() != 0,
4561 ("Guest OS types array must be filled"));
4562
4563 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4564 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
4565 it != m->allGuestOSTypes.end();
4566 ++it)
4567 {
4568 const Utf8Str &typeId = (*it)->i_id();
4569 AssertMsg(!typeId.isEmpty(), ("ID must not be NULL"));
4570 if (strOSType.compare(typeId, Utf8Str::CaseInsensitive) == 0)
4571 {
4572 guestOSType = *it;
4573 return S_OK;
4574 }
4575 }
4576
4577 return setError(VBOX_E_OBJECT_NOT_FOUND,
4578 tr("'%s' is not a valid Guest OS type"),
4579 strOSType.c_str());
4580}
4581
4582/**
4583 * Walk the list of GuestOSType objects and return a list of guest OS
4584 * subtypes which correspond to the supplied guest OS family ID.
4585 *
4586 * @param strOSFamily Guest OS family ID.
4587 * @param aOSSubtypes Where to store the list of guest OS subtypes.
4588 *
4589 * @note Locks the guest OS types list for reading.
4590 */
4591HRESULT VirtualBox::getGuestOSSubtypesByFamilyId(const Utf8Str &strOSFamily,
4592 std::vector<com::Utf8Str> &aOSSubtypes)
4593{
4594 std::list<com::Utf8Str> allOSSubtypes;
4595
4596 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4597
4598 bool fFoundGuestOSType = false;
4599 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
4600 it != m->allGuestOSTypes.end(); ++it)
4601 {
4602 const Utf8Str &familyId = (*it)->i_familyId();
4603 AssertMsg(!familyId.isEmpty(), ("familfyId must not be NULL"));
4604 if (familyId.compare(strOSFamily, Utf8Str::CaseInsensitive) == 0)
4605 {
4606 fFoundGuestOSType = true;
4607 break;
4608 }
4609 }
4610
4611 if (!fFoundGuestOSType)
4612 return setError(VBOX_E_OBJECT_NOT_FOUND,
4613 tr("'%s' is not a valid guest OS family identifier."), strOSFamily.c_str());
4614
4615 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
4616 it != m->allGuestOSTypes.end(); ++it)
4617 {
4618 const Utf8Str &familyId = (*it)->i_familyId();
4619 AssertMsg(!familyId.isEmpty(), ("familfyId must not be NULL"));
4620 if (familyId.compare(strOSFamily, Utf8Str::CaseInsensitive) == 0)
4621 {
4622 const Utf8Str &strOSSubtype = (*it)->i_subtype();
4623 if (!strOSSubtype.isEmpty())
4624 allOSSubtypes.push_back(strOSSubtype);
4625 }
4626 }
4627
4628 /* throw out any duplicates */
4629 allOSSubtypes.sort();
4630 allOSSubtypes.unique();
4631
4632 aOSSubtypes.resize(allOSSubtypes.size());
4633 size_t i = 0;
4634 for (std::list<com::Utf8Str>::const_iterator it = allOSSubtypes.begin();
4635 it != allOSSubtypes.end(); ++it, ++i)
4636 aOSSubtypes[i] = (*it);
4637
4638 return S_OK;
4639}
4640
4641/**
4642 * Walk the list of GuestOSType objects and return a list of guest OS
4643 * descriptions which correspond to the supplied guest OS subtype.
4644 *
4645 * @param strOSSubtype Guest OS subtype.
4646 * @param aGuestOSDescs Where to store the list of guest OS descriptions..
4647 *
4648 * @note Locks the guest OS types list for reading.
4649 */
4650HRESULT VirtualBox::getGuestOSDescsBySubtype(const Utf8Str &strOSSubtype,
4651 std::vector<com::Utf8Str> &aGuestOSDescs)
4652{
4653 std::list<com::Utf8Str> allOSDescs;
4654
4655 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4656
4657 bool fFoundGuestOSSubtype = false;
4658 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
4659 it != m->allGuestOSTypes.end(); ++it)
4660 {
4661 const Utf8Str &guestOSSubtype = (*it)->i_subtype();
4662 /* Only some guest OS types have a populated subtype value. */
4663 if (guestOSSubtype.isNotEmpty() &&
4664 guestOSSubtype.compare(strOSSubtype, Utf8Str::CaseInsensitive) == 0)
4665 {
4666 fFoundGuestOSSubtype = true;
4667 break;
4668 }
4669 }
4670
4671 if (!fFoundGuestOSSubtype)
4672 return setError(VBOX_E_OBJECT_NOT_FOUND,
4673 tr("'%s' is not a valid guest OS subtype."), strOSSubtype.c_str());
4674
4675 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
4676 it != m->allGuestOSTypes.end(); ++it)
4677 {
4678 const Utf8Str &guestOSSubtype = (*it)->i_subtype();
4679 /* Only some guest OS types have a populated subtype value. */
4680 if (guestOSSubtype.isNotEmpty() &&
4681 guestOSSubtype.compare(strOSSubtype, Utf8Str::CaseInsensitive) == 0)
4682 {
4683 const Utf8Str &strOSDesc = (*it)->i_description();
4684 allOSDescs.push_back(strOSDesc);
4685 }
4686 }
4687
4688 aGuestOSDescs.resize(allOSDescs.size());
4689 size_t i = 0;
4690 for (std::list<com::Utf8Str>::const_iterator it = allOSDescs.begin();
4691 it != allOSDescs.end(); ++it, ++i)
4692 aGuestOSDescs[i] = (*it);
4693
4694 return S_OK;
4695}
4696
4697/**
4698 * Returns the constant pseudo-machine UUID that is used to identify the
4699 * global media registry.
4700 *
4701 * Starting with VirtualBox 4.0 each medium remembers in its instance data
4702 * in which media registry it is saved (if any): this can either be a machine
4703 * UUID, if it's in a per-machine media registry, or this global ID.
4704 *
4705 * This UUID is only used to identify the VirtualBox object while VirtualBox
4706 * is running. It is a compile-time constant and not saved anywhere.
4707 *
4708 * @return
4709 */
4710const Guid& VirtualBox::i_getGlobalRegistryId() const
4711{
4712 return m->uuidMediaRegistry;
4713}
4714
4715const ComObjPtr<Host>& VirtualBox::i_host() const
4716{
4717 return m->pHost;
4718}
4719
4720SystemProperties* VirtualBox::i_getSystemProperties() const
4721{
4722 return m->pSystemProperties;
4723}
4724
4725CloudProviderManager *VirtualBox::i_getCloudProviderManager() const
4726{
4727 return m->pCloudProviderManager;
4728}
4729
4730#ifdef VBOX_WITH_EXTPACK
4731/**
4732 * Getter that SystemProperties and others can use to talk to the extension
4733 * pack manager.
4734 */
4735ExtPackManager* VirtualBox::i_getExtPackManager() const
4736{
4737 return m->ptrExtPackManager;
4738}
4739#endif
4740
4741/**
4742 * Getter that machines can talk to the autostart database.
4743 */
4744AutostartDb* VirtualBox::i_getAutostartDb() const
4745{
4746 return m->pAutostartDb;
4747}
4748
4749#ifdef VBOX_WITH_RESOURCE_USAGE_API
4750const ComObjPtr<PerformanceCollector>& VirtualBox::i_performanceCollector() const
4751{
4752 return m->pPerformanceCollector;
4753}
4754#endif /* VBOX_WITH_RESOURCE_USAGE_API */
4755
4756/**
4757 * Returns the default machine folder from the system properties
4758 * with proper locking.
4759 */
4760void VirtualBox::i_getDefaultMachineFolder(Utf8Str &str) const
4761{
4762 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
4763 str = m->pSystemProperties->m->strDefaultMachineFolder;
4764}
4765
4766/**
4767 * Returns the default hard disk format from the system properties
4768 * with proper locking.
4769 */
4770void VirtualBox::i_getDefaultHardDiskFormat(Utf8Str &str) const
4771{
4772 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
4773 str = m->pSystemProperties->m->strDefaultHardDiskFormat;
4774}
4775
4776const Utf8Str& VirtualBox::i_homeDir() const
4777{
4778 return m->strHomeDir;
4779}
4780
4781/**
4782 * Calculates the absolute path of the given path taking the VirtualBox home
4783 * directory as the current directory.
4784 *
4785 * @param strPath Path to calculate the absolute path for.
4786 * @param aResult Where to put the result (used only on success, can be the
4787 * same Utf8Str instance as passed in @a aPath).
4788 * @return IPRT result.
4789 *
4790 * @note Doesn't lock any object.
4791 */
4792int VirtualBox::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
4793{
4794 AutoCaller autoCaller(this);
4795 AssertComRCReturn(autoCaller.hrc(), VERR_GENERAL_FAILURE);
4796
4797 /* no need to lock since strHomeDir is const */
4798
4799 char szFolder[RTPATH_MAX];
4800 size_t cbFolder = sizeof(szFolder);
4801 int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
4802 strPath.c_str(),
4803 RTPATH_STR_F_STYLE_HOST,
4804 szFolder,
4805 &cbFolder);
4806 if (RT_SUCCESS(vrc))
4807 aResult = szFolder;
4808
4809 return vrc;
4810}
4811
4812/**
4813 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
4814 * if it is a subdirectory thereof, or simply copying it otherwise.
4815 *
4816 * @param strSource Path to evalue and copy.
4817 * @param strTarget Buffer to receive target path.
4818 */
4819void VirtualBox::i_copyPathRelativeToConfig(const Utf8Str &strSource,
4820 Utf8Str &strTarget)
4821{
4822 AutoCaller autoCaller(this);
4823 AssertComRCReturnVoid(autoCaller.hrc());
4824
4825 // no need to lock since mHomeDir is const
4826
4827 // use strTarget as a temporary buffer to hold the machine settings dir
4828 strTarget = m->strHomeDir;
4829 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
4830 // is relative: then append what's left
4831 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
4832 else
4833 // is not relative: then overwrite
4834 strTarget = strSource;
4835}
4836
4837// private methods
4838/////////////////////////////////////////////////////////////////////////////
4839
4840/**
4841 * Checks if there is a hard disk, DVD or floppy image with the given ID or
4842 * location already registered.
4843 *
4844 * On return, sets @a aConflict to the string describing the conflicting medium,
4845 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
4846 * either case. A failure is unexpected.
4847 *
4848 * @param aId UUID to check.
4849 * @param aLocation Location to check.
4850 * @param aConflict Where to return parameters of the conflicting medium.
4851 * @param ppMedium Medium reference in case this is simply a duplicate.
4852 *
4853 * @note Locks the media tree and media objects for reading.
4854 */
4855HRESULT VirtualBox::i_checkMediaForConflicts(const Guid &aId,
4856 const Utf8Str &aLocation,
4857 Utf8Str &aConflict,
4858 ComObjPtr<Medium> *ppMedium)
4859{
4860 AssertReturn(!aId.isZero() && !aLocation.isEmpty(), E_FAIL);
4861 AssertReturn(ppMedium, E_INVALIDARG);
4862
4863 aConflict.setNull();
4864 ppMedium->setNull();
4865
4866 AutoReadLock alock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4867
4868 HRESULT hrc = S_OK;
4869
4870 ComObjPtr<Medium> pMediumFound;
4871 const char *pcszType = NULL;
4872
4873 if (aId.isValid() && !aId.isZero())
4874 hrc = i_findHardDiskById(aId, false /* aSetError */, &pMediumFound);
4875 if (FAILED(hrc) && !aLocation.isEmpty())
4876 hrc = i_findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
4877 if (SUCCEEDED(hrc))
4878 pcszType = tr("hard disk");
4879
4880 if (!pcszType)
4881 {
4882 hrc = i_findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
4883 if (SUCCEEDED(hrc))
4884 pcszType = tr("CD/DVD image");
4885 }
4886
4887 if (!pcszType)
4888 {
4889 hrc = i_findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
4890 if (SUCCEEDED(hrc))
4891 pcszType = tr("floppy image");
4892 }
4893
4894 if (pcszType && pMediumFound)
4895 {
4896 /* Note: no AutoCaller since bound to this */
4897 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
4898
4899 Utf8Str strLocFound = pMediumFound->i_getLocationFull();
4900 Guid idFound = pMediumFound->i_getId();
4901
4902 if ( (RTPathCompare(strLocFound.c_str(), aLocation.c_str()) == 0)
4903 && (idFound == aId)
4904 )
4905 *ppMedium = pMediumFound;
4906
4907 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
4908 pcszType,
4909 strLocFound.c_str(),
4910 idFound.raw());
4911 }
4912
4913 return S_OK;
4914}
4915
4916/**
4917 * Checks whether the given UUID is already in use by one medium for the
4918 * given device type.
4919 *
4920 * @returns true if the UUID is already in use
4921 * fale otherwise
4922 * @param aId The UUID to check.
4923 * @param deviceType The device type the UUID is going to be checked for
4924 * conflicts.
4925 */
4926bool VirtualBox::i_isMediaUuidInUse(const Guid &aId, DeviceType_T deviceType)
4927{
4928 /* A zero UUID is invalid here, always claim that it is already used. */
4929 AssertReturn(!aId.isZero(), true);
4930
4931 AutoReadLock alock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4932
4933 bool fInUse = false;
4934
4935 ComObjPtr<Medium> pMediumFound;
4936
4937 HRESULT hrc;
4938 switch (deviceType)
4939 {
4940 case DeviceType_HardDisk:
4941 hrc = i_findHardDiskById(aId, false /* aSetError */, &pMediumFound);
4942 break;
4943 case DeviceType_DVD:
4944 hrc = i_findDVDOrFloppyImage(DeviceType_DVD, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
4945 break;
4946 case DeviceType_Floppy:
4947 hrc = i_findDVDOrFloppyImage(DeviceType_Floppy, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound);
4948 break;
4949 default:
4950 AssertMsgFailed(("Invalid device type %d\n", deviceType));
4951 hrc = S_OK;
4952 break;
4953 }
4954
4955 if (SUCCEEDED(hrc) && pMediumFound)
4956 fInUse = true;
4957
4958 return fInUse;
4959}
4960
4961/**
4962 * Called from Machine::prepareSaveSettings() when it has detected
4963 * that a machine has been renamed. Such renames will require
4964 * updating the global media registry during the
4965 * VirtualBox::i_saveSettings() that follows later.
4966*
4967 * When a machine is renamed, there may well be media (in particular,
4968 * diff images for snapshots) in the global registry that will need
4969 * to have their paths updated. Before 3.2, Machine::saveSettings
4970 * used to call VirtualBox::i_saveSettings implicitly, which was both
4971 * unintuitive and caused locking order problems. Now, we remember
4972 * such pending name changes with this method so that
4973 * VirtualBox::i_saveSettings() can process them properly.
4974 */
4975void VirtualBox::i_rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
4976 const Utf8Str &strNewConfigDir)
4977{
4978 AutoWriteLock mediaLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4979
4980 Data::PendingMachineRename pmr;
4981 pmr.strConfigDirOld = strOldConfigDir;
4982 pmr.strConfigDirNew = strNewConfigDir;
4983 m->llPendingMachineRenames.push_back(pmr);
4984}
4985
4986static DECLCALLBACK(int) fntSaveMediaRegistries(void *pvUser);
4987
4988class SaveMediaRegistriesDesc : public ThreadTask
4989{
4990
4991public:
4992 SaveMediaRegistriesDesc()
4993 {
4994 m_strTaskName = "SaveMediaReg";
4995 }
4996 virtual ~SaveMediaRegistriesDesc(void) { }
4997
4998private:
4999 void handler()
5000 {
5001 try
5002 {
5003 fntSaveMediaRegistries(this);
5004 }
5005 catch(...)
5006 {
5007 LogRel(("Exception in the function fntSaveMediaRegistries()\n"));
5008 }
5009 }
5010
5011 MediaList llMedia;
5012 ComObjPtr<VirtualBox> pVirtualBox;
5013
5014 friend DECLCALLBACK(int) fntSaveMediaRegistries(void *pvUser);
5015 friend void VirtualBox::i_saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
5016 const Guid &uuidRegistry,
5017 const Utf8Str &strMachineFolder);
5018};
5019
5020DECLCALLBACK(int) fntSaveMediaRegistries(void *pvUser)
5021{
5022 SaveMediaRegistriesDesc *pDesc = (SaveMediaRegistriesDesc *)pvUser;
5023 if (!pDesc)
5024 {
5025 LogRelFunc(("Thread for saving media registries lacks parameters\n"));
5026 return VERR_INVALID_PARAMETER;
5027 }
5028
5029 for (MediaList::const_iterator it = pDesc->llMedia.begin();
5030 it != pDesc->llMedia.end();
5031 ++it)
5032 {
5033 Medium *pMedium = *it;
5034 pMedium->i_markRegistriesModified();
5035 }
5036
5037 pDesc->pVirtualBox->i_saveModifiedRegistries();
5038
5039 pDesc->llMedia.clear();
5040 pDesc->pVirtualBox.setNull();
5041
5042 return VINF_SUCCESS;
5043}
5044
5045/**
5046 * Goes through all known media (hard disks, floppies and DVDs) and saves
5047 * those into the given settings::MediaRegistry structures whose registry
5048 * ID match the given UUID.
5049 *
5050 * Before actually writing to the structures, all media paths (not just the
5051 * ones for the given registry) are updated if machines have been renamed
5052 * since the last call.
5053 *
5054 * This gets called from two contexts:
5055 *
5056 * -- VirtualBox::i_saveSettings() with the UUID of the global registry
5057 * (VirtualBox::Data.uuidRegistry); this will save those media
5058 * which had been loaded from the global registry or have been
5059 * attached to a "legacy" machine which can't save its own registry;
5060 *
5061 * -- Machine::saveSettings() with the UUID of a machine, if a medium
5062 * has been attached to a machine created with VirtualBox 4.0 or later.
5063 *
5064 * Media which have only been temporarily opened without having been
5065 * attached to a machine have a NULL registry UUID and therefore don't
5066 * get saved.
5067 *
5068 * This locks the media tree. Throws HRESULT on errors!
5069 *
5070 * @param mediaRegistry Settings structure to fill.
5071 * @param uuidRegistry The UUID of the media registry; either a machine UUID
5072 * (if machine registry) or the UUID of the global registry.
5073 * @param strMachineFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
5074 */
5075void VirtualBox::i_saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
5076 const Guid &uuidRegistry,
5077 const Utf8Str &strMachineFolder)
5078{
5079 // lock all media for the following; use a write lock because we're
5080 // modifying the PendingMachineRenamesList, which is protected by this
5081 AutoWriteLock mediaLock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5082
5083 // if a machine was renamed, then we'll need to refresh media paths
5084 if (m->llPendingMachineRenames.size())
5085 {
5086 // make a single list from the three media lists so we don't need three loops
5087 MediaList llAllMedia;
5088 // with hard disks, we must use the map, not the list, because the list only has base images
5089 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
5090 llAllMedia.push_back(it->second);
5091 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
5092 llAllMedia.push_back(*it);
5093 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
5094 llAllMedia.push_back(*it);
5095
5096 SaveMediaRegistriesDesc *pDesc = new SaveMediaRegistriesDesc();
5097 for (MediaList::iterator it = llAllMedia.begin();
5098 it != llAllMedia.end();
5099 ++it)
5100 {
5101 Medium *pMedium = *it;
5102 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
5103 it2 != m->llPendingMachineRenames.end();
5104 ++it2)
5105 {
5106 const Data::PendingMachineRename &pmr = *it2;
5107 HRESULT hrc = pMedium->i_updatePath(pmr.strConfigDirOld, pmr.strConfigDirNew);
5108 if (SUCCEEDED(hrc))
5109 {
5110 // Remember which medium objects has been changed,
5111 // to trigger saving their registries later.
5112 pDesc->llMedia.push_back(pMedium);
5113 } else if (hrc == VBOX_E_FILE_ERROR)
5114 /* nothing */;
5115 else
5116 AssertComRC(hrc);
5117 }
5118 }
5119 // done, don't do it again until we have more machine renames
5120 m->llPendingMachineRenames.clear();
5121
5122 if (pDesc->llMedia.size())
5123 {
5124 // Handle the media registry saving in a separate thread, to
5125 // avoid giant locking problems and passing up the list many
5126 // levels up to whoever triggered saveSettings, as there are
5127 // lots of places which would need to handle saving more settings.
5128 pDesc->pVirtualBox = this;
5129
5130 //the function createThread() takes ownership of pDesc
5131 //so there is no need to use delete operator for pDesc
5132 //after calling this function
5133 HRESULT hrc = pDesc->createThread();
5134 pDesc = NULL;
5135
5136 if (FAILED(hrc))
5137 {
5138 // failure means that settings aren't saved, but there isn't
5139 // much we can do besides avoiding memory leaks
5140 LogRelFunc(("Failed to create thread for saving media registries (%Rhr)\n", hrc));
5141 }
5142 }
5143 else
5144 delete pDesc;
5145 }
5146
5147 struct {
5148 MediaOList &llSource;
5149 settings::MediaList &llTarget;
5150 } s[] =
5151 {
5152 // hard disks
5153 { m->allHardDisks, mediaRegistry.llHardDisks },
5154 // CD/DVD images
5155 { m->allDVDImages, mediaRegistry.llDvdImages },
5156 // floppy images
5157 { m->allFloppyImages, mediaRegistry.llFloppyImages }
5158 };
5159
5160 for (size_t i = 0; i < RT_ELEMENTS(s); ++i)
5161 {
5162 MediaOList &llSource = s[i].llSource;
5163 settings::MediaList &llTarget = s[i].llTarget;
5164 llTarget.clear();
5165 for (MediaList::const_iterator it = llSource.begin();
5166 it != llSource.end();
5167 ++it)
5168 {
5169 Medium *pMedium = *it;
5170 AutoCaller autoCaller(pMedium);
5171 if (FAILED(autoCaller.hrc())) throw autoCaller.hrc();
5172 AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
5173
5174 if (pMedium->i_isInRegistry(uuidRegistry))
5175 {
5176 llTarget.push_back(settings::Medium::Empty);
5177 HRESULT hrc = pMedium->i_saveSettings(llTarget.back(), strMachineFolder); // this recurses into child hard disks
5178 if (FAILED(hrc))
5179 {
5180 llTarget.pop_back();
5181 throw hrc;
5182 }
5183 }
5184 }
5185 }
5186}
5187
5188/**
5189 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
5190 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
5191 * places internally when settings need saving.
5192 *
5193 * @note Caller must have locked the VirtualBox object for writing and must not hold any
5194 * other locks since this locks all kinds of member objects and trees temporarily,
5195 * which could cause conflicts.
5196 */
5197HRESULT VirtualBox::i_saveSettings()
5198{
5199 AutoCaller autoCaller(this);
5200 AssertComRCReturnRC(autoCaller.hrc());
5201
5202 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
5203 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
5204
5205 i_unmarkRegistryModified(i_getGlobalRegistryId());
5206
5207 HRESULT hrc = S_OK;
5208
5209 try
5210 {
5211 // machines
5212 m->pMainConfigFile->llMachines.clear();
5213 {
5214 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5215 for (MachinesOList::iterator it = m->allMachines.begin();
5216 it != m->allMachines.end();
5217 ++it)
5218 {
5219 Machine *pMachine = *it;
5220 // save actual machine registry entry
5221 settings::MachineRegistryEntry mre;
5222 hrc = pMachine->i_saveRegistryEntry(mre);
5223 if (FAILED(hrc)) throw hrc;
5224 m->pMainConfigFile->llMachines.push_back(mre);
5225 }
5226 }
5227
5228 i_saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
5229 m->uuidMediaRegistry, // global media registry ID
5230 Utf8Str::Empty); // strMachineFolder
5231
5232 m->pMainConfigFile->llDhcpServers.clear();
5233 {
5234 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5235 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
5236 it != m->allDHCPServers.end();
5237 ++it)
5238 {
5239 settings::DHCPServer d;
5240 hrc = (*it)->i_saveSettings(d);
5241 if (FAILED(hrc)) throw hrc;
5242 m->pMainConfigFile->llDhcpServers.push_back(d);
5243 }
5244 }
5245
5246#ifdef VBOX_WITH_NAT_SERVICE
5247 /* Saving NAT Network configuration */
5248 m->pMainConfigFile->llNATNetworks.clear();
5249 {
5250 AutoReadLock natNetworkLock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5251 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
5252 it != m->allNATNetworks.end();
5253 ++it)
5254 {
5255 settings::NATNetwork n;
5256 hrc = (*it)->i_saveSettings(n);
5257 if (FAILED(hrc)) throw hrc;
5258 m->pMainConfigFile->llNATNetworks.push_back(n);
5259 }
5260 }
5261#endif
5262
5263#ifdef VBOX_WITH_VMNET
5264 m->pMainConfigFile->llHostOnlyNetworks.clear();
5265 {
5266 AutoReadLock hostOnlyNetworkLock(m->allHostOnlyNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5267 for (HostOnlyNetworksOList::const_iterator it = m->allHostOnlyNetworks.begin();
5268 it != m->allHostOnlyNetworks.end();
5269 ++it)
5270 {
5271 settings::HostOnlyNetwork n;
5272 hrc = (*it)->i_saveSettings(n);
5273 if (FAILED(hrc)) throw hrc;
5274 m->pMainConfigFile->llHostOnlyNetworks.push_back(n);
5275 }
5276 }
5277#endif /* VBOX_WITH_VMNET */
5278
5279#ifdef VBOX_WITH_CLOUD_NET
5280 m->pMainConfigFile->llCloudNetworks.clear();
5281 {
5282 AutoReadLock cloudNetworkLock(m->allCloudNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5283 for (CloudNetworksOList::const_iterator it = m->allCloudNetworks.begin();
5284 it != m->allCloudNetworks.end();
5285 ++it)
5286 {
5287 settings::CloudNetwork n;
5288 hrc = (*it)->i_saveSettings(n);
5289 if (FAILED(hrc)) throw hrc;
5290 m->pMainConfigFile->llCloudNetworks.push_back(n);
5291 }
5292 }
5293#endif /* VBOX_WITH_CLOUD_NET */
5294 // leave extra data alone, it's still in the config file
5295
5296 // host data (USB filters)
5297 hrc = m->pHost->i_saveSettings(m->pMainConfigFile->host);
5298 if (FAILED(hrc)) throw hrc;
5299
5300 hrc = m->pSystemProperties->i_saveSettings(m->pMainConfigFile->systemProperties);
5301 if (FAILED(hrc)) throw hrc;
5302
5303 // and write out the XML, still under the lock
5304 m->pMainConfigFile->write(m->strSettingsFilePath);
5305 }
5306 catch (HRESULT hrcXcpt)
5307 {
5308 /* we assume that error info is set by the thrower */
5309 hrc = hrcXcpt;
5310 }
5311 catch (...)
5312 {
5313 hrc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
5314 }
5315
5316 return hrc;
5317}
5318
5319/**
5320 * Helper to register the machine.
5321 *
5322 * When called during VirtualBox startup, adds the given machine to the
5323 * collection of registered machines. Otherwise tries to mark the machine
5324 * as registered, and, if succeeded, adds it to the collection and
5325 * saves global settings.
5326 *
5327 * @note The caller must have added itself as a caller of the @a aMachine
5328 * object if calls this method not on VirtualBox startup.
5329 *
5330 * @param aMachine machine to register
5331 *
5332 * @note Locks objects!
5333 */
5334HRESULT VirtualBox::i_registerMachine(Machine *aMachine)
5335{
5336 ComAssertRet(aMachine, E_INVALIDARG);
5337
5338 AutoCaller autoCaller(this);
5339 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
5340
5341 HRESULT hrc = S_OK;
5342
5343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5344
5345 {
5346 ComObjPtr<Machine> pMachine;
5347 hrc = i_findMachine(aMachine->i_getId(),
5348 true /* fPermitInaccessible */,
5349 false /* aDoSetError */,
5350 &pMachine);
5351 if (SUCCEEDED(hrc))
5352 {
5353 /* sanity */
5354 AutoLimitedCaller machCaller(pMachine);
5355 AssertComRC(machCaller.hrc());
5356
5357 return setError(E_INVALIDARG,
5358 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
5359 aMachine->i_getId().raw(),
5360 pMachine->i_getSettingsFileFull().c_str());
5361 }
5362
5363 ComAssertRet(hrc == VBOX_E_OBJECT_NOT_FOUND, hrc);
5364 hrc = S_OK;
5365 }
5366
5367 if (getObjectState().getState() != ObjectState::InInit)
5368 {
5369 hrc = aMachine->i_prepareRegister();
5370 if (FAILED(hrc)) return hrc;
5371 }
5372
5373 /* add to the collection of registered machines */
5374 m->allMachines.addChild(aMachine);
5375
5376 if (getObjectState().getState() != ObjectState::InInit)
5377 hrc = i_saveSettings();
5378
5379 return hrc;
5380}
5381
5382/**
5383 * Remembers the given medium object by storing it in either the global
5384 * medium registry or a machine one.
5385 *
5386 * @note Caller must hold the media tree lock for writing; in addition, this
5387 * locks @a pMedium for reading
5388 *
5389 * @param pMedium Medium object to remember.
5390 * @param ppMedium Actually stored medium object. Can be different if due
5391 * to an unavoidable race there was a duplicate Medium object
5392 * created.
5393 * @param mediaTreeLock Reference to the AutoWriteLock holding the media tree
5394 * lock, necessary to release it in the right spot.
5395 * @param fCalledFromMediumInit Flag whether this is called from Medium::init().
5396 * @return
5397 */
5398HRESULT VirtualBox::i_registerMedium(const ComObjPtr<Medium> &pMedium,
5399 ComObjPtr<Medium> *ppMedium,
5400 AutoWriteLock &mediaTreeLock,
5401 bool fCalledFromMediumInit)
5402{
5403 AssertReturn(pMedium != NULL, E_INVALIDARG);
5404 AssertReturn(ppMedium != NULL, E_INVALIDARG);
5405
5406 // caller must hold the media tree write lock
5407 Assert(i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5408
5409 AutoCaller autoCaller(this);
5410 AssertComRCReturnRC(autoCaller.hrc());
5411
5412 AutoCaller mediumCaller(pMedium);
5413 AssertComRCReturnRC(mediumCaller.hrc());
5414
5415 bool fAddToGlobalRegistry = false;
5416 const char *pszDevType = NULL;
5417 Guid regId;
5418 ObjectsList<Medium> *pall = NULL;
5419 DeviceType_T devType;
5420 {
5421 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5422 devType = pMedium->i_getDeviceType();
5423
5424 if (!pMedium->i_getFirstRegistryMachineId(regId))
5425 fAddToGlobalRegistry = true;
5426 }
5427 switch (devType)
5428 {
5429 case DeviceType_HardDisk:
5430 pall = &m->allHardDisks;
5431 pszDevType = tr("hard disk");
5432 break;
5433 case DeviceType_DVD:
5434 pszDevType = tr("DVD image");
5435 pall = &m->allDVDImages;
5436 break;
5437 case DeviceType_Floppy:
5438 pszDevType = tr("floppy image");
5439 pall = &m->allFloppyImages;
5440 break;
5441 default:
5442 AssertMsgFailedReturn(("invalid device type %d", devType), E_INVALIDARG);
5443 }
5444
5445 Guid id;
5446 Utf8Str strLocationFull;
5447 ComObjPtr<Medium> pParent;
5448 {
5449 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5450 id = pMedium->i_getId();
5451 strLocationFull = pMedium->i_getLocationFull();
5452 pParent = pMedium->i_getParent();
5453
5454 /*
5455 * If a separate thread has called Medium::close() for this medium at the same
5456 * time as this i_registerMedium() call then there is a window of opportunity in
5457 * Medium::i_close() where the media tree lock is dropped before calling
5458 * Medium::uninit() (which reacquires the lock) that we can end up here attempting
5459 * to register a medium which is in the process of being closed. In addition, if
5460 * this is a differencing medium and Medium::close() is in progress for one its
5461 * parent media then we are similarly operating on a media registry in flux. In
5462 * either case registering a medium just before calling Medium::uninit() will
5463 * lead to an inconsistent media registry so bail out here since Medium::close()
5464 * got to this medium (or one of its parents) first.
5465 */
5466 if (devType == DeviceType_HardDisk)
5467 {
5468 ComObjPtr<Medium> pTmpMedium = pMedium;
5469 while (pTmpMedium.isNotNull())
5470 {
5471 AutoCaller mediumAC(pTmpMedium);
5472 if (FAILED(mediumAC.hrc())) return mediumAC.hrc();
5473 AutoReadLock mlock(pTmpMedium COMMA_LOCKVAL_SRC_POS);
5474
5475 if (pTmpMedium->i_isClosing())
5476 return setError(E_INVALIDARG,
5477 tr("Cannot register %s '%s' {%RTuuid} because it is in the process of being closed"),
5478 pszDevType,
5479 pTmpMedium->i_getLocationFull().c_str(),
5480 pTmpMedium->i_getId().raw());
5481
5482 pTmpMedium = pTmpMedium->i_getParent();
5483 }
5484 }
5485 }
5486
5487 HRESULT hrc;
5488
5489 Utf8Str strConflict;
5490 ComObjPtr<Medium> pDupMedium;
5491 hrc = i_checkMediaForConflicts(id, strLocationFull, strConflict, &pDupMedium);
5492 if (FAILED(hrc)) return hrc;
5493
5494 if (pDupMedium.isNull())
5495 {
5496 if (strConflict.length())
5497 return setError(E_INVALIDARG,
5498 tr("Cannot register the %s '%s' {%RTuuid} because a %s already exists"),
5499 pszDevType,
5500 strLocationFull.c_str(),
5501 id.raw(),
5502 strConflict.c_str(),
5503 m->strSettingsFilePath.c_str());
5504
5505 // add to the collection if it is a base medium
5506 if (pParent.isNull())
5507 pall->getList().push_back(pMedium);
5508
5509 // store all hard disks (even differencing images) in the map
5510 if (devType == DeviceType_HardDisk)
5511 m->mapHardDisks[id] = pMedium;
5512 }
5513
5514 /*
5515 * If we have been called from Medium::initFromSettings() then the Medium object's
5516 * AutoCaller status will be 'InInit' which means that when making the assigment to
5517 * ppMedium below the Medium object will not call Medium::uninit(). By excluding
5518 * this code path from releasing and reacquiring the media tree lock we avoid a
5519 * potential deadlock with other threads which may be operating on the
5520 * disks/DVDs/floppies in the VM's media registry at the same time such as
5521 * Machine::unregister().
5522 */
5523 if (!fCalledFromMediumInit)
5524 {
5525 // pMedium may be the last reference to the Medium object, and the
5526 // caller may have specified the same ComObjPtr as the output parameter.
5527 // In this case the assignment will uninit the object, and we must not
5528 // have a caller pending.
5529 mediumCaller.release();
5530 // release media tree lock, must not be held at uninit time.
5531 mediaTreeLock.release();
5532 // must not hold the media tree write lock any more
5533 Assert(!i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5534 }
5535
5536 *ppMedium = pDupMedium.isNull() ? pMedium : pDupMedium;
5537
5538 if (fAddToGlobalRegistry)
5539 {
5540 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5541 if ( fCalledFromMediumInit
5542 ? (*ppMedium)->i_addRegistryNoCallerCheck(m->uuidMediaRegistry)
5543 : (*ppMedium)->i_addRegistry(m->uuidMediaRegistry))
5544 i_markRegistryModified(m->uuidMediaRegistry);
5545 }
5546
5547 // Restore the initial lock state, so that no unexpected lock changes are
5548 // done by this method, which would need adjustments everywhere.
5549 if (!fCalledFromMediumInit)
5550 mediaTreeLock.acquire();
5551
5552 return hrc;
5553}
5554
5555/**
5556 * Removes the given medium from the respective registry.
5557 *
5558 * @param pMedium Hard disk object to remove.
5559 *
5560 * @note Caller must hold the media tree lock for writing; in addition, this locks @a pMedium for reading
5561 */
5562HRESULT VirtualBox::i_unregisterMedium(Medium *pMedium)
5563{
5564 AssertReturn(pMedium != NULL, E_INVALIDARG);
5565
5566 AutoCaller autoCaller(this);
5567 AssertComRCReturnRC(autoCaller.hrc());
5568
5569 AutoCaller mediumCaller(pMedium);
5570 AssertComRCReturnRC(mediumCaller.hrc());
5571
5572 // caller must hold the media tree write lock
5573 Assert(i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5574
5575 Guid id;
5576 ComObjPtr<Medium> pParent;
5577 DeviceType_T devType;
5578 {
5579 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5580 id = pMedium->i_getId();
5581 pParent = pMedium->i_getParent();
5582 devType = pMedium->i_getDeviceType();
5583 }
5584
5585 ObjectsList<Medium> *pall = NULL;
5586 switch (devType)
5587 {
5588 case DeviceType_HardDisk:
5589 pall = &m->allHardDisks;
5590 break;
5591 case DeviceType_DVD:
5592 pall = &m->allDVDImages;
5593 break;
5594 case DeviceType_Floppy:
5595 pall = &m->allFloppyImages;
5596 break;
5597 default:
5598 AssertMsgFailedReturn(("invalid device type %d", devType), E_INVALIDARG);
5599 }
5600
5601 // remove from the collection if it is a base medium
5602 if (pParent.isNull())
5603 pall->getList().remove(pMedium);
5604
5605 // remove all hard disks (even differencing images) from map
5606 if (devType == DeviceType_HardDisk)
5607 {
5608 size_t cnt = m->mapHardDisks.erase(id);
5609 Assert(cnt == 1);
5610 NOREF(cnt);
5611 }
5612
5613 return S_OK;
5614}
5615
5616/**
5617 * Unregisters all Medium objects which belong to the given machine registry.
5618 * Gets called from Machine::uninit() just before the machine object dies
5619 * and must only be called with a machine UUID as the registry ID.
5620 *
5621 * Locks the media tree.
5622 *
5623 * @param uuidMachine Medium registry ID (always a machine UUID)
5624 * @return
5625 */
5626HRESULT VirtualBox::i_unregisterMachineMedia(const Guid &uuidMachine)
5627{
5628 Assert(!uuidMachine.isZero() && uuidMachine.isValid());
5629
5630 LogFlowFuncEnter();
5631
5632 AutoCaller autoCaller(this);
5633 AssertComRCReturnRC(autoCaller.hrc());
5634
5635 MediaList llMedia2Close;
5636
5637 {
5638 AutoWriteLock tlock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5639
5640 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
5641 it != m->allHardDisks.getList().end();
5642 ++it)
5643 {
5644 ComObjPtr<Medium> pMedium = *it;
5645 AutoCaller medCaller(pMedium);
5646 if (FAILED(medCaller.hrc())) return medCaller.hrc();
5647 AutoReadLock medlock(pMedium COMMA_LOCKVAL_SRC_POS);
5648 Log(("Looking at medium %RTuuid\n", pMedium->i_getId().raw()));
5649
5650 /* If the medium is still in the registry then either some code is
5651 * seriously buggy (unregistering a VM removes it automatically),
5652 * or the reference to a Machine object is destroyed without ever
5653 * being registered. The second condition checks if a medium is
5654 * in no registry, which indicates (set by unregistering) that a
5655 * medium is not used by any other VM and thus can be closed. */
5656 Guid dummy;
5657 if ( pMedium->i_isInRegistry(uuidMachine)
5658 || !pMedium->i_getFirstRegistryMachineId(dummy))
5659 {
5660 /* Collect all medium objects into llMedia2Close,
5661 * in right order for closing. */
5662 MediaList llMediaTodo;
5663 llMediaTodo.push_back(pMedium);
5664
5665 while (llMediaTodo.size() > 0)
5666 {
5667 ComObjPtr<Medium> pCurrent = llMediaTodo.front();
5668 llMediaTodo.pop_front();
5669
5670 /* Add to front, order must be children then parent. */
5671 Log(("Pushing medium %RTuuid (front)\n", pCurrent->i_getId().raw()));
5672 llMedia2Close.push_front(pCurrent);
5673
5674 /* process all children */
5675 MediaList::const_iterator itBegin = pCurrent->i_getChildren().begin();
5676 MediaList::const_iterator itEnd = pCurrent->i_getChildren().end();
5677 for (MediaList::const_iterator it2 = itBegin; it2 != itEnd; ++it2)
5678 llMediaTodo.push_back(*it2);
5679 }
5680 }
5681 }
5682 }
5683
5684 for (MediaList::iterator it = llMedia2Close.begin();
5685 it != llMedia2Close.end();
5686 ++it)
5687 {
5688 ComObjPtr<Medium> pMedium = *it;
5689 Log(("Closing medium %RTuuid\n", pMedium->i_getId().raw()));
5690 AutoCaller mac(pMedium);
5691 HRESULT hrc = pMedium->i_close(mac);
5692 if (FAILED(hrc))
5693 return hrc;
5694 }
5695
5696 LogFlowFuncLeave();
5697
5698 return S_OK;
5699}
5700
5701/**
5702 * Removes the given machine object from the internal list of registered machines.
5703 * Called from Machine::Unregister().
5704 * @param pMachine
5705 * @param aCleanupMode How to handle medium attachments. For
5706 * CleanupMode_UnregisterOnly the associated medium objects will be
5707 * closed when the Machine object is uninitialized, otherwise they will
5708 * go to the global registry if no better registry is found.
5709 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
5710 * @return
5711 */
5712HRESULT VirtualBox::i_unregisterMachine(Machine *pMachine,
5713 CleanupMode_T aCleanupMode,
5714 const Guid &id)
5715{
5716 // remove from the collection of registered machines
5717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5718 m->allMachines.removeChild(pMachine);
5719 // save the global registry
5720 HRESULT hrc = i_saveSettings();
5721 alock.release();
5722
5723 /*
5724 * Now go over all known media and checks if they were registered in the
5725 * media registry of the given machine. Each such medium is then moved to
5726 * a different media registry to make sure it doesn't get lost since its
5727 * media registry is about to go away.
5728 *
5729 * This fixes the following use case: Image A.vdi of machine A is also used
5730 * by machine B, but registered in the media registry of machine A. If machine
5731 * A is deleted, A.vdi must be moved to the registry of B, or else B will
5732 * become inaccessible.
5733 */
5734 {
5735 AutoReadLock tlock(i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5736 // iterate over the list of *base* images
5737 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
5738 it != m->allHardDisks.getList().end();
5739 ++it)
5740 {
5741 ComObjPtr<Medium> &pMedium = *it;
5742 AutoCaller medCaller(pMedium);
5743 if (FAILED(medCaller.hrc())) return medCaller.hrc();
5744 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
5745
5746 if (pMedium->i_removeRegistryAll(id))
5747 {
5748 // machine ID was found in base medium's registry list:
5749 // move this base image and all its children to another registry then
5750 // 1) first, find a better registry to add things to
5751 const Guid *puuidBetter = pMedium->i_getAnyMachineBackref(id);
5752 if (puuidBetter)
5753 {
5754 // 2) better registry found: then use that
5755 pMedium->i_addRegistryAll(*puuidBetter);
5756 // 3) and make sure the registry is saved below
5757 mlock.release();
5758 tlock.release();
5759 i_markRegistryModified(*puuidBetter);
5760 tlock.acquire();
5761 mlock.acquire();
5762 }
5763 else if (aCleanupMode != CleanupMode_UnregisterOnly)
5764 {
5765 pMedium->i_addRegistryAll(i_getGlobalRegistryId());
5766 mlock.release();
5767 tlock.release();
5768 i_markRegistryModified(i_getGlobalRegistryId());
5769 tlock.acquire();
5770 mlock.acquire();
5771 }
5772 }
5773 }
5774 }
5775
5776 i_saveModifiedRegistries();
5777
5778 /* fire an event */
5779 i_onMachineRegistered(id, FALSE);
5780
5781 return hrc;
5782}
5783
5784/**
5785 * Marks the registry for @a uuid as modified, so that it's saved in a later
5786 * call to saveModifiedRegistries().
5787 *
5788 * @param uuid
5789 */
5790void VirtualBox::i_markRegistryModified(const Guid &uuid)
5791{
5792 if (uuid == i_getGlobalRegistryId())
5793 ASMAtomicIncU64(&m->uRegistryNeedsSaving);
5794 else
5795 {
5796 ComObjPtr<Machine> pMachine;
5797 HRESULT hrc = i_findMachine(uuid, false /* fPermitInaccessible */, false /* aSetError */, &pMachine);
5798 if (SUCCEEDED(hrc))
5799 {
5800 AutoCaller machineCaller(pMachine);
5801 if (SUCCEEDED(machineCaller.hrc()) && pMachine->i_isAccessible())
5802 ASMAtomicIncU64(&pMachine->uRegistryNeedsSaving);
5803 }
5804 }
5805}
5806
5807/**
5808 * Marks the registry for @a uuid as unmodified, so that it's not saved in
5809 * a later call to saveModifiedRegistries().
5810 *
5811 * @param uuid
5812 */
5813void VirtualBox::i_unmarkRegistryModified(const Guid &uuid)
5814{
5815 uint64_t uOld;
5816 if (uuid == i_getGlobalRegistryId())
5817 {
5818 for (;;)
5819 {
5820 uOld = ASMAtomicReadU64(&m->uRegistryNeedsSaving);
5821 if (!uOld)
5822 break;
5823 if (ASMAtomicCmpXchgU64(&m->uRegistryNeedsSaving, 0, uOld))
5824 break;
5825 ASMNopPause();
5826 }
5827 }
5828 else
5829 {
5830 ComObjPtr<Machine> pMachine;
5831 HRESULT hrc = i_findMachine(uuid, false /* fPermitInaccessible */, false /* aSetError */, &pMachine);
5832 if (SUCCEEDED(hrc))
5833 {
5834 AutoCaller machineCaller(pMachine);
5835 if (SUCCEEDED(machineCaller.hrc()))
5836 {
5837 for (;;)
5838 {
5839 uOld = ASMAtomicReadU64(&pMachine->uRegistryNeedsSaving);
5840 if (!uOld)
5841 break;
5842 if (ASMAtomicCmpXchgU64(&pMachine->uRegistryNeedsSaving, 0, uOld))
5843 break;
5844 ASMNopPause();
5845 }
5846 }
5847 }
5848 }
5849}
5850
5851/**
5852 * Saves all settings files according to the modified flags in the Machine
5853 * objects and in the VirtualBox object.
5854 *
5855 * This locks machines and the VirtualBox object as necessary, so better not
5856 * hold any locks before calling this.
5857 */
5858void VirtualBox::i_saveModifiedRegistries()
5859{
5860 HRESULT hrc = S_OK;
5861 bool fNeedsGlobalSettings = false;
5862 uint64_t uOld;
5863
5864 {
5865 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
5866 for (MachinesOList::iterator it = m->allMachines.begin();
5867 it != m->allMachines.end();
5868 ++it)
5869 {
5870 const ComObjPtr<Machine> &pMachine = *it;
5871
5872 for (;;)
5873 {
5874 uOld = ASMAtomicReadU64(&pMachine->uRegistryNeedsSaving);
5875 if (!uOld)
5876 break;
5877 if (ASMAtomicCmpXchgU64(&pMachine->uRegistryNeedsSaving, 0, uOld))
5878 break;
5879 ASMNopPause();
5880 }
5881 if (uOld)
5882 {
5883 AutoCaller autoCaller(pMachine);
5884 if (FAILED(autoCaller.hrc()))
5885 continue;
5886 /* object is already dead, no point in saving settings */
5887 if (getObjectState().getState() != ObjectState::Ready)
5888 continue;
5889 AutoWriteLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
5890 hrc = pMachine->i_saveSettings(&fNeedsGlobalSettings, mlock,
5891 Machine::SaveS_Force); // caller said save, so stop arguing
5892 }
5893 }
5894 }
5895
5896 for (;;)
5897 {
5898 uOld = ASMAtomicReadU64(&m->uRegistryNeedsSaving);
5899 if (!uOld)
5900 break;
5901 if (ASMAtomicCmpXchgU64(&m->uRegistryNeedsSaving, 0, uOld))
5902 break;
5903 ASMNopPause();
5904 }
5905 if (uOld || fNeedsGlobalSettings)
5906 {
5907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5908 hrc = i_saveSettings();
5909 }
5910 NOREF(hrc); /* XXX */
5911}
5912
5913
5914/* static */
5915const com::Utf8Str &VirtualBox::i_getVersionNormalized()
5916{
5917 return sVersionNormalized;
5918}
5919
5920/**
5921 * Checks if the path to the specified file exists, according to the path
5922 * information present in the file name. Optionally the path is created.
5923 *
5924 * Note that the given file name must contain the full path otherwise the
5925 * extracted relative path will be created based on the current working
5926 * directory which is normally unknown.
5927 *
5928 * @param strFileName Full file name which path is checked/created.
5929 * @param fCreate Flag if the path should be created if it doesn't exist.
5930 *
5931 * @return Extended error information on failure to check/create the path.
5932 */
5933/* static */
5934HRESULT VirtualBox::i_ensureFilePathExists(const Utf8Str &strFileName, bool fCreate)
5935{
5936 Utf8Str strDir(strFileName);
5937 strDir.stripFilename();
5938 if (!RTDirExists(strDir.c_str()))
5939 {
5940 if (fCreate)
5941 {
5942 int vrc = RTDirCreateFullPath(strDir.c_str(), 0700);
5943 if (RT_FAILURE(vrc))
5944 return i_setErrorStaticBoth(VBOX_E_IPRT_ERROR, vrc,
5945 tr("Could not create the directory '%s' (%Rrc)"),
5946 strDir.c_str(),
5947 vrc);
5948 }
5949 else
5950 return i_setErrorStaticBoth(VBOX_E_IPRT_ERROR, VERR_FILE_NOT_FOUND,
5951 tr("Directory '%s' does not exist"), strDir.c_str());
5952 }
5953
5954 return S_OK;
5955}
5956
5957const Utf8Str& VirtualBox::i_settingsFilePath()
5958{
5959 return m->strSettingsFilePath;
5960}
5961
5962/**
5963 * Returns the lock handle which protects the machines list. As opposed
5964 * to version 3.1 and earlier, these lists are no longer protected by the
5965 * VirtualBox lock, but by this more specialized lock. Mind the locking
5966 * order: always request this lock after the VirtualBox object lock but
5967 * before the locks of any machine object. See AutoLock.h.
5968 */
5969RWLockHandle& VirtualBox::i_getMachinesListLockHandle()
5970{
5971 return m->lockMachines;
5972}
5973
5974/**
5975 * Returns the lock handle which protects the media trees (hard disks,
5976 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
5977 * are no longer protected by the VirtualBox lock, but by this more
5978 * specialized lock. Mind the locking order: always request this lock
5979 * after the VirtualBox object lock but before the locks of the media
5980 * objects contained in these lists. See AutoLock.h.
5981 */
5982RWLockHandle& VirtualBox::i_getMediaTreeLockHandle()
5983{
5984 return m->lockMedia;
5985}
5986
5987/**
5988 * Thread function that handles custom events posted using #i_postEvent().
5989 */
5990// static
5991DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
5992{
5993 LogFlowFuncEnter();
5994
5995 AssertReturn(pvUser, VERR_INVALID_POINTER);
5996
5997 HRESULT hrc = com::Initialize();
5998 if (FAILED(hrc))
5999 return VERR_COM_UNEXPECTED;
6000
6001 int vrc = VINF_SUCCESS;
6002
6003 try
6004 {
6005 /* Create an event queue for the current thread. */
6006 EventQueue *pEventQueue = new EventQueue();
6007 AssertPtr(pEventQueue);
6008
6009 /* Return the queue to the one who created this thread. */
6010 *(static_cast <EventQueue **>(pvUser)) = pEventQueue;
6011
6012 /* signal that we're ready. */
6013 RTThreadUserSignal(thread);
6014
6015 /*
6016 * In case of spurious wakeups causing VERR_TIMEOUTs and/or other return codes
6017 * we must not stop processing events and delete the pEventQueue object. This must
6018 * be done ONLY when we stop this loop via interruptEventQueueProcessing().
6019 * See @bugref{5724}.
6020 */
6021 for (;;)
6022 {
6023 vrc = pEventQueue->processEventQueue(RT_INDEFINITE_WAIT);
6024 if (vrc == VERR_INTERRUPTED)
6025 {
6026 LogFlow(("Event queue processing ended with vrc=%Rrc\n", vrc));
6027 vrc = VINF_SUCCESS; /* Set success when exiting. */
6028 break;
6029 }
6030 }
6031
6032 delete pEventQueue;
6033 }
6034 catch (std::bad_alloc &ba)
6035 {
6036 vrc = VERR_NO_MEMORY;
6037 NOREF(ba);
6038 }
6039
6040 com::Shutdown();
6041
6042 LogFlowFuncLeaveRC(vrc);
6043 return vrc;
6044}
6045
6046
6047////////////////////////////////////////////////////////////////////////////////
6048
6049#if 0 /* obsoleted by AsyncEvent */
6050/**
6051 * Prepare the event using the overwritten #prepareEventDesc method and fire.
6052 *
6053 * @note Locks the managed VirtualBox object for reading but leaves the lock
6054 * before iterating over callbacks and calling their methods.
6055 */
6056void *VirtualBox::CallbackEvent::handler()
6057{
6058 if (!mVirtualBox)
6059 return NULL;
6060
6061 AutoCaller autoCaller(mVirtualBox);
6062 if (!autoCaller.isOk())
6063 {
6064 Log1WarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
6065 mVirtualBox->getObjectState().getState()));
6066 /* We don't need mVirtualBox any more, so release it */
6067 mVirtualBox = NULL;
6068 return NULL;
6069 }
6070
6071 {
6072 VBoxEventDesc evDesc;
6073 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
6074
6075 evDesc.fire(/* don't wait for delivery */0);
6076 }
6077
6078 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
6079 return NULL;
6080}
6081#endif
6082
6083/**
6084 * Called on the event handler thread.
6085 *
6086 * @note Locks the managed VirtualBox object for reading but leaves the lock
6087 * before iterating over callbacks and calling their methods.
6088 */
6089void *VirtualBox::AsyncEvent::handler()
6090{
6091 if (mVirtualBox)
6092 {
6093 AutoCaller autoCaller(mVirtualBox);
6094 if (autoCaller.isOk())
6095 {
6096 VBoxEventDesc EvtDesc(mEvent, mVirtualBox->m->pEventSource);
6097 EvtDesc.fire(/* don't wait for delivery */0);
6098 }
6099 else
6100 Log1WarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
6101 mVirtualBox->getObjectState().getState()));
6102 mVirtualBox = NULL; /* Old code did this, not really necessary, but whatever. */
6103 }
6104 mEvent.setNull();
6105 return NULL;
6106}
6107
6108//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
6109//{
6110// return E_NOTIMPL;
6111//}
6112
6113HRESULT VirtualBox::createDHCPServer(const com::Utf8Str &aName,
6114 ComPtr<IDHCPServer> &aServer)
6115{
6116 ComObjPtr<DHCPServer> dhcpServer;
6117 dhcpServer.createObject();
6118 HRESULT hrc = dhcpServer->init(this, aName);
6119 if (FAILED(hrc)) return hrc;
6120
6121 hrc = i_registerDHCPServer(dhcpServer, true);
6122 if (FAILED(hrc)) return hrc;
6123
6124 dhcpServer.queryInterfaceTo(aServer.asOutParam());
6125
6126 return hrc;
6127}
6128
6129HRESULT VirtualBox::findDHCPServerByNetworkName(const com::Utf8Str &aName,
6130 ComPtr<IDHCPServer> &aServer)
6131{
6132 ComPtr<DHCPServer> found;
6133
6134 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
6135
6136 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
6137 it != m->allDHCPServers.end();
6138 ++it)
6139 {
6140 Bstr bstrNetworkName;
6141 HRESULT hrc = (*it)->COMGETTER(NetworkName)(bstrNetworkName.asOutParam());
6142 if (FAILED(hrc)) return hrc;
6143
6144 if (Utf8Str(bstrNetworkName) == aName)
6145 {
6146 found = *it;
6147 break;
6148 }
6149 }
6150
6151 if (!found)
6152 return E_INVALIDARG;
6153 return found.queryInterfaceTo(aServer.asOutParam());
6154}
6155
6156HRESULT VirtualBox::removeDHCPServer(const ComPtr<IDHCPServer> &aServer)
6157{
6158 IDHCPServer *aP = aServer;
6159 return i_unregisterDHCPServer(static_cast<DHCPServer *>(aP));
6160}
6161
6162/**
6163 * Remembers the given DHCP server in the settings.
6164 *
6165 * @param aDHCPServer DHCP server object to remember.
6166 * @param aSaveSettings @c true to save settings to disk (default).
6167 *
6168 * When @a aSaveSettings is @c true, this operation may fail because of the
6169 * failed #i_saveSettings() method it calls. In this case, the dhcp server object
6170 * will not be remembered. It is therefore the responsibility of the caller to
6171 * call this method as the last step of some action that requires registration
6172 * in order to make sure that only fully functional dhcp server objects get
6173 * registered.
6174 *
6175 * @note Locks this object for writing and @a aDHCPServer for reading.
6176 */
6177HRESULT VirtualBox::i_registerDHCPServer(DHCPServer *aDHCPServer,
6178 bool aSaveSettings /*= true*/)
6179{
6180 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
6181
6182 AutoCaller autoCaller(this);
6183 AssertComRCReturnRC(autoCaller.hrc());
6184
6185 // Acquire a lock on the VirtualBox object early to avoid lock order issues
6186 // when we call i_saveSettings() later on.
6187 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
6188 // need it below, in findDHCPServerByNetworkName (reading) and in
6189 // m->allDHCPServers.addChild, so need to get it here to avoid lock
6190 // order trouble with dhcpServerCaller
6191 AutoWriteLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
6192
6193 AutoCaller dhcpServerCaller(aDHCPServer);
6194 AssertComRCReturnRC(dhcpServerCaller.hrc());
6195
6196 Bstr bstrNetworkName;
6197 HRESULT hrc = aDHCPServer->COMGETTER(NetworkName)(bstrNetworkName.asOutParam());
6198 if (FAILED(hrc)) return hrc;
6199
6200 ComPtr<IDHCPServer> existing;
6201 hrc = findDHCPServerByNetworkName(Utf8Str(bstrNetworkName), existing);
6202 if (SUCCEEDED(hrc))
6203 return E_INVALIDARG;
6204 hrc = S_OK;
6205
6206 m->allDHCPServers.addChild(aDHCPServer);
6207 // we need to release the list lock before we attempt to acquire locks
6208 // on other objects in i_saveSettings (see @bugref{7500})
6209 alock.release();
6210
6211 if (aSaveSettings)
6212 {
6213 // we acquired the lock on 'this' earlier to avoid lock order issues
6214 hrc = i_saveSettings();
6215
6216 if (FAILED(hrc))
6217 {
6218 alock.acquire();
6219 m->allDHCPServers.removeChild(aDHCPServer);
6220 }
6221 }
6222
6223 return hrc;
6224}
6225
6226/**
6227 * Removes the given DHCP server from the settings.
6228 *
6229 * @param aDHCPServer DHCP server object to remove.
6230 *
6231 * This operation may fail because of the failed #i_saveSettings() method it
6232 * calls. In this case, the DHCP server will NOT be removed from the settings
6233 * when this method returns.
6234 *
6235 * @note Locks this object for writing.
6236 */
6237HRESULT VirtualBox::i_unregisterDHCPServer(DHCPServer *aDHCPServer)
6238{
6239 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
6240
6241 AutoCaller autoCaller(this);
6242 AssertComRCReturnRC(autoCaller.hrc());
6243
6244 AutoCaller dhcpServerCaller(aDHCPServer);
6245 AssertComRCReturnRC(dhcpServerCaller.hrc());
6246
6247 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
6248 AutoWriteLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
6249 m->allDHCPServers.removeChild(aDHCPServer);
6250 // we need to release the list lock before we attempt to acquire locks
6251 // on other objects in i_saveSettings (see @bugref{7500})
6252 alock.release();
6253
6254 HRESULT hrc = i_saveSettings();
6255
6256 // undo the changes if we failed to save them
6257 if (FAILED(hrc))
6258 {
6259 alock.acquire();
6260 m->allDHCPServers.addChild(aDHCPServer);
6261 }
6262
6263 return hrc;
6264}
6265
6266
6267/**
6268 * NAT Network
6269 */
6270HRESULT VirtualBox::createNATNetwork(const com::Utf8Str &aNetworkName,
6271 ComPtr<INATNetwork> &aNetwork)
6272{
6273#ifdef VBOX_WITH_NAT_SERVICE
6274 ComObjPtr<NATNetwork> natNetwork;
6275 natNetwork.createObject();
6276 HRESULT hrc = natNetwork->init(this, aNetworkName);
6277 if (FAILED(hrc)) return hrc;
6278
6279 hrc = i_registerNATNetwork(natNetwork, true);
6280 if (FAILED(hrc)) return hrc;
6281
6282 natNetwork.queryInterfaceTo(aNetwork.asOutParam());
6283
6284 ::FireNATNetworkCreationDeletionEvent(m->pEventSource, aNetworkName, TRUE);
6285
6286 return hrc;
6287#else
6288 NOREF(aNetworkName);
6289 NOREF(aNetwork);
6290 return E_NOTIMPL;
6291#endif
6292}
6293
6294HRESULT VirtualBox::findNATNetworkByName(const com::Utf8Str &aNetworkName,
6295 ComPtr<INATNetwork> &aNetwork)
6296{
6297#ifdef VBOX_WITH_NAT_SERVICE
6298
6299 HRESULT hrc = S_OK;
6300 ComPtr<NATNetwork> found;
6301
6302 AutoReadLock alock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
6303
6304 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();
6305 it != m->allNATNetworks.end();
6306 ++it)
6307 {
6308 Bstr bstrNATNetworkName;
6309 hrc = (*it)->COMGETTER(NetworkName)(bstrNATNetworkName.asOutParam());
6310 if (FAILED(hrc)) return hrc;
6311
6312 if (Utf8Str(bstrNATNetworkName) == aNetworkName)
6313 {
6314 found = *it;
6315 break;
6316 }
6317 }
6318
6319 if (!found)
6320 return E_INVALIDARG;
6321 found.queryInterfaceTo(aNetwork.asOutParam());
6322 return hrc;
6323#else
6324 NOREF(aNetworkName);
6325 NOREF(aNetwork);
6326 return E_NOTIMPL;
6327#endif
6328}
6329
6330HRESULT VirtualBox::removeNATNetwork(const ComPtr<INATNetwork> &aNetwork)
6331{
6332#ifdef VBOX_WITH_NAT_SERVICE
6333 Bstr name;
6334 HRESULT hrc = aNetwork->COMGETTER(NetworkName)(name.asOutParam());
6335 if (FAILED(hrc))
6336 return hrc;
6337 INATNetwork *p = aNetwork;
6338 NATNetwork *network = static_cast<NATNetwork *>(p);
6339 hrc = i_unregisterNATNetwork(network, true);
6340 ::FireNATNetworkCreationDeletionEvent(m->pEventSource, name.raw(), FALSE);
6341 return hrc;
6342#else
6343 NOREF(aNetwork);
6344 return E_NOTIMPL;
6345#endif
6346
6347}
6348/**
6349 * Remembers the given NAT network in the settings.
6350 *
6351 * @param aNATNetwork NAT Network object to remember.
6352 * @param aSaveSettings @c true to save settings to disk (default).
6353 *
6354 *
6355 * @note Locks this object for writing and @a aNATNetwork for reading.
6356 */
6357HRESULT VirtualBox::i_registerNATNetwork(NATNetwork *aNATNetwork,
6358 bool aSaveSettings /*= true*/)
6359{
6360#ifdef VBOX_WITH_NAT_SERVICE
6361 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
6362
6363 AutoCaller autoCaller(this);
6364 AssertComRCReturnRC(autoCaller.hrc());
6365
6366 AutoCaller natNetworkCaller(aNATNetwork);
6367 AssertComRCReturnRC(natNetworkCaller.hrc());
6368
6369 Bstr name;
6370 HRESULT hrc;
6371 hrc = aNATNetwork->COMGETTER(NetworkName)(name.asOutParam());
6372 AssertComRCReturnRC(hrc);
6373
6374 /* returned value isn't 0 and aSaveSettings is true
6375 * means that we create duplicate, otherwise we just load settings.
6376 */
6377 if ( sNatNetworkNameToRefCount[name]
6378 && aSaveSettings)
6379 AssertComRCReturnRC(E_INVALIDARG);
6380
6381 hrc = S_OK;
6382
6383 sNatNetworkNameToRefCount[name] = 0;
6384
6385 m->allNATNetworks.addChild(aNATNetwork);
6386
6387 if (aSaveSettings)
6388 {
6389 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
6390 hrc = i_saveSettings();
6391 vboxLock.release();
6392
6393 if (FAILED(hrc))
6394 i_unregisterNATNetwork(aNATNetwork, false /* aSaveSettings */);
6395 }
6396
6397 return hrc;
6398#else
6399 NOREF(aNATNetwork);
6400 NOREF(aSaveSettings);
6401 /* No panic please (silently ignore) */
6402 return S_OK;
6403#endif
6404}
6405
6406/**
6407 * Removes the given NAT network from the settings.
6408 *
6409 * @param aNATNetwork NAT network object to remove.
6410 * @param aSaveSettings @c true to save settings to disk (default).
6411 *
6412 * When @a aSaveSettings is @c true, this operation may fail because of the
6413 * failed #i_saveSettings() method it calls. In this case, the DHCP server
6414 * will NOT be removed from the settingsi when this method returns.
6415 *
6416 * @note Locks this object for writing.
6417 */
6418HRESULT VirtualBox::i_unregisterNATNetwork(NATNetwork *aNATNetwork,
6419 bool aSaveSettings /*= true*/)
6420{
6421#ifdef VBOX_WITH_NAT_SERVICE
6422 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);
6423
6424 AutoCaller autoCaller(this);
6425 AssertComRCReturnRC(autoCaller.hrc());
6426
6427 AutoCaller natNetworkCaller(aNATNetwork);
6428 AssertComRCReturnRC(natNetworkCaller.hrc());
6429
6430 Bstr name;
6431 HRESULT hrc = aNATNetwork->COMGETTER(NetworkName)(name.asOutParam());
6432 /* Hm, there're still running clients. */
6433 if (FAILED(hrc) || sNatNetworkNameToRefCount[name])
6434 AssertComRCReturnRC(E_INVALIDARG);
6435
6436 m->allNATNetworks.removeChild(aNATNetwork);
6437
6438 if (aSaveSettings)
6439 {
6440 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
6441 hrc = i_saveSettings();
6442 vboxLock.release();
6443
6444 if (FAILED(hrc))
6445 i_registerNATNetwork(aNATNetwork, false /* aSaveSettings */);
6446 }
6447
6448 return hrc;
6449#else
6450 NOREF(aNATNetwork);
6451 NOREF(aSaveSettings);
6452 return E_NOTIMPL;
6453#endif
6454}
6455
6456HRESULT VirtualBox::findProgressById(const com::Guid &aId,
6457 ComPtr<IProgress> &aProgressObject)
6458{
6459 if (!aId.isValid())
6460 return setError(E_INVALIDARG, tr("The provided progress object GUID is invalid"));
6461
6462#if 0 /** @todo def VBOX_WITH_MAIN_OBJECT_TRACKER - never used */
6463 std::vector<com::Utf8Str> lObjIdMap;
6464 gTrackedObjectsCollector.getObjIdsByClassIID(IID_IProgress, lObjIdMap);
6465
6466 for (const com::Utf8Str &item : lObjIdMap)
6467 {
6468 if (gTrackedObjectsCollector.checkObj(item.c_str()))
6469 {
6470 TrackedObjectData temp;
6471 gTrackedObjectsCollector.getObj(item.c_str(), temp);
6472 Log2(("Tracked Progress Object with objectId %s was found\n", temp.objectIdStr().c_str()));
6473
6474 ComPtr<IProgress> pProgress;
6475 temp.getInterface()->QueryInterface(IID_IProgress, (void **)pProgress.asOutParam());
6476 if (pProgress.isNotNull())
6477 {
6478 com::Bstr reqId(aId.toString());
6479 Bstr foundId;
6480 hrc = pProgress->COMGETTER(Id)(foundId.asOutParam());
6481 if (reqId == foundId)
6482 {
6483 BOOL aCompleted;
6484 pProgress->COMGETTER(Completed)(&aCompleted);
6485
6486 BOOL aCanceled;
6487 pProgress->COMGETTER(Canceled)(&aCanceled);
6488 LogRel(("Requested progress was found:\n id %s\n completed %s\n canceled %s\n",
6489 aId.toString().c_str(),
6490 aCompleted ? "True" : "False",
6491 aCanceled ? "True" : "False"));
6492
6493 aProgressObject = pProgress;
6494 return S_OK;
6495 }
6496 }
6497 }
6498 }
6499
6500#else /* !VBOX_WITH_MAIN_OBJECT_TRACKER */
6501 /* protect mProgressOperations */
6502 AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
6503
6504 ProgressMap::const_iterator it = m->mapProgressOperations.find(aId);
6505 if (it != m->mapProgressOperations.end())
6506 {
6507 aProgressObject = it->second;
6508 return S_OK;
6509 }
6510#endif /* !VBOX_WITH_MAIN_OBJECT_TRACKER */
6511
6512 /** @todo r=bird: E_INVALIDARG isn't a good choice here... */
6513 return setError(E_INVALIDARG, tr("The progress object with the given GUID could not be found"));
6514}
6515
6516HRESULT VirtualBox::getTrackedObject(const com::Utf8Str &aTrObjId,
6517 ComPtr<IUnknown> &aPIface,
6518 TrackedObjectState_T *aState,
6519 LONG64 *aCreationTime,
6520 LONG64 *aDeletionTime)
6521{
6522#ifdef VBOX_WITH_MAIN_OBJECT_TRACKER
6523 TrackedObjectData trObjData;
6524 HRESULT hrc = gTrackedObjectsCollector.getObj(aTrObjId, trObjData);
6525 if (SUCCEEDED(hrc))
6526 {
6527 trObjData.getInterface().queryInterfaceTo(aPIface.asOutParam());
6528 RTTIMESPEC time = trObjData.creationTime();
6529 *aCreationTime = RTTimeSpecGetMilli(&time);
6530 *aState = trObjData.state();
6531 if (*aState != TrackedObjectState_Alive)
6532 {
6533 time = trObjData.deletionTime();
6534 *aDeletionTime = RTTimeSpecGetMilli(&time);
6535 }
6536 /** @todo aDeletionTime isn't set */
6537 }
6538
6539 return hrc;
6540
6541#else
6542 RT_NOREF(aTrObjId, aPIface, aState, aCreationTime, aDeletionTime);
6543 return E_NOTIMPL;
6544#endif
6545}
6546
6547
6548#ifdef VBOX_WITH_MAIN_OBJECT_TRACKER
6549static std::map<com::Utf8Str, com::Utf8Str> const g_lMapInterfaceNameToIID = {
6550 {"IProgress", Guid(IID_IProgress).toString()},
6551 {"ISession", Guid(IID_ISession).toString()},
6552 {"IMedium", Guid(IID_IMedium).toString()},
6553 {"IMachine", Guid(IID_IMachine).toString()}
6554};
6555#endif
6556
6557/**
6558 * Get the tracked object Ids list by the interface name.
6559 *
6560 * @param aName Interface name (like "IProgress", "ISession", "IMedium" etc)
6561 * @param aObjIdsList The list of Ids of the found objects
6562 *
6563 * @return The list of the found objects Ids
6564 */
6565HRESULT VirtualBox::getTrackedObjectIds(const com::Utf8Str &aName,
6566 std::vector<com::Utf8Str> &aObjIdsList)
6567{
6568#ifdef VBOX_WITH_MAIN_OBJECT_TRACKER
6569 HRESULT hrc = S_OK;
6570
6571 try
6572 {
6573 /* Check the supported tracked classes to avoid "out of range" exception */
6574 if (g_lMapInterfaceNameToIID.count(aName))
6575 {
6576 hrc = gTrackedObjectsCollector.getObjIdsByClassIID(g_lMapInterfaceNameToIID.at(aName), aObjIdsList);
6577 if (FAILED(hrc))
6578 hrc = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No objects were found for the passed interface name '%s'."),
6579 aName.c_str());
6580 }
6581 else
6582 hrc = setError(E_INVALIDARG, tr("The objects of the passed interface '%s' are not tracked at moment."), aName.c_str());
6583 }
6584 catch (...)
6585 {
6586 hrc = setError(E_FAIL, tr("The unknown exception in the VirtualBox::getTrackedObjectIds()."));
6587 }
6588
6589 return hrc;
6590
6591#else
6592 RT_NOREF(aName, aObjIdsList);
6593 return E_NOTIMPL;
6594#endif
6595}
6596
6597/**
6598 * Retains a reference to the default cryptographic interface.
6599 *
6600 * @returns COM status code.
6601 * @param ppCryptoIf Where to store the pointer to the cryptographic interface on success.
6602 *
6603 * @note Locks this object for writing.
6604 */
6605HRESULT VirtualBox::i_retainCryptoIf(PCVBOXCRYPTOIF *ppCryptoIf)
6606{
6607 AssertReturn(ppCryptoIf != NULL, E_INVALIDARG);
6608
6609 AutoCaller autoCaller(this);
6610 AssertComRCReturnRC(autoCaller.hrc());
6611
6612 /*
6613 * No object lock due to some lock order fun with Machine objects.
6614 * There is a dedicated critical section to protect against concurrency
6615 * issues when loading the module.
6616 */
6617 RTCritSectEnter(&m->CritSectModCrypto);
6618
6619 /* Try to load the extension pack module if it isn't currently. */
6620 HRESULT hrc = S_OK;
6621 if (m->hLdrModCrypto == NIL_RTLDRMOD)
6622 {
6623#ifdef VBOX_WITH_EXTPACK
6624 /*
6625 * Check that a crypto extension pack name is set and resolve it into a
6626 * library path.
6627 */
6628 Utf8Str strExtPack;
6629 hrc = m->pSystemProperties->getDefaultCryptoExtPack(strExtPack);
6630 if (FAILED(hrc))
6631 {
6632 RTCritSectLeave(&m->CritSectModCrypto);
6633 return hrc;
6634 }
6635 if (strExtPack.isEmpty())
6636 {
6637 RTCritSectLeave(&m->CritSectModCrypto);
6638 return setError(VBOX_E_OBJECT_NOT_FOUND,
6639 tr("Ńo extension pack providing a cryptographic support module could be found"));
6640 }
6641
6642 Utf8Str strCryptoLibrary;
6643 int vrc = m->ptrExtPackManager->i_getCryptoLibraryPathForExtPack(&strExtPack, &strCryptoLibrary);
6644 if (RT_SUCCESS(vrc))
6645 {
6646 RTERRINFOSTATIC ErrInfo;
6647 vrc = SUPR3HardenedLdrLoadPlugIn(strCryptoLibrary.c_str(), &m->hLdrModCrypto, RTErrInfoInitStatic(&ErrInfo));
6648 if (RT_SUCCESS(vrc))
6649 {
6650 /* Resolve the entry point and query the pointer to the cryptographic interface. */
6651 PFNVBOXCRYPTOENTRY pfnCryptoEntry = NULL;
6652 vrc = RTLdrGetSymbol(m->hLdrModCrypto, VBOX_CRYPTO_MOD_ENTRY_POINT, (void **)&pfnCryptoEntry);
6653 if (RT_SUCCESS(vrc))
6654 {
6655 vrc = pfnCryptoEntry(&m->pCryptoIf);
6656 if (RT_FAILURE(vrc))
6657 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6658 tr("Failed to query the interface callback table from the cryptographic support module '%s' from extension pack '%s'"),
6659 strCryptoLibrary.c_str(), strExtPack.c_str());
6660 }
6661 else
6662 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6663 tr("Failed to resolve the entry point for the cryptographic support module '%s' from extension pack '%s'"),
6664 strCryptoLibrary.c_str(), strExtPack.c_str());
6665 }
6666 else
6667 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6668 tr("Couldn't load the cryptographic support module '%s' from extension pack '%s' (error: '%s')"),
6669 strCryptoLibrary.c_str(), strExtPack.c_str(), ErrInfo.Core.pszMsg);
6670 }
6671 else
6672 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6673 tr("Couldn't resolve the library path of the crpytographic support module for extension pack '%s'"),
6674 strExtPack.c_str());
6675#else
6676 hrc = setError(VBOX_E_NOT_SUPPORTED,
6677 tr("The cryptographic support module is not supported in this build because extension packs are not supported"));
6678#endif
6679 }
6680
6681 if (SUCCEEDED(hrc))
6682 {
6683 ASMAtomicIncU32(&m->cRefsCrypto);
6684 *ppCryptoIf = m->pCryptoIf;
6685 }
6686
6687 RTCritSectLeave(&m->CritSectModCrypto);
6688
6689 return hrc;
6690}
6691
6692
6693/**
6694 * Releases the reference of the given cryptographic interface.
6695 *
6696 * @returns COM status code.
6697 * @param pCryptoIf Pointer to the cryptographic interface to release.
6698 *
6699 * @note Locks this object for writing.
6700 */
6701HRESULT VirtualBox::i_releaseCryptoIf(PCVBOXCRYPTOIF pCryptoIf)
6702{
6703 AutoCaller autoCaller(this);
6704 AssertComRCReturnRC(autoCaller.hrc());
6705
6706 AssertReturn(pCryptoIf == m->pCryptoIf, E_INVALIDARG);
6707
6708 ASMAtomicDecU32(&m->cRefsCrypto);
6709 return S_OK;
6710}
6711
6712
6713/**
6714 * Tries to unload any loaded cryptographic support module if it is not in use currently.
6715 *
6716 * @returns COM status code.
6717 *
6718 * @note Locks this object for writing.
6719 */
6720HRESULT VirtualBox::i_unloadCryptoIfModule(void)
6721{
6722 AutoCaller autoCaller(this);
6723 AssertComRCReturnRC(autoCaller.hrc());
6724
6725 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
6726
6727 if (m->cRefsCrypto)
6728 return setError(E_ACCESSDENIED,
6729 tr("The cryptographic support module is in use and can't be unloaded"));
6730
6731 RTCritSectEnter(&m->CritSectModCrypto);
6732 if (m->hLdrModCrypto != NIL_RTLDRMOD)
6733 {
6734 int vrc = RTLdrClose(m->hLdrModCrypto);
6735 AssertRC(vrc);
6736 m->hLdrModCrypto = NIL_RTLDRMOD;
6737 }
6738 RTCritSectLeave(&m->CritSectModCrypto);
6739
6740 return S_OK;
6741}
6742
6743
6744#ifdef RT_OS_WINDOWS
6745#include <psapi.h>
6746
6747/**
6748 * Report versions of installed drivers to release log.
6749 */
6750void VirtualBox::i_reportDriverVersions()
6751{
6752 /** @todo r=klaus this code is very confusing, as it uses TCHAR (and
6753 * randomly also _TCHAR, which sounds to me like asking for trouble),
6754 * the "sz" variable prefix but "%ls" for the format string - so the whole
6755 * thing is better compiled with UNICODE and _UNICODE defined. Would be
6756 * far easier to read if it would be coded explicitly for the unicode
6757 * case, as it won't work otherwise. */
6758 DWORD err;
6759 HRESULT hrc;
6760 LPVOID aDrivers[1024];
6761 LPVOID *pDrivers = aDrivers;
6762 UINT cNeeded = 0;
6763 TCHAR szSystemRoot[MAX_PATH];
6764 TCHAR *pszSystemRoot = szSystemRoot;
6765 LPVOID pVerInfo = NULL;
6766 DWORD cbVerInfo = 0;
6767
6768 do
6769 {
6770 cNeeded = GetWindowsDirectory(szSystemRoot, RT_ELEMENTS(szSystemRoot));
6771 if (cNeeded == 0)
6772 {
6773 err = GetLastError();
6774 hrc = HRESULT_FROM_WIN32(err);
6775 AssertLogRelMsgFailed(("GetWindowsDirectory failed, hrc=%Rhrc (0x%x) err=%u\n",
6776 hrc, hrc, err));
6777 break;
6778 }
6779 else if (cNeeded > RT_ELEMENTS(szSystemRoot))
6780 {
6781 /* The buffer is too small, allocate big one. */
6782 pszSystemRoot = (TCHAR *)RTMemTmpAlloc(cNeeded * sizeof(_TCHAR));
6783 if (!pszSystemRoot)
6784 {
6785 AssertLogRelMsgFailed(("RTMemTmpAlloc failed to allocate %d bytes\n", cNeeded));
6786 break;
6787 }
6788 if (GetWindowsDirectory(pszSystemRoot, cNeeded) == 0)
6789 {
6790 err = GetLastError();
6791 hrc = HRESULT_FROM_WIN32(err);
6792 AssertLogRelMsgFailed(("GetWindowsDirectory failed, hrc=%Rhrc (0x%x) err=%u\n",
6793 hrc, hrc, err));
6794 break;
6795 }
6796 }
6797
6798 DWORD cbNeeded = 0;
6799 if (!EnumDeviceDrivers(aDrivers, sizeof(aDrivers), &cbNeeded) || cbNeeded > sizeof(aDrivers))
6800 {
6801 pDrivers = (LPVOID *)RTMemTmpAlloc(cbNeeded);
6802 if (!EnumDeviceDrivers(pDrivers, cbNeeded, &cbNeeded))
6803 {
6804 err = GetLastError();
6805 hrc = HRESULT_FROM_WIN32(err);
6806 AssertLogRelMsgFailed(("EnumDeviceDrivers failed, hrc=%Rhrc (0x%x) err=%u\n",
6807 hrc, hrc, err));
6808 break;
6809 }
6810 }
6811
6812 LogRel(("Installed Drivers:\n"));
6813
6814 TCHAR szDriver[1024];
6815 int cDrivers = cbNeeded / sizeof(pDrivers[0]);
6816 for (int i = 0; i < cDrivers; i++)
6817 {
6818 if (GetDeviceDriverBaseName(pDrivers[i], szDriver, sizeof(szDriver) / sizeof(szDriver[0])))
6819 {
6820 if (_tcsnicmp(TEXT("vbox"), szDriver, 4))
6821 continue;
6822 }
6823 else
6824 continue;
6825 if (GetDeviceDriverFileName(pDrivers[i], szDriver, sizeof(szDriver) / sizeof(szDriver[0])))
6826 {
6827 _TCHAR szTmpDrv[1024];
6828 _TCHAR *pszDrv = szDriver;
6829 if (!_tcsncmp(TEXT("\\SystemRoot"), szDriver, 11))
6830 {
6831 _tcscpy_s(szTmpDrv, pszSystemRoot);
6832 _tcsncat_s(szTmpDrv, szDriver + 11, sizeof(szTmpDrv) / sizeof(szTmpDrv[0]) - _tclen(pszSystemRoot));
6833 pszDrv = szTmpDrv;
6834 }
6835 else if (!_tcsncmp(TEXT("\\??\\"), szDriver, 4))
6836 pszDrv = szDriver + 4;
6837
6838 /* Allocate a buffer for version info. Reuse if large enough. */
6839 DWORD cbNewVerInfo = GetFileVersionInfoSize(pszDrv, NULL);
6840 if (cbNewVerInfo > cbVerInfo)
6841 {
6842 if (pVerInfo)
6843 RTMemTmpFree(pVerInfo);
6844 cbVerInfo = cbNewVerInfo;
6845 pVerInfo = RTMemTmpAlloc(cbVerInfo);
6846 if (!pVerInfo)
6847 {
6848 AssertLogRelMsgFailed(("RTMemTmpAlloc failed to allocate %d bytes\n", cbVerInfo));
6849 break;
6850 }
6851 }
6852
6853 if (GetFileVersionInfo(pszDrv, NULL, cbVerInfo, pVerInfo))
6854 {
6855 UINT cbSize = 0;
6856 LPBYTE lpBuffer = NULL;
6857 if (VerQueryValue(pVerInfo, TEXT("\\"), (VOID FAR* FAR*)&lpBuffer, &cbSize))
6858 {
6859 if (cbSize)
6860 {
6861 VS_FIXEDFILEINFO *pFileInfo = (VS_FIXEDFILEINFO *)lpBuffer;
6862 if (pFileInfo->dwSignature == 0xfeef04bd)
6863 {
6864 LogRel((" %ls (Version: %d.%d.%d.%d)\n", pszDrv,
6865 (pFileInfo->dwFileVersionMS >> 16) & 0xffff,
6866 (pFileInfo->dwFileVersionMS >> 0) & 0xffff,
6867 (pFileInfo->dwFileVersionLS >> 16) & 0xffff,
6868 (pFileInfo->dwFileVersionLS >> 0) & 0xffff));
6869 }
6870 }
6871 }
6872 }
6873 }
6874 }
6875
6876 }
6877 while (0);
6878
6879 if (pVerInfo)
6880 RTMemTmpFree(pVerInfo);
6881
6882 if (pDrivers != aDrivers)
6883 RTMemTmpFree(pDrivers);
6884
6885 if (pszSystemRoot != szSystemRoot)
6886 RTMemTmpFree(pszSystemRoot);
6887}
6888#else /* !RT_OS_WINDOWS */
6889void VirtualBox::i_reportDriverVersions(void)
6890{
6891}
6892#endif /* !RT_OS_WINDOWS */
6893
6894#if defined(RT_OS_WINDOWS) && defined(VBOXSVC_WITH_CLIENT_WATCHER)
6895
6896# include <psapi.h> /* for GetProcessImageFileNameW */
6897
6898/**
6899 * Callout from the wrapper.
6900 */
6901void VirtualBox::i_callHook(const char *a_pszFunction)
6902{
6903 RT_NOREF(a_pszFunction);
6904
6905 /*
6906 * Let'see figure out who is calling.
6907 * Note! Requires Vista+, so skip this entirely on older systems.
6908 */
6909 if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
6910 {
6911 RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
6912 RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
6913 if ( rcRpc == RPC_S_OK
6914 && CallAttribs.ClientPID != 0)
6915 {
6916 RTPROCESS const pidClient = (RTPROCESS)(uintptr_t)CallAttribs.ClientPID;
6917 if (pidClient != RTProcSelf())
6918 {
6919 /** @todo LogRel2 later: */
6920 LogRel(("i_callHook: %Rfn [ClientPID=%#zx/%zu IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid]\n",
6921 a_pszFunction, CallAttribs.ClientPID, CallAttribs.ClientPID, CallAttribs.IsClientLocal,
6922 CallAttribs.ProtocolSequence, CallAttribs.CallStatus, CallAttribs.CallType, CallAttribs.OpNum,
6923 &CallAttribs.InterfaceUuid));
6924
6925 /*
6926 * Do we know this client PID already?
6927 */
6928 RTCritSectRwEnterShared(&m->WatcherCritSect);
6929 WatchedClientProcessMap::iterator It = m->WatchedProcesses.find(pidClient);
6930 if (It != m->WatchedProcesses.end())
6931 RTCritSectRwLeaveShared(&m->WatcherCritSect); /* Known process, nothing to do. */
6932 else
6933 {
6934 /* This is a new client process, start watching it. */
6935 RTCritSectRwLeaveShared(&m->WatcherCritSect);
6936 i_watchClientProcess(pidClient, a_pszFunction);
6937 }
6938 }
6939 }
6940 else
6941 LogRel(("i_callHook: %Rfn - rcRpc=%#x ClientPID=%#zx/%zu !! [IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid]\n",
6942 a_pszFunction, rcRpc, CallAttribs.ClientPID, CallAttribs.ClientPID, CallAttribs.IsClientLocal,
6943 CallAttribs.ProtocolSequence, CallAttribs.CallStatus, CallAttribs.CallType, CallAttribs.OpNum,
6944 &CallAttribs.InterfaceUuid));
6945 }
6946}
6947
6948
6949/**
6950 * Watches @a a_pidClient for termination.
6951 *
6952 * @returns true if successfully enabled watching of it, false if not.
6953 * @param a_pidClient The PID to watch.
6954 * @param a_pszFunction The function we which we detected the client in.
6955 */
6956bool VirtualBox::i_watchClientProcess(RTPROCESS a_pidClient, const char *a_pszFunction)
6957{
6958 RT_NOREF_PV(a_pszFunction);
6959
6960 /*
6961 * Open the client process.
6962 */
6963 HANDLE hClient = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE /*fInherit*/, a_pidClient);
6964 if (hClient == NULL)
6965 hClient = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE , a_pidClient);
6966 if (hClient == NULL)
6967 hClient = OpenProcess(SYNCHRONIZE, FALSE , a_pidClient);
6968 AssertLogRelMsgReturn(hClient != NULL, ("pidClient=%d (%#x) err=%d\n", a_pidClient, a_pidClient, GetLastError()),
6969 m->fWatcherIsReliable = false);
6970
6971 /*
6972 * Create a new watcher structure and try add it to the map.
6973 */
6974 bool fRet = true;
6975 WatchedClientProcess *pWatched = new (std::nothrow) WatchedClientProcess(a_pidClient, hClient);
6976 if (pWatched)
6977 {
6978 RTCritSectRwEnterExcl(&m->WatcherCritSect);
6979
6980 WatchedClientProcessMap::iterator It = m->WatchedProcesses.find(a_pidClient);
6981 if (It == m->WatchedProcesses.end())
6982 {
6983 try
6984 {
6985 m->WatchedProcesses.insert(WatchedClientProcessMap::value_type(a_pidClient, pWatched));
6986 }
6987 catch (std::bad_alloc &)
6988 {
6989 fRet = false;
6990 }
6991 if (fRet)
6992 {
6993 /*
6994 * Schedule it on a watcher thread.
6995 */
6996 /** @todo later. */
6997 RTCritSectRwLeaveExcl(&m->WatcherCritSect);
6998 }
6999 else
7000 {
7001 RTCritSectRwLeaveExcl(&m->WatcherCritSect);
7002 delete pWatched;
7003 LogRel(("VirtualBox::i_watchClientProcess: out of memory inserting into client map!\n"));
7004 }
7005 }
7006 else
7007 {
7008 /*
7009 * Someone raced us here, we lost.
7010 */
7011 RTCritSectRwLeaveExcl(&m->WatcherCritSect);
7012 delete pWatched;
7013 }
7014 }
7015 else
7016 {
7017 LogRel(("VirtualBox::i_watchClientProcess: out of memory!\n"));
7018 CloseHandle(hClient);
7019 m->fWatcherIsReliable = fRet = false;
7020 }
7021 return fRet;
7022}
7023
7024
7025/** Logs the RPC caller info to the release log. */
7026/*static*/ void VirtualBox::i_logCaller(const char *a_pszFormat, ...)
7027{
7028 if (RTSystemGetNtVersion() >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0))
7029 {
7030 char szTmp[80];
7031 va_list va;
7032 va_start(va, a_pszFormat);
7033 RTStrPrintfV(szTmp, sizeof(szTmp), a_pszFormat, va);
7034 va_end(va);
7035
7036 RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
7037 RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
7038
7039 RTUTF16 wszProcName[256];
7040 wszProcName[0] = '\0';
7041 if (rcRpc == 0 && CallAttribs.ClientPID != 0)
7042 {
7043 HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)(uintptr_t)CallAttribs.ClientPID);
7044 if (hProcess)
7045 {
7046 RT_ZERO(wszProcName);
7047 GetProcessImageFileNameW(hProcess, wszProcName, RT_ELEMENTS(wszProcName) - 1);
7048 CloseHandle(hProcess);
7049 }
7050 }
7051 LogRel(("%s [rcRpc=%#x ClientPID=%#zx/%zu (%ls) IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid]\n",
7052 szTmp, rcRpc, CallAttribs.ClientPID, CallAttribs.ClientPID, wszProcName, CallAttribs.IsClientLocal,
7053 CallAttribs.ProtocolSequence, CallAttribs.CallStatus, CallAttribs.CallType, CallAttribs.OpNum,
7054 &CallAttribs.InterfaceUuid));
7055 }
7056}
7057
7058#endif /* RT_OS_WINDOWS && VBOXSVC_WITH_CLIENT_WATCHER */
7059
7060
7061/* 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