VirtualBox

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

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

doc/manual,include/VBox,Frontends/{VBoxManage,VirtualBox/src},Main/{include,SharedFolder,Console,Machine,VirtualBox,VirtualBox.xidl}: Added global shared folders and adjusted fetching and handling of folders between shared folder types bugref:3544

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