VirtualBox

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

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

Backed out 55600; unable to create new VMs

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

© 2023 Oracle
ContactPrivacy policyTerms of Use