VirtualBox

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

Last change on this file since 25877 was 25860, checked in by vboxsync, 15 years ago

Main: cleanup: get rid of VirtualBoxBaseProto, move AutoCaller*/*Span* classes out of VirtualBoxBaseProto class scope and into separate header; move CombinedProgress into separate header (it's only used by Console any more)

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