VirtualBox

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

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

Main: rework 'save registries' logic to ensure that all media registries get saved, not just the global VirtualBox.xml file

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 147.8 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 33921 2010-11-09 17:51:38Z 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 Locks this object for reading.
2476 */
2477ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2478{
2479 ComObjPtr<GuestOSType> type;
2480 AutoCaller autoCaller(this);
2481 AssertComRCReturn(autoCaller.rc(), type);
2482
2483 /* unknown type must always be the first */
2484 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
2485
2486 return m->allGuestOSTypes.front();
2487}
2488
2489/**
2490 * Returns the list of opened machines (machines having direct sessions opened
2491 * by client processes) and optionally the list of direct session controls.
2492 *
2493 * @param aMachines Where to put opened machines (will be empty if none).
2494 * @param aControls Where to put direct session controls (optional).
2495 *
2496 * @note The returned lists contain smart pointers. So, clear it as soon as
2497 * it becomes no more necessary to release instances.
2498 *
2499 * @note It can be possible that a session machine from the list has been
2500 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2501 * when accessing unprotected data directly.
2502 *
2503 * @note Locks objects for reading.
2504 */
2505void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
2506 InternalControlList *aControls /*= NULL*/)
2507{
2508 AutoCaller autoCaller(this);
2509 AssertComRCReturnVoid(autoCaller.rc());
2510
2511 aMachines.clear();
2512 if (aControls)
2513 aControls->clear();
2514
2515 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2516
2517 for (MachinesOList::iterator it = m->allMachines.begin();
2518 it != m->allMachines.end();
2519 ++it)
2520 {
2521 ComObjPtr<SessionMachine> sm;
2522 ComPtr<IInternalSessionControl> ctl;
2523 if ((*it)->isSessionOpen(sm, &ctl))
2524 {
2525 aMachines.push_back(sm);
2526 if (aControls)
2527 aControls->push_back(ctl);
2528 }
2529 }
2530}
2531
2532/**
2533 * Searches for a machine object with the given ID in the collection
2534 * of registered machines.
2535 *
2536 * @param aId Machine UUID to look for.
2537 * @param aPermitInaccessible If true, inaccessible machines will be found;
2538 * if false, this will fail if the given machine is inaccessible.
2539 * @param aSetError If true, set errorinfo if the machine is not found.
2540 * @param aMachine Returned machine, if found.
2541 * @return
2542 */
2543HRESULT VirtualBox::findMachine(const Guid &aId,
2544 bool fPermitInaccessible,
2545 bool aSetError,
2546 ComObjPtr<Machine> *aMachine /* = NULL */)
2547{
2548 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
2549
2550 AutoCaller autoCaller(this);
2551 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2552
2553 {
2554 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2555
2556 for (MachinesOList::iterator it = m->allMachines.begin();
2557 it != m->allMachines.end();
2558 ++it)
2559 {
2560 ComObjPtr<Machine> pMachine2 = *it;
2561
2562 if (!fPermitInaccessible)
2563 {
2564 // skip inaccessible machines
2565 AutoCaller machCaller(pMachine2);
2566 if (FAILED(machCaller.rc()))
2567 continue;
2568 }
2569
2570 if (pMachine2->getId() == aId)
2571 {
2572 rc = S_OK;
2573 if (aMachine)
2574 *aMachine = pMachine2;
2575 break;
2576 }
2577 }
2578 }
2579
2580 if (aSetError && FAILED(rc))
2581 rc = setError(rc,
2582 tr("Could not find a registered machine with UUID {%RTuuid}"),
2583 aId.raw());
2584
2585 return rc;
2586}
2587
2588/**
2589 * Searches for a Medium object with the given ID in the list of registered
2590 * hard disks.
2591 *
2592 * @param aId ID of the hard disk. Must not be empty.
2593 * @param aSetError If @c true , the appropriate error info is set in case
2594 * when the hard disk is not found.
2595 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2596 *
2597 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2598 *
2599 * @note Locks the media tree for reading.
2600 */
2601HRESULT VirtualBox::findHardDiskById(const Guid &id,
2602 bool aSetError,
2603 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2604{
2605 AssertReturn(!id.isEmpty(), E_INVALIDARG);
2606
2607 // we use the hard disks map, but it is protected by the
2608 // hard disk _list_ lock handle
2609 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2610
2611 HardDiskMap::const_iterator it = m->mapHardDisks.find(id);
2612 if (it != m->mapHardDisks.end())
2613 {
2614 if (aHardDisk)
2615 *aHardDisk = (*it).second;
2616 return S_OK;
2617 }
2618
2619 if (aSetError)
2620 return setError(VBOX_E_OBJECT_NOT_FOUND,
2621 tr("Could not find an open hard disk with UUID {%RTuuid}"),
2622 id.raw());
2623
2624 return VBOX_E_OBJECT_NOT_FOUND;
2625}
2626
2627/**
2628 * Searches for a Medium object with the given ID or location in the list of
2629 * registered hard disks. If both ID and location are specified, the first
2630 * object that matches either of them (not necessarily both) is returned.
2631 *
2632 * @param aLocation Full location specification. Must not be empty.
2633 * @param aSetError If @c true , the appropriate error info is set in case
2634 * when the hard disk is not found.
2635 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2636 *
2637 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2638 *
2639 * @note Locks the media tree for reading.
2640 */
2641HRESULT VirtualBox::findHardDiskByLocation(const Utf8Str &strLocation,
2642 bool aSetError,
2643 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2644{
2645 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
2646
2647 // we use the hard disks map, but it is protected by the
2648 // hard disk _list_ lock handle
2649 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2650
2651 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
2652 it != m->mapHardDisks.end();
2653 ++it)
2654 {
2655 const ComObjPtr<Medium> &pHD = (*it).second;
2656
2657 AutoCaller autoCaller(pHD);
2658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2659 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
2660
2661 Utf8Str strLocationFull = pHD->getLocationFull();
2662
2663 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
2664 {
2665 if (aHardDisk)
2666 *aHardDisk = pHD;
2667 return S_OK;
2668 }
2669 }
2670
2671 if (aSetError)
2672 return setError(VBOX_E_OBJECT_NOT_FOUND,
2673 tr("Could not find an open hard disk with location '%s'"),
2674 strLocation.c_str());
2675
2676 return VBOX_E_OBJECT_NOT_FOUND;
2677}
2678
2679/**
2680 * Searches for a Medium object with the given ID or location in the list of
2681 * registered DVD or floppy images, depending on the @a mediumType argument.
2682 * If both ID and file path are specified, the first object that matches either
2683 * of them (not necessarily both) is returned.
2684 *
2685 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
2686 * @param aId ID of the image file (unused when NULL).
2687 * @param aLocation Full path to the image file (unused when NULL).
2688 * @param aSetError If @c true, the appropriate error info is set in case when
2689 * the image is not found.
2690 * @param aImage Where to store the found image object (can be NULL).
2691 *
2692 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2693 *
2694 * @note Locks the media tree for reading.
2695 */
2696HRESULT VirtualBox::findDVDOrFloppyImage(DeviceType_T mediumType,
2697 const Guid *aId,
2698 const Utf8Str &aLocation,
2699 bool aSetError,
2700 ComObjPtr<Medium> *aImage /* = NULL */)
2701{
2702 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
2703
2704 Utf8Str location;
2705 if (!aLocation.isEmpty())
2706 {
2707 int vrc = calculateFullPath(aLocation, location);
2708 if (RT_FAILURE(vrc))
2709 return setError(VBOX_E_FILE_ERROR,
2710 tr("Invalid image file location '%ls' (%Rrc)"),
2711 aLocation.c_str(),
2712 vrc);
2713 }
2714
2715 MediaOList *pMediaList;
2716
2717 switch (mediumType)
2718 {
2719 case DeviceType_DVD:
2720 pMediaList = &m->allDVDImages;
2721 break;
2722
2723 case DeviceType_Floppy:
2724 pMediaList = &m->allFloppyImages;
2725 break;
2726
2727 default:
2728 return E_INVALIDARG;
2729 }
2730
2731 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
2732
2733 bool found = false;
2734
2735 for (MediaList::const_iterator it = pMediaList->begin();
2736 it != pMediaList->end();
2737 ++it)
2738 {
2739 // no AutoCaller, registered image life time is bound to this
2740 Medium *pMedium = *it;
2741 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
2742 const Utf8Str &strLocationFull = pMedium->getLocationFull();
2743
2744 found = ( aId
2745 && pMedium->getId() == *aId)
2746 || ( !aLocation.isEmpty()
2747 && RTPathCompare(location.c_str(),
2748 strLocationFull.c_str()) == 0);
2749 if (found)
2750 {
2751 if (pMedium->getDeviceType() != mediumType)
2752 {
2753 if (mediumType == DeviceType_DVD)
2754 return setError(E_INVALIDARG,
2755 "Cannot mount DVD medium '%s' as floppy", strLocationFull.c_str());
2756 else
2757 return setError(E_INVALIDARG,
2758 "Cannot mount floppy medium '%s' as DVD", strLocationFull.c_str());
2759 }
2760
2761 if (aImage)
2762 *aImage = pMedium;
2763 break;
2764 }
2765 }
2766
2767 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2768
2769 if (aSetError && !found)
2770 {
2771 if (aId)
2772 setError(rc,
2773 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
2774 aId->raw(),
2775 m->strSettingsFilePath.c_str());
2776 else
2777 setError(rc,
2778 tr("Could not find an image file with location '%ls' in the media registry ('%s')"),
2779 aLocation.c_str(),
2780 m->strSettingsFilePath.c_str());
2781 }
2782
2783 return rc;
2784}
2785
2786/**
2787 * Searches for an IMedium object that represents the given UUID.
2788 *
2789 * If the UUID is empty (indicating an empty drive), this sets pMedium
2790 * to NULL and returns S_OK.
2791 *
2792 * If the UUID refers to a host drive of the given device type, this
2793 * sets pMedium to the object from the list in IHost and returns S_OK.
2794 *
2795 * If the UUID is an image file, this sets pMedium to the object that
2796 * findDVDOrFloppyImage() returned.
2797 *
2798 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
2799 *
2800 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
2801 * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
2802 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
2803 * @param pMedium out: IMedium object found.
2804 * @return
2805 */
2806HRESULT VirtualBox::findRemoveableMedium(DeviceType_T mediumType,
2807 const Guid &uuid,
2808 bool fRefresh,
2809 ComObjPtr<Medium> &pMedium)
2810{
2811 if (uuid.isEmpty())
2812 {
2813 // that's easy
2814 pMedium.setNull();
2815 return S_OK;
2816 }
2817
2818 // first search for host drive with that UUID
2819 HRESULT rc = m->pHost->findHostDrive(mediumType,
2820 uuid,
2821 fRefresh,
2822 pMedium);
2823 if (rc == VBOX_E_OBJECT_NOT_FOUND)
2824 // then search for an image with that UUID
2825 rc = findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, true /* aSetError */, &pMedium);
2826
2827 return rc;
2828}
2829
2830HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
2831 GuestOSType*& pGuestOSType)
2832{
2833 /* Look for a GuestOSType object */
2834 AssertMsg(m->allGuestOSTypes.size() != 0,
2835 ("Guest OS types array must be filled"));
2836
2837 if (bstrOSType.isEmpty())
2838 {
2839 pGuestOSType = NULL;
2840 return S_OK;
2841 }
2842
2843 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2844 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
2845 it != m->allGuestOSTypes.end();
2846 ++it)
2847 {
2848 if ((*it)->id() == bstrOSType)
2849 {
2850 pGuestOSType = *it;
2851 return S_OK;
2852 }
2853 }
2854
2855 return setError(VBOX_E_OBJECT_NOT_FOUND,
2856 tr("Guest OS type '%ls' is invalid"),
2857 bstrOSType.raw());
2858}
2859
2860/**
2861 * Returns the constant pseudo-machine UUID that is used to identify the
2862 * global media registry.
2863 *
2864 * Starting with VirtualBox 4.0 each medium remembers in its instance data
2865 * in which media registry it is saved (if any): this can either be a machine
2866 * UUID, if it's in a per-machine media registry, or this global ID.
2867 *
2868 * This UUID is only used to identify the VirtualBox object while VirtualBox
2869 * is running. It is a compile-time constant and not saved anywhere.
2870 *
2871 * @return
2872 */
2873const Guid& VirtualBox::getGlobalRegistryId() const
2874{
2875 return m->uuidMediaRegistry;
2876}
2877
2878const ComObjPtr<Host>& VirtualBox::host() const
2879{
2880 return m->pHost;
2881}
2882
2883SystemProperties* VirtualBox::getSystemProperties() const
2884{
2885 return m->pSystemProperties;
2886}
2887
2888#ifdef VBOX_WITH_RESOURCE_USAGE_API
2889const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
2890{
2891 return m->pPerformanceCollector;
2892}
2893#endif /* VBOX_WITH_RESOURCE_USAGE_API */
2894
2895/**
2896 * Returns the default machine folder from the system properties
2897 * with proper locking.
2898 * @return
2899 */
2900void VirtualBox::getDefaultMachineFolder(Utf8Str &str) const
2901{
2902 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2903 str = m->pSystemProperties->m->strDefaultMachineFolder;
2904}
2905
2906/**
2907 * Returns the default hard disk format from the system properties
2908 * with proper locking.
2909 * @return
2910 */
2911void VirtualBox::getDefaultHardDiskFormat(Utf8Str &str) const
2912{
2913 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2914 str = m->pSystemProperties->m->strDefaultHardDiskFormat;
2915}
2916
2917const Utf8Str& VirtualBox::homeDir() const
2918{
2919 return m->strHomeDir;
2920}
2921
2922/**
2923 * Calculates the absolute path of the given path taking the VirtualBox home
2924 * directory as the current directory.
2925 *
2926 * @param aPath Path to calculate the absolute path for.
2927 * @param aResult Where to put the result (used only on success, can be the
2928 * same Utf8Str instance as passed in @a aPath).
2929 * @return IPRT result.
2930 *
2931 * @note Doesn't lock any object.
2932 */
2933int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
2934{
2935 AutoCaller autoCaller(this);
2936 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
2937
2938 /* no need to lock since mHomeDir is const */
2939
2940 char folder[RTPATH_MAX];
2941 int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
2942 strPath.c_str(),
2943 folder,
2944 sizeof(folder));
2945 if (RT_SUCCESS(vrc))
2946 aResult = folder;
2947
2948 return vrc;
2949}
2950
2951/**
2952 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
2953 * if it is a subdirectory thereof, or simply copying it otherwise.
2954 *
2955 * @param strSource Path to evalue and copy.
2956 * @param strTarget Buffer to receive target path.
2957 */
2958void VirtualBox::copyPathRelativeToConfig(const Utf8Str &strSource,
2959 Utf8Str &strTarget)
2960{
2961 AutoCaller autoCaller(this);
2962 AssertComRCReturnVoid(autoCaller.rc());
2963
2964 // no need to lock since mHomeDir is const
2965
2966 // use strTarget as a temporary buffer to hold the machine settings dir
2967 strTarget = m->strHomeDir;
2968 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
2969 // is relative: then append what's left
2970 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
2971 else
2972 // is not relative: then overwrite
2973 strTarget = strSource;
2974}
2975
2976// private methods
2977/////////////////////////////////////////////////////////////////////////////
2978
2979/**
2980 * Checks if there is a hard disk, DVD or floppy image with the given ID or
2981 * location already registered.
2982 *
2983 * On return, sets @a aConflict to the string describing the conflicting medium,
2984 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
2985 * either case. A failure is unexpected.
2986 *
2987 * @param aId UUID to check.
2988 * @param aLocation Location to check.
2989 * @param aConflict Where to return parameters of the conflicting medium.
2990 *
2991 * @note Locks the media tree and media objects for reading.
2992 */
2993HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId,
2994 const Utf8Str &aLocation,
2995 Utf8Str &aConflict,
2996 bool &fIdentical)
2997{
2998 aConflict.setNull();
2999
3000 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
3001
3002 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3003
3004 HRESULT rc = S_OK;
3005
3006 aConflict.setNull();
3007 fIdentical = false;
3008
3009 ComObjPtr<Medium> pMediumFound;
3010 const char *pcszType = NULL;
3011
3012 if (!aId.isEmpty())
3013 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
3014 if (FAILED(rc) && !aLocation.isEmpty())
3015 rc = findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
3016 if (SUCCEEDED(rc))
3017 pcszType = tr("hard disk");
3018
3019 if (!pcszType)
3020 {
3021 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
3022 if (SUCCEEDED(rc))
3023 pcszType = tr("CD/DVD image");
3024 }
3025
3026 if (!pcszType)
3027 {
3028 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
3029 if (SUCCEEDED(rc))
3030 pcszType = tr("floppy image");
3031 }
3032
3033 if (pcszType && pMediumFound)
3034 {
3035 /* Note: no AutoCaller since bound to this */
3036 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
3037
3038 Utf8Str strLocFound = pMediumFound->getLocationFull();
3039 Guid idFound = pMediumFound->getId();
3040
3041 if ( (strLocFound == aLocation)
3042 && (idFound == aId)
3043 )
3044 fIdentical = true;
3045
3046 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
3047 pcszType,
3048 strLocFound.c_str(),
3049 idFound.raw());
3050 }
3051
3052 return S_OK;
3053}
3054
3055/**
3056 * Called from Machine::prepareSaveSettings() when it has detected
3057 * that a machine has been renamed. Such renames will require
3058 * updating the global media registry during the
3059 * VirtualBox::saveSettings() that follows later.
3060*
3061 * When a machine is renamed, there may well be media (in particular,
3062 * diff images for snapshots) in the global registry that will need
3063 * to have their paths updated. Before 3.2, Machine::saveSettings
3064 * used to call VirtualBox::saveSettings implicitly, which was both
3065 * unintuitive and caused locking order problems. Now, we remember
3066 * such pending name changes with this method so that
3067 * VirtualBox::saveSettings() can process them properly.
3068 */
3069void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3070 const Utf8Str &strNewConfigDir)
3071{
3072 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3073
3074 Data::PendingMachineRename pmr;
3075 pmr.strConfigDirOld = strOldConfigDir;
3076 pmr.strConfigDirNew = strNewConfigDir;
3077 m->llPendingMachineRenames.push_back(pmr);
3078}
3079
3080/**
3081 * Goes through all known media (hard disks, floppies and DVDs) and saves
3082 * those into the given settings::MediaRegistry structures whose registry
3083 * ID match the given UUID.
3084 *
3085 * Before actually writing to the structures, all media paths (not just the
3086 * ones for the given registry) are updated if machines have been renamed
3087 * since the last call.
3088 *
3089 * This gets called from two contexts:
3090 *
3091 * -- VirtualBox::saveSettings() with the UUID of the global registry
3092 * (VirtualBox::Data.uuidRegistry); this will save those media
3093 * which had been loaded from the global registry or have been
3094 * attached to a "legacy" machine which can't save its own registry;
3095 *
3096 * -- Machine::saveSettings() with the UUID of a machine, if a medium
3097 * has been attached to a machine created with VirtualBox 4.0 or later.
3098 *
3099 * Media which have only been temporarily opened without having been
3100 * attached to a machine have a NULL registry UUID and therefore don't
3101 * get saved.
3102 *
3103 * This locks the media tree. Throws HRESULT on errors!
3104 *
3105 * @param mediaRegistry Settings structure to fill.
3106 * @param uuidRegistry The UUID of the media registry; either a machine UUID (if machine registry) or the UUID of the global registry.
3107 * @param hardDiskFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
3108 */
3109void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
3110 const Guid &uuidRegistry,
3111 const Utf8Str &strMachineFolder)
3112{
3113 // lock all media for the following; use a write lock because we're
3114 // modifying the PendingMachineRenamesList, which is protected by this
3115 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3116
3117 // if a machine was renamed, then we'll need to refresh media paths
3118 if (m->llPendingMachineRenames.size())
3119 {
3120 // make a single list from the three media lists so we don't need three loops
3121 MediaList llAllMedia;
3122 // with hard disks, we must use the map, not the list, because the list only has base images
3123 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3124 llAllMedia.push_back(it->second);
3125 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
3126 llAllMedia.push_back(*it);
3127 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
3128 llAllMedia.push_back(*it);
3129
3130 for (MediaList::iterator it = llAllMedia.begin();
3131 it != llAllMedia.end();
3132 ++it)
3133 {
3134 Medium *pMedium = *it;
3135 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3136 it2 != m->llPendingMachineRenames.end();
3137 ++it2)
3138 {
3139 const Data::PendingMachineRename &pmr = *it2;
3140 pMedium->updatePath(pmr.strConfigDirOld,
3141 pmr.strConfigDirNew);
3142 }
3143 }
3144 // done, don't do it again until we have more machine renames
3145 m->llPendingMachineRenames.clear();
3146 }
3147
3148 HRESULT rc;
3149 // hard disks
3150 mediaRegistry.llHardDisks.clear();
3151 for (MediaList::const_iterator it = m->allHardDisks.begin();
3152 it != m->allHardDisks.end();
3153 ++it)
3154 {
3155 Medium *pMedium = *it;
3156 if (pMedium->isInRegistry(uuidRegistry))
3157 {
3158 settings::Medium med;
3159 rc = pMedium->saveSettings(med, strMachineFolder); // this recurses into its children
3160 if (FAILED(rc)) throw rc;
3161 mediaRegistry.llHardDisks.push_back(med);
3162 }
3163 }
3164
3165 // CD/DVD images
3166 mediaRegistry.llDvdImages.clear();
3167 for (MediaList::const_iterator it = m->allDVDImages.begin();
3168 it != m->allDVDImages.end();
3169 ++it)
3170 {
3171 Medium *pMedium = *it;
3172 if (pMedium->isInRegistry(uuidRegistry))
3173 {
3174 settings::Medium med;
3175 rc = pMedium->saveSettings(med, strMachineFolder);
3176 if (FAILED(rc)) throw rc;
3177 mediaRegistry.llDvdImages.push_back(med);
3178 }
3179 }
3180
3181 // floppy images
3182 mediaRegistry.llFloppyImages.clear();
3183 for (MediaList::const_iterator it = m->allFloppyImages.begin();
3184 it != m->allFloppyImages.end();
3185 ++it)
3186 {
3187 Medium *pMedium = *it;
3188 if (pMedium->isInRegistry(uuidRegistry))
3189 {
3190 settings::Medium med;
3191 rc = pMedium->saveSettings(med, strMachineFolder);
3192 if (FAILED(rc)) throw rc;
3193 mediaRegistry.llFloppyImages.push_back(med);
3194 }
3195 }
3196}
3197
3198/**
3199 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
3200 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
3201 * places internally when settings need saving.
3202 *
3203 * @note Caller must have locked the VirtualBox object for writing and must not hold any
3204 * other locks since this locks all kinds of member objects and trees temporarily,
3205 * which could cause conflicts.
3206 */
3207HRESULT VirtualBox::saveSettings()
3208{
3209 AutoCaller autoCaller(this);
3210 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3211
3212 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3213 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3214
3215 HRESULT rc = S_OK;
3216
3217 try
3218 {
3219 // machines
3220 m->pMainConfigFile->llMachines.clear();
3221 {
3222 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3223 for (MachinesOList::iterator it = m->allMachines.begin();
3224 it != m->allMachines.end();
3225 ++it)
3226 {
3227 Machine *pMachine = *it;
3228 // save actual machine registry entry
3229 settings::MachineRegistryEntry mre;
3230 rc = pMachine->saveRegistryEntry(mre);
3231 m->pMainConfigFile->llMachines.push_back(mre);
3232 }
3233 }
3234
3235 saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
3236 m->uuidMediaRegistry, // global media registry ID
3237 Utf8Str::Empty); // strMachineFolder
3238
3239 m->pMainConfigFile->llDhcpServers.clear();
3240 {
3241 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3242 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
3243 it != m->allDHCPServers.end();
3244 ++it)
3245 {
3246 settings::DHCPServer d;
3247 rc = (*it)->saveSettings(d);
3248 if (FAILED(rc)) throw rc;
3249 m->pMainConfigFile->llDhcpServers.push_back(d);
3250 }
3251 }
3252
3253 // leave extra data alone, it's still in the config file
3254
3255 // host data (USB filters)
3256 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3257 if (FAILED(rc)) throw rc;
3258
3259 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3260 if (FAILED(rc)) throw rc;
3261
3262 // and write out the XML, still under the lock
3263 m->pMainConfigFile->write(m->strSettingsFilePath);
3264 }
3265 catch (HRESULT err)
3266 {
3267 /* we assume that error info is set by the thrower */
3268 rc = err;
3269 }
3270 catch (...)
3271 {
3272 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3273 }
3274
3275 return rc;
3276}
3277
3278/**
3279 * Helper to register the machine.
3280 *
3281 * When called during VirtualBox startup, adds the given machine to the
3282 * collection of registered machines. Otherwise tries to mark the machine
3283 * as registered, and, if succeeded, adds it to the collection and
3284 * saves global settings.
3285 *
3286 * @note The caller must have added itself as a caller of the @a aMachine
3287 * object if calls this method not on VirtualBox startup.
3288 *
3289 * @param aMachine machine to register
3290 *
3291 * @note Locks objects!
3292 */
3293HRESULT VirtualBox::registerMachine(Machine *aMachine)
3294{
3295 ComAssertRet(aMachine, E_INVALIDARG);
3296
3297 AutoCaller autoCaller(this);
3298 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3299
3300 HRESULT rc = S_OK;
3301
3302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3303
3304 {
3305 ComObjPtr<Machine> pMachine;
3306 rc = findMachine(aMachine->getId(),
3307 true /* fPermitInaccessible */,
3308 false /* aDoSetError */,
3309 &pMachine);
3310 if (SUCCEEDED(rc))
3311 {
3312 /* sanity */
3313 AutoLimitedCaller machCaller(pMachine);
3314 AssertComRC(machCaller.rc());
3315
3316 return setError(E_INVALIDARG,
3317 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
3318 aMachine->getId().raw(),
3319 pMachine->getSettingsFileFull().c_str());
3320 }
3321
3322 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3323 rc = S_OK;
3324 }
3325
3326 if (autoCaller.state() != InInit)
3327 {
3328 rc = aMachine->prepareRegister();
3329 if (FAILED(rc)) return rc;
3330 }
3331
3332 /* add to the collection of registered machines */
3333 m->allMachines.addChild(aMachine);
3334
3335 if (autoCaller.state() != InInit)
3336 rc = saveSettings();
3337
3338 return rc;
3339}
3340
3341/**
3342 * Remembers the given hard disk by storing it in either the global hard disk registry
3343 * or a machine one.
3344 *
3345 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3346 *
3347 * @param aHardDisk Hard disk object to remember.
3348 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3349 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs of media registries that need saving.
3350 * @return
3351 */
3352HRESULT VirtualBox::registerHardDisk(Medium *pMedium,
3353 GuidList *pllRegistriesThatNeedSaving)
3354{
3355 AssertReturn(pMedium != NULL, E_INVALIDARG);
3356
3357 AutoCaller autoCaller(this);
3358 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3359
3360 AutoCaller hardDiskCaller(pMedium);
3361 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3362
3363 // caller must hold the media tree write lock
3364 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3365
3366 Guid id;
3367 Utf8Str strLocationFull;
3368 ComObjPtr<Medium> pParent;
3369 {
3370 AutoReadLock hardDiskLock(pMedium COMMA_LOCKVAL_SRC_POS);
3371 id = pMedium->getId();
3372 strLocationFull = pMedium->getLocationFull();
3373 pParent = pMedium->getParent();
3374 }
3375
3376 HRESULT rc;
3377
3378 Utf8Str strConflict;
3379 bool fIdentical;
3380 rc = checkMediaForConflicts(id,
3381 strLocationFull,
3382 strConflict,
3383 fIdentical);
3384 if (FAILED(rc)) return rc;
3385
3386 if (!fIdentical)
3387 {
3388 if (strConflict.length())
3389 return setError(E_INVALIDARG,
3390 tr("Cannot register the hard disk '%s' {%RTuuid} because a %s already exists"),
3391 strLocationFull.c_str(),
3392 id.raw(),
3393 strConflict.c_str(),
3394 m->strSettingsFilePath.c_str());
3395
3396 // store base (root) hard disks in the list
3397 if (pParent.isNull())
3398 m->allHardDisks.getList().push_back(pMedium);
3399 // access the list directly because we already locked the list above
3400
3401 // store all hard disks (even differencing images) in the map
3402 m->mapHardDisks[id] = pMedium;
3403
3404 if (pllRegistriesThatNeedSaving)
3405 pMedium->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3406 }
3407
3408 return rc;
3409}
3410
3411/**
3412 * Removes the given hard disk from the hard disk registry.
3413 *
3414 * @param aHardDisk Hard disk object to remove.
3415 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3416 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3417 *
3418 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3419 */
3420HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3421 GuidList *pllRegistriesThatNeedSaving)
3422{
3423 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3424
3425 AutoCaller autoCaller(this);
3426 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3427
3428 AutoCaller hardDiskCaller(aHardDisk);
3429 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3430
3431 // caller must hold the media tree write lock
3432 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3433
3434 Guid id;
3435 ComObjPtr<Medium> pParent;
3436 {
3437 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3438 id = aHardDisk->getId();
3439 pParent = aHardDisk->getParent();
3440 }
3441
3442 // remove base (root) hard disks from the list
3443 if (pParent.isNull())
3444 m->allHardDisks.getList().remove(aHardDisk);
3445 // access the list directly because caller must have locked the list
3446
3447 // remove all hard disks (even differencing images) from map
3448 size_t cnt = m->mapHardDisks.erase(id);
3449 Assert(cnt == 1);
3450 NOREF(cnt);
3451
3452 if (pllRegistriesThatNeedSaving)
3453 aHardDisk->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3454
3455 return S_OK;
3456}
3457
3458/**
3459 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3460 *
3461 * @param argImage Image object to remember.
3462 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3463 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3464 *
3465 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3466 */
3467HRESULT VirtualBox::registerImage(Medium *pMedium,
3468 DeviceType_T argType,
3469 GuidList *pllRegistriesThatNeedSaving)
3470{
3471 AssertReturn(pMedium != NULL, E_INVALIDARG);
3472
3473 AutoCaller autoCaller(this);
3474 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3475
3476 AutoCaller imageCaller(pMedium);
3477 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3478
3479 // caller must hold the media tree write lock
3480 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3481
3482 Guid id;
3483 Utf8Str strLocationFull;
3484 ComObjPtr<Medium> pParent;
3485 {
3486 AutoReadLock al(pMedium COMMA_LOCKVAL_SRC_POS);
3487 id = pMedium->getId();
3488 strLocationFull = pMedium->getLocationFull();
3489 pParent = pMedium->getParent();
3490 }
3491
3492 // work on DVDs or floppies list?
3493 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3494
3495 HRESULT rc;
3496 // lock the images lists (list + map) while checking for conflicts
3497 AutoWriteLock al(all.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3498
3499 Utf8Str strConflict;
3500 bool fIdentical;
3501 rc = checkMediaForConflicts(id,
3502 strLocationFull,
3503 strConflict,
3504 fIdentical);
3505 if (FAILED(rc)) return rc;
3506
3507 if (!fIdentical)
3508 {
3509 if (strConflict.length())
3510 return setError(VBOX_E_INVALID_OBJECT_STATE,
3511 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists"),
3512 strLocationFull.c_str(),
3513 id.raw(),
3514 strConflict.c_str());
3515
3516 // add to the collection
3517 all.getList().push_back(pMedium);
3518 // access the list directly because we already locked the list above
3519
3520 if (pllRegistriesThatNeedSaving)
3521 pMedium->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3522 }
3523
3524 return rc;
3525}
3526
3527/**
3528 * Removes the given image from the CD/DVD or floppy image registry.
3529 *
3530 * @param argImage Image object to remove.
3531 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3532 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3533 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3534 *
3535 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3536 */
3537HRESULT VirtualBox::unregisterImage(Medium *argImage,
3538 DeviceType_T argType,
3539 GuidList *pllRegistriesThatNeedSaving)
3540{
3541 AssertReturn(argImage != NULL, E_INVALIDARG);
3542
3543 AutoCaller autoCaller(this);
3544 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3545
3546 AutoCaller imageCaller(argImage);
3547 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3548
3549 // caller must hold the media tree write lock
3550 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3551
3552 Guid id;
3553 ComObjPtr<Medium> pParent;
3554 {
3555 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3556 id = argImage->getId();
3557 pParent = argImage->getParent();
3558 }
3559
3560 // work on DVDs or floppies list?
3561 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3562
3563 // access the list directly because the caller must have requested the lock
3564 all.getList().remove(argImage);
3565
3566 HRESULT rc = S_OK;
3567
3568 if (pllRegistriesThatNeedSaving)
3569 argImage->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3570
3571 return rc;
3572}
3573
3574/**
3575 * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
3576 * with children appearing before their parents.
3577 * @param llMedia
3578 * @param pMedium
3579 */
3580void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
3581{
3582 // recurse first, then add ourselves; this way children end up on the
3583 // list before their parents
3584
3585 const MediaList &llChildren = pMedium->getChildren();
3586 for (MediaList::const_iterator it = llChildren.begin();
3587 it != llChildren.end();
3588 ++it)
3589 {
3590 Medium *pChild = *it;
3591 pushMediumToListWithChildren(llMedia, pChild);
3592 }
3593
3594 Log(("Pushing medium %RTuuid\n", pMedium->getId().raw()));
3595 llMedia.push_back(pMedium);
3596}
3597
3598/**
3599 * Unregisters all Medium objects which belong to the given machine registry.
3600 * Gets called from Machine::uninit() just before the machine object dies
3601 * and must only be called with a machine UUID as the registry ID.
3602 *
3603 * Locks the media tree.
3604 *
3605 * @param uuidMachine Medium registry ID (always a machine UUID)
3606 * @return
3607 */
3608HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine)
3609{
3610 Assert(!uuidMachine.isEmpty());
3611
3612 LogFlowFuncEnter();
3613
3614 AutoCaller autoCaller(this);
3615 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3616
3617 MediaList llMedia2Close;
3618
3619 {
3620 AutoWriteLock mlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3621 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
3622 it != m->allHardDisks.getList().end();
3623 ++it)
3624 {
3625 ComObjPtr<Medium> pMedium = *it;
3626
3627 if (pMedium->isInRegistry(uuidMachine))
3628 // recursively with children first
3629 pushMediumToListWithChildren(llMedia2Close, pMedium);
3630 }
3631 }
3632
3633 for (MediaList::iterator it = llMedia2Close.begin();
3634 it != llMedia2Close.end();
3635 ++it)
3636 {
3637 ComObjPtr<Medium> pMedium = *it;
3638 Log(("Closing medium %RTuuid\n", pMedium->getId().raw()));
3639 AutoCaller mac(pMedium);
3640 pMedium->close(NULL /* pfNeedsGlobalSaveSettings*/, mac);
3641 }
3642
3643 LogFlowFuncLeave();
3644
3645 return S_OK;
3646}
3647
3648/**
3649 * Removes the given machine object from the internal list of registered machines.
3650 * Called from Machine::Unregister().
3651 * @param pMachine
3652 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
3653 * @return
3654 */
3655HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
3656 const Guid &id)
3657{
3658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3659
3660 // remove from the collection of registered machines
3661 m->allMachines.removeChild(pMachine);
3662
3663 // save the global registry
3664 HRESULT rc = saveSettings();
3665
3666 alock.release();
3667
3668 /* fire an event */
3669 onMachineRegistered(id, FALSE);
3670
3671 return rc;
3672}
3673
3674/**
3675 * Adds uuid to llRegistriesThatNeedSaving unless it's already on the list.
3676 *
3677 * @todo maybe there's something in libstdc++ for this
3678 *
3679 * @param llRegistriesThatNeedSaving
3680 * @param uuid
3681 */
3682void VirtualBox::addGuidToListUniquely(GuidList &llRegistriesThatNeedSaving,
3683 Guid uuid)
3684{
3685 for (GuidList::const_iterator it = llRegistriesThatNeedSaving.begin();
3686 it != llRegistriesThatNeedSaving.end();
3687 ++it)
3688 {
3689 if (*it == uuid)
3690 // uuid is already in list:
3691 return;
3692 }
3693
3694 llRegistriesThatNeedSaving.push_back(uuid);
3695}
3696
3697HRESULT VirtualBox::saveRegistries(const GuidList &llRegistriesThatNeedSaving)
3698{
3699 bool fNeedsGlobalSettings = false;
3700 HRESULT rc = S_OK;
3701
3702 for (GuidList::const_iterator it = llRegistriesThatNeedSaving.begin();
3703 it != llRegistriesThatNeedSaving.end();
3704 ++it)
3705 {
3706 const Guid &uuid = *it;
3707
3708 if (uuid == getGlobalRegistryId())
3709 fNeedsGlobalSettings = true;
3710 else
3711 {
3712 // should be machine ID then:
3713 ComObjPtr<Machine> pMachine;
3714 rc = findMachine(uuid,
3715 false /* fPermitInaccessible */,
3716 false /* aSetError */,
3717 &pMachine);
3718 if (SUCCEEDED(rc))
3719 {
3720 AutoCaller autoCaller(pMachine);
3721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3722 AutoWriteLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
3723 rc = pMachine->saveSettings(&fNeedsGlobalSettings,
3724 Machine::SaveS_Force); // caller said save, so stop arguing
3725 }
3726
3727 if (FAILED(rc))
3728 return rc;
3729 }
3730 }
3731
3732 if (fNeedsGlobalSettings)
3733 {
3734 AutoWriteLock vlock(this COMMA_LOCKVAL_SRC_POS);
3735 rc = saveSettings();
3736 }
3737
3738 return S_OK;
3739}
3740
3741/**
3742 * Creates the path to the specified file according to the path information
3743 * present in the file name.
3744 *
3745 * Note that the given file name must contain the full path otherwise the
3746 * extracted relative path will be created based on the current working
3747 * directory which is normally unknown.
3748 *
3749 * @param aFileName Full file name which path needs to be created.
3750 *
3751 * @return Extended error information on failure to create the path.
3752 */
3753/* static */
3754HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3755{
3756 Utf8Str strDir(strFileName);
3757 strDir.stripFilename();
3758 if (!RTDirExists(strDir.c_str()))
3759 {
3760 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3761 if (RT_FAILURE(vrc))
3762 return setErrorStatic(E_FAIL,
3763 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
3764 strDir.c_str(),
3765 vrc));
3766 }
3767
3768 return S_OK;
3769}
3770
3771/**
3772 * Handles unexpected exceptions by turning them into COM errors in release
3773 * builds or by hitting a breakpoint in the release builds.
3774 *
3775 * Usage pattern:
3776 * @code
3777 try
3778 {
3779 // ...
3780 }
3781 catch (LaLalA)
3782 {
3783 // ...
3784 }
3785 catch (...)
3786 {
3787 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3788 }
3789 * @endcode
3790 *
3791 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3792 */
3793/* static */
3794HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3795{
3796 try
3797 {
3798 /* re-throw the current exception */
3799 throw;
3800 }
3801 catch (const iprt::Error &err) // includes all XML exceptions
3802 {
3803 return setErrorStatic(E_FAIL,
3804 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
3805 err.what(),
3806 pszFile, iLine, pszFunction).c_str());
3807 }
3808 catch (const std::exception &err)
3809 {
3810 return setErrorStatic(E_FAIL,
3811 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3812 err.what(), typeid(err).name(),
3813 pszFile, iLine, pszFunction).c_str());
3814 }
3815 catch (...)
3816 {
3817 return setErrorStatic(E_FAIL,
3818 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
3819 pszFile, iLine, pszFunction).c_str());
3820 }
3821
3822 /* should not get here */
3823 AssertFailed();
3824 return E_FAIL;
3825}
3826
3827const Utf8Str& VirtualBox::settingsFilePath()
3828{
3829 return m->strSettingsFilePath;
3830}
3831
3832/**
3833 * Returns the lock handle which protects the media trees (hard disks,
3834 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3835 * are no longer protected by the VirtualBox lock, but by this more
3836 * specialized lock. Mind the locking order: always request this lock
3837 * after the VirtualBox object lock but before the locks of the media
3838 * objects contained in these lists. See AutoLock.h.
3839 */
3840RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3841{
3842 return m->lockMedia;
3843}
3844
3845/**
3846 * Thread function that watches the termination of all client processes
3847 * that have opened sessions using IMachine::LockMachine()
3848 */
3849// static
3850DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3851{
3852 LogFlowFuncEnter();
3853
3854 VirtualBox *that = (VirtualBox*)pvUser;
3855 Assert(that);
3856
3857 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3858 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3859
3860 SessionMachineVector machines;
3861 MachineVector spawnedMachines;
3862
3863 size_t cnt = 0;
3864 size_t cntSpawned = 0;
3865
3866#if defined(RT_OS_WINDOWS)
3867
3868 HRESULT hrc = CoInitializeEx(NULL,
3869 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3870 COINIT_SPEED_OVER_MEMORY);
3871 AssertComRC(hrc);
3872
3873 /// @todo (dmik) processes reaping!
3874
3875 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3876 handles[0] = that->m->updateReq;
3877
3878 do
3879 {
3880 AutoCaller autoCaller(that);
3881 /* VirtualBox has been early uninitialized, terminate */
3882 if (!autoCaller.isOk())
3883 break;
3884
3885 do
3886 {
3887 /* release the caller to let uninit() ever proceed */
3888 autoCaller.release();
3889
3890 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3891 handles,
3892 FALSE,
3893 INFINITE);
3894
3895 /* Restore the caller before using VirtualBox. If it fails, this
3896 * means VirtualBox is being uninitialized and we must terminate. */
3897 autoCaller.add();
3898 if (!autoCaller.isOk())
3899 break;
3900
3901 bool update = false;
3902
3903 if (rc == WAIT_OBJECT_0)
3904 {
3905 /* update event is signaled */
3906 update = true;
3907 }
3908 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3909 {
3910 /* machine mutex is released */
3911 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3912 update = true;
3913 }
3914 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3915 {
3916 /* machine mutex is abandoned due to client process termination */
3917 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3918 update = true;
3919 }
3920 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3921 {
3922 /* spawned VM process has terminated (normally or abnormally) */
3923 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
3924 checkForSpawnFailure();
3925 update = true;
3926 }
3927
3928 if (update)
3929 {
3930 /* close old process handles */
3931 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
3932 CloseHandle(handles[i]);
3933
3934 // lock the machines list for reading
3935 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3936
3937 /* obtain a new set of opened machines */
3938 cnt = 0;
3939 machines.clear();
3940
3941 for (MachinesOList::iterator it = that->m->allMachines.begin();
3942 it != that->m->allMachines.end();
3943 ++it)
3944 {
3945 /// @todo handle situations with more than 64 objects
3946 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3947 ("MAXIMUM_WAIT_OBJECTS reached"));
3948
3949 ComObjPtr<SessionMachine> sm;
3950 HANDLE ipcSem;
3951 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
3952 {
3953 machines.push_back(sm);
3954 handles[1 + cnt] = ipcSem;
3955 ++cnt;
3956 }
3957 }
3958
3959 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
3960
3961 /* obtain a new set of spawned machines */
3962 cntSpawned = 0;
3963 spawnedMachines.clear();
3964
3965 for (MachinesOList::iterator it = that->m->allMachines.begin();
3966 it != that->m->allMachines.end();
3967 ++it)
3968 {
3969 /// @todo handle situations with more than 64 objects
3970 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
3971 ("MAXIMUM_WAIT_OBJECTS reached"));
3972
3973 RTPROCESS pid;
3974 if ((*it)->isSessionSpawning(&pid))
3975 {
3976 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
3977 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
3978 pid, GetLastError()));
3979 if (rc == 0)
3980 {
3981 spawnedMachines.push_back(*it);
3982 handles[1 + cnt + cntSpawned] = ph;
3983 ++cntSpawned;
3984 }
3985 }
3986 }
3987
3988 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
3989
3990 // machines lock unwinds here
3991 }
3992 }
3993 while (true);
3994 }
3995 while (0);
3996
3997 /* close old process handles */
3998 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3999 CloseHandle(handles[i]);
4000
4001 /* release sets of machines if any */
4002 machines.clear();
4003 spawnedMachines.clear();
4004
4005 ::CoUninitialize();
4006
4007#elif defined(RT_OS_OS2)
4008
4009 /// @todo (dmik) processes reaping!
4010
4011 /* according to PMREF, 64 is the maximum for the muxwait list */
4012 SEMRECORD handles[64];
4013
4014 HMUX muxSem = NULLHANDLE;
4015
4016 do
4017 {
4018 AutoCaller autoCaller(that);
4019 /* VirtualBox has been early uninitialized, terminate */
4020 if (!autoCaller.isOk())
4021 break;
4022
4023 do
4024 {
4025 /* release the caller to let uninit() ever proceed */
4026 autoCaller.release();
4027
4028 int vrc = RTSemEventWait(that->m->updateReq, 500);
4029
4030 /* Restore the caller before using VirtualBox. If it fails, this
4031 * means VirtualBox is being uninitialized and we must terminate. */
4032 autoCaller.add();
4033 if (!autoCaller.isOk())
4034 break;
4035
4036 bool update = false;
4037 bool updateSpawned = false;
4038
4039 if (RT_SUCCESS(vrc))
4040 {
4041 /* update event is signaled */
4042 update = true;
4043 updateSpawned = true;
4044 }
4045 else
4046 {
4047 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4048 ("RTSemEventWait returned %Rrc\n", vrc));
4049
4050 /* are there any mutexes? */
4051 if (cnt > 0)
4052 {
4053 /* figure out what's going on with machines */
4054
4055 unsigned long semId = 0;
4056 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
4057 SEM_IMMEDIATE_RETURN, &semId);
4058
4059 if (arc == NO_ERROR)
4060 {
4061 /* machine mutex is normally released */
4062 Assert(semId >= 0 && semId < cnt);
4063 if (semId >= 0 && semId < cnt)
4064 {
4065#if 0//def DEBUG
4066 {
4067 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4068 LogFlowFunc(("released mutex: machine='%ls'\n",
4069 machines[semId]->name().raw()));
4070 }
4071#endif
4072 machines[semId]->checkForDeath();
4073 }
4074 update = true;
4075 }
4076 else if (arc == ERROR_SEM_OWNER_DIED)
4077 {
4078 /* machine mutex is abandoned due to client process
4079 * termination; find which mutex is in the Owner Died
4080 * state */
4081 for (size_t i = 0; i < cnt; ++ i)
4082 {
4083 PID pid; TID tid;
4084 unsigned long reqCnt;
4085 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
4086 if (arc == ERROR_SEM_OWNER_DIED)
4087 {
4088 /* close the dead mutex as asked by PMREF */
4089 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
4090
4091 Assert(i >= 0 && i < cnt);
4092 if (i >= 0 && i < cnt)
4093 {
4094#if 0//def DEBUG
4095 {
4096 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4097 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4098 machines[i]->name().raw()));
4099 }
4100#endif
4101 machines[i]->checkForDeath();
4102 }
4103 }
4104 }
4105 update = true;
4106 }
4107 else
4108 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4109 ("DosWaitMuxWaitSem returned %d\n", arc));
4110 }
4111
4112 /* are there any spawning sessions? */
4113 if (cntSpawned > 0)
4114 {
4115 for (size_t i = 0; i < cntSpawned; ++ i)
4116 updateSpawned |= (spawnedMachines[i])->
4117 checkForSpawnFailure();
4118 }
4119 }
4120
4121 if (update || updateSpawned)
4122 {
4123 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4124
4125 if (update)
4126 {
4127 /* close the old muxsem */
4128 if (muxSem != NULLHANDLE)
4129 ::DosCloseMuxWaitSem(muxSem);
4130
4131 /* obtain a new set of opened machines */
4132 cnt = 0;
4133 machines.clear();
4134
4135 for (MachinesOList::iterator it = that->m->allMachines.begin();
4136 it != that->m->allMachines.end(); ++ it)
4137 {
4138 /// @todo handle situations with more than 64 objects
4139 AssertMsg(cnt <= 64 /* according to PMREF */,
4140 ("maximum of 64 mutex semaphores reached (%d)",
4141 cnt));
4142
4143 ComObjPtr<SessionMachine> sm;
4144 HMTX ipcSem;
4145 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4146 {
4147 machines.push_back(sm);
4148 handles[cnt].hsemCur = (HSEM)ipcSem;
4149 handles[cnt].ulUser = cnt;
4150 ++ cnt;
4151 }
4152 }
4153
4154 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4155
4156 if (cnt > 0)
4157 {
4158 /* create a new muxsem */
4159 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4160 handles,
4161 DCMW_WAIT_ANY);
4162 AssertMsg(arc == NO_ERROR,
4163 ("DosCreateMuxWaitSem returned %d\n", arc));
4164 NOREF(arc);
4165 }
4166 }
4167
4168 if (updateSpawned)
4169 {
4170 /* obtain a new set of spawned machines */
4171 spawnedMachines.clear();
4172
4173 for (MachinesOList::iterator it = that->m->allMachines.begin();
4174 it != that->m->allMachines.end(); ++ it)
4175 {
4176 if ((*it)->isSessionSpawning())
4177 spawnedMachines.push_back(*it);
4178 }
4179
4180 cntSpawned = spawnedMachines.size();
4181 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4182 }
4183 }
4184 }
4185 while (true);
4186 }
4187 while (0);
4188
4189 /* close the muxsem */
4190 if (muxSem != NULLHANDLE)
4191 ::DosCloseMuxWaitSem(muxSem);
4192
4193 /* release sets of machines if any */
4194 machines.clear();
4195 spawnedMachines.clear();
4196
4197#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4198
4199 bool update = false;
4200 bool updateSpawned = false;
4201
4202 do
4203 {
4204 AutoCaller autoCaller(that);
4205 if (!autoCaller.isOk())
4206 break;
4207
4208 do
4209 {
4210 /* release the caller to let uninit() ever proceed */
4211 autoCaller.release();
4212
4213 int rc = RTSemEventWait(that->m->updateReq, 500);
4214
4215 /*
4216 * Restore the caller before using VirtualBox. If it fails, this
4217 * means VirtualBox is being uninitialized and we must terminate.
4218 */
4219 autoCaller.add();
4220 if (!autoCaller.isOk())
4221 break;
4222
4223 if (RT_SUCCESS(rc) || update || updateSpawned)
4224 {
4225 /* RT_SUCCESS(rc) means an update event is signaled */
4226
4227 // lock the machines list for reading
4228 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4229
4230 if (RT_SUCCESS(rc) || update)
4231 {
4232 /* obtain a new set of opened machines */
4233 machines.clear();
4234
4235 for (MachinesOList::iterator it = that->m->allMachines.begin();
4236 it != that->m->allMachines.end();
4237 ++it)
4238 {
4239 ComObjPtr<SessionMachine> sm;
4240 if ((*it)->isSessionOpenOrClosing(sm))
4241 machines.push_back(sm);
4242 }
4243
4244 cnt = machines.size();
4245 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4246 }
4247
4248 if (RT_SUCCESS(rc) || updateSpawned)
4249 {
4250 /* obtain a new set of spawned machines */
4251 spawnedMachines.clear();
4252
4253 for (MachinesOList::iterator it = that->m->allMachines.begin();
4254 it != that->m->allMachines.end();
4255 ++it)
4256 {
4257 if ((*it)->isSessionSpawning())
4258 spawnedMachines.push_back(*it);
4259 }
4260
4261 cntSpawned = spawnedMachines.size();
4262 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4263 }
4264
4265 // machines lock unwinds here
4266 }
4267
4268 update = false;
4269 for (size_t i = 0; i < cnt; ++ i)
4270 update |= (machines[i])->checkForDeath();
4271
4272 updateSpawned = false;
4273 for (size_t i = 0; i < cntSpawned; ++ i)
4274 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4275
4276 /* reap child processes */
4277 {
4278 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4279 if (that->m->llProcesses.size())
4280 {
4281 LogFlowFunc(("UPDATE: child process count = %d\n",
4282 that->m->llProcesses.size()));
4283 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4284 while (it != that->m->llProcesses.end())
4285 {
4286 RTPROCESS pid = *it;
4287 RTPROCSTATUS status;
4288 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4289 if (vrc == VINF_SUCCESS)
4290 {
4291 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4292 pid, pid, status.iStatus,
4293 status.enmReason));
4294 it = that->m->llProcesses.erase(it);
4295 }
4296 else
4297 {
4298 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4299 pid, pid, vrc));
4300 if (vrc != VERR_PROCESS_RUNNING)
4301 {
4302 /* remove the process if it is not already running */
4303 it = that->m->llProcesses.erase(it);
4304 }
4305 else
4306 ++ it;
4307 }
4308 }
4309 }
4310 }
4311 }
4312 while (true);
4313 }
4314 while (0);
4315
4316 /* release sets of machines if any */
4317 machines.clear();
4318 spawnedMachines.clear();
4319
4320#else
4321# error "Port me!"
4322#endif
4323
4324 LogFlowFuncLeave();
4325 return 0;
4326}
4327
4328/**
4329 * Thread function that handles custom events posted using #postEvent().
4330 */
4331// static
4332DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4333{
4334 LogFlowFuncEnter();
4335
4336 AssertReturn(pvUser, VERR_INVALID_POINTER);
4337
4338 // create an event queue for the current thread
4339 EventQueue *eventQ = new EventQueue();
4340 AssertReturn(eventQ, VERR_NO_MEMORY);
4341
4342 // return the queue to the one who created this thread
4343 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4344 // signal that we're ready
4345 RTThreadUserSignal(thread);
4346
4347 while (RT_SUCCESS(eventQ->processEventQueue(RT_INDEFINITE_WAIT)))
4348 /* nothing */ ;
4349
4350 delete eventQ;
4351
4352 LogFlowFuncLeave();
4353
4354 return 0;
4355}
4356
4357
4358////////////////////////////////////////////////////////////////////////////////
4359
4360/**
4361 * Takes the current list of registered callbacks of the managed VirtualBox
4362 * instance, and calls #handleCallback() for every callback item from the
4363 * list, passing the item as an argument.
4364 *
4365 * @note Locks the managed VirtualBox object for reading but leaves the lock
4366 * before iterating over callbacks and calling their methods.
4367 */
4368void *VirtualBox::CallbackEvent::handler()
4369{
4370 if (!mVirtualBox)
4371 return NULL;
4372
4373 AutoCaller autoCaller(mVirtualBox);
4374 if (!autoCaller.isOk())
4375 {
4376 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4377 autoCaller.state()));
4378 /* We don't need mVirtualBox any more, so release it */
4379 mVirtualBox = NULL;
4380 return NULL;
4381 }
4382
4383
4384 {
4385 VBoxEventDesc evDesc;
4386 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4387
4388 evDesc.fire(/* don't wait for delivery */0);
4389 }
4390
4391 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4392 return NULL;
4393}
4394
4395//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4396//{
4397// return E_NOTIMPL;
4398//}
4399
4400STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4401{
4402 CheckComArgStrNotEmptyOrNull(aName);
4403 CheckComArgNotNull(aServer);
4404
4405 AutoCaller autoCaller(this);
4406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4407
4408 ComObjPtr<DHCPServer> dhcpServer;
4409 dhcpServer.createObject();
4410 HRESULT rc = dhcpServer->init(this, aName);
4411 if (FAILED(rc)) return rc;
4412
4413 rc = registerDHCPServer(dhcpServer, true);
4414 if (FAILED(rc)) return rc;
4415
4416 dhcpServer.queryInterfaceTo(aServer);
4417
4418 return rc;
4419}
4420
4421STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4422{
4423 CheckComArgStrNotEmptyOrNull(aName);
4424 CheckComArgNotNull(aServer);
4425
4426 AutoCaller autoCaller(this);
4427 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4428
4429 HRESULT rc;
4430 Bstr bstr;
4431 ComPtr<DHCPServer> found;
4432
4433 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4434
4435 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4436 it != m->allDHCPServers.end();
4437 ++it)
4438 {
4439 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4440 if (FAILED(rc)) throw rc;
4441
4442 if (bstr == aName)
4443 {
4444 found = *it;
4445 break;
4446 }
4447 }
4448
4449 if (!found)
4450 return E_INVALIDARG;
4451
4452 return found.queryInterfaceTo(aServer);
4453}
4454
4455STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4456{
4457 CheckComArgNotNull(aServer);
4458
4459 AutoCaller autoCaller(this);
4460 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4461
4462 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4463
4464 return rc;
4465}
4466
4467/**
4468 * Remembers the given dhcp server by storing it in the hard disk registry.
4469 *
4470 * @param aDHCPServer Dhcp Server object to remember.
4471 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4472 *
4473 * When @a aSaveRegistry is @c true, this operation may fail because of the
4474 * failed #saveSettings() method it calls. In this case, the dhcp server object
4475 * will not be remembered. It is therefore the responsibility of the caller to
4476 * call this method as the last step of some action that requires registration
4477 * in order to make sure that only fully functional dhcp server objects get
4478 * registered.
4479 *
4480 * @note Locks this object for writing and @a aDHCPServer for reading.
4481 */
4482HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4483 bool aSaveRegistry /*= true*/)
4484{
4485 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4486
4487 AutoCaller autoCaller(this);
4488 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4489
4490 AutoCaller dhcpServerCaller(aDHCPServer);
4491 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4492
4493 Bstr name;
4494 HRESULT rc;
4495 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4496 if (FAILED(rc)) return rc;
4497
4498 ComPtr<IDHCPServer> existing;
4499 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
4500 if (SUCCEEDED(rc))
4501 return E_INVALIDARG;
4502
4503 rc = S_OK;
4504
4505 m->allDHCPServers.addChild(aDHCPServer);
4506
4507 if (aSaveRegistry)
4508 {
4509 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4510 rc = saveSettings();
4511 vboxLock.release();
4512
4513 if (FAILED(rc))
4514 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4515 }
4516
4517 return rc;
4518}
4519
4520/**
4521 * Removes the given hard disk from the hard disk registry.
4522 *
4523 * @param aHardDisk Hard disk object to remove.
4524 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4525 *
4526 * When @a aSaveRegistry is @c true, this operation may fail because of the
4527 * failed #saveSettings() method it calls. In this case, the hard disk object
4528 * will NOT be removed from the registry when this method returns. It is
4529 * therefore the responsibility of the caller to call this method as the first
4530 * step of some action that requires unregistration, before calling uninit() on
4531 * @a aHardDisk.
4532 *
4533 * @note Locks this object for writing and @a aHardDisk for reading.
4534 */
4535HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4536 bool aSaveRegistry /*= true*/)
4537{
4538 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4539
4540 AutoCaller autoCaller(this);
4541 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4542
4543 AutoCaller dhcpServerCaller(aDHCPServer);
4544 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4545
4546 m->allDHCPServers.removeChild(aDHCPServer);
4547
4548 HRESULT rc = S_OK;
4549
4550 if (aSaveRegistry)
4551 {
4552 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4553 rc = saveSettings();
4554 vboxLock.release();
4555
4556 if (FAILED(rc))
4557 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4558 }
4559
4560 return rc;
4561}
4562
4563STDMETHODIMP VirtualBox::VRDERegisterLibrary(IN_BSTR aName)
4564{
4565 CheckComArgStrNotEmptyOrNull(aName);
4566
4567 AutoCaller autoCaller(this);
4568 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4569
4570 Utf8Str strName(aName);
4571
4572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4573
4574 /* If the name already exists, there is nothing to do. */
4575 settings::VRDELibrariesList::const_iterator it;
4576
4577 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4578 it != m->pMainConfigFile->llVRDELibraries.end();
4579 ++it)
4580 {
4581 const Utf8Str &str = *it;
4582 if (strName == str)
4583 {
4584 break;
4585 }
4586 }
4587
4588 if (it == m->pMainConfigFile->llVRDELibraries.end())
4589 {
4590 /* Not found, add it. */
4591 m->pMainConfigFile->llVRDELibraries.push_back(strName);
4592
4593 /* Save settings on success. */
4594 HRESULT rc = saveSettings();
4595 if (FAILED(rc)) return rc;
4596 }
4597
4598 return S_OK;
4599}
4600
4601STDMETHODIMP VirtualBox::VRDEUnregisterLibrary(IN_BSTR aName)
4602{
4603 CheckComArgStrNotEmptyOrNull(aName);
4604
4605 AutoCaller autoCaller(this);
4606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4607
4608 Utf8Str strName(aName);
4609
4610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4611
4612 settings::VRDELibrariesList::const_iterator it;
4613
4614 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4615 it != m->pMainConfigFile->llVRDELibraries.end();
4616 ++it)
4617 {
4618 const Utf8Str &str = *it;
4619 if (strName == str)
4620 {
4621 break;
4622 }
4623 }
4624
4625 if (it != m->pMainConfigFile->llVRDELibraries.end())
4626 {
4627 /* Found, remove it. */
4628 m->pMainConfigFile->llVRDELibraries.remove(strName);
4629
4630 /* Save settings on success. */
4631 HRESULT rc = saveSettings();
4632 if (FAILED(rc)) return rc;
4633 }
4634
4635 return S_OK;
4636}
4637
4638STDMETHODIMP VirtualBox::VRDEListLibraries(ComSafeArrayOut(BSTR, aNames))
4639{
4640 using namespace settings;
4641
4642 if (ComSafeArrayOutIsNull(aNames))
4643 return E_POINTER;
4644
4645 AutoCaller autoCaller(this);
4646 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4647
4648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4649
4650 com::SafeArray<BSTR> saNames(m->pMainConfigFile->llVRDELibraries.size());
4651 int i = 0;
4652 for (VRDELibrariesList::const_iterator it = m->pMainConfigFile->llVRDELibraries.begin();
4653 it != m->pMainConfigFile->llVRDELibraries.end();
4654 ++it, ++i)
4655 {
4656 const Utf8Str &strName = *it;
4657 strName.cloneTo(&saNames[i]);
4658 }
4659 saNames.detachTo(ComSafeArrayOutArg(aNames));
4660
4661 return S_OK;
4662}
4663
4664STDMETHODIMP VirtualBox::VRDEIsLibraryRegistered(IN_BSTR aName, BOOL *aRegistered)
4665{
4666 CheckComArgStrNotEmptyOrNull(aName);
4667 CheckComArgOutPointerValid(aRegistered);
4668
4669 AutoCaller autoCaller(this);
4670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4671
4672 Utf8Str strName(aName);
4673
4674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4675
4676 settings::VRDELibrariesList::const_iterator it;
4677
4678 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4679 it != m->pMainConfigFile->llVRDELibraries.end();
4680 ++it)
4681 {
4682 const Utf8Str &str = *it;
4683 if (strName == str)
4684 {
4685 break;
4686 }
4687 }
4688
4689 *aRegistered = (it != m->pMainConfigFile->llVRDELibraries.end());
4690
4691 return S_OK;
4692}
4693
4694/* 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