VirtualBox

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

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

Main: fix tiny memory leak in GetExtraData

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