VirtualBox

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

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

Main/NLS: TRCOMPONENT -> PTRCOMPONENT w/ some added type safety. unregisterTranslation should mostly ignore NULL. Docs. bugref:1909

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

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