VirtualBox

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

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

Main: separate internal machine data structs into MachineImplPrivate.h to significantly speed up compilation and for better interface separation; remove obsolete ConsoleEvents.h file

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