VirtualBox

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

Last change on this file since 24696 was 24602, checked in by vboxsync, 15 years ago

EFI: firmware check API in Main

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette