VirtualBox

source: vbox/trunk/src/VBox/Main/VirtualBoxImpl.cpp@ 34086

Last change on this file since 34086 was 34086, checked in by vboxsync, 14 years ago

Main: Pass more IVirtualBox pointers to the extension pack. VRDE registration hook, VirtualBoxReady hook.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 148.5 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 34086 2010-11-15 17:45:50Z vboxsync $ */
2
3/** @file
4 * Implementation of IVirtualBox in VBoxSVC.
5 */
6
7/*
8 * Copyright (C) 2006-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/asm.h>
20#include <iprt/buildconfig.h>
21#include <iprt/cpp/utils.h>
22#include <iprt/dir.h>
23#include <iprt/env.h>
24#include <iprt/file.h>
25#include <iprt/path.h>
26#include <iprt/process.h>
27#include <iprt/string.h>
28#include <iprt/stream.h>
29#include <iprt/thread.h>
30#include <iprt/uuid.h>
31#include <iprt/cpp/xml.h>
32
33#include <VBox/com/com.h>
34#include <VBox/com/array.h>
35#include "VBox/com/EventQueue.h"
36
37#include <VBox/err.h>
38#include <VBox/param.h>
39#include <VBox/settings.h>
40#include <VBox/version.h>
41
42#include <package-generated.h>
43
44#include <algorithm>
45#include <set>
46#include <vector>
47#include <memory> // for auto_ptr
48
49#include <typeinfo>
50
51#include "VirtualBoxImpl.h"
52
53#include "Global.h"
54#include "MachineImpl.h"
55#include "MediumImpl.h"
56#include "SharedFolderImpl.h"
57#include "ProgressImpl.h"
58#include "ProgressProxyImpl.h"
59#include "HostImpl.h"
60#include "USBControllerImpl.h"
61#include "SystemPropertiesImpl.h"
62#include "GuestOSTypeImpl.h"
63#include "DHCPServerRunner.h"
64#include "DHCPServerImpl.h"
65#ifdef VBOX_WITH_RESOURCE_USAGE_API
66# include "PerformanceImpl.h"
67#endif /* VBOX_WITH_RESOURCE_USAGE_API */
68#include "EventImpl.h"
69#ifdef VBOX_WITH_EXTPACK
70# include "ExtPackManagerImpl.h"
71#endif
72
73#include "AutoCaller.h"
74#include "Logging.h"
75#include "objectslist.h"
76
77#ifdef RT_OS_WINDOWS
78# include "win/svchlp.h"
79# include "win/VBoxComEvents.h"
80#endif
81
82////////////////////////////////////////////////////////////////////////////////
83//
84// Definitions
85//
86////////////////////////////////////////////////////////////////////////////////
87
88#define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
89
90////////////////////////////////////////////////////////////////////////////////
91//
92// Global variables
93//
94////////////////////////////////////////////////////////////////////////////////
95
96// static
97Bstr VirtualBox::sVersion;
98
99// static
100ULONG VirtualBox::sRevision;
101
102// static
103Bstr VirtualBox::sPackageType;
104
105////////////////////////////////////////////////////////////////////////////////
106//
107// VirtualBoxCallbackRegistration
108//
109////////////////////////////////////////////////////////////////////////////////
110
111/**
112 * Registered IVirtualBoxCallback, used by VirtualBox::CallbackList and
113 * VirtualBox::Data::llCallbacks.
114 *
115 * In addition to keeping the interface pointer this also keeps track of the
116 * methods that asked to not be called again. The latter is for reducing
117 * unnecessary IPC.
118 */
119class VirtualBoxCallbackRegistration
120{
121public:
122 /** Callback bit indexes (for bmDisabled). */
123 typedef enum
124 {
125 kOnMachineStateChanged = 0,
126 kOnMachineDataChanged,
127 kOnExtraDataCanChange,
128 kOnExtraDataChanged,
129 kOnMediumRegistered,
130 kOnMachineRegistered,
131 kOnSessionStateChanged,
132 kOnSnapshotTaken,
133 kOnSnapshotDeleted,
134 kOnSnapshotChanged,
135 kOnGuestPropertyChanged
136 } CallbackBit;
137
138 VirtualBoxCallbackRegistration()
139 {
140 /* nothing */
141 }
142
143 ~VirtualBoxCallbackRegistration()
144 {
145 /* nothing */
146 }
147};
148
149////////////////////////////////////////////////////////////////////////////////
150//
151// CallbackEvent class
152//
153////////////////////////////////////////////////////////////////////////////////
154
155/**
156 * Abstract callback event class to asynchronously call VirtualBox callbacks
157 * on a dedicated event thread. Subclasses reimplement #handleCallback()
158 * to call appropriate IVirtualBoxCallback methods depending on the event
159 * to be dispatched.
160 *
161 * @note The VirtualBox instance passed to the constructor is strongly
162 * referenced, so that the VirtualBox singleton won't be released until the
163 * event gets handled by the event thread.
164 */
165class VirtualBox::CallbackEvent : public Event
166{
167public:
168
169 CallbackEvent(VirtualBox *aVirtualBox, VirtualBoxCallbackRegistration::CallbackBit aWhat)
170 : mVirtualBox(aVirtualBox), mWhat(aWhat)
171 {
172 Assert(aVirtualBox);
173 }
174
175 void *handler();
176
177 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) = 0;
178
179private:
180
181 /**
182 * Note that this is a weak ref -- the CallbackEvent handler thread
183 * is bound to the lifetime of the VirtualBox instance, so it's safe.
184 */
185 VirtualBox *mVirtualBox;
186protected:
187 VirtualBoxCallbackRegistration::CallbackBit mWhat;
188};
189
190////////////////////////////////////////////////////////////////////////////////
191//
192// VirtualBox private member data definition
193//
194////////////////////////////////////////////////////////////////////////////////
195
196#if defined(RT_OS_WINDOWS)
197 #define UPDATEREQARG NULL
198 #define UPDATEREQTYPE HANDLE
199#elif defined(RT_OS_OS2)
200 #define UPDATEREQARG NIL_RTSEMEVENT
201 #define UPDATEREQTYPE RTSEMEVENT
202#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
203 #define UPDATEREQARG
204 #define UPDATEREQTYPE RTSEMEVENT
205#else
206# error "Port me!"
207#endif
208
209typedef ObjectsList<Machine> MachinesOList;
210typedef ObjectsList<Medium> MediaOList;
211typedef ObjectsList<GuestOSType> GuestOSTypesOList;
212typedef ObjectsList<SharedFolder> SharedFoldersOList;
213typedef ObjectsList<DHCPServer> DHCPServersOList;
214
215typedef std::map<Guid, ComPtr<IProgress> > ProgressMap;
216typedef std::map<Guid, ComObjPtr<Medium> > HardDiskMap;
217
218/**
219 * Main VirtualBox data structure.
220 * @note |const| members are persistent during lifetime so can be accessed
221 * without locking.
222 */
223struct VirtualBox::Data
224{
225 Data()
226 : pMainConfigFile(NULL),
227 uuidMediaRegistry("48024e5c-fdd9-470f-93af-ec29f7ea518c"),
228 lockMachines(LOCKCLASS_LISTOFMACHINES),
229 allMachines(lockMachines),
230 lockGuestOSTypes(LOCKCLASS_LISTOFOTHEROBJECTS),
231 allGuestOSTypes(lockGuestOSTypes),
232 lockMedia(LOCKCLASS_LISTOFMEDIA),
233 allHardDisks(lockMedia),
234 allDVDImages(lockMedia),
235 allFloppyImages(lockMedia),
236 lockSharedFolders(LOCKCLASS_LISTOFOTHEROBJECTS),
237 allSharedFolders(lockSharedFolders),
238 lockDHCPServers(LOCKCLASS_LISTOFOTHEROBJECTS),
239 allDHCPServers(lockDHCPServers),
240 mtxProgressOperations(LOCKCLASS_PROGRESSLIST),
241 updateReq(UPDATEREQARG),
242 threadClientWatcher(NIL_RTTHREAD),
243 threadAsyncEvent(NIL_RTTHREAD),
244 pAsyncEventQ(NULL)
245 {
246 }
247
248 ~Data()
249 {
250 if (pMainConfigFile)
251 {
252 delete pMainConfigFile;
253 pMainConfigFile = NULL;
254 }
255 };
256
257 // const data members not requiring locking
258 const Utf8Str strHomeDir;
259
260 // VirtualBox main settings file
261 const Utf8Str strSettingsFilePath;
262 settings::MainConfigFile *pMainConfigFile;
263
264 // constant pseudo-machine ID for global media registry
265 const Guid uuidMediaRegistry;
266
267 // const objects not requiring locking
268 const ComObjPtr<Host> pHost;
269 const ComObjPtr<SystemProperties> pSystemProperties;
270#ifdef VBOX_WITH_RESOURCE_USAGE_API
271 const ComObjPtr<PerformanceCollector> pPerformanceCollector;
272#endif /* VBOX_WITH_RESOURCE_USAGE_API */
273
274 // Each of the following lists use a particular lock handle that protects the
275 // list as a whole. As opposed to version 3.1 and earlier, these lists no
276 // longer need the main VirtualBox object lock, but only the respective list
277 // lock. In each case, the locking order is defined that the list must be
278 // requested before object locks of members of the lists (see the order definitions
279 // in AutoLock.h; e.g. LOCKCLASS_LISTOFMACHINES before LOCKCLASS_MACHINEOBJECT).
280 RWLockHandle lockMachines;
281 MachinesOList allMachines;
282
283 RWLockHandle lockGuestOSTypes;
284 GuestOSTypesOList allGuestOSTypes;
285
286 // All the media lists are protected by the following locking handle:
287 RWLockHandle lockMedia;
288 MediaOList allHardDisks, // base images only!
289 allDVDImages,
290 allFloppyImages;
291 // the hard disks map is an additional map sorted by UUID for quick lookup
292 // and contains ALL hard disks (base and differencing); it is protected by
293 // the same lock as the other media lists above
294 HardDiskMap mapHardDisks;
295
296 // list of pending machine renames (also protected by media tree lock;
297 // see VirtualBox::rememberMachineNameChangeForMedia())
298 struct PendingMachineRename
299 {
300 Utf8Str strConfigDirOld;
301 Utf8Str strConfigDirNew;
302 };
303 typedef std::list<PendingMachineRename> PendingMachineRenamesList;
304 PendingMachineRenamesList llPendingMachineRenames;
305
306 RWLockHandle lockSharedFolders;
307 SharedFoldersOList allSharedFolders;
308
309 RWLockHandle lockDHCPServers;
310 DHCPServersOList allDHCPServers;
311
312 RWLockHandle mtxProgressOperations;
313 ProgressMap mapProgressOperations;
314
315 // the following are data for the client watcher thread
316 const UPDATEREQTYPE updateReq;
317 const RTTHREAD threadClientWatcher;
318 typedef std::list<RTPROCESS> ProcessList;
319 ProcessList llProcesses;
320
321 // the following are data for the async event thread
322 const RTTHREAD threadAsyncEvent;
323 EventQueue * const pAsyncEventQ;
324 const ComObjPtr<EventSource> pEventSource;
325
326#ifdef VBOX_WITH_EXTPACK
327 /** The extension pack manager object lives here. */
328 const ComObjPtr<ExtPackManager> ptrExtPackManager;
329#endif
330};
331
332// constructor / destructor
333/////////////////////////////////////////////////////////////////////////////
334
335VirtualBox::VirtualBox()
336{}
337
338VirtualBox::~VirtualBox()
339{}
340
341HRESULT VirtualBox::FinalConstruct()
342{
343 LogFlowThisFunc(("\n"));
344
345 return init();
346}
347
348void VirtualBox::FinalRelease()
349{
350 LogFlowThisFunc(("\n"));
351
352 uninit();
353}
354
355// public initializer/uninitializer for internal purposes only
356/////////////////////////////////////////////////////////////////////////////
357
358/**
359 * Initializes the VirtualBox object.
360 *
361 * @return COM result code
362 */
363HRESULT VirtualBox::init()
364{
365 /* Enclose the state transition NotReady->InInit->Ready */
366 AutoInitSpan autoInitSpan(this);
367 AssertReturn(autoInitSpan.isOk(), E_FAIL);
368
369 /* Locking this object for writing during init sounds a bit paradoxical,
370 * but in the current locking mess this avoids that some code gets a
371 * read lock and later calls code which wants the same write lock. */
372 AutoWriteLock lock(this COMMA_LOCKVAL_SRC_POS);
373
374 // allocate our instance data
375 m = new Data;
376
377 LogFlow(("===========================================================\n"));
378 LogFlowThisFuncEnter();
379
380 if (sVersion.isEmpty())
381 sVersion = VBOX_VERSION_STRING;
382 sRevision = RTBldCfgRevision();
383 if (sPackageType.isEmpty())
384 sPackageType = VBOX_PACKAGE_STRING;
385 LogFlowThisFunc(("Version: %ls, Package: %ls\n", sVersion.raw(), sPackageType.raw()));
386
387 /* Get the VirtualBox home directory. */
388 {
389 char szHomeDir[RTPATH_MAX];
390 int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir));
391 if (RT_FAILURE(vrc))
392 return setError(E_FAIL,
393 tr("Could not create the VirtualBox home directory '%s' (%Rrc)"),
394 szHomeDir, vrc);
395
396 unconst(m->strHomeDir) = szHomeDir;
397 }
398
399 /* compose the VirtualBox.xml file name */
400 unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s",
401 m->strHomeDir.c_str(),
402 RTPATH_DELIMITER,
403 VBOX_GLOBAL_SETTINGS_FILE);
404 HRESULT rc = S_OK;
405 bool fCreate = false;
406 try
407 {
408 // load and parse VirtualBox.xml; this will throw on XML or logic errors
409 try
410 {
411 m->pMainConfigFile = new settings::MainConfigFile(&m->strSettingsFilePath);
412 }
413 catch (xml::EIPRTFailure &e)
414 {
415 // this is thrown by the XML backend if the RTOpen() call fails;
416 // only if the main settings file does not exist, create it,
417 // if there's something more serious, then do fail!
418 if (e.rc() == VERR_FILE_NOT_FOUND)
419 fCreate = true;
420 else
421 throw;
422 }
423
424 if (fCreate)
425 m->pMainConfigFile = new settings::MainConfigFile(NULL);
426
427#ifdef VBOX_WITH_RESOURCE_USAGE_API
428 /* create the performance collector object BEFORE host */
429 unconst(m->pPerformanceCollector).createObject();
430 rc = m->pPerformanceCollector->init();
431 ComAssertComRCThrowRC(rc);
432#endif /* VBOX_WITH_RESOURCE_USAGE_API */
433
434 /* create the host object early, machines will need it */
435 unconst(m->pHost).createObject();
436 rc = m->pHost->init(this);
437 ComAssertComRCThrowRC(rc);
438
439 rc = m->pHost->loadSettings(m->pMainConfigFile->host);
440 if (FAILED(rc)) throw rc;
441
442 /* create the system properties object, someone may need it too */
443 unconst(m->pSystemProperties).createObject();
444 rc = m->pSystemProperties->init(this);
445 ComAssertComRCThrowRC(rc);
446
447 rc = m->pSystemProperties->loadSettings(m->pMainConfigFile->systemProperties);
448 if (FAILED(rc)) throw rc;
449
450 /* guest OS type objects, needed by machines */
451 for (size_t i = 0; i < RT_ELEMENTS(Global::sOSTypes); ++i)
452 {
453 ComObjPtr<GuestOSType> guestOSTypeObj;
454 rc = guestOSTypeObj.createObject();
455 if (SUCCEEDED(rc))
456 {
457 rc = guestOSTypeObj->init(Global::sOSTypes[i]);
458 if (SUCCEEDED(rc))
459 m->allGuestOSTypes.addChild(guestOSTypeObj);
460 }
461 ComAssertComRCThrowRC(rc);
462 }
463
464 /* all registered media, needed by machines */
465 if (FAILED(rc = initMedia(m->uuidMediaRegistry,
466 m->pMainConfigFile->mediaRegistry,
467 Utf8Str::Empty))) // const Utf8Str &machineFolder
468 throw rc;
469
470 /* machines */
471 if (FAILED(rc = initMachines()))
472 throw rc;
473
474
475#ifdef DEBUG
476 LogFlowThisFunc(("Dumping media backreferences\n"));
477 dumpAllBackRefs();
478#endif
479
480 /* net services */
481 for (settings::DHCPServersList::const_iterator it = m->pMainConfigFile->llDhcpServers.begin();
482 it != m->pMainConfigFile->llDhcpServers.end();
483 ++it)
484 {
485 const settings::DHCPServer &data = *it;
486
487 ComObjPtr<DHCPServer> pDhcpServer;
488 if (SUCCEEDED(rc = pDhcpServer.createObject()))
489 rc = pDhcpServer->init(this, data);
490 if (FAILED(rc)) throw rc;
491
492 rc = registerDHCPServer(pDhcpServer, false /* aSaveRegistry */);
493 if (FAILED(rc)) throw rc;
494 }
495
496 /* events */
497 if (SUCCEEDED(rc = unconst(m->pEventSource).createObject()))
498 rc = m->pEventSource->init(static_cast<IVirtualBox*>(this));
499 if (FAILED(rc)) throw rc;
500
501#ifdef VBOX_WITH_EXTPACK
502 /* extension manager */
503 rc = unconst(m->ptrExtPackManager).createObject();
504 if (SUCCEEDED(rc))
505 /** @todo Define drop zone location. */
506 rc = m->ptrExtPackManager->init(this, NULL /*a_pszDropZoneDir*/, false /*a_fCheckDropZone*/);
507 if (FAILED(rc))
508 throw rc;
509#endif
510 }
511 catch (HRESULT err)
512 {
513 /* we assume that error info is set by the thrower */
514 rc = err;
515 }
516 catch (...)
517 {
518 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
519 }
520
521 if (SUCCEEDED(rc))
522 {
523 /* start the client watcher thread */
524#if defined(RT_OS_WINDOWS)
525 unconst(m->updateReq) = ::CreateEvent(NULL, FALSE, FALSE, NULL);
526#elif defined(RT_OS_OS2)
527 RTSemEventCreate(&unconst(m->updateReq));
528#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
529 RTSemEventCreate(&unconst(m->updateReq));
530#else
531# error "Port me!"
532#endif
533 int vrc = RTThreadCreate(&unconst(m->threadClientWatcher),
534 ClientWatcher,
535 (void *)this,
536 0,
537 RTTHREADTYPE_MAIN_WORKER,
538 RTTHREADFLAGS_WAITABLE,
539 "Watcher");
540 ComAssertRC(vrc);
541 if (RT_FAILURE(vrc))
542 rc = E_FAIL;
543 }
544
545 if (SUCCEEDED(rc))
546 {
547 try
548 {
549 /* start the async event handler thread */
550 int vrc = RTThreadCreate(&unconst(m->threadAsyncEvent),
551 AsyncEventHandler,
552 &unconst(m->pAsyncEventQ),
553 0,
554 RTTHREADTYPE_MAIN_WORKER,
555 RTTHREADFLAGS_WAITABLE,
556 "EventHandler");
557 ComAssertRCThrow(vrc, E_FAIL);
558
559 /* wait until the thread sets m->pAsyncEventQ */
560 RTThreadUserWait(m->threadAsyncEvent, RT_INDEFINITE_WAIT);
561 ComAssertThrow(m->pAsyncEventQ, E_FAIL);
562 }
563 catch (HRESULT aRC)
564 {
565 rc = aRC;
566 }
567 }
568
569 /* Confirm a successful initialization when it's the case */
570 if (SUCCEEDED(rc))
571 autoInitSpan.setSucceeded();
572
573#ifdef VBOX_WITH_EXTPACK
574 /* Let the extension packs have a go at things. */
575 if (SUCCEEDED(rc))
576 m->ptrExtPackManager->callAllVirtualBoxReadyHooks();
577#endif
578
579 LogFlowThisFunc(("rc=%08X\n", rc));
580 LogFlowThisFuncLeave();
581 LogFlow(("===========================================================\n"));
582 return rc;
583}
584
585HRESULT VirtualBox::initMachines()
586{
587 for (settings::MachinesRegistry::const_iterator it = m->pMainConfigFile->llMachines.begin();
588 it != m->pMainConfigFile->llMachines.end();
589 ++it)
590 {
591 HRESULT rc = S_OK;
592 const settings::MachineRegistryEntry &xmlMachine = *it;
593 Guid uuid = xmlMachine.uuid;
594
595 ComObjPtr<Machine> pMachine;
596 if (SUCCEEDED(rc = pMachine.createObject()))
597 {
598 rc = pMachine->init(this,
599 xmlMachine.strSettingsFile,
600 &uuid);
601 if (SUCCEEDED(rc))
602 rc = registerMachine(pMachine);
603 if (FAILED(rc))
604 return rc;
605 }
606 }
607
608 return S_OK;
609}
610
611/**
612 * Loads a media registry from XML and adds the media contained therein to
613 * the global lists of known media.
614 *
615 * This now (4.0) gets called from two locations:
616 *
617 * -- VirtualBox::init(), to load the global media registry from VirtualBox.xml;
618 *
619 * -- Machine::loadMachineDataFromSettings(), to load the per-machine registry
620 * from machine XML, for machines created with VirtualBox 4.0 or later.
621 *
622 * In both cases, the media found are added to the global lists so the
623 * global arrays of media (including the GUI's virtual media manager)
624 * continue to work as before.
625 *
626 * @param uuidMachineRegistry The UUID of the media registry. This is either the
627 * transient UUID created at VirtualBox startup for the global registry or
628 * a machine ID.
629 * @param mediaRegistry The XML settings structure to load, either from VirtualBox.xml
630 * or a machine XML.
631 * @return
632 */
633HRESULT VirtualBox::initMedia(const Guid &uuidRegistry,
634 const settings::MediaRegistry mediaRegistry,
635 const Utf8Str &strMachineFolder)
636{
637 LogFlow(("VirtualBox::initMedia ENTERING, uuidRegistry=%s, strMachineFolder=%s\n",
638 uuidRegistry.toString().c_str(),
639 strMachineFolder.c_str()));
640
641 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
642
643 HRESULT rc = S_OK;
644 settings::MediaList::const_iterator it;
645 for (it = mediaRegistry.llHardDisks.begin();
646 it != mediaRegistry.llHardDisks.end();
647 ++it)
648 {
649 const settings::Medium &xmlHD = *it;
650
651 ComObjPtr<Medium> pHardDisk;
652 if (SUCCEEDED(rc = pHardDisk.createObject()))
653 rc = pHardDisk->init(this,
654 NULL, // parent
655 DeviceType_HardDisk,
656 uuidRegistry,
657 xmlHD, // XML data; this recurses to processes the children
658 strMachineFolder);
659 if (FAILED(rc)) return rc;
660
661 rc = registerHardDisk(pHardDisk, NULL /* pllRegistriesThatNeedSaving */);
662 if (FAILED(rc)) return rc;
663 }
664
665 for (it = mediaRegistry.llDvdImages.begin();
666 it != mediaRegistry.llDvdImages.end();
667 ++it)
668 {
669 const settings::Medium &xmlDvd = *it;
670
671 ComObjPtr<Medium> pImage;
672 if (SUCCEEDED(pImage.createObject()))
673 rc = pImage->init(this,
674 NULL,
675 DeviceType_DVD,
676 uuidRegistry,
677 xmlDvd,
678 strMachineFolder);
679 if (FAILED(rc)) return rc;
680
681 rc = registerImage(pImage,
682 DeviceType_DVD,
683 NULL /* pllRegistriesThatNeedSaving */);
684 if (FAILED(rc)) return rc;
685 }
686
687 for (it = mediaRegistry.llFloppyImages.begin();
688 it != mediaRegistry.llFloppyImages.end();
689 ++it)
690 {
691 const settings::Medium &xmlFloppy = *it;
692
693 ComObjPtr<Medium> pImage;
694 if (SUCCEEDED(pImage.createObject()))
695 rc = pImage->init(this,
696 NULL,
697 DeviceType_Floppy,
698 uuidRegistry,
699 xmlFloppy,
700 strMachineFolder);
701 if (FAILED(rc)) return rc;
702
703 rc = registerImage(pImage,
704 DeviceType_Floppy,
705 NULL /* pllRegistriesThatNeedSaving */);
706 if (FAILED(rc)) return rc;
707 }
708
709 LogFlow(("VirtualBox::initMedia LEAVING\n"));
710
711 return S_OK;
712}
713
714void VirtualBox::uninit()
715{
716 /* Enclose the state transition Ready->InUninit->NotReady */
717 AutoUninitSpan autoUninitSpan(this);
718 if (autoUninitSpan.uninitDone())
719 return;
720
721 LogFlow(("===========================================================\n"));
722 LogFlowThisFuncEnter();
723 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
724
725 /* tell all our child objects we've been uninitialized */
726
727 LogFlowThisFunc(("Uninitializing machines (%d)...\n", m->allMachines.size()));
728 if (m->pHost)
729 {
730 /* It is necessary to hold the VirtualBox and Host locks here because
731 we may have to uninitialize SessionMachines. */
732 AutoMultiWriteLock2 multilock(this, m->pHost COMMA_LOCKVAL_SRC_POS);
733 m->allMachines.uninitAll();
734 }
735 else
736 m->allMachines.uninitAll();
737 m->allFloppyImages.uninitAll();
738 m->allDVDImages.uninitAll();
739 m->allHardDisks.uninitAll();
740 m->allDHCPServers.uninitAll();
741
742 m->mapProgressOperations.clear();
743
744 m->allGuestOSTypes.uninitAll();
745
746 /* Note that we release singleton children after we've all other children.
747 * In some cases this is important because these other children may use
748 * some resources of the singletons which would prevent them from
749 * uninitializing (as for example, mSystemProperties which owns
750 * MediumFormat objects which Medium objects refer to) */
751 if (m->pSystemProperties)
752 {
753 m->pSystemProperties->uninit();
754 unconst(m->pSystemProperties).setNull();
755 }
756
757 if (m->pHost)
758 {
759 m->pHost->uninit();
760 unconst(m->pHost).setNull();
761 }
762
763#ifdef VBOX_WITH_RESOURCE_USAGE_API
764 if (m->pPerformanceCollector)
765 {
766 m->pPerformanceCollector->uninit();
767 unconst(m->pPerformanceCollector).setNull();
768 }
769#endif /* VBOX_WITH_RESOURCE_USAGE_API */
770
771 LogFlowThisFunc(("Terminating the async event handler...\n"));
772 if (m->threadAsyncEvent != NIL_RTTHREAD)
773 {
774 /* signal to exit the event loop */
775 if (RT_SUCCESS(m->pAsyncEventQ->interruptEventQueueProcessing()))
776 {
777 /*
778 * Wait for thread termination (only after we've successfully
779 * interrupted the event queue processing!)
780 */
781 int vrc = RTThreadWait(m->threadAsyncEvent, 60000, NULL);
782 if (RT_FAILURE(vrc))
783 LogWarningFunc(("RTThreadWait(%RTthrd) -> %Rrc\n",
784 m->threadAsyncEvent, vrc));
785 }
786 else
787 {
788 AssertMsgFailed(("interruptEventQueueProcessing() failed\n"));
789 RTThreadWait(m->threadAsyncEvent, 0, NULL);
790 }
791
792 unconst(m->threadAsyncEvent) = NIL_RTTHREAD;
793 unconst(m->pAsyncEventQ) = NULL;
794 }
795
796 LogFlowThisFunc(("Releasing event source...\n"));
797 if (m->pEventSource)
798 {
799 // we don't perform uninit() as it's possible that some pending event refers to this source
800 unconst(m->pEventSource).setNull();
801 }
802
803 LogFlowThisFunc(("Terminating the client watcher...\n"));
804 if (m->threadClientWatcher != NIL_RTTHREAD)
805 {
806 /* signal the client watcher thread */
807 updateClientWatcher();
808 /* wait for the termination */
809 RTThreadWait(m->threadClientWatcher, RT_INDEFINITE_WAIT, NULL);
810 unconst(m->threadClientWatcher) = NIL_RTTHREAD;
811 }
812 m->llProcesses.clear();
813#if defined(RT_OS_WINDOWS)
814 if (m->updateReq != NULL)
815 {
816 ::CloseHandle(m->updateReq);
817 unconst(m->updateReq) = NULL;
818 }
819#elif defined(RT_OS_OS2)
820 if (m->updateReq != NIL_RTSEMEVENT)
821 {
822 RTSemEventDestroy(m->updateReq);
823 unconst(m->updateReq) = NIL_RTSEMEVENT;
824 }
825#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
826 if (m->updateReq != NIL_RTSEMEVENT)
827 {
828 RTSemEventDestroy(m->updateReq);
829 unconst(m->updateReq) = NIL_RTSEMEVENT;
830 }
831#else
832# error "Port me!"
833#endif
834
835 // clean up our instance data
836 delete m;
837
838 /* Unload hard disk plugin backends. */
839 VDShutdown();
840
841 LogFlowThisFuncLeave();
842 LogFlow(("===========================================================\n"));
843}
844
845// IVirtualBox properties
846/////////////////////////////////////////////////////////////////////////////
847
848STDMETHODIMP VirtualBox::COMGETTER(Version)(BSTR *aVersion)
849{
850 CheckComArgNotNull(aVersion);
851
852 AutoCaller autoCaller(this);
853 if (FAILED(autoCaller.rc())) return autoCaller.rc();
854
855 sVersion.cloneTo(aVersion);
856 return S_OK;
857}
858
859STDMETHODIMP VirtualBox::COMGETTER(Revision)(ULONG *aRevision)
860{
861 CheckComArgNotNull(aRevision);
862
863 AutoCaller autoCaller(this);
864 if (FAILED(autoCaller.rc())) return autoCaller.rc();
865
866 *aRevision = sRevision;
867 return S_OK;
868}
869
870STDMETHODIMP VirtualBox::COMGETTER(PackageType)(BSTR *aPackageType)
871{
872 CheckComArgNotNull(aPackageType);
873
874 AutoCaller autoCaller(this);
875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
876
877 sPackageType.cloneTo(aPackageType);
878 return S_OK;
879}
880
881STDMETHODIMP VirtualBox::COMGETTER(HomeFolder)(BSTR *aHomeFolder)
882{
883 CheckComArgNotNull(aHomeFolder);
884
885 AutoCaller autoCaller(this);
886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
887
888 /* mHomeDir is const and doesn't need a lock */
889 m->strHomeDir.cloneTo(aHomeFolder);
890 return S_OK;
891}
892
893STDMETHODIMP VirtualBox::COMGETTER(SettingsFilePath)(BSTR *aSettingsFilePath)
894{
895 CheckComArgNotNull(aSettingsFilePath);
896
897 AutoCaller autoCaller(this);
898 if (FAILED(autoCaller.rc())) return autoCaller.rc();
899
900 /* mCfgFile.mName is const and doesn't need a lock */
901 m->strSettingsFilePath.cloneTo(aSettingsFilePath);
902 return S_OK;
903}
904
905STDMETHODIMP VirtualBox::COMGETTER(Host)(IHost **aHost)
906{
907 CheckComArgOutSafeArrayPointerValid(aHost);
908
909 AutoCaller autoCaller(this);
910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
911
912 /* mHost is const, no need to lock */
913 m->pHost.queryInterfaceTo(aHost);
914 return S_OK;
915}
916
917STDMETHODIMP
918VirtualBox::COMGETTER(SystemProperties)(ISystemProperties **aSystemProperties)
919{
920 CheckComArgOutSafeArrayPointerValid(aSystemProperties);
921
922 AutoCaller autoCaller(this);
923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
924
925 /* mSystemProperties is const, no need to lock */
926 m->pSystemProperties.queryInterfaceTo(aSystemProperties);
927 return S_OK;
928}
929
930STDMETHODIMP
931VirtualBox::COMGETTER(Machines)(ComSafeArrayOut(IMachine *, aMachines))
932{
933 if (ComSafeArrayOutIsNull(aMachines))
934 return E_POINTER;
935
936 AutoCaller autoCaller(this);
937 if (FAILED(autoCaller.rc())) return autoCaller.rc();
938
939 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
940 SafeIfaceArray<IMachine> machines(m->allMachines.getList());
941 machines.detachTo(ComSafeArrayOutArg(aMachines));
942
943 return S_OK;
944}
945
946STDMETHODIMP VirtualBox::COMGETTER(HardDisks)(ComSafeArrayOut(IMedium *, aHardDisks))
947{
948 if (ComSafeArrayOutIsNull(aHardDisks))
949 return E_POINTER;
950
951 AutoCaller autoCaller(this);
952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
953
954 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
955 SafeIfaceArray<IMedium> hardDisks(m->allHardDisks.getList());
956 hardDisks.detachTo(ComSafeArrayOutArg(aHardDisks));
957
958 return S_OK;
959}
960
961STDMETHODIMP VirtualBox::COMGETTER(DVDImages)(ComSafeArrayOut(IMedium *, aDVDImages))
962{
963 if (ComSafeArrayOutIsNull(aDVDImages))
964 return E_POINTER;
965
966 AutoCaller autoCaller(this);
967 if (FAILED(autoCaller.rc())) return autoCaller.rc();
968
969 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
970 SafeIfaceArray<IMedium> images(m->allDVDImages.getList());
971 images.detachTo(ComSafeArrayOutArg(aDVDImages));
972
973 return S_OK;
974}
975
976STDMETHODIMP VirtualBox::COMGETTER(FloppyImages)(ComSafeArrayOut(IMedium *, aFloppyImages))
977{
978 if (ComSafeArrayOutIsNull(aFloppyImages))
979 return E_POINTER;
980
981 AutoCaller autoCaller(this);
982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
983
984 AutoReadLock al(m->allFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
985 SafeIfaceArray<IMedium> images(m->allFloppyImages.getList());
986 images.detachTo(ComSafeArrayOutArg(aFloppyImages));
987
988 return S_OK;
989}
990
991STDMETHODIMP VirtualBox::COMGETTER(ProgressOperations)(ComSafeArrayOut(IProgress *, aOperations))
992{
993 CheckComArgOutSafeArrayPointerValid(aOperations);
994
995 AutoCaller autoCaller(this);
996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
997
998 /* protect mProgressOperations */
999 AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1000 SafeIfaceArray<IProgress> progress(m->mapProgressOperations);
1001 progress.detachTo(ComSafeArrayOutArg(aOperations));
1002
1003 return S_OK;
1004}
1005
1006STDMETHODIMP VirtualBox::COMGETTER(GuestOSTypes)(ComSafeArrayOut(IGuestOSType *, aGuestOSTypes))
1007{
1008 CheckComArgOutSafeArrayPointerValid(aGuestOSTypes);
1009
1010 AutoCaller autoCaller(this);
1011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1012
1013 AutoReadLock al(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1014 SafeIfaceArray<IGuestOSType> ostypes(m->allGuestOSTypes.getList());
1015 ostypes.detachTo(ComSafeArrayOutArg(aGuestOSTypes));
1016
1017 return S_OK;
1018}
1019
1020STDMETHODIMP VirtualBox::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
1021{
1022#ifndef RT_OS_WINDOWS
1023 NOREF(aSharedFoldersSize);
1024#endif /* RT_OS_WINDOWS */
1025
1026 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
1027
1028 AutoCaller autoCaller(this);
1029 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1030
1031 return setError(E_NOTIMPL, "Not yet implemented");
1032}
1033
1034STDMETHODIMP
1035VirtualBox::COMGETTER(PerformanceCollector)(IPerformanceCollector **aPerformanceCollector)
1036{
1037#ifdef VBOX_WITH_RESOURCE_USAGE_API
1038 CheckComArgOutSafeArrayPointerValid(aPerformanceCollector);
1039
1040 AutoCaller autoCaller(this);
1041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1042
1043 /* mPerformanceCollector is const, no need to lock */
1044 m->pPerformanceCollector.queryInterfaceTo(aPerformanceCollector);
1045
1046 return S_OK;
1047#else /* !VBOX_WITH_RESOURCE_USAGE_API */
1048 ReturnComNotImplemented();
1049#endif /* !VBOX_WITH_RESOURCE_USAGE_API */
1050}
1051
1052STDMETHODIMP
1053VirtualBox::COMGETTER(DHCPServers)(ComSafeArrayOut(IDHCPServer *, aDHCPServers))
1054{
1055 if (ComSafeArrayOutIsNull(aDHCPServers))
1056 return E_POINTER;
1057
1058 AutoCaller autoCaller(this);
1059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1060
1061 AutoReadLock al(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1062 SafeIfaceArray<IDHCPServer> svrs(m->allDHCPServers.getList());
1063 svrs.detachTo(ComSafeArrayOutArg(aDHCPServers));
1064
1065 return S_OK;
1066}
1067
1068STDMETHODIMP
1069VirtualBox::COMGETTER(EventSource)(IEventSource ** aEventSource)
1070{
1071 CheckComArgOutPointerValid(aEventSource);
1072
1073 AutoCaller autoCaller(this);
1074 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1075
1076 /* event source is const, no need to lock */
1077 m->pEventSource.queryInterfaceTo(aEventSource);
1078
1079 return S_OK;
1080}
1081
1082STDMETHODIMP
1083VirtualBox::COMGETTER(ExtensionPackManager)(IExtPackManager **aExtPackManager)
1084{
1085 CheckComArgOutPointerValid(aExtPackManager);
1086
1087 AutoCaller autoCaller(this);
1088 HRESULT hrc = autoCaller.rc();
1089 if (SUCCEEDED(hrc))
1090 {
1091#ifdef VBOX_WITH_EXTPACK
1092 /* The extension pack manager is const, no need to lock. */
1093 hrc = m->ptrExtPackManager.queryInterfaceTo(aExtPackManager);
1094#else
1095 hrc = E_NOTIMPL;
1096#endif
1097 }
1098
1099 return hrc;
1100}
1101
1102STDMETHODIMP
1103VirtualBox::CheckFirmwarePresent(FirmwareType_T aFirmwareType,
1104 IN_BSTR aVersion,
1105 BSTR *aUrl,
1106 BSTR *aFile,
1107 BOOL *aResult)
1108{
1109 CheckComArgNotNull(aResult);
1110
1111 AutoCaller autoCaller(this);
1112 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1113
1114 const char * url = NULL;
1115
1116 NOREF(aVersion);
1117
1118 static const struct {
1119 FirmwareType_T type;
1120 const char* fileName;
1121 const char* url;
1122 } firmwareDesc[] = {
1123 {
1124 /* compiled-in firmware */
1125 FirmwareType_BIOS, NULL, NULL
1126 },
1127 {
1128 FirmwareType_EFI32, "VBoxEFI32.fd", "http://virtualbox.org/firmware/VBoxEFI32.fd"
1129 },
1130 {
1131 FirmwareType_EFI64, "VBoxEFI64.fd", "http://virtualbox.org/firmware/VBoxEFI64.fd"
1132 },
1133 {
1134 FirmwareType_EFIDUAL, "VBoxEFIDual.fd", "http://virtualbox.org/firmware/VBoxEFIDual.fd"
1135 }
1136 };
1137
1138 for (size_t i = 0; i < sizeof(firmwareDesc) / sizeof(firmwareDesc[0]); i++)
1139 {
1140 if (aFirmwareType != firmwareDesc[i].type)
1141 continue;
1142
1143 /* compiled-in firmware */
1144 if (firmwareDesc[i].fileName == NULL)
1145 {
1146 *aResult = TRUE;
1147 break;
1148 }
1149
1150 Utf8Str shortName, fullName;
1151 int rc;
1152
1153 shortName = Utf8StrFmt("Firmware%c%s",
1154 RTPATH_DELIMITER,
1155 firmwareDesc[i].fileName);
1156 rc = calculateFullPath(shortName, fullName); AssertRCReturn(rc, rc);
1157 if (RTFileExists(fullName.c_str()))
1158 {
1159 *aResult = TRUE;
1160 if (aFile)
1161 Utf8Str(fullName).cloneTo(aFile);
1162 break;
1163 }
1164
1165 char pszVBoxPath[RTPATH_MAX];
1166 rc = RTPathExecDir(pszVBoxPath, RTPATH_MAX); AssertRCReturn(rc, rc);
1167 fullName = Utf8StrFmt("%s%c%s",
1168 pszVBoxPath,
1169 RTPATH_DELIMITER,
1170 firmwareDesc[i].fileName);
1171 if (RTFileExists(fullName.c_str()))
1172 {
1173 *aResult = TRUE;
1174 if (aFile)
1175 Utf8Str(fullName).cloneTo(aFile);
1176 break;
1177 }
1178
1179
1180 url = firmwareDesc[i].url;
1181 /** @todo: account for version in the URL */
1182 if (aUrl != NULL)
1183 {
1184 Utf8Str strUrl(firmwareDesc[i].url);
1185 strUrl.cloneTo(aUrl);
1186 }
1187 *aResult = FALSE;
1188
1189 /* Assume single record per firmware type */
1190 break;
1191 }
1192
1193 return S_OK;
1194}
1195// IVirtualBox methods
1196/////////////////////////////////////////////////////////////////////////////
1197
1198STDMETHODIMP VirtualBox::ComposeMachineFilename(IN_BSTR aName,
1199 IN_BSTR aBaseFolder,
1200 BSTR *aFilename)
1201{
1202 LogFlowThisFuncEnter();
1203 LogFlowThisFunc(("aName=\"%ls\",aBaseFolder=\"%ls\"\n", aName, aBaseFolder));
1204
1205 CheckComArgStrNotEmptyOrNull(aName);
1206 CheckComArgOutPointerValid(aFilename);
1207
1208 AutoCaller autoCaller(this);
1209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1210
1211 /* Compose the settings file name using the following scheme:
1212 *
1213 * <base_folder>/<machine_name>/<machine_name>.xml
1214 *
1215 * If a non-null and non-empty base folder is specified, the default
1216 * machine folder will be used as a base folder.
1217 */
1218 Utf8Str strBase = aBaseFolder;
1219 if (strBase.isEmpty())
1220 /* we use the non-full folder value below to keep the path relative */
1221 getDefaultMachineFolder(strBase);
1222
1223 calculateFullPath(strBase, strBase);
1224
1225 Bstr bstrSettingsFile = BstrFmt("%s%c%ls%c%ls.vbox",
1226 strBase.c_str(),
1227 RTPATH_DELIMITER,
1228 aName,
1229 RTPATH_DELIMITER,
1230 aName);
1231
1232 bstrSettingsFile.detachTo(aFilename);
1233
1234 return S_OK;
1235}
1236
1237/** @note Locks mSystemProperties object for reading. */
1238STDMETHODIMP VirtualBox::CreateMachine(IN_BSTR aSettingsFile,
1239 IN_BSTR aName,
1240 IN_BSTR aOsTypeId,
1241 IN_BSTR aId,
1242 BOOL forceOverwrite,
1243 IMachine **aMachine)
1244{
1245 LogFlowThisFuncEnter();
1246 LogFlowThisFunc(("aSettingsFile=\"%ls\", aName=\"%ls\", aOsTypeId =\"%ls\"\n", aSettingsFile, aName, aOsTypeId));
1247
1248 CheckComArgStrNotEmptyOrNull(aName);
1249 /** @todo tighten checks on aId? */
1250 CheckComArgOutPointerValid(aMachine);
1251
1252 AutoCaller autoCaller(this);
1253 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1254
1255 /* NULL settings file means compose automatically */
1256 HRESULT rc;
1257 Bstr bstrSettingsFile(aSettingsFile);
1258 if (bstrSettingsFile.isEmpty())
1259 {
1260 rc = ComposeMachineFilename(aName,
1261 NULL,
1262 bstrSettingsFile.asOutParam());
1263 if (FAILED(rc)) return rc;
1264 }
1265
1266 /* create a new object */
1267 ComObjPtr<Machine> machine;
1268 rc = machine.createObject();
1269 if (FAILED(rc)) return rc;
1270
1271 /* Create UUID if an empty one was specified. */
1272 Guid id(aId);
1273 if (id.isEmpty())
1274 id.create();
1275
1276 GuestOSType *osType = NULL;
1277 rc = findGuestOSType(Bstr(aOsTypeId), osType);
1278 if (FAILED(rc)) return rc;
1279
1280 /* initialize the machine object */
1281 rc = machine->init(this,
1282 Utf8Str(bstrSettingsFile),
1283 Utf8Str(aName),
1284 osType,
1285 id,
1286 !!forceOverwrite);
1287 if (SUCCEEDED(rc))
1288 {
1289#ifdef VBOX_WITH_EXTPACK
1290 /* call the extension pack hooks */
1291 m->ptrExtPackManager->callAllVmCreatedHooks(machine);
1292#endif
1293
1294 /* set the return value */
1295 rc = machine.queryInterfaceTo(aMachine);
1296 AssertComRC(rc);
1297 }
1298
1299 LogFlowThisFuncLeave();
1300
1301 return rc;
1302}
1303
1304STDMETHODIMP VirtualBox::OpenMachine(IN_BSTR aSettingsFile,
1305 IMachine **aMachine)
1306{
1307 CheckComArgStrNotEmptyOrNull(aSettingsFile);
1308 CheckComArgOutSafeArrayPointerValid(aMachine);
1309
1310 AutoCaller autoCaller(this);
1311 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1312
1313 HRESULT rc = E_FAIL;
1314
1315 /* create a new object */
1316 ComObjPtr<Machine> machine;
1317 rc = machine.createObject();
1318 if (SUCCEEDED(rc))
1319 {
1320 /* initialize the machine object */
1321 rc = machine->init(this,
1322 aSettingsFile,
1323 NULL); /* const Guid *aId */
1324 if (SUCCEEDED(rc))
1325 {
1326 /* set the return value */
1327 rc = machine.queryInterfaceTo(aMachine);
1328 ComAssertComRC(rc);
1329 }
1330 }
1331
1332 return rc;
1333}
1334
1335/** @note Locks objects! */
1336STDMETHODIMP VirtualBox::RegisterMachine(IMachine *aMachine)
1337{
1338 CheckComArgNotNull(aMachine);
1339
1340 AutoCaller autoCaller(this);
1341 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1342
1343 HRESULT rc;
1344
1345 Bstr name;
1346 rc = aMachine->COMGETTER(Name)(name.asOutParam());
1347 if (FAILED(rc)) return rc;
1348
1349 /* We can safely cast child to Machine * here because only Machine
1350 * implementations of IMachine can be among our children. */
1351 Machine *pMachine = static_cast<Machine*>(aMachine);
1352
1353 AutoCaller machCaller(pMachine);
1354 ComAssertComRCRetRC(machCaller.rc());
1355
1356 rc = registerMachine(pMachine);
1357 /* fire an event */
1358 if (SUCCEEDED(rc))
1359 onMachineRegistered(pMachine->getId(), TRUE);
1360
1361 return rc;
1362}
1363
1364/** @note Locks this object for reading, then some machine objects for reading. */
1365STDMETHODIMP VirtualBox::FindMachine(IN_BSTR aNameOrId, IMachine **aMachine)
1366{
1367 LogFlowThisFuncEnter();
1368 LogFlowThisFunc(("aName=\"%ls\", aMachine={%p}\n", aNameOrId, aMachine));
1369
1370 CheckComArgStrNotEmptyOrNull(aNameOrId);
1371 CheckComArgOutSafeArrayPointerValid(aMachine);
1372
1373 AutoCaller autoCaller(this);
1374 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1375
1376 /* start with not found */
1377 HRESULT rc = S_OK;
1378 ComObjPtr<Machine> pMachineFound;
1379
1380 Guid id(aNameOrId);
1381 if (!id.isEmpty())
1382 rc = findMachine(id,
1383 true /* fPermitInaccessible */,
1384 true /* setError */,
1385 &pMachineFound);
1386 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
1387 else
1388 {
1389 Utf8Str strName(aNameOrId);
1390 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1391 for (MachinesOList::iterator it = m->allMachines.begin();
1392 it != m->allMachines.end();
1393 ++it)
1394 {
1395 ComObjPtr<Machine> &pMachine2 = *it;
1396 AutoCaller machCaller(pMachine2);
1397 if (machCaller.rc())
1398 continue; // we can't ask inaccessible machines for their names
1399
1400 AutoReadLock machLock(pMachine2 COMMA_LOCKVAL_SRC_POS);
1401 if (pMachine2->getName() == strName)
1402 {
1403 pMachineFound = pMachine2;
1404 break;
1405 }
1406 }
1407
1408 if (!pMachineFound)
1409 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1410 tr("Could not find a registered machine named '%ls'"), aNameOrId);
1411 }
1412
1413 /* this will set (*machine) to NULL if machineObj is null */
1414 pMachineFound.queryInterfaceTo(aMachine);
1415
1416 LogFlowThisFunc(("aName=\"%ls\", aMachine=%p, rc=%08X\n", aNameOrId, *aMachine, rc));
1417 LogFlowThisFuncLeave();
1418
1419 return rc;
1420}
1421
1422STDMETHODIMP VirtualBox::CreateHardDisk(IN_BSTR aFormat,
1423 IN_BSTR aLocation,
1424 IMedium **aHardDisk)
1425{
1426 CheckComArgOutPointerValid(aHardDisk);
1427
1428 AutoCaller autoCaller(this);
1429 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1430
1431 /* we don't access non-const data members so no need to lock */
1432
1433 Utf8Str format(aFormat);
1434 if (format.isEmpty())
1435 getDefaultHardDiskFormat(format);
1436
1437 ComObjPtr<Medium> hardDisk;
1438 hardDisk.createObject();
1439 HRESULT rc = hardDisk->init(this,
1440 format,
1441 aLocation,
1442 Guid::Empty, // media registry: none yet
1443 NULL /* pllRegistriesThatNeedSaving */);
1444
1445 if (SUCCEEDED(rc))
1446 hardDisk.queryInterfaceTo(aHardDisk);
1447
1448 return rc;
1449}
1450
1451STDMETHODIMP VirtualBox::OpenMedium(IN_BSTR aLocation,
1452 DeviceType_T deviceType,
1453 AccessMode_T accessMode,
1454 IMedium **aMedium)
1455{
1456 CheckComArgStrNotEmptyOrNull(aLocation);
1457 CheckComArgOutSafeArrayPointerValid(aMedium);
1458
1459 AutoCaller autoCaller(this);
1460 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1461
1462 ComObjPtr<Medium> pMedium;
1463
1464 /* we don't access non-const data members so no need to lock */
1465
1466 // check if the device type is correct, and see if a medium for the
1467 // given path has already initialized; if so, return that
1468 switch (deviceType)
1469 {
1470 case DeviceType_HardDisk:
1471 findHardDiskByLocation(aLocation,
1472 false, /* aSetError */
1473 &pMedium);
1474 break;
1475
1476 case DeviceType_Floppy:
1477 case DeviceType_DVD:
1478 findDVDOrFloppyImage(deviceType,
1479 NULL, /* guid */
1480 aLocation,
1481 false, /* aSetError */
1482 &pMedium);
1483
1484 // enforce read-only for DVDs even if caller specified ReadWrite
1485 if (deviceType == DeviceType_DVD)
1486 accessMode = AccessMode_ReadOnly;
1487 break;
1488
1489 default:
1490 return setError(E_INVALIDARG, "Device type must be HardDisk, DVD or Floppy");
1491 }
1492
1493 HRESULT rc = S_OK;
1494
1495 if (pMedium.isNull())
1496 {
1497 pMedium.createObject();
1498 rc = pMedium->init(this,
1499 aLocation,
1500 (accessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
1501 deviceType);
1502
1503 if (SUCCEEDED(rc))
1504 {
1505 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1506
1507 switch (deviceType)
1508 {
1509 case DeviceType_HardDisk:
1510 rc = registerHardDisk(pMedium, NULL /* pllRegistriesThatNeedSaving */);
1511 break;
1512
1513 case DeviceType_DVD:
1514 case DeviceType_Floppy:
1515 rc = registerImage(pMedium,
1516 deviceType,
1517 NULL /* pllRegistriesThatNeedSaving */);
1518 break;
1519 }
1520
1521 treeLock.release();
1522
1523 /* Note that it's important to call uninit() on failure to register
1524 * because the differencing hard disk would have been already associated
1525 * with the parent and this association needs to be broken. */
1526
1527 if (FAILED(rc))
1528 pMedium->uninit();
1529 }
1530 }
1531
1532 if (SUCCEEDED(rc))
1533 pMedium.queryInterfaceTo(aMedium);
1534
1535 return rc;
1536}
1537
1538STDMETHODIMP VirtualBox::FindMedium(IN_BSTR aLocation,
1539 DeviceType_T aDeviceType,
1540 IMedium **aMedium)
1541{
1542 CheckComArgOutSafeArrayPointerValid(aMedium);
1543
1544 AutoCaller autoCaller(this);
1545 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1546
1547 Guid id(aLocation);
1548 Utf8Str strLocation(aLocation);
1549
1550 HRESULT rc;
1551 ComObjPtr<Medium> pMedium;
1552
1553 switch (aDeviceType)
1554 {
1555 case DeviceType_HardDisk:
1556 if (!id.isEmpty())
1557 rc = findHardDiskById(id, true /* setError */, &pMedium);
1558 else
1559 rc = findHardDiskByLocation(strLocation, true /* setError */, &pMedium);
1560 break;
1561
1562 case DeviceType_Floppy:
1563 case DeviceType_DVD:
1564 if (!id.isEmpty())
1565 rc = findDVDOrFloppyImage(aDeviceType, &id, Utf8Str::Empty, true /* setError */, &pMedium);
1566 else
1567 rc = findDVDOrFloppyImage(aDeviceType, NULL, strLocation, true /* setError */, &pMedium);
1568 break;
1569
1570 default:
1571 return setError(E_INVALIDARG,
1572 tr("Invalid device type %d"), aDeviceType);
1573 }
1574
1575 /* the below will set *aHardDisk to NULL if hardDisk is null */
1576 pMedium.queryInterfaceTo(aMedium);
1577
1578 return rc;
1579}
1580
1581/** @note Locks this object for reading. */
1582STDMETHODIMP VirtualBox::GetGuestOSType(IN_BSTR aId, IGuestOSType **aType)
1583{
1584 /* Old ID to new ID conversion table. See r39691 for a source */
1585 static const wchar_t *kOldNewIDs[] =
1586 {
1587 L"unknown", L"Other",
1588 L"win31", L"Windows31",
1589 L"win95", L"Windows95",
1590 L"win98", L"Windows98",
1591 L"winme", L"WindowsMe",
1592 L"winnt4", L"WindowsNT4",
1593 L"win2k", L"Windows2000",
1594 L"winxp", L"WindowsXP",
1595 L"win2k3", L"Windows2003",
1596 L"winvista", L"WindowsVista",
1597 L"win2k8", L"Windows2008",
1598 L"ecs", L"OS2eCS",
1599 L"fedoracore", L"Fedora",
1600 /* the rest is covered by the case-insensitive comparison */
1601 };
1602
1603 CheckComArgNotNull(aType);
1604
1605 AutoCaller autoCaller(this);
1606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1607
1608 /* first, look for a substitution */
1609 Bstr id = aId;
1610 for (size_t i = 0; i < RT_ELEMENTS(kOldNewIDs) / 2; i += 2)
1611 {
1612 if (id == kOldNewIDs[i])
1613 {
1614 id = kOldNewIDs[i + 1];
1615 break;
1616 }
1617 }
1618
1619 *aType = NULL;
1620
1621 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1622 for (GuestOSTypesOList::iterator it = m->allGuestOSTypes.begin();
1623 it != m->allGuestOSTypes.end();
1624 ++it)
1625 {
1626 const Bstr &typeId = (*it)->id();
1627 AssertMsg(!typeId.isEmpty(), ("ID must not be NULL"));
1628 if (typeId.compare(id, Bstr::CaseInsensitive) == 0)
1629 {
1630 (*it).queryInterfaceTo(aType);
1631 break;
1632 }
1633 }
1634
1635 return (*aType) ? S_OK :
1636 setError(E_INVALIDARG,
1637 tr("'%ls' is not a valid Guest OS type"),
1638 aId);
1639}
1640
1641STDMETHODIMP VirtualBox::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath,
1642 BOOL /* aWritable */, BOOL /* aAutoMount */)
1643{
1644 CheckComArgStrNotEmptyOrNull(aName);
1645 CheckComArgStrNotEmptyOrNull(aHostPath);
1646
1647 AutoCaller autoCaller(this);
1648 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1649
1650 return setError(E_NOTIMPL, "Not yet implemented");
1651}
1652
1653STDMETHODIMP VirtualBox::RemoveSharedFolder(IN_BSTR aName)
1654{
1655 CheckComArgStrNotEmptyOrNull(aName);
1656
1657 AutoCaller autoCaller(this);
1658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1659
1660 return setError(E_NOTIMPL, "Not yet implemented");
1661}
1662
1663/**
1664 * @note Locks this object for reading.
1665 */
1666STDMETHODIMP VirtualBox::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
1667{
1668 using namespace settings;
1669
1670 if (ComSafeArrayOutIsNull(aKeys))
1671 return E_POINTER;
1672
1673 AutoCaller autoCaller(this);
1674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1675
1676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 com::SafeArray<BSTR> saKeys(m->pMainConfigFile->mapExtraDataItems.size());
1679 int i = 0;
1680 for (StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin();
1681 it != m->pMainConfigFile->mapExtraDataItems.end();
1682 ++it, ++i)
1683 {
1684 const Utf8Str &strName = it->first; // the key
1685 strName.cloneTo(&saKeys[i]);
1686 }
1687 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
1688
1689 return S_OK;
1690}
1691
1692/**
1693 * @note Locks this object for reading.
1694 */
1695STDMETHODIMP VirtualBox::GetExtraData(IN_BSTR aKey,
1696 BSTR *aValue)
1697{
1698 CheckComArgStrNotEmptyOrNull(aKey);
1699 CheckComArgNotNull(aValue);
1700
1701 AutoCaller autoCaller(this);
1702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1703
1704 /* start with nothing found */
1705 Utf8Str strKey(aKey);
1706 Bstr bstrResult;
1707
1708 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
1709 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1710 // found:
1711 bstrResult = it->second; // source is a Utf8Str
1712
1713 /* return the result to caller (may be empty) */
1714 bstrResult.cloneTo(aValue);
1715
1716 return S_OK;
1717}
1718
1719/**
1720 * @note Locks this object for writing.
1721 */
1722STDMETHODIMP VirtualBox::SetExtraData(IN_BSTR aKey,
1723 IN_BSTR aValue)
1724{
1725 CheckComArgStrNotEmptyOrNull(aKey);
1726
1727 AutoCaller autoCaller(this);
1728 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1729
1730 Utf8Str strKey(aKey);
1731 Utf8Str strValue(aValue);
1732 Utf8Str strOldValue; // empty
1733
1734 // locking note: we only hold the read lock briefly to look up the old value,
1735 // then release it and call the onExtraCanChange callbacks. There is a small
1736 // chance of a race insofar as the callback might be called twice if two callers
1737 // change the same key at the same time, but that's a much better solution
1738 // than the deadlock we had here before. The actual changing of the extradata
1739 // is then performed under the write lock and race-free.
1740
1741 // look up the old value first; if nothing has changed then we need not do anything
1742 {
1743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
1744 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
1745 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1746 strOldValue = it->second;
1747 }
1748
1749 bool fChanged;
1750 if ((fChanged = (strOldValue != strValue)))
1751 {
1752 // ask for permission from all listeners outside the locks;
1753 // onExtraDataCanChange() only briefly requests the VirtualBox
1754 // lock to copy the list of callbacks to invoke
1755 Bstr error;
1756 Bstr bstrValue(aValue);
1757
1758 if (!onExtraDataCanChange(Guid::Empty, aKey, bstrValue.raw(), error))
1759 {
1760 const char *sep = error.isEmpty() ? "" : ": ";
1761 CBSTR err = error.raw();
1762 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
1763 sep, err));
1764 return setError(E_ACCESSDENIED,
1765 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
1766 aKey,
1767 bstrValue.raw(),
1768 sep,
1769 err);
1770 }
1771
1772 // data is changing and change not vetoed: then write it out under the lock
1773
1774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1775
1776 if (strValue.isEmpty())
1777 m->pMainConfigFile->mapExtraDataItems.erase(strKey);
1778 else
1779 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
1780 // creates a new key if needed
1781
1782 /* save settings on success */
1783 HRESULT rc = saveSettings();
1784 if (FAILED(rc)) return rc;
1785 }
1786
1787 // fire notification outside the lock
1788 if (fChanged)
1789 onExtraDataChange(Guid::Empty, aKey, aValue);
1790
1791 return S_OK;
1792}
1793
1794// public methods only for internal purposes
1795/////////////////////////////////////////////////////////////////////////////
1796
1797#ifdef DEBUG
1798void VirtualBox::dumpAllBackRefs()
1799{
1800 {
1801 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1802 for (MediaList::const_iterator mt = m->allHardDisks.begin();
1803 mt != m->allHardDisks.end();
1804 ++mt)
1805 {
1806 ComObjPtr<Medium> pMedium = *mt;
1807 pMedium->dumpBackRefs();
1808 }
1809 }
1810 {
1811 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1812 for (MediaList::const_iterator mt = m->allDVDImages.begin();
1813 mt != m->allDVDImages.end();
1814 ++mt)
1815 {
1816 ComObjPtr<Medium> pMedium = *mt;
1817 pMedium->dumpBackRefs();
1818 }
1819 }
1820}
1821#endif
1822
1823/**
1824 * Posts an event to the event queue that is processed asynchronously
1825 * on a dedicated thread.
1826 *
1827 * Posting events to the dedicated event queue is useful to perform secondary
1828 * actions outside any object locks -- for example, to iterate over a list
1829 * of callbacks and inform them about some change caused by some object's
1830 * method call.
1831 *
1832 * @param event event to post; must have been allocated using |new|, will
1833 * be deleted automatically by the event thread after processing
1834 *
1835 * @note Doesn't lock any object.
1836 */
1837HRESULT VirtualBox::postEvent(Event *event)
1838{
1839 AssertReturn(event, E_FAIL);
1840
1841 HRESULT rc;
1842 AutoCaller autoCaller(this);
1843 if (SUCCEEDED((rc = autoCaller.rc())))
1844 {
1845 if (autoCaller.state() != Ready)
1846 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
1847 autoCaller.state()));
1848 // return S_OK
1849 else if ( (m->pAsyncEventQ)
1850 && (m->pAsyncEventQ->postEvent(event))
1851 )
1852 return S_OK;
1853 else
1854 rc = E_FAIL;
1855 }
1856
1857 // in any event of failure, we must clean up here, or we'll leak;
1858 // the caller has allocated the object using new()
1859 delete event;
1860 return rc;
1861}
1862
1863/**
1864 * Adds a progress to the global collection of pending operations.
1865 * Usually gets called upon progress object initialization.
1866 *
1867 * @param aProgress Operation to add to the collection.
1868 *
1869 * @note Doesn't lock objects.
1870 */
1871HRESULT VirtualBox::addProgress(IProgress *aProgress)
1872{
1873 CheckComArgNotNull(aProgress);
1874
1875 AutoCaller autoCaller(this);
1876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1877
1878 Bstr id;
1879 HRESULT rc = aProgress->COMGETTER(Id)(id.asOutParam());
1880 AssertComRCReturnRC(rc);
1881
1882 /* protect mProgressOperations */
1883 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1884
1885 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
1886 return S_OK;
1887}
1888
1889/**
1890 * Removes the progress from the global collection of pending operations.
1891 * Usually gets called upon progress completion.
1892 *
1893 * @param aId UUID of the progress operation to remove
1894 *
1895 * @note Doesn't lock objects.
1896 */
1897HRESULT VirtualBox::removeProgress(IN_GUID aId)
1898{
1899 AutoCaller autoCaller(this);
1900 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1901
1902 ComPtr<IProgress> progress;
1903
1904 /* protect mProgressOperations */
1905 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1906
1907 size_t cnt = m->mapProgressOperations.erase(aId);
1908 Assert(cnt == 1);
1909 NOREF(cnt);
1910
1911 return S_OK;
1912}
1913
1914#ifdef RT_OS_WINDOWS
1915
1916struct StartSVCHelperClientData
1917{
1918 ComObjPtr<VirtualBox> that;
1919 ComObjPtr<Progress> progress;
1920 bool privileged;
1921 VirtualBox::SVCHelperClientFunc func;
1922 void *user;
1923};
1924
1925/**
1926 * Helper method that starts a worker thread that:
1927 * - creates a pipe communication channel using SVCHlpClient;
1928 * - starts an SVC Helper process that will inherit this channel;
1929 * - executes the supplied function by passing it the created SVCHlpClient
1930 * and opened instance to communicate to the Helper process and the given
1931 * Progress object.
1932 *
1933 * The user function is supposed to communicate to the helper process
1934 * using the \a aClient argument to do the requested job and optionally expose
1935 * the progress through the \a aProgress object. The user function should never
1936 * call notifyComplete() on it: this will be done automatically using the
1937 * result code returned by the function.
1938 *
1939 * Before the user function is started, the communication channel passed to
1940 * the \a aClient argument is fully set up, the function should start using
1941 * its write() and read() methods directly.
1942 *
1943 * The \a aVrc parameter of the user function may be used to return an error
1944 * code if it is related to communication errors (for example, returned by
1945 * the SVCHlpClient members when they fail). In this case, the correct error
1946 * message using this value will be reported to the caller. Note that the
1947 * value of \a aVrc is inspected only if the user function itself returns
1948 * success.
1949 *
1950 * If a failure happens anywhere before the user function would be normally
1951 * called, it will be called anyway in special "cleanup only" mode indicated
1952 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
1953 * all the function is supposed to do is to cleanup its aUser argument if
1954 * necessary (it's assumed that the ownership of this argument is passed to
1955 * the user function once #startSVCHelperClient() returns a success, thus
1956 * making it responsible for the cleanup).
1957 *
1958 * After the user function returns, the thread will send the SVCHlpMsg::Null
1959 * message to indicate a process termination.
1960 *
1961 * @param aPrivileged |true| to start the SVC Helper process as a privileged
1962 * user that can perform administrative tasks
1963 * @param aFunc user function to run
1964 * @param aUser argument to the user function
1965 * @param aProgress progress object that will track operation completion
1966 *
1967 * @note aPrivileged is currently ignored (due to some unsolved problems in
1968 * Vista) and the process will be started as a normal (unprivileged)
1969 * process.
1970 *
1971 * @note Doesn't lock anything.
1972 */
1973HRESULT VirtualBox::startSVCHelperClient(bool aPrivileged,
1974 SVCHelperClientFunc aFunc,
1975 void *aUser, Progress *aProgress)
1976{
1977 AssertReturn(aFunc, E_POINTER);
1978 AssertReturn(aProgress, E_POINTER);
1979
1980 AutoCaller autoCaller(this);
1981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1982
1983 /* create the SVCHelperClientThread() argument */
1984 std::auto_ptr <StartSVCHelperClientData>
1985 d(new StartSVCHelperClientData());
1986 AssertReturn(d.get(), E_OUTOFMEMORY);
1987
1988 d->that = this;
1989 d->progress = aProgress;
1990 d->privileged = aPrivileged;
1991 d->func = aFunc;
1992 d->user = aUser;
1993
1994 RTTHREAD tid = NIL_RTTHREAD;
1995 int vrc = RTThreadCreate(&tid, SVCHelperClientThread,
1996 static_cast <void *>(d.get()),
1997 0, RTTHREADTYPE_MAIN_WORKER,
1998 RTTHREADFLAGS_WAITABLE, "SVCHelper");
1999 if (RT_FAILURE(vrc))
2000 return setError(E_FAIL, "Could not create SVCHelper thread (%Rrc)", vrc);
2001
2002 /* d is now owned by SVCHelperClientThread(), so release it */
2003 d.release();
2004
2005 return S_OK;
2006}
2007
2008/**
2009 * Worker thread for startSVCHelperClient().
2010 */
2011/* static */
2012DECLCALLBACK(int)
2013VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser)
2014{
2015 LogFlowFuncEnter();
2016
2017 std::auto_ptr<StartSVCHelperClientData>
2018 d(static_cast<StartSVCHelperClientData*>(aUser));
2019
2020 HRESULT rc = S_OK;
2021 bool userFuncCalled = false;
2022
2023 do
2024 {
2025 AssertBreakStmt(d.get(), rc = E_POINTER);
2026 AssertReturn(!d->progress.isNull(), E_POINTER);
2027
2028 /* protect VirtualBox from uninitialization */
2029 AutoCaller autoCaller(d->that);
2030 if (!autoCaller.isOk())
2031 {
2032 /* it's too late */
2033 rc = autoCaller.rc();
2034 break;
2035 }
2036
2037 int vrc = VINF_SUCCESS;
2038
2039 Guid id;
2040 id.create();
2041 SVCHlpClient client;
2042 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
2043 id.raw()).c_str());
2044 if (RT_FAILURE(vrc))
2045 {
2046 rc = d->that->setError(E_FAIL,
2047 tr("Could not create the communication channel (%Rrc)"), vrc);
2048 break;
2049 }
2050
2051 /* get the path to the executable */
2052 char exePathBuf[RTPATH_MAX];
2053 char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX);
2054 if (!exePath)
2055 {
2056 rc = d->that->setError(E_FAIL, tr("Cannot get executable name"));
2057 break;
2058 }
2059
2060 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().c_str());
2061
2062 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.c_str()));
2063
2064 RTPROCESS pid = NIL_RTPROCESS;
2065
2066 if (d->privileged)
2067 {
2068 /* Attempt to start a privileged process using the Run As dialog */
2069
2070 Bstr file = exePath;
2071 Bstr parameters = argsStr;
2072
2073 SHELLEXECUTEINFO shExecInfo;
2074
2075 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
2076
2077 shExecInfo.fMask = NULL;
2078 shExecInfo.hwnd = NULL;
2079 shExecInfo.lpVerb = L"runas";
2080 shExecInfo.lpFile = file.raw();
2081 shExecInfo.lpParameters = parameters.raw();
2082 shExecInfo.lpDirectory = NULL;
2083 shExecInfo.nShow = SW_NORMAL;
2084 shExecInfo.hInstApp = NULL;
2085
2086 if (!ShellExecuteEx(&shExecInfo))
2087 {
2088 int vrc2 = RTErrConvertFromWin32(GetLastError());
2089 /* hide excessive details in case of a frequent error
2090 * (pressing the Cancel button to close the Run As dialog) */
2091 if (vrc2 == VERR_CANCELLED)
2092 rc = d->that->setError(E_FAIL,
2093 tr("Operation canceled by the user"));
2094 else
2095 rc = d->that->setError(E_FAIL,
2096 tr("Could not launch a privileged process '%s' (%Rrc)"),
2097 exePath, vrc2);
2098 break;
2099 }
2100 }
2101 else
2102 {
2103 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2104 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
2105 if (RT_FAILURE(vrc))
2106 {
2107 rc = d->that->setError(E_FAIL,
2108 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2109 break;
2110 }
2111 }
2112
2113 /* wait for the client to connect */
2114 vrc = client.connect();
2115 if (RT_SUCCESS(vrc))
2116 {
2117 /* start the user supplied function */
2118 rc = d->func(&client, d->progress, d->user, &vrc);
2119 userFuncCalled = true;
2120 }
2121
2122 /* send the termination signal to the process anyway */
2123 {
2124 int vrc2 = client.write(SVCHlpMsg::Null);
2125 if (RT_SUCCESS(vrc))
2126 vrc = vrc2;
2127 }
2128
2129 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2130 {
2131 rc = d->that->setError(E_FAIL,
2132 tr("Could not operate the communication channel (%Rrc)"), vrc);
2133 break;
2134 }
2135 }
2136 while (0);
2137
2138 if (FAILED(rc) && !userFuncCalled)
2139 {
2140 /* call the user function in the "cleanup only" mode
2141 * to let it free resources passed to in aUser */
2142 d->func(NULL, NULL, d->user, NULL);
2143 }
2144
2145 d->progress->notifyComplete(rc);
2146
2147 LogFlowFuncLeave();
2148 return 0;
2149}
2150
2151#endif /* RT_OS_WINDOWS */
2152
2153/**
2154 * Sends a signal to the client watcher thread to rescan the set of machines
2155 * that have open sessions.
2156 *
2157 * @note Doesn't lock anything.
2158 */
2159void VirtualBox::updateClientWatcher()
2160{
2161 AutoCaller autoCaller(this);
2162 AssertComRCReturnVoid(autoCaller.rc());
2163
2164 AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD);
2165
2166 /* sent an update request */
2167#if defined(RT_OS_WINDOWS)
2168 ::SetEvent(m->updateReq);
2169#elif defined(RT_OS_OS2)
2170 RTSemEventSignal(m->updateReq);
2171#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2172 RTSemEventSignal(m->updateReq);
2173#else
2174# error "Port me!"
2175#endif
2176}
2177
2178/**
2179 * Adds the given child process ID to the list of processes to be reaped.
2180 * This call should be followed by #updateClientWatcher() to take the effect.
2181 */
2182void VirtualBox::addProcessToReap(RTPROCESS pid)
2183{
2184 AutoCaller autoCaller(this);
2185 AssertComRCReturnVoid(autoCaller.rc());
2186
2187 /// @todo (dmik) Win32?
2188#ifndef RT_OS_WINDOWS
2189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2190 m->llProcesses.push_back(pid);
2191#endif
2192}
2193
2194/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2195struct MachineEvent : public VirtualBox::CallbackEvent
2196{
2197 MachineEvent(VirtualBox *aVB, const Guid &aId)
2198 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineDataChanged), id(aId.toUtf16())
2199 {}
2200
2201 MachineEvent(VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2202 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineStateChanged), id(aId.toUtf16())
2203 , state(aState)
2204 {}
2205
2206 MachineEvent(VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2207 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineRegistered), id(aId.toUtf16())
2208 , registered(aRegistered)
2209 {}
2210
2211 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2212 {
2213 switch (mWhat)
2214 {
2215 case VirtualBoxCallbackRegistration::kOnMachineDataChanged:
2216 aEvDesc.init(aSource, VBoxEventType_OnMachineDataChanged, id.raw());
2217 break;
2218
2219 case VirtualBoxCallbackRegistration::kOnMachineStateChanged:
2220 aEvDesc.init(aSource, VBoxEventType_OnMachineStateChanged, id.raw(), state);
2221 break;
2222
2223 case VirtualBoxCallbackRegistration::kOnMachineRegistered:
2224 aEvDesc.init(aSource, VBoxEventType_OnMachineRegistered, id.raw(), registered);
2225 break;
2226
2227 default:
2228 AssertFailedReturn(S_OK);
2229 }
2230 return S_OK;
2231 }
2232
2233 Bstr id;
2234 MachineState_T state;
2235 BOOL registered;
2236};
2237
2238/**
2239 * @note Doesn't lock any object.
2240 */
2241void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2242{
2243 postEvent(new MachineEvent(this, aId, aState));
2244}
2245
2246/**
2247 * @note Doesn't lock any object.
2248 */
2249void VirtualBox::onMachineDataChange(const Guid &aId)
2250{
2251 postEvent(new MachineEvent(this, aId));
2252}
2253
2254/**
2255 * @note Locks this object for reading.
2256 */
2257BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2258 Bstr &aError)
2259{
2260 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2261 aId.toString().c_str(), aKey, aValue));
2262
2263 AutoCaller autoCaller(this);
2264 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2265
2266 BOOL allowChange = TRUE;
2267 Bstr id = aId.toUtf16();
2268
2269 VBoxEventDesc evDesc;
2270 evDesc.init(m->pEventSource, VBoxEventType_OnExtraDataCanChange, id.raw(), aKey, aValue);
2271 BOOL fDelivered = evDesc.fire(3000); /* Wait up to 3 secs for delivery */
2272 //Assert(fDelivered);
2273 if (fDelivered)
2274 {
2275 ComPtr<IEvent> aEvent;
2276 evDesc.getEvent(aEvent.asOutParam());
2277 ComPtr<IExtraDataCanChangeEvent> aCanChangeEvent = aEvent;
2278 Assert(aCanChangeEvent);
2279 BOOL fVetoed = FALSE;
2280 aCanChangeEvent->IsVetoed(&fVetoed);
2281 allowChange = !fVetoed;
2282
2283 if (!allowChange)
2284 {
2285 SafeArray<BSTR> aVetos;
2286 aCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos));
2287 if (aVetos.size() > 0)
2288 aError = aVetos[0];
2289 }
2290 }
2291 else
2292 allowChange = TRUE;
2293
2294 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2295 return allowChange;
2296}
2297
2298/** Event for onExtraDataChange() */
2299struct ExtraDataEvent : public VirtualBox::CallbackEvent
2300{
2301 ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId,
2302 IN_BSTR aKey, IN_BSTR aVal)
2303 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnExtraDataChanged)
2304 , machineId(aMachineId.toUtf16()), key(aKey), val(aVal)
2305 {}
2306
2307 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2308 {
2309 return aEvDesc.init(aSource, VBoxEventType_OnExtraDataChanged, machineId.raw(), key.raw(), val.raw());
2310 }
2311
2312 Bstr machineId, key, val;
2313};
2314
2315/**
2316 * @note Doesn't lock any object.
2317 */
2318void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2319{
2320 postEvent(new ExtraDataEvent(this, aId, aKey, aValue));
2321}
2322
2323/**
2324 * @note Doesn't lock any object.
2325 */
2326void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered)
2327{
2328 postEvent(new MachineEvent(this, aId, aRegistered));
2329}
2330
2331/** Event for onSessionStateChange() */
2332struct SessionEvent : public VirtualBox::CallbackEvent
2333{
2334 SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2335 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnSessionStateChanged)
2336 , machineId(aMachineId.toUtf16()), sessionState(aState)
2337 {}
2338
2339 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2340 {
2341 return aEvDesc.init(aSource, VBoxEventType_OnSessionStateChanged, machineId.raw(), sessionState);
2342 }
2343 Bstr machineId;
2344 SessionState_T sessionState;
2345};
2346
2347/**
2348 * @note Doesn't lock any object.
2349 */
2350void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState)
2351{
2352 postEvent(new SessionEvent(this, aId, aState));
2353}
2354
2355/** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */
2356struct SnapshotEvent : public VirtualBox::CallbackEvent
2357{
2358 SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2359 VirtualBoxCallbackRegistration::CallbackBit aWhat)
2360 : CallbackEvent(aVB, aWhat)
2361 , machineId(aMachineId), snapshotId(aSnapshotId)
2362 {}
2363
2364 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2365 {
2366 return aEvDesc.init(aSource, VBoxEventType_OnSnapshotTaken,
2367 machineId.toUtf16().raw(), snapshotId.toUtf16().raw());
2368 }
2369
2370 Guid machineId;
2371 Guid snapshotId;
2372};
2373
2374/**
2375 * @note Doesn't lock any object.
2376 */
2377void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
2378{
2379 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2380 VirtualBoxCallbackRegistration::kOnSnapshotTaken));
2381}
2382
2383/**
2384 * @note Doesn't lock any object.
2385 */
2386void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
2387{
2388 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2389 VirtualBoxCallbackRegistration::kOnSnapshotDeleted));
2390}
2391
2392/**
2393 * @note Doesn't lock any object.
2394 */
2395void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
2396{
2397 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2398 VirtualBoxCallbackRegistration::kOnSnapshotChanged));
2399}
2400
2401/** Event for onGuestPropertyChange() */
2402struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2403{
2404 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
2405 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2406 : CallbackEvent(aVBox, VirtualBoxCallbackRegistration::kOnGuestPropertyChanged),
2407 machineId(aMachineId),
2408 name(aName),
2409 value(aValue),
2410 flags(aFlags)
2411 {}
2412
2413 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2414 {
2415 return aEvDesc.init(aSource, VBoxEventType_OnGuestPropertyChanged,
2416 machineId.toUtf16().raw(), name.raw(), value.raw(), flags.raw());
2417 }
2418
2419 Guid machineId;
2420 Bstr name, value, flags;
2421};
2422
2423/**
2424 * @note Doesn't lock any object.
2425 */
2426void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
2427 IN_BSTR aValue, IN_BSTR aFlags)
2428{
2429 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
2430}
2431
2432/** Event for onMachineUninit(), this is not a CallbackEvent */
2433class MachineUninitEvent : public Event
2434{
2435public:
2436
2437 MachineUninitEvent(VirtualBox *aVirtualBox, Machine *aMachine)
2438 : mVirtualBox(aVirtualBox), mMachine(aMachine)
2439 {
2440 Assert(aVirtualBox);
2441 Assert(aMachine);
2442 }
2443
2444 void *handler()
2445 {
2446#ifdef VBOX_WITH_RESOURCE_USAGE_API
2447 /* Handle unregistering metrics here, as it is not vital to get
2448 * it done immediately. It reduces the number of locks needed and
2449 * the lock contention in SessionMachine::uninit. */
2450 {
2451 AutoWriteLock mLock(mMachine COMMA_LOCKVAL_SRC_POS);
2452 mMachine->unregisterMetrics(mVirtualBox->performanceCollector(), mMachine);
2453 }
2454#endif /* VBOX_WITH_RESOURCE_USAGE_API */
2455
2456 return NULL;
2457 }
2458
2459private:
2460
2461 /**
2462 * Note that this is a weak ref -- the CallbackEvent handler thread
2463 * is bound to the lifetime of the VirtualBox instance, so it's safe.
2464 */
2465 VirtualBox *mVirtualBox;
2466
2467 /** Reference to the machine object. */
2468 ComObjPtr<Machine> mMachine;
2469};
2470
2471/**
2472 * Trigger internal event. This isn't meant to be signalled to clients.
2473 * @note Doesn't lock any object.
2474 */
2475void VirtualBox::onMachineUninit(Machine *aMachine)
2476{
2477 postEvent(new MachineUninitEvent(this, aMachine));
2478}
2479
2480/**
2481 * @note Doesn't lock any object.
2482 */
2483void VirtualBox::onNatRedirectChange(const Guid &aMachineId, ULONG ulSlot, bool fRemove, IN_BSTR aName,
2484 NATProtocol_T aProto, IN_BSTR aHostIp, uint16_t aHostPort,
2485 IN_BSTR aGuestIp, uint16_t aGuestPort)
2486{
2487 VBoxEventDesc evDesc;
2488 evDesc.init(m->pEventSource, VBoxEventType_OnNATRedirectEvent, aMachineId.toUtf16().raw(), ulSlot, fRemove, aName, aProto, aHostIp,
2489 aHostPort, aGuestIp, aGuestPort);
2490 evDesc.fire(0);
2491}
2492
2493/**
2494 * @note Locks this object for reading.
2495 */
2496ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2497{
2498 ComObjPtr<GuestOSType> type;
2499 AutoCaller autoCaller(this);
2500 AssertComRCReturn(autoCaller.rc(), type);
2501
2502 /* unknown type must always be the first */
2503 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
2504
2505 return m->allGuestOSTypes.front();
2506}
2507
2508/**
2509 * Returns the list of opened machines (machines having direct sessions opened
2510 * by client processes) and optionally the list of direct session controls.
2511 *
2512 * @param aMachines Where to put opened machines (will be empty if none).
2513 * @param aControls Where to put direct session controls (optional).
2514 *
2515 * @note The returned lists contain smart pointers. So, clear it as soon as
2516 * it becomes no more necessary to release instances.
2517 *
2518 * @note It can be possible that a session machine from the list has been
2519 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2520 * when accessing unprotected data directly.
2521 *
2522 * @note Locks objects for reading.
2523 */
2524void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
2525 InternalControlList *aControls /*= NULL*/)
2526{
2527 AutoCaller autoCaller(this);
2528 AssertComRCReturnVoid(autoCaller.rc());
2529
2530 aMachines.clear();
2531 if (aControls)
2532 aControls->clear();
2533
2534 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2535
2536 for (MachinesOList::iterator it = m->allMachines.begin();
2537 it != m->allMachines.end();
2538 ++it)
2539 {
2540 ComObjPtr<SessionMachine> sm;
2541 ComPtr<IInternalSessionControl> ctl;
2542 if ((*it)->isSessionOpen(sm, &ctl))
2543 {
2544 aMachines.push_back(sm);
2545 if (aControls)
2546 aControls->push_back(ctl);
2547 }
2548 }
2549}
2550
2551/**
2552 * Searches for a machine object with the given ID in the collection
2553 * of registered machines.
2554 *
2555 * @param aId Machine UUID to look for.
2556 * @param aPermitInaccessible If true, inaccessible machines will be found;
2557 * if false, this will fail if the given machine is inaccessible.
2558 * @param aSetError If true, set errorinfo if the machine is not found.
2559 * @param aMachine Returned machine, if found.
2560 * @return
2561 */
2562HRESULT VirtualBox::findMachine(const Guid &aId,
2563 bool fPermitInaccessible,
2564 bool aSetError,
2565 ComObjPtr<Machine> *aMachine /* = NULL */)
2566{
2567 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
2568
2569 AutoCaller autoCaller(this);
2570 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2571
2572 {
2573 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2574
2575 for (MachinesOList::iterator it = m->allMachines.begin();
2576 it != m->allMachines.end();
2577 ++it)
2578 {
2579 ComObjPtr<Machine> pMachine2 = *it;
2580
2581 if (!fPermitInaccessible)
2582 {
2583 // skip inaccessible machines
2584 AutoCaller machCaller(pMachine2);
2585 if (FAILED(machCaller.rc()))
2586 continue;
2587 }
2588
2589 if (pMachine2->getId() == aId)
2590 {
2591 rc = S_OK;
2592 if (aMachine)
2593 *aMachine = pMachine2;
2594 break;
2595 }
2596 }
2597 }
2598
2599 if (aSetError && FAILED(rc))
2600 rc = setError(rc,
2601 tr("Could not find a registered machine with UUID {%RTuuid}"),
2602 aId.raw());
2603
2604 return rc;
2605}
2606
2607/**
2608 * Searches for a Medium object with the given ID in the list of registered
2609 * hard disks.
2610 *
2611 * @param aId ID of the hard disk. Must not be empty.
2612 * @param aSetError If @c true , the appropriate error info is set in case
2613 * when the hard disk is not found.
2614 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2615 *
2616 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2617 *
2618 * @note Locks the media tree for reading.
2619 */
2620HRESULT VirtualBox::findHardDiskById(const Guid &id,
2621 bool aSetError,
2622 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2623{
2624 AssertReturn(!id.isEmpty(), E_INVALIDARG);
2625
2626 // we use the hard disks map, but it is protected by the
2627 // hard disk _list_ lock handle
2628 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2629
2630 HardDiskMap::const_iterator it = m->mapHardDisks.find(id);
2631 if (it != m->mapHardDisks.end())
2632 {
2633 if (aHardDisk)
2634 *aHardDisk = (*it).second;
2635 return S_OK;
2636 }
2637
2638 if (aSetError)
2639 return setError(VBOX_E_OBJECT_NOT_FOUND,
2640 tr("Could not find an open hard disk with UUID {%RTuuid}"),
2641 id.raw());
2642
2643 return VBOX_E_OBJECT_NOT_FOUND;
2644}
2645
2646/**
2647 * Searches for a Medium object with the given ID or location in the list of
2648 * registered hard disks. If both ID and location are specified, the first
2649 * object that matches either of them (not necessarily both) is returned.
2650 *
2651 * @param aLocation Full location specification. Must not be empty.
2652 * @param aSetError If @c true , the appropriate error info is set in case
2653 * when the hard disk is not found.
2654 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2655 *
2656 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2657 *
2658 * @note Locks the media tree for reading.
2659 */
2660HRESULT VirtualBox::findHardDiskByLocation(const Utf8Str &strLocation,
2661 bool aSetError,
2662 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2663{
2664 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
2665
2666 // we use the hard disks map, but it is protected by the
2667 // hard disk _list_ lock handle
2668 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2669
2670 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
2671 it != m->mapHardDisks.end();
2672 ++it)
2673 {
2674 const ComObjPtr<Medium> &pHD = (*it).second;
2675
2676 AutoCaller autoCaller(pHD);
2677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2678 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
2679
2680 Utf8Str strLocationFull = pHD->getLocationFull();
2681
2682 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
2683 {
2684 if (aHardDisk)
2685 *aHardDisk = pHD;
2686 return S_OK;
2687 }
2688 }
2689
2690 if (aSetError)
2691 return setError(VBOX_E_OBJECT_NOT_FOUND,
2692 tr("Could not find an open hard disk with location '%s'"),
2693 strLocation.c_str());
2694
2695 return VBOX_E_OBJECT_NOT_FOUND;
2696}
2697
2698/**
2699 * Searches for a Medium object with the given ID or location in the list of
2700 * registered DVD or floppy images, depending on the @a mediumType argument.
2701 * If both ID and file path are specified, the first object that matches either
2702 * of them (not necessarily both) is returned.
2703 *
2704 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
2705 * @param aId ID of the image file (unused when NULL).
2706 * @param aLocation Full path to the image file (unused when NULL).
2707 * @param aSetError If @c true, the appropriate error info is set in case when
2708 * the image is not found.
2709 * @param aImage Where to store the found image object (can be NULL).
2710 *
2711 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2712 *
2713 * @note Locks the media tree for reading.
2714 */
2715HRESULT VirtualBox::findDVDOrFloppyImage(DeviceType_T mediumType,
2716 const Guid *aId,
2717 const Utf8Str &aLocation,
2718 bool aSetError,
2719 ComObjPtr<Medium> *aImage /* = NULL */)
2720{
2721 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
2722
2723 Utf8Str location;
2724 if (!aLocation.isEmpty())
2725 {
2726 int vrc = calculateFullPath(aLocation, location);
2727 if (RT_FAILURE(vrc))
2728 return setError(VBOX_E_FILE_ERROR,
2729 tr("Invalid image file location '%ls' (%Rrc)"),
2730 aLocation.c_str(),
2731 vrc);
2732 }
2733
2734 MediaOList *pMediaList;
2735
2736 switch (mediumType)
2737 {
2738 case DeviceType_DVD:
2739 pMediaList = &m->allDVDImages;
2740 break;
2741
2742 case DeviceType_Floppy:
2743 pMediaList = &m->allFloppyImages;
2744 break;
2745
2746 default:
2747 return E_INVALIDARG;
2748 }
2749
2750 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
2751
2752 bool found = false;
2753
2754 for (MediaList::const_iterator it = pMediaList->begin();
2755 it != pMediaList->end();
2756 ++it)
2757 {
2758 // no AutoCaller, registered image life time is bound to this
2759 Medium *pMedium = *it;
2760 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
2761 const Utf8Str &strLocationFull = pMedium->getLocationFull();
2762
2763 found = ( aId
2764 && pMedium->getId() == *aId)
2765 || ( !aLocation.isEmpty()
2766 && RTPathCompare(location.c_str(),
2767 strLocationFull.c_str()) == 0);
2768 if (found)
2769 {
2770 if (pMedium->getDeviceType() != mediumType)
2771 {
2772 if (mediumType == DeviceType_DVD)
2773 return setError(E_INVALIDARG,
2774 "Cannot mount DVD medium '%s' as floppy", strLocationFull.c_str());
2775 else
2776 return setError(E_INVALIDARG,
2777 "Cannot mount floppy medium '%s' as DVD", strLocationFull.c_str());
2778 }
2779
2780 if (aImage)
2781 *aImage = pMedium;
2782 break;
2783 }
2784 }
2785
2786 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2787
2788 if (aSetError && !found)
2789 {
2790 if (aId)
2791 setError(rc,
2792 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
2793 aId->raw(),
2794 m->strSettingsFilePath.c_str());
2795 else
2796 setError(rc,
2797 tr("Could not find an image file with location '%ls' in the media registry ('%s')"),
2798 aLocation.c_str(),
2799 m->strSettingsFilePath.c_str());
2800 }
2801
2802 return rc;
2803}
2804
2805/**
2806 * Searches for an IMedium object that represents the given UUID.
2807 *
2808 * If the UUID is empty (indicating an empty drive), this sets pMedium
2809 * to NULL and returns S_OK.
2810 *
2811 * If the UUID refers to a host drive of the given device type, this
2812 * sets pMedium to the object from the list in IHost and returns S_OK.
2813 *
2814 * If the UUID is an image file, this sets pMedium to the object that
2815 * findDVDOrFloppyImage() returned.
2816 *
2817 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
2818 *
2819 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
2820 * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
2821 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
2822 * @param pMedium out: IMedium object found.
2823 * @return
2824 */
2825HRESULT VirtualBox::findRemoveableMedium(DeviceType_T mediumType,
2826 const Guid &uuid,
2827 bool fRefresh,
2828 ComObjPtr<Medium> &pMedium)
2829{
2830 if (uuid.isEmpty())
2831 {
2832 // that's easy
2833 pMedium.setNull();
2834 return S_OK;
2835 }
2836
2837 // first search for host drive with that UUID
2838 HRESULT rc = m->pHost->findHostDrive(mediumType,
2839 uuid,
2840 fRefresh,
2841 pMedium);
2842 if (rc == VBOX_E_OBJECT_NOT_FOUND)
2843 // then search for an image with that UUID
2844 rc = findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, true /* aSetError */, &pMedium);
2845
2846 return rc;
2847}
2848
2849HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
2850 GuestOSType*& pGuestOSType)
2851{
2852 /* Look for a GuestOSType object */
2853 AssertMsg(m->allGuestOSTypes.size() != 0,
2854 ("Guest OS types array must be filled"));
2855
2856 if (bstrOSType.isEmpty())
2857 {
2858 pGuestOSType = NULL;
2859 return S_OK;
2860 }
2861
2862 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2863 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
2864 it != m->allGuestOSTypes.end();
2865 ++it)
2866 {
2867 if ((*it)->id() == bstrOSType)
2868 {
2869 pGuestOSType = *it;
2870 return S_OK;
2871 }
2872 }
2873
2874 return setError(VBOX_E_OBJECT_NOT_FOUND,
2875 tr("Guest OS type '%ls' is invalid"),
2876 bstrOSType.raw());
2877}
2878
2879/**
2880 * Returns the constant pseudo-machine UUID that is used to identify the
2881 * global media registry.
2882 *
2883 * Starting with VirtualBox 4.0 each medium remembers in its instance data
2884 * in which media registry it is saved (if any): this can either be a machine
2885 * UUID, if it's in a per-machine media registry, or this global ID.
2886 *
2887 * This UUID is only used to identify the VirtualBox object while VirtualBox
2888 * is running. It is a compile-time constant and not saved anywhere.
2889 *
2890 * @return
2891 */
2892const Guid& VirtualBox::getGlobalRegistryId() const
2893{
2894 return m->uuidMediaRegistry;
2895}
2896
2897const ComObjPtr<Host>& VirtualBox::host() const
2898{
2899 return m->pHost;
2900}
2901
2902SystemProperties* VirtualBox::getSystemProperties() const
2903{
2904 return m->pSystemProperties;
2905}
2906
2907#ifdef VBOX_WITH_RESOURCE_USAGE_API
2908const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
2909{
2910 return m->pPerformanceCollector;
2911}
2912#endif /* VBOX_WITH_RESOURCE_USAGE_API */
2913
2914/**
2915 * Returns the default machine folder from the system properties
2916 * with proper locking.
2917 * @return
2918 */
2919void VirtualBox::getDefaultMachineFolder(Utf8Str &str) const
2920{
2921 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2922 str = m->pSystemProperties->m->strDefaultMachineFolder;
2923}
2924
2925/**
2926 * Returns the default hard disk format from the system properties
2927 * with proper locking.
2928 * @return
2929 */
2930void VirtualBox::getDefaultHardDiskFormat(Utf8Str &str) const
2931{
2932 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2933 str = m->pSystemProperties->m->strDefaultHardDiskFormat;
2934}
2935
2936const Utf8Str& VirtualBox::homeDir() const
2937{
2938 return m->strHomeDir;
2939}
2940
2941/**
2942 * Calculates the absolute path of the given path taking the VirtualBox home
2943 * directory as the current directory.
2944 *
2945 * @param aPath Path to calculate the absolute path for.
2946 * @param aResult Where to put the result (used only on success, can be the
2947 * same Utf8Str instance as passed in @a aPath).
2948 * @return IPRT result.
2949 *
2950 * @note Doesn't lock any object.
2951 */
2952int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
2953{
2954 AutoCaller autoCaller(this);
2955 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
2956
2957 /* no need to lock since mHomeDir is const */
2958
2959 char folder[RTPATH_MAX];
2960 int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
2961 strPath.c_str(),
2962 folder,
2963 sizeof(folder));
2964 if (RT_SUCCESS(vrc))
2965 aResult = folder;
2966
2967 return vrc;
2968}
2969
2970/**
2971 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
2972 * if it is a subdirectory thereof, or simply copying it otherwise.
2973 *
2974 * @param strSource Path to evalue and copy.
2975 * @param strTarget Buffer to receive target path.
2976 */
2977void VirtualBox::copyPathRelativeToConfig(const Utf8Str &strSource,
2978 Utf8Str &strTarget)
2979{
2980 AutoCaller autoCaller(this);
2981 AssertComRCReturnVoid(autoCaller.rc());
2982
2983 // no need to lock since mHomeDir is const
2984
2985 // use strTarget as a temporary buffer to hold the machine settings dir
2986 strTarget = m->strHomeDir;
2987 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
2988 // is relative: then append what's left
2989 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
2990 else
2991 // is not relative: then overwrite
2992 strTarget = strSource;
2993}
2994
2995// private methods
2996/////////////////////////////////////////////////////////////////////////////
2997
2998/**
2999 * Checks if there is a hard disk, DVD or floppy image with the given ID or
3000 * location already registered.
3001 *
3002 * On return, sets @a aConflict to the string describing the conflicting medium,
3003 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3004 * either case. A failure is unexpected.
3005 *
3006 * @param aId UUID to check.
3007 * @param aLocation Location to check.
3008 * @param aConflict Where to return parameters of the conflicting medium.
3009 *
3010 * @note Locks the media tree and media objects for reading.
3011 */
3012HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId,
3013 const Utf8Str &aLocation,
3014 Utf8Str &aConflict,
3015 bool &fIdentical)
3016{
3017 aConflict.setNull();
3018
3019 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
3020
3021 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3022
3023 HRESULT rc = S_OK;
3024
3025 aConflict.setNull();
3026 fIdentical = false;
3027
3028 ComObjPtr<Medium> pMediumFound;
3029 const char *pcszType = NULL;
3030
3031 if (!aId.isEmpty())
3032 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
3033 if (FAILED(rc) && !aLocation.isEmpty())
3034 rc = findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
3035 if (SUCCEEDED(rc))
3036 pcszType = tr("hard disk");
3037
3038 if (!pcszType)
3039 {
3040 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
3041 if (SUCCEEDED(rc))
3042 pcszType = tr("CD/DVD image");
3043 }
3044
3045 if (!pcszType)
3046 {
3047 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
3048 if (SUCCEEDED(rc))
3049 pcszType = tr("floppy image");
3050 }
3051
3052 if (pcszType && pMediumFound)
3053 {
3054 /* Note: no AutoCaller since bound to this */
3055 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
3056
3057 Utf8Str strLocFound = pMediumFound->getLocationFull();
3058 Guid idFound = pMediumFound->getId();
3059
3060 if ( (strLocFound == aLocation)
3061 && (idFound == aId)
3062 )
3063 fIdentical = true;
3064
3065 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
3066 pcszType,
3067 strLocFound.c_str(),
3068 idFound.raw());
3069 }
3070
3071 return S_OK;
3072}
3073
3074/**
3075 * Called from Machine::prepareSaveSettings() when it has detected
3076 * that a machine has been renamed. Such renames will require
3077 * updating the global media registry during the
3078 * VirtualBox::saveSettings() that follows later.
3079*
3080 * When a machine is renamed, there may well be media (in particular,
3081 * diff images for snapshots) in the global registry that will need
3082 * to have their paths updated. Before 3.2, Machine::saveSettings
3083 * used to call VirtualBox::saveSettings implicitly, which was both
3084 * unintuitive and caused locking order problems. Now, we remember
3085 * such pending name changes with this method so that
3086 * VirtualBox::saveSettings() can process them properly.
3087 */
3088void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3089 const Utf8Str &strNewConfigDir)
3090{
3091 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3092
3093 Data::PendingMachineRename pmr;
3094 pmr.strConfigDirOld = strOldConfigDir;
3095 pmr.strConfigDirNew = strNewConfigDir;
3096 m->llPendingMachineRenames.push_back(pmr);
3097}
3098
3099/**
3100 * Goes through all known media (hard disks, floppies and DVDs) and saves
3101 * those into the given settings::MediaRegistry structures whose registry
3102 * ID match the given UUID.
3103 *
3104 * Before actually writing to the structures, all media paths (not just the
3105 * ones for the given registry) are updated if machines have been renamed
3106 * since the last call.
3107 *
3108 * This gets called from two contexts:
3109 *
3110 * -- VirtualBox::saveSettings() with the UUID of the global registry
3111 * (VirtualBox::Data.uuidRegistry); this will save those media
3112 * which had been loaded from the global registry or have been
3113 * attached to a "legacy" machine which can't save its own registry;
3114 *
3115 * -- Machine::saveSettings() with the UUID of a machine, if a medium
3116 * has been attached to a machine created with VirtualBox 4.0 or later.
3117 *
3118 * Media which have only been temporarily opened without having been
3119 * attached to a machine have a NULL registry UUID and therefore don't
3120 * get saved.
3121 *
3122 * This locks the media tree. Throws HRESULT on errors!
3123 *
3124 * @param mediaRegistry Settings structure to fill.
3125 * @param uuidRegistry The UUID of the media registry; either a machine UUID (if machine registry) or the UUID of the global registry.
3126 * @param hardDiskFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
3127 */
3128void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
3129 const Guid &uuidRegistry,
3130 const Utf8Str &strMachineFolder)
3131{
3132 // lock all media for the following; use a write lock because we're
3133 // modifying the PendingMachineRenamesList, which is protected by this
3134 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3135
3136 // if a machine was renamed, then we'll need to refresh media paths
3137 if (m->llPendingMachineRenames.size())
3138 {
3139 // make a single list from the three media lists so we don't need three loops
3140 MediaList llAllMedia;
3141 // with hard disks, we must use the map, not the list, because the list only has base images
3142 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3143 llAllMedia.push_back(it->second);
3144 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
3145 llAllMedia.push_back(*it);
3146 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
3147 llAllMedia.push_back(*it);
3148
3149 for (MediaList::iterator it = llAllMedia.begin();
3150 it != llAllMedia.end();
3151 ++it)
3152 {
3153 Medium *pMedium = *it;
3154 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3155 it2 != m->llPendingMachineRenames.end();
3156 ++it2)
3157 {
3158 const Data::PendingMachineRename &pmr = *it2;
3159 pMedium->updatePath(pmr.strConfigDirOld,
3160 pmr.strConfigDirNew);
3161 }
3162 }
3163 // done, don't do it again until we have more machine renames
3164 m->llPendingMachineRenames.clear();
3165 }
3166
3167 HRESULT rc;
3168 // hard disks
3169 mediaRegistry.llHardDisks.clear();
3170 for (MediaList::const_iterator it = m->allHardDisks.begin();
3171 it != m->allHardDisks.end();
3172 ++it)
3173 {
3174 Medium *pMedium = *it;
3175 if (pMedium->isInRegistry(uuidRegistry))
3176 {
3177 settings::Medium med;
3178 rc = pMedium->saveSettings(med, strMachineFolder); // this recurses into its children
3179 if (FAILED(rc)) throw rc;
3180 mediaRegistry.llHardDisks.push_back(med);
3181 }
3182 }
3183
3184 // CD/DVD images
3185 mediaRegistry.llDvdImages.clear();
3186 for (MediaList::const_iterator it = m->allDVDImages.begin();
3187 it != m->allDVDImages.end();
3188 ++it)
3189 {
3190 Medium *pMedium = *it;
3191 if (pMedium->isInRegistry(uuidRegistry))
3192 {
3193 settings::Medium med;
3194 rc = pMedium->saveSettings(med, strMachineFolder);
3195 if (FAILED(rc)) throw rc;
3196 mediaRegistry.llDvdImages.push_back(med);
3197 }
3198 }
3199
3200 // floppy images
3201 mediaRegistry.llFloppyImages.clear();
3202 for (MediaList::const_iterator it = m->allFloppyImages.begin();
3203 it != m->allFloppyImages.end();
3204 ++it)
3205 {
3206 Medium *pMedium = *it;
3207 if (pMedium->isInRegistry(uuidRegistry))
3208 {
3209 settings::Medium med;
3210 rc = pMedium->saveSettings(med, strMachineFolder);
3211 if (FAILED(rc)) throw rc;
3212 mediaRegistry.llFloppyImages.push_back(med);
3213 }
3214 }
3215}
3216
3217/**
3218 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
3219 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
3220 * places internally when settings need saving.
3221 *
3222 * @note Caller must have locked the VirtualBox object for writing and must not hold any
3223 * other locks since this locks all kinds of member objects and trees temporarily,
3224 * which could cause conflicts.
3225 */
3226HRESULT VirtualBox::saveSettings()
3227{
3228 AutoCaller autoCaller(this);
3229 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3230
3231 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3232 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3233
3234 HRESULT rc = S_OK;
3235
3236 try
3237 {
3238 // machines
3239 m->pMainConfigFile->llMachines.clear();
3240 {
3241 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3242 for (MachinesOList::iterator it = m->allMachines.begin();
3243 it != m->allMachines.end();
3244 ++it)
3245 {
3246 Machine *pMachine = *it;
3247 // save actual machine registry entry
3248 settings::MachineRegistryEntry mre;
3249 rc = pMachine->saveRegistryEntry(mre);
3250 m->pMainConfigFile->llMachines.push_back(mre);
3251 }
3252 }
3253
3254 saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
3255 m->uuidMediaRegistry, // global media registry ID
3256 Utf8Str::Empty); // strMachineFolder
3257
3258 m->pMainConfigFile->llDhcpServers.clear();
3259 {
3260 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3261 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
3262 it != m->allDHCPServers.end();
3263 ++it)
3264 {
3265 settings::DHCPServer d;
3266 rc = (*it)->saveSettings(d);
3267 if (FAILED(rc)) throw rc;
3268 m->pMainConfigFile->llDhcpServers.push_back(d);
3269 }
3270 }
3271
3272 // leave extra data alone, it's still in the config file
3273
3274 // host data (USB filters)
3275 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3276 if (FAILED(rc)) throw rc;
3277
3278 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3279 if (FAILED(rc)) throw rc;
3280
3281 // and write out the XML, still under the lock
3282 m->pMainConfigFile->write(m->strSettingsFilePath);
3283 }
3284 catch (HRESULT err)
3285 {
3286 /* we assume that error info is set by the thrower */
3287 rc = err;
3288 }
3289 catch (...)
3290 {
3291 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3292 }
3293
3294 return rc;
3295}
3296
3297/**
3298 * Helper to register the machine.
3299 *
3300 * When called during VirtualBox startup, adds the given machine to the
3301 * collection of registered machines. Otherwise tries to mark the machine
3302 * as registered, and, if succeeded, adds it to the collection and
3303 * saves global settings.
3304 *
3305 * @note The caller must have added itself as a caller of the @a aMachine
3306 * object if calls this method not on VirtualBox startup.
3307 *
3308 * @param aMachine machine to register
3309 *
3310 * @note Locks objects!
3311 */
3312HRESULT VirtualBox::registerMachine(Machine *aMachine)
3313{
3314 ComAssertRet(aMachine, E_INVALIDARG);
3315
3316 AutoCaller autoCaller(this);
3317 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3318
3319 HRESULT rc = S_OK;
3320
3321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3322
3323 {
3324 ComObjPtr<Machine> pMachine;
3325 rc = findMachine(aMachine->getId(),
3326 true /* fPermitInaccessible */,
3327 false /* aDoSetError */,
3328 &pMachine);
3329 if (SUCCEEDED(rc))
3330 {
3331 /* sanity */
3332 AutoLimitedCaller machCaller(pMachine);
3333 AssertComRC(machCaller.rc());
3334
3335 return setError(E_INVALIDARG,
3336 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
3337 aMachine->getId().raw(),
3338 pMachine->getSettingsFileFull().c_str());
3339 }
3340
3341 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3342 rc = S_OK;
3343 }
3344
3345 if (autoCaller.state() != InInit)
3346 {
3347 rc = aMachine->prepareRegister();
3348 if (FAILED(rc)) return rc;
3349 }
3350
3351 /* add to the collection of registered machines */
3352 m->allMachines.addChild(aMachine);
3353
3354 if (autoCaller.state() != InInit)
3355 rc = saveSettings();
3356
3357 return rc;
3358}
3359
3360/**
3361 * Remembers the given hard disk by storing it in either the global hard disk registry
3362 * or a machine one.
3363 *
3364 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3365 *
3366 * @param aHardDisk Hard disk object to remember.
3367 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3368 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs of media registries that need saving.
3369 * @return
3370 */
3371HRESULT VirtualBox::registerHardDisk(Medium *pMedium,
3372 GuidList *pllRegistriesThatNeedSaving)
3373{
3374 AssertReturn(pMedium != NULL, E_INVALIDARG);
3375
3376 AutoCaller autoCaller(this);
3377 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3378
3379 AutoCaller hardDiskCaller(pMedium);
3380 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3381
3382 // caller must hold the media tree write lock
3383 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3384
3385 Guid id;
3386 Utf8Str strLocationFull;
3387 ComObjPtr<Medium> pParent;
3388 {
3389 AutoReadLock hardDiskLock(pMedium COMMA_LOCKVAL_SRC_POS);
3390 id = pMedium->getId();
3391 strLocationFull = pMedium->getLocationFull();
3392 pParent = pMedium->getParent();
3393 }
3394
3395 HRESULT rc;
3396
3397 Utf8Str strConflict;
3398 bool fIdentical;
3399 rc = checkMediaForConflicts(id,
3400 strLocationFull,
3401 strConflict,
3402 fIdentical);
3403 if (FAILED(rc)) return rc;
3404
3405 if (!fIdentical)
3406 {
3407 if (strConflict.length())
3408 return setError(E_INVALIDARG,
3409 tr("Cannot register the hard disk '%s' {%RTuuid} because a %s already exists"),
3410 strLocationFull.c_str(),
3411 id.raw(),
3412 strConflict.c_str(),
3413 m->strSettingsFilePath.c_str());
3414
3415 // store base (root) hard disks in the list
3416 if (pParent.isNull())
3417 m->allHardDisks.getList().push_back(pMedium);
3418 // access the list directly because we already locked the list above
3419
3420 // store all hard disks (even differencing images) in the map
3421 m->mapHardDisks[id] = pMedium;
3422
3423 if (pllRegistriesThatNeedSaving)
3424 pMedium->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3425 }
3426
3427 return rc;
3428}
3429
3430/**
3431 * Removes the given hard disk from the hard disk registry.
3432 *
3433 * @param aHardDisk Hard disk object to remove.
3434 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3435 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3436 *
3437 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3438 */
3439HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3440 GuidList *pllRegistriesThatNeedSaving)
3441{
3442 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3443
3444 AutoCaller autoCaller(this);
3445 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3446
3447 AutoCaller hardDiskCaller(aHardDisk);
3448 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3449
3450 // caller must hold the media tree write lock
3451 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3452
3453 Guid id;
3454 ComObjPtr<Medium> pParent;
3455 {
3456 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3457 id = aHardDisk->getId();
3458 pParent = aHardDisk->getParent();
3459 }
3460
3461 // remove base (root) hard disks from the list
3462 if (pParent.isNull())
3463 m->allHardDisks.getList().remove(aHardDisk);
3464 // access the list directly because caller must have locked the list
3465
3466 // remove all hard disks (even differencing images) from map
3467 size_t cnt = m->mapHardDisks.erase(id);
3468 Assert(cnt == 1);
3469 NOREF(cnt);
3470
3471 if (pllRegistriesThatNeedSaving)
3472 aHardDisk->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3473
3474 return S_OK;
3475}
3476
3477/**
3478 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3479 *
3480 * @param argImage Image object to remember.
3481 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3482 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3483 *
3484 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3485 */
3486HRESULT VirtualBox::registerImage(Medium *pMedium,
3487 DeviceType_T argType,
3488 GuidList *pllRegistriesThatNeedSaving)
3489{
3490 AssertReturn(pMedium != NULL, E_INVALIDARG);
3491
3492 AutoCaller autoCaller(this);
3493 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3494
3495 AutoCaller imageCaller(pMedium);
3496 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3497
3498 // caller must hold the media tree write lock
3499 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3500
3501 Guid id;
3502 Utf8Str strLocationFull;
3503 ComObjPtr<Medium> pParent;
3504 {
3505 AutoReadLock al(pMedium COMMA_LOCKVAL_SRC_POS);
3506 id = pMedium->getId();
3507 strLocationFull = pMedium->getLocationFull();
3508 pParent = pMedium->getParent();
3509 }
3510
3511 // work on DVDs or floppies list?
3512 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3513
3514 HRESULT rc;
3515 // lock the images lists (list + map) while checking for conflicts
3516 AutoWriteLock al(all.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3517
3518 Utf8Str strConflict;
3519 bool fIdentical;
3520 rc = checkMediaForConflicts(id,
3521 strLocationFull,
3522 strConflict,
3523 fIdentical);
3524 if (FAILED(rc)) return rc;
3525
3526 if (!fIdentical)
3527 {
3528 if (strConflict.length())
3529 return setError(VBOX_E_INVALID_OBJECT_STATE,
3530 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists"),
3531 strLocationFull.c_str(),
3532 id.raw(),
3533 strConflict.c_str());
3534
3535 // add to the collection
3536 all.getList().push_back(pMedium);
3537 // access the list directly because we already locked the list above
3538
3539 if (pllRegistriesThatNeedSaving)
3540 pMedium->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3541 }
3542
3543 return rc;
3544}
3545
3546/**
3547 * Removes the given image from the CD/DVD or floppy image registry.
3548 *
3549 * @param argImage Image object to remove.
3550 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3551 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3552 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3553 *
3554 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3555 */
3556HRESULT VirtualBox::unregisterImage(Medium *argImage,
3557 DeviceType_T argType,
3558 GuidList *pllRegistriesThatNeedSaving)
3559{
3560 AssertReturn(argImage != NULL, E_INVALIDARG);
3561
3562 AutoCaller autoCaller(this);
3563 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3564
3565 AutoCaller imageCaller(argImage);
3566 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3567
3568 // caller must hold the media tree write lock
3569 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3570
3571 Guid id;
3572 ComObjPtr<Medium> pParent;
3573 {
3574 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3575 id = argImage->getId();
3576 pParent = argImage->getParent();
3577 }
3578
3579 // work on DVDs or floppies list?
3580 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3581
3582 // access the list directly because the caller must have requested the lock
3583 all.getList().remove(argImage);
3584
3585 HRESULT rc = S_OK;
3586
3587 if (pllRegistriesThatNeedSaving)
3588 argImage->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3589
3590 return rc;
3591}
3592
3593/**
3594 * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
3595 * with children appearing before their parents.
3596 * @param llMedia
3597 * @param pMedium
3598 */
3599void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
3600{
3601 // recurse first, then add ourselves; this way children end up on the
3602 // list before their parents
3603
3604 const MediaList &llChildren = pMedium->getChildren();
3605 for (MediaList::const_iterator it = llChildren.begin();
3606 it != llChildren.end();
3607 ++it)
3608 {
3609 Medium *pChild = *it;
3610 pushMediumToListWithChildren(llMedia, pChild);
3611 }
3612
3613 Log(("Pushing medium %RTuuid\n", pMedium->getId().raw()));
3614 llMedia.push_back(pMedium);
3615}
3616
3617/**
3618 * Unregisters all Medium objects which belong to the given machine registry.
3619 * Gets called from Machine::uninit() just before the machine object dies
3620 * and must only be called with a machine UUID as the registry ID.
3621 *
3622 * Locks the media tree.
3623 *
3624 * @param uuidMachine Medium registry ID (always a machine UUID)
3625 * @return
3626 */
3627HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine)
3628{
3629 Assert(!uuidMachine.isEmpty());
3630
3631 LogFlowFuncEnter();
3632
3633 AutoCaller autoCaller(this);
3634 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3635
3636 MediaList llMedia2Close;
3637
3638 {
3639 AutoWriteLock mlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3640 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
3641 it != m->allHardDisks.getList().end();
3642 ++it)
3643 {
3644 ComObjPtr<Medium> pMedium = *it;
3645
3646 if (pMedium->isInRegistry(uuidMachine))
3647 // recursively with children first
3648 pushMediumToListWithChildren(llMedia2Close, pMedium);
3649 }
3650 }
3651
3652 for (MediaList::iterator it = llMedia2Close.begin();
3653 it != llMedia2Close.end();
3654 ++it)
3655 {
3656 ComObjPtr<Medium> pMedium = *it;
3657 Log(("Closing medium %RTuuid\n", pMedium->getId().raw()));
3658 AutoCaller mac(pMedium);
3659 pMedium->close(NULL /* pfNeedsGlobalSaveSettings*/, mac);
3660 }
3661
3662 LogFlowFuncLeave();
3663
3664 return S_OK;
3665}
3666
3667/**
3668 * Removes the given machine object from the internal list of registered machines.
3669 * Called from Machine::Unregister().
3670 * @param pMachine
3671 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
3672 * @return
3673 */
3674HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
3675 const Guid &id)
3676{
3677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3678
3679 // remove from the collection of registered machines
3680 m->allMachines.removeChild(pMachine);
3681
3682 // save the global registry
3683 HRESULT rc = saveSettings();
3684
3685 alock.release();
3686
3687 /* fire an event */
3688 onMachineRegistered(id, FALSE);
3689
3690 return rc;
3691}
3692
3693/**
3694 * Adds uuid to llRegistriesThatNeedSaving unless it's already on the list.
3695 *
3696 * @todo maybe there's something in libstdc++ for this
3697 *
3698 * @param llRegistriesThatNeedSaving
3699 * @param uuid
3700 */
3701void VirtualBox::addGuidToListUniquely(GuidList &llRegistriesThatNeedSaving,
3702 Guid uuid)
3703{
3704 for (GuidList::const_iterator it = llRegistriesThatNeedSaving.begin();
3705 it != llRegistriesThatNeedSaving.end();
3706 ++it)
3707 {
3708 if (*it == uuid)
3709 // uuid is already in list:
3710 return;
3711 }
3712
3713 llRegistriesThatNeedSaving.push_back(uuid);
3714}
3715
3716HRESULT VirtualBox::saveRegistries(const GuidList &llRegistriesThatNeedSaving)
3717{
3718 bool fNeedsGlobalSettings = false;
3719 HRESULT rc = S_OK;
3720
3721 for (GuidList::const_iterator it = llRegistriesThatNeedSaving.begin();
3722 it != llRegistriesThatNeedSaving.end();
3723 ++it)
3724 {
3725 const Guid &uuid = *it;
3726
3727 if (uuid == getGlobalRegistryId())
3728 fNeedsGlobalSettings = true;
3729 else
3730 {
3731 // should be machine ID then:
3732 ComObjPtr<Machine> pMachine;
3733 rc = findMachine(uuid,
3734 false /* fPermitInaccessible */,
3735 false /* aSetError */,
3736 &pMachine);
3737 if (SUCCEEDED(rc))
3738 {
3739 AutoCaller autoCaller(pMachine);
3740 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3741 AutoWriteLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
3742 rc = pMachine->saveSettings(&fNeedsGlobalSettings,
3743 Machine::SaveS_Force); // caller said save, so stop arguing
3744 }
3745
3746 if (FAILED(rc))
3747 return rc;
3748 }
3749 }
3750
3751 if (fNeedsGlobalSettings)
3752 {
3753 AutoWriteLock vlock(this COMMA_LOCKVAL_SRC_POS);
3754 rc = saveSettings();
3755 }
3756
3757 return S_OK;
3758}
3759
3760/**
3761 * Creates the path to the specified file according to the path information
3762 * present in the file name.
3763 *
3764 * Note that the given file name must contain the full path otherwise the
3765 * extracted relative path will be created based on the current working
3766 * directory which is normally unknown.
3767 *
3768 * @param aFileName Full file name which path needs to be created.
3769 *
3770 * @return Extended error information on failure to create the path.
3771 */
3772/* static */
3773HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3774{
3775 Utf8Str strDir(strFileName);
3776 strDir.stripFilename();
3777 if (!RTDirExists(strDir.c_str()))
3778 {
3779 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3780 if (RT_FAILURE(vrc))
3781 return setErrorStatic(E_FAIL,
3782 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
3783 strDir.c_str(),
3784 vrc));
3785 }
3786
3787 return S_OK;
3788}
3789
3790/**
3791 * Handles unexpected exceptions by turning them into COM errors in release
3792 * builds or by hitting a breakpoint in the release builds.
3793 *
3794 * Usage pattern:
3795 * @code
3796 try
3797 {
3798 // ...
3799 }
3800 catch (LaLalA)
3801 {
3802 // ...
3803 }
3804 catch (...)
3805 {
3806 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3807 }
3808 * @endcode
3809 *
3810 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3811 */
3812/* static */
3813HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3814{
3815 try
3816 {
3817 /* re-throw the current exception */
3818 throw;
3819 }
3820 catch (const iprt::Error &err) // includes all XML exceptions
3821 {
3822 return setErrorStatic(E_FAIL,
3823 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
3824 err.what(),
3825 pszFile, iLine, pszFunction).c_str());
3826 }
3827 catch (const std::exception &err)
3828 {
3829 return setErrorStatic(E_FAIL,
3830 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3831 err.what(), typeid(err).name(),
3832 pszFile, iLine, pszFunction).c_str());
3833 }
3834 catch (...)
3835 {
3836 return setErrorStatic(E_FAIL,
3837 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
3838 pszFile, iLine, pszFunction).c_str());
3839 }
3840
3841 /* should not get here */
3842 AssertFailed();
3843 return E_FAIL;
3844}
3845
3846const Utf8Str& VirtualBox::settingsFilePath()
3847{
3848 return m->strSettingsFilePath;
3849}
3850
3851/**
3852 * Returns the lock handle which protects the media trees (hard disks,
3853 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3854 * are no longer protected by the VirtualBox lock, but by this more
3855 * specialized lock. Mind the locking order: always request this lock
3856 * after the VirtualBox object lock but before the locks of the media
3857 * objects contained in these lists. See AutoLock.h.
3858 */
3859RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3860{
3861 return m->lockMedia;
3862}
3863
3864/**
3865 * Thread function that watches the termination of all client processes
3866 * that have opened sessions using IMachine::LockMachine()
3867 */
3868// static
3869DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3870{
3871 LogFlowFuncEnter();
3872
3873 VirtualBox *that = (VirtualBox*)pvUser;
3874 Assert(that);
3875
3876 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3877 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3878
3879 SessionMachineVector machines;
3880 MachineVector spawnedMachines;
3881
3882 size_t cnt = 0;
3883 size_t cntSpawned = 0;
3884
3885#if defined(RT_OS_WINDOWS)
3886
3887 HRESULT hrc = CoInitializeEx(NULL,
3888 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3889 COINIT_SPEED_OVER_MEMORY);
3890 AssertComRC(hrc);
3891
3892 /// @todo (dmik) processes reaping!
3893
3894 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3895 handles[0] = that->m->updateReq;
3896
3897 do
3898 {
3899 AutoCaller autoCaller(that);
3900 /* VirtualBox has been early uninitialized, terminate */
3901 if (!autoCaller.isOk())
3902 break;
3903
3904 do
3905 {
3906 /* release the caller to let uninit() ever proceed */
3907 autoCaller.release();
3908
3909 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3910 handles,
3911 FALSE,
3912 INFINITE);
3913
3914 /* Restore the caller before using VirtualBox. If it fails, this
3915 * means VirtualBox is being uninitialized and we must terminate. */
3916 autoCaller.add();
3917 if (!autoCaller.isOk())
3918 break;
3919
3920 bool update = false;
3921
3922 if (rc == WAIT_OBJECT_0)
3923 {
3924 /* update event is signaled */
3925 update = true;
3926 }
3927 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3928 {
3929 /* machine mutex is released */
3930 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3931 update = true;
3932 }
3933 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3934 {
3935 /* machine mutex is abandoned due to client process termination */
3936 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3937 update = true;
3938 }
3939 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3940 {
3941 /* spawned VM process has terminated (normally or abnormally) */
3942 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
3943 checkForSpawnFailure();
3944 update = true;
3945 }
3946
3947 if (update)
3948 {
3949 /* close old process handles */
3950 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
3951 CloseHandle(handles[i]);
3952
3953 // lock the machines list for reading
3954 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3955
3956 /* obtain a new set of opened machines */
3957 cnt = 0;
3958 machines.clear();
3959
3960 for (MachinesOList::iterator it = that->m->allMachines.begin();
3961 it != that->m->allMachines.end();
3962 ++it)
3963 {
3964 /// @todo handle situations with more than 64 objects
3965 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3966 ("MAXIMUM_WAIT_OBJECTS reached"));
3967
3968 ComObjPtr<SessionMachine> sm;
3969 HANDLE ipcSem;
3970 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
3971 {
3972 machines.push_back(sm);
3973 handles[1 + cnt] = ipcSem;
3974 ++cnt;
3975 }
3976 }
3977
3978 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
3979
3980 /* obtain a new set of spawned machines */
3981 cntSpawned = 0;
3982 spawnedMachines.clear();
3983
3984 for (MachinesOList::iterator it = that->m->allMachines.begin();
3985 it != that->m->allMachines.end();
3986 ++it)
3987 {
3988 /// @todo handle situations with more than 64 objects
3989 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
3990 ("MAXIMUM_WAIT_OBJECTS reached"));
3991
3992 RTPROCESS pid;
3993 if ((*it)->isSessionSpawning(&pid))
3994 {
3995 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
3996 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
3997 pid, GetLastError()));
3998 if (rc == 0)
3999 {
4000 spawnedMachines.push_back(*it);
4001 handles[1 + cnt + cntSpawned] = ph;
4002 ++cntSpawned;
4003 }
4004 }
4005 }
4006
4007 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4008
4009 // machines lock unwinds here
4010 }
4011 }
4012 while (true);
4013 }
4014 while (0);
4015
4016 /* close old process handles */
4017 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4018 CloseHandle(handles[i]);
4019
4020 /* release sets of machines if any */
4021 machines.clear();
4022 spawnedMachines.clear();
4023
4024 ::CoUninitialize();
4025
4026#elif defined(RT_OS_OS2)
4027
4028 /// @todo (dmik) processes reaping!
4029
4030 /* according to PMREF, 64 is the maximum for the muxwait list */
4031 SEMRECORD handles[64];
4032
4033 HMUX muxSem = NULLHANDLE;
4034
4035 do
4036 {
4037 AutoCaller autoCaller(that);
4038 /* VirtualBox has been early uninitialized, terminate */
4039 if (!autoCaller.isOk())
4040 break;
4041
4042 do
4043 {
4044 /* release the caller to let uninit() ever proceed */
4045 autoCaller.release();
4046
4047 int vrc = RTSemEventWait(that->m->updateReq, 500);
4048
4049 /* Restore the caller before using VirtualBox. If it fails, this
4050 * means VirtualBox is being uninitialized and we must terminate. */
4051 autoCaller.add();
4052 if (!autoCaller.isOk())
4053 break;
4054
4055 bool update = false;
4056 bool updateSpawned = false;
4057
4058 if (RT_SUCCESS(vrc))
4059 {
4060 /* update event is signaled */
4061 update = true;
4062 updateSpawned = true;
4063 }
4064 else
4065 {
4066 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4067 ("RTSemEventWait returned %Rrc\n", vrc));
4068
4069 /* are there any mutexes? */
4070 if (cnt > 0)
4071 {
4072 /* figure out what's going on with machines */
4073
4074 unsigned long semId = 0;
4075 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
4076 SEM_IMMEDIATE_RETURN, &semId);
4077
4078 if (arc == NO_ERROR)
4079 {
4080 /* machine mutex is normally released */
4081 Assert(semId >= 0 && semId < cnt);
4082 if (semId >= 0 && semId < cnt)
4083 {
4084#if 0//def DEBUG
4085 {
4086 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4087 LogFlowFunc(("released mutex: machine='%ls'\n",
4088 machines[semId]->name().raw()));
4089 }
4090#endif
4091 machines[semId]->checkForDeath();
4092 }
4093 update = true;
4094 }
4095 else if (arc == ERROR_SEM_OWNER_DIED)
4096 {
4097 /* machine mutex is abandoned due to client process
4098 * termination; find which mutex is in the Owner Died
4099 * state */
4100 for (size_t i = 0; i < cnt; ++ i)
4101 {
4102 PID pid; TID tid;
4103 unsigned long reqCnt;
4104 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
4105 if (arc == ERROR_SEM_OWNER_DIED)
4106 {
4107 /* close the dead mutex as asked by PMREF */
4108 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
4109
4110 Assert(i >= 0 && i < cnt);
4111 if (i >= 0 && i < cnt)
4112 {
4113#if 0//def DEBUG
4114 {
4115 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4116 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4117 machines[i]->name().raw()));
4118 }
4119#endif
4120 machines[i]->checkForDeath();
4121 }
4122 }
4123 }
4124 update = true;
4125 }
4126 else
4127 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4128 ("DosWaitMuxWaitSem returned %d\n", arc));
4129 }
4130
4131 /* are there any spawning sessions? */
4132 if (cntSpawned > 0)
4133 {
4134 for (size_t i = 0; i < cntSpawned; ++ i)
4135 updateSpawned |= (spawnedMachines[i])->
4136 checkForSpawnFailure();
4137 }
4138 }
4139
4140 if (update || updateSpawned)
4141 {
4142 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4143
4144 if (update)
4145 {
4146 /* close the old muxsem */
4147 if (muxSem != NULLHANDLE)
4148 ::DosCloseMuxWaitSem(muxSem);
4149
4150 /* obtain a new set of opened machines */
4151 cnt = 0;
4152 machines.clear();
4153
4154 for (MachinesOList::iterator it = that->m->allMachines.begin();
4155 it != that->m->allMachines.end(); ++ it)
4156 {
4157 /// @todo handle situations with more than 64 objects
4158 AssertMsg(cnt <= 64 /* according to PMREF */,
4159 ("maximum of 64 mutex semaphores reached (%d)",
4160 cnt));
4161
4162 ComObjPtr<SessionMachine> sm;
4163 HMTX ipcSem;
4164 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4165 {
4166 machines.push_back(sm);
4167 handles[cnt].hsemCur = (HSEM)ipcSem;
4168 handles[cnt].ulUser = cnt;
4169 ++ cnt;
4170 }
4171 }
4172
4173 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4174
4175 if (cnt > 0)
4176 {
4177 /* create a new muxsem */
4178 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4179 handles,
4180 DCMW_WAIT_ANY);
4181 AssertMsg(arc == NO_ERROR,
4182 ("DosCreateMuxWaitSem returned %d\n", arc));
4183 NOREF(arc);
4184 }
4185 }
4186
4187 if (updateSpawned)
4188 {
4189 /* obtain a new set of spawned machines */
4190 spawnedMachines.clear();
4191
4192 for (MachinesOList::iterator it = that->m->allMachines.begin();
4193 it != that->m->allMachines.end(); ++ it)
4194 {
4195 if ((*it)->isSessionSpawning())
4196 spawnedMachines.push_back(*it);
4197 }
4198
4199 cntSpawned = spawnedMachines.size();
4200 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4201 }
4202 }
4203 }
4204 while (true);
4205 }
4206 while (0);
4207
4208 /* close the muxsem */
4209 if (muxSem != NULLHANDLE)
4210 ::DosCloseMuxWaitSem(muxSem);
4211
4212 /* release sets of machines if any */
4213 machines.clear();
4214 spawnedMachines.clear();
4215
4216#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4217
4218 bool update = false;
4219 bool updateSpawned = false;
4220
4221 do
4222 {
4223 AutoCaller autoCaller(that);
4224 if (!autoCaller.isOk())
4225 break;
4226
4227 do
4228 {
4229 /* release the caller to let uninit() ever proceed */
4230 autoCaller.release();
4231
4232 int rc = RTSemEventWait(that->m->updateReq, 500);
4233
4234 /*
4235 * Restore the caller before using VirtualBox. If it fails, this
4236 * means VirtualBox is being uninitialized and we must terminate.
4237 */
4238 autoCaller.add();
4239 if (!autoCaller.isOk())
4240 break;
4241
4242 if (RT_SUCCESS(rc) || update || updateSpawned)
4243 {
4244 /* RT_SUCCESS(rc) means an update event is signaled */
4245
4246 // lock the machines list for reading
4247 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4248
4249 if (RT_SUCCESS(rc) || update)
4250 {
4251 /* obtain a new set of opened machines */
4252 machines.clear();
4253
4254 for (MachinesOList::iterator it = that->m->allMachines.begin();
4255 it != that->m->allMachines.end();
4256 ++it)
4257 {
4258 ComObjPtr<SessionMachine> sm;
4259 if ((*it)->isSessionOpenOrClosing(sm))
4260 machines.push_back(sm);
4261 }
4262
4263 cnt = machines.size();
4264 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4265 }
4266
4267 if (RT_SUCCESS(rc) || updateSpawned)
4268 {
4269 /* obtain a new set of spawned machines */
4270 spawnedMachines.clear();
4271
4272 for (MachinesOList::iterator it = that->m->allMachines.begin();
4273 it != that->m->allMachines.end();
4274 ++it)
4275 {
4276 if ((*it)->isSessionSpawning())
4277 spawnedMachines.push_back(*it);
4278 }
4279
4280 cntSpawned = spawnedMachines.size();
4281 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4282 }
4283
4284 // machines lock unwinds here
4285 }
4286
4287 update = false;
4288 for (size_t i = 0; i < cnt; ++ i)
4289 update |= (machines[i])->checkForDeath();
4290
4291 updateSpawned = false;
4292 for (size_t i = 0; i < cntSpawned; ++ i)
4293 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4294
4295 /* reap child processes */
4296 {
4297 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4298 if (that->m->llProcesses.size())
4299 {
4300 LogFlowFunc(("UPDATE: child process count = %d\n",
4301 that->m->llProcesses.size()));
4302 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4303 while (it != that->m->llProcesses.end())
4304 {
4305 RTPROCESS pid = *it;
4306 RTPROCSTATUS status;
4307 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4308 if (vrc == VINF_SUCCESS)
4309 {
4310 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4311 pid, pid, status.iStatus,
4312 status.enmReason));
4313 it = that->m->llProcesses.erase(it);
4314 }
4315 else
4316 {
4317 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4318 pid, pid, vrc));
4319 if (vrc != VERR_PROCESS_RUNNING)
4320 {
4321 /* remove the process if it is not already running */
4322 it = that->m->llProcesses.erase(it);
4323 }
4324 else
4325 ++ it;
4326 }
4327 }
4328 }
4329 }
4330 }
4331 while (true);
4332 }
4333 while (0);
4334
4335 /* release sets of machines if any */
4336 machines.clear();
4337 spawnedMachines.clear();
4338
4339#else
4340# error "Port me!"
4341#endif
4342
4343 LogFlowFuncLeave();
4344 return 0;
4345}
4346
4347/**
4348 * Thread function that handles custom events posted using #postEvent().
4349 */
4350// static
4351DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4352{
4353 LogFlowFuncEnter();
4354
4355 AssertReturn(pvUser, VERR_INVALID_POINTER);
4356
4357 // create an event queue for the current thread
4358 EventQueue *eventQ = new EventQueue();
4359 AssertReturn(eventQ, VERR_NO_MEMORY);
4360
4361 // return the queue to the one who created this thread
4362 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4363 // signal that we're ready
4364 RTThreadUserSignal(thread);
4365
4366 while (RT_SUCCESS(eventQ->processEventQueue(RT_INDEFINITE_WAIT)))
4367 /* nothing */ ;
4368
4369 delete eventQ;
4370
4371 LogFlowFuncLeave();
4372
4373 return 0;
4374}
4375
4376
4377////////////////////////////////////////////////////////////////////////////////
4378
4379/**
4380 * Takes the current list of registered callbacks of the managed VirtualBox
4381 * instance, and calls #handleCallback() for every callback item from the
4382 * list, passing the item as an argument.
4383 *
4384 * @note Locks the managed VirtualBox object for reading but leaves the lock
4385 * before iterating over callbacks and calling their methods.
4386 */
4387void *VirtualBox::CallbackEvent::handler()
4388{
4389 if (!mVirtualBox)
4390 return NULL;
4391
4392 AutoCaller autoCaller(mVirtualBox);
4393 if (!autoCaller.isOk())
4394 {
4395 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4396 autoCaller.state()));
4397 /* We don't need mVirtualBox any more, so release it */
4398 mVirtualBox = NULL;
4399 return NULL;
4400 }
4401
4402
4403 {
4404 VBoxEventDesc evDesc;
4405 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4406
4407 evDesc.fire(/* don't wait for delivery */0);
4408 }
4409
4410 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4411 return NULL;
4412}
4413
4414//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4415//{
4416// return E_NOTIMPL;
4417//}
4418
4419STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4420{
4421 CheckComArgStrNotEmptyOrNull(aName);
4422 CheckComArgNotNull(aServer);
4423
4424 AutoCaller autoCaller(this);
4425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4426
4427 ComObjPtr<DHCPServer> dhcpServer;
4428 dhcpServer.createObject();
4429 HRESULT rc = dhcpServer->init(this, aName);
4430 if (FAILED(rc)) return rc;
4431
4432 rc = registerDHCPServer(dhcpServer, true);
4433 if (FAILED(rc)) return rc;
4434
4435 dhcpServer.queryInterfaceTo(aServer);
4436
4437 return rc;
4438}
4439
4440STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4441{
4442 CheckComArgStrNotEmptyOrNull(aName);
4443 CheckComArgNotNull(aServer);
4444
4445 AutoCaller autoCaller(this);
4446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4447
4448 HRESULT rc;
4449 Bstr bstr;
4450 ComPtr<DHCPServer> found;
4451
4452 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4453
4454 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4455 it != m->allDHCPServers.end();
4456 ++it)
4457 {
4458 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4459 if (FAILED(rc)) throw rc;
4460
4461 if (bstr == aName)
4462 {
4463 found = *it;
4464 break;
4465 }
4466 }
4467
4468 if (!found)
4469 return E_INVALIDARG;
4470
4471 return found.queryInterfaceTo(aServer);
4472}
4473
4474STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4475{
4476 CheckComArgNotNull(aServer);
4477
4478 AutoCaller autoCaller(this);
4479 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4480
4481 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4482
4483 return rc;
4484}
4485
4486/**
4487 * Remembers the given dhcp server by storing it in the hard disk registry.
4488 *
4489 * @param aDHCPServer Dhcp Server object to remember.
4490 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4491 *
4492 * When @a aSaveRegistry is @c true, this operation may fail because of the
4493 * failed #saveSettings() method it calls. In this case, the dhcp server object
4494 * will not be remembered. It is therefore the responsibility of the caller to
4495 * call this method as the last step of some action that requires registration
4496 * in order to make sure that only fully functional dhcp server objects get
4497 * registered.
4498 *
4499 * @note Locks this object for writing and @a aDHCPServer for reading.
4500 */
4501HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4502 bool aSaveRegistry /*= true*/)
4503{
4504 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4505
4506 AutoCaller autoCaller(this);
4507 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4508
4509 AutoCaller dhcpServerCaller(aDHCPServer);
4510 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4511
4512 Bstr name;
4513 HRESULT rc;
4514 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4515 if (FAILED(rc)) return rc;
4516
4517 ComPtr<IDHCPServer> existing;
4518 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
4519 if (SUCCEEDED(rc))
4520 return E_INVALIDARG;
4521
4522 rc = S_OK;
4523
4524 m->allDHCPServers.addChild(aDHCPServer);
4525
4526 if (aSaveRegistry)
4527 {
4528 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4529 rc = saveSettings();
4530 vboxLock.release();
4531
4532 if (FAILED(rc))
4533 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4534 }
4535
4536 return rc;
4537}
4538
4539/**
4540 * Removes the given hard disk from the hard disk registry.
4541 *
4542 * @param aHardDisk Hard disk object to remove.
4543 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4544 *
4545 * When @a aSaveRegistry is @c true, this operation may fail because of the
4546 * failed #saveSettings() method it calls. In this case, the hard disk object
4547 * will NOT be removed from the registry when this method returns. It is
4548 * therefore the responsibility of the caller to call this method as the first
4549 * step of some action that requires unregistration, before calling uninit() on
4550 * @a aHardDisk.
4551 *
4552 * @note Locks this object for writing and @a aHardDisk for reading.
4553 */
4554HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4555 bool aSaveRegistry /*= true*/)
4556{
4557 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4558
4559 AutoCaller autoCaller(this);
4560 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4561
4562 AutoCaller dhcpServerCaller(aDHCPServer);
4563 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4564
4565 m->allDHCPServers.removeChild(aDHCPServer);
4566
4567 HRESULT rc = S_OK;
4568
4569 if (aSaveRegistry)
4570 {
4571 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4572 rc = saveSettings();
4573 vboxLock.release();
4574
4575 if (FAILED(rc))
4576 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4577 }
4578
4579 return rc;
4580}
4581
4582STDMETHODIMP VirtualBox::VRDERegisterLibrary(IN_BSTR aName)
4583{
4584 CheckComArgStrNotEmptyOrNull(aName);
4585
4586 AutoCaller autoCaller(this);
4587 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4588
4589 Utf8Str strName(aName);
4590
4591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4592
4593 /* If the name already exists, there is nothing to do. */
4594 settings::VRDELibrariesList::const_iterator it;
4595
4596 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4597 it != m->pMainConfigFile->llVRDELibraries.end();
4598 ++it)
4599 {
4600 const Utf8Str &str = *it;
4601 if (strName == str)
4602 {
4603 break;
4604 }
4605 }
4606
4607 if (it == m->pMainConfigFile->llVRDELibraries.end())
4608 {
4609 /* Not found, add it. */
4610 m->pMainConfigFile->llVRDELibraries.push_back(strName);
4611
4612 /* Save settings on success. */
4613 HRESULT rc = saveSettings();
4614 if (FAILED(rc)) return rc;
4615 }
4616
4617 return S_OK;
4618}
4619
4620STDMETHODIMP VirtualBox::VRDEUnregisterLibrary(IN_BSTR aName)
4621{
4622 CheckComArgStrNotEmptyOrNull(aName);
4623
4624 AutoCaller autoCaller(this);
4625 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4626
4627 Utf8Str strName(aName);
4628
4629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4630
4631 settings::VRDELibrariesList::const_iterator it;
4632
4633 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4634 it != m->pMainConfigFile->llVRDELibraries.end();
4635 ++it)
4636 {
4637 const Utf8Str &str = *it;
4638 if (strName == str)
4639 {
4640 break;
4641 }
4642 }
4643
4644 if (it != m->pMainConfigFile->llVRDELibraries.end())
4645 {
4646 /* Found, remove it. */
4647 m->pMainConfigFile->llVRDELibraries.remove(strName);
4648
4649 /* Save settings on success. */
4650 HRESULT rc = saveSettings();
4651 if (FAILED(rc)) return rc;
4652 }
4653
4654 return S_OK;
4655}
4656
4657STDMETHODIMP VirtualBox::VRDEListLibraries(ComSafeArrayOut(BSTR, aNames))
4658{
4659 using namespace settings;
4660
4661 if (ComSafeArrayOutIsNull(aNames))
4662 return E_POINTER;
4663
4664 AutoCaller autoCaller(this);
4665 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4666
4667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4668
4669 com::SafeArray<BSTR> saNames(m->pMainConfigFile->llVRDELibraries.size());
4670 int i = 0;
4671 for (VRDELibrariesList::const_iterator it = m->pMainConfigFile->llVRDELibraries.begin();
4672 it != m->pMainConfigFile->llVRDELibraries.end();
4673 ++it, ++i)
4674 {
4675 const Utf8Str &strName = *it;
4676 strName.cloneTo(&saNames[i]);
4677 }
4678 saNames.detachTo(ComSafeArrayOutArg(aNames));
4679
4680 return S_OK;
4681}
4682
4683STDMETHODIMP VirtualBox::VRDEIsLibraryRegistered(IN_BSTR aName, BOOL *aRegistered)
4684{
4685 CheckComArgStrNotEmptyOrNull(aName);
4686 CheckComArgOutPointerValid(aRegistered);
4687
4688 AutoCaller autoCaller(this);
4689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4690
4691 Utf8Str strName(aName);
4692
4693 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4694
4695 settings::VRDELibrariesList::const_iterator it;
4696
4697 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4698 it != m->pMainConfigFile->llVRDELibraries.end();
4699 ++it)
4700 {
4701 const Utf8Str &str = *it;
4702 if (strName == str)
4703 {
4704 break;
4705 }
4706 }
4707
4708 *aRegistered = (it != m->pMainConfigFile->llVRDELibraries.end());
4709
4710 return S_OK;
4711}
4712
4713/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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