VirtualBox

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

Last change on this file since 101299 was 101299, checked in by vboxsync, 20 months ago

Main: Build fix for r159265 - gcc (on solaris/extpack at least) want to see a space between the closing '>>' of nested templates. bugref:10384

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

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