VirtualBox

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

Last change on this file since 86327 was 86069, checked in by vboxsync, 4 years ago

OCI: Make ICloudProviderRegisteredEvent a non-waitable notification
sent after the fact. Introduce separate ICloudProviderUninstallEvent
sent before unistallation attempt, waitable. Introduce separate
ICloudProviderListChangedEvent that is sent when we are done with all
the extpack providers and the list of providers can be re-read.

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

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