VirtualBox

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

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

Main: replaces callback mechanism of NAT redirect change event notification with event-driven model.

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