VirtualBox

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

Last change on this file since 91312 was 91312, checked in by vboxsync, 3 years ago

Main: bugref:1909: Prepared the API translation engine to using in ExtPacks and VBoxManage. Added using API translation engine in ExtPacks. Allowed VBox compilation with NLS enabled and GUI disabled. Allowed ExtPacks only compilation with NLS translation enabled.

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