VirtualBox

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

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

Main: IVirtualBox::getGuestOSType() now will only return the supported guest OS types for the host platform currently running on. This in turn is controlled by ISystemProperties::getSupportedPlatformArchitectures(). bugref:10384

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