VirtualBox

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

Last change on this file since 24865 was 24858, checked in by vboxsync, 15 years ago

EFI: more bitness work

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