VirtualBox

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

Last change on this file since 78202 was 78202, checked in by vboxsync, 6 years ago

Main/VirtualBox+Machine: Fix bogus saving of VM config when the VM is inaccessible due to the 2nd or further media registry entry, by making sure that the medium registry isn't marked as modified. For good measure block saving of VM configs when they're inaccessible. Thanks, Socratis, for reporting with reproduction instructions in ticketref:17908.

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