VirtualBox

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

Last change on this file since 101461 was 101461, checked in by vboxsync, 8 months ago

Main: Allow creation of x86 VMs (based on guest OS type or the set architecture) on non-x86 hosts. bugref:10384

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

© 2023 Oracle
ContactPrivacy policyTerms of Use