VirtualBox

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

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

Main: snapshots code cleanup, renames, documentation, coding style

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 136.2 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 24298 2009-11-03 17:20:02Z 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(("aName=\"%ls\", aMachine=%p, rc=%08X\n", aName, *aMachine, 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 Bstr bstrResult("");
1610
1611 settings::ExtraDataItemsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
1612 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1613 // found:
1614 bstrResult = it->second; // source is a Utf8Str
1615
1616 /* return the result to caller (may be empty) */
1617 bstrResult.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::onSnapshotDeleted(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),
2518 machineId(aMachineId),
2519 name(aName),
2520 value(aValue),
2521 flags(aFlags)
2522 {}
2523
2524 void handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2525 {
2526 LogFlow(("OnGuestPropertyChange: machineId={%RTuuid}, name='%ls', value='%ls', flags='%ls'\n",
2527 machineId.ptr(), name.raw(), value.raw(), flags.raw()));
2528 aCallback->OnGuestPropertyChange (machineId.toUtf16(), name, value, flags);
2529 }
2530
2531 Guid machineId;
2532 Bstr name, value, flags;
2533};
2534
2535/**
2536 * @note Doesn't lock any object.
2537 */
2538void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
2539 IN_BSTR aValue, IN_BSTR aFlags)
2540{
2541 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
2542}
2543
2544/**
2545 * @note Locks this object for reading.
2546 */
2547ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2548{
2549 ComObjPtr<GuestOSType> type;
2550
2551 AutoCaller autoCaller(this);
2552 AssertComRCReturn(autoCaller.rc(), type);
2553
2554 AutoReadLock alock(this);
2555
2556 /* unknown type must always be the first */
2557 ComAssertRet (m->llGuestOSTypes.size() > 0, type);
2558
2559 type = m->llGuestOSTypes.front();
2560 return type;
2561}
2562
2563/**
2564 * Returns the list of opened machines (machines having direct sessions opened
2565 * by client processes) and optionally the list of direct session controls.
2566 *
2567 * @param aMachines Where to put opened machines (will be empty if none).
2568 * @param aControls Where to put direct session controls (optional).
2569 *
2570 * @note The returned lists contain smart pointers. So, clear it as soon as
2571 * it becomes no more necessary to release instances.
2572 *
2573 * @note It can be possible that a session machine from the list has been
2574 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2575 * when accessing unprotected data directly.
2576 *
2577 * @note Locks objects for reading.
2578 */
2579void VirtualBox::getOpenedMachines(SessionMachineList &aMachines,
2580 InternalControlList *aControls /*= NULL*/)
2581{
2582 AutoCaller autoCaller(this);
2583 AssertComRCReturnVoid (autoCaller.rc());
2584
2585 aMachines.clear();
2586 if (aControls)
2587 aControls->clear();
2588
2589 AutoReadLock alock(this);
2590
2591 for (MachineList::iterator it = m->llMachines.begin();
2592 it != m->llMachines.end();
2593 ++it)
2594 {
2595 ComObjPtr<SessionMachine> sm;
2596 ComPtr<IInternalSessionControl> ctl;
2597 if ((*it)->isSessionOpen(sm, &ctl))
2598 {
2599 aMachines.push_back(sm);
2600 if (aControls)
2601 aControls->push_back(ctl);
2602 }
2603 }
2604}
2605
2606/**
2607 * Searches for a Machine object with the given ID in the collection
2608 * of registered machines.
2609 *
2610 * @param id
2611 * ID of the machine
2612 * @param doSetError
2613 * if TRUE, the appropriate error info is set in case when the machine
2614 * is not found
2615 * @param machine
2616 * where to store the found machine object (can be NULL)
2617 *
2618 * @return
2619 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
2620 *
2621 * @note Locks this object for reading.
2622 */
2623HRESULT VirtualBox::findMachine (const Guid &aId, bool aSetError,
2624 ComObjPtr<Machine> *aMachine /* = NULL */)
2625{
2626 AutoCaller autoCaller(this);
2627 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2628
2629 bool found = false;
2630
2631 {
2632 AutoReadLock alock(this);
2633
2634 for (MachineList::iterator it = m->llMachines.begin();
2635 !found && it != m->llMachines.end();
2636 ++ it)
2637 {
2638 /* sanity */
2639 AutoLimitedCaller machCaller (*it);
2640 AssertComRC (machCaller.rc());
2641
2642 found = (*it)->id() == aId;
2643 if (found && aMachine)
2644 *aMachine = *it;
2645 }
2646 }
2647
2648 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2649
2650 if (aSetError && !found)
2651 {
2652 setError (VBOX_E_OBJECT_NOT_FOUND,
2653 tr ("Could not find a registered machine with UUID {%RTuuid}"),
2654 aId.raw());
2655 }
2656
2657 return rc;
2658}
2659
2660/**
2661 * Searches for a Medium object with the given ID or location in the list of
2662 * registered hard disks. If both ID and location are specified, the first
2663 * object that matches either of them (not necessarily both) is returned.
2664 *
2665 * @param aId ID of the hard disk (unused when NULL).
2666 * @param aLocation Full location specification (unused NULL).
2667 * @param aSetError If @c true , the appropriate error info is set in case
2668 * when the hard disk is not found.
2669 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2670 *
2671 * @return S_OK when found or E_INVALIDARG when not found.
2672 *
2673 * @note Locks this object and hard disk objects for reading.
2674 */
2675HRESULT VirtualBox::findHardDisk(const Guid *aId,
2676 CBSTR aLocation,
2677 bool aSetError,
2678 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2679{
2680 AssertReturn(aId || aLocation, E_INVALIDARG);
2681
2682 AutoReadLock alock(this);
2683
2684 /* first, look up by UUID in the map if UUID is provided */
2685 if (aId)
2686 {
2687 HardDiskMap::const_iterator it = m->mapHardDisks.find (*aId);
2688 if (it != m->mapHardDisks.end())
2689 {
2690 if (aHardDisk)
2691 *aHardDisk = (*it).second;
2692 return S_OK;
2693 }
2694 }
2695
2696 /* then iterate and search by location */
2697 int result = -1;
2698 if (aLocation)
2699 {
2700 Utf8Str location = aLocation;
2701
2702 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
2703 it != m->mapHardDisks.end();
2704 ++ it)
2705 {
2706 const ComObjPtr<Medium> &hd = (*it).second;
2707
2708 HRESULT rc = hd->compareLocationTo(location.c_str(), result);
2709 CheckComRCReturnRC(rc);
2710
2711 if (result == 0)
2712 {
2713 if (aHardDisk)
2714 *aHardDisk = hd;
2715 break;
2716 }
2717 }
2718 }
2719
2720 HRESULT rc = result == 0 ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2721
2722 if (aSetError && result != 0)
2723 {
2724 if (aId)
2725 setError(rc,
2726 tr("Could not find a hard disk with UUID {%RTuuid} in the media registry ('%s')"),
2727 aId->raw(),
2728 m->strSettingsFilePath.raw());
2729 else
2730 setError(rc,
2731 tr("Could not find a hard disk with location '%ls' in the media registry ('%s')"),
2732 aLocation,
2733 m->strSettingsFilePath.raw());
2734 }
2735
2736 return rc;
2737}
2738
2739/**
2740 * Searches for a Medium object with the given ID or location in the list of
2741 * registered DVD images. If both ID and file path are specified, the first
2742 * object that matches either of them (not necessarily both) is returned.
2743 *
2744 * @param aId ID of the DVD image (unused when NULL).
2745 * @param aLocation Full path to the image file (unused when NULL).
2746 * @param aSetError If @c true, the appropriate error info is set in case when
2747 * the image is not found.
2748 * @param aImage Where to store the found image object (can be NULL).
2749 *
2750 * @return S_OK when found or E_INVALIDARG when not found.
2751 *
2752 * @note Locks this object and image objects for reading.
2753 */
2754HRESULT VirtualBox::findDVDImage(const Guid *aId,
2755 CBSTR aLocation,
2756 bool aSetError,
2757 ComObjPtr<Medium> *aImage /* = NULL */)
2758{
2759 AssertReturn(aId || aLocation, E_INVALIDARG);
2760
2761 Utf8Str location;
2762
2763 if (aLocation != NULL)
2764 {
2765 int vrc = calculateFullPath(Utf8Str(aLocation), location);
2766 if (RT_FAILURE(vrc))
2767 return setError(VBOX_E_FILE_ERROR,
2768 tr("Invalid image file location '%ls' (%Rrc)"),
2769 aLocation,
2770 vrc);
2771 }
2772
2773 AutoReadLock alock(this);
2774
2775 bool found = false;
2776
2777 for (DVDImageList::const_iterator it = m->llDVDImages.begin();
2778 it != m->llDVDImages.end();
2779 ++ it)
2780 {
2781 /* no AutoCaller, registered image life time is bound to this */
2782 AutoReadLock imageLock (*it);
2783
2784 found = (aId && (*it)->id() == *aId) ||
2785 (aLocation != NULL &&
2786 RTPathCompare(location.c_str(),
2787 (*it)->locationFull().c_str()
2788 ) == 0);
2789 if (found)
2790 {
2791 if (aImage)
2792 *aImage = *it;
2793 break;
2794 }
2795 }
2796
2797 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2798
2799 if (aSetError && !found)
2800 {
2801 if (aId)
2802 setError(rc,
2803 tr("Could not find a CD/DVD image with UUID {%RTuuid} in the media registry ('%s')"),
2804 aId->raw(),
2805 m->strSettingsFilePath.raw());
2806 else
2807 setError(rc,
2808 tr("Could not find a CD/DVD image with location '%ls' in the media registry ('%s')"),
2809 aLocation,
2810 m->strSettingsFilePath.raw());
2811 }
2812
2813 return rc;
2814}
2815
2816/**
2817 * Searches for a Medium object with the given ID or location in the
2818 * collection of registered DVD images. If both ID and file path are specified,
2819 * the first object that matches either of them (not necessarily both) is
2820 * returned.
2821 *
2822 * @param aId ID of the DVD image (unused when NULL).
2823 * @param aLocation Full path to the image file (unused when NULL).
2824 * @param aSetError If @c true, the appropriate error info is set in case when
2825 * the image is not found.
2826 * @param aImage Where to store the found image object (can be NULL).
2827 *
2828 * @return S_OK when found or E_INVALIDARG when not found.
2829 *
2830 * @note Locks this object and image objects for reading.
2831 */
2832HRESULT VirtualBox::findFloppyImage(const Guid *aId, CBSTR aLocation,
2833 bool aSetError,
2834 ComObjPtr<Medium> *aImage /* = NULL */)
2835{
2836 AssertReturn(aId || aLocation, E_INVALIDARG);
2837
2838 Utf8Str location;
2839
2840 if (aLocation != NULL)
2841 {
2842 int vrc = calculateFullPath(Utf8Str(aLocation), location);
2843 if (RT_FAILURE(vrc))
2844 return setError (VBOX_E_FILE_ERROR,
2845 tr ("Invalid image file location '%ls' (%Rrc)"),
2846 aLocation, vrc);
2847 }
2848
2849 AutoReadLock alock(this);
2850
2851 bool found = false;
2852
2853 for (FloppyImageList::const_iterator it = m->llFloppyImages.begin();
2854 it != m->llFloppyImages.end();
2855 ++ it)
2856 {
2857 /* no AutoCaller, registered image life time is bound to this */
2858 AutoReadLock imageLock (*it);
2859
2860 found = (aId && (*it)->id() == *aId) ||
2861 (aLocation != NULL &&
2862 RTPathCompare(location.c_str(),
2863 (*it)->locationFull().c_str()
2864 ) == 0);
2865 if (found)
2866 {
2867 if (aImage)
2868 *aImage = *it;
2869 break;
2870 }
2871 }
2872
2873 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2874
2875 if (aSetError && !found)
2876 {
2877 if (aId)
2878 setError(rc,
2879 tr("Could not find a floppy image with UUID {%RTuuid} in the media registry ('%s')"),
2880 aId->raw(),
2881 m->strSettingsFilePath.raw());
2882 else
2883 setError(rc,
2884 tr("Could not find a floppy image with location '%ls' in the media registry ('%s')"),
2885 aLocation,
2886 m->strSettingsFilePath.raw());
2887 }
2888
2889 return rc;
2890}
2891
2892HRESULT VirtualBox::findGuestOSType(CBSTR bstrOSType,
2893 GuestOSType*& pGuestOSType)
2894{
2895 AutoReadLock alock(this);
2896
2897 /* Look for a GuestOSType object */
2898 AssertMsg(m->llGuestOSTypes.size() != 0,
2899 ("Guest OS types array must be filled"));
2900
2901 if (bstrOSType == NULL)
2902 {
2903 pGuestOSType = NULL;
2904 return S_OK;
2905 }
2906
2907 for (GuestOSTypeList::const_iterator it = m->llGuestOSTypes.begin();
2908 it != m->llGuestOSTypes.end();
2909 ++it)
2910 {
2911 if ((*it)->id() == bstrOSType)
2912 {
2913 pGuestOSType = *it;
2914 return S_OK;
2915 }
2916 }
2917
2918 return setError(VBOX_E_OBJECT_NOT_FOUND,
2919 tr("Guest OS type '%ls' is invalid"),
2920 bstrOSType);
2921}
2922
2923const ComObjPtr<Host>& VirtualBox::host() const
2924{
2925 return m->pHost;
2926}
2927
2928const ComObjPtr<SystemProperties>& VirtualBox::systemProperties() const
2929{
2930 return m->pSystemProperties;
2931}
2932
2933#ifdef VBOX_WITH_RESOURCE_USAGE_API
2934const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
2935{
2936 return m->pPerformanceCollector;
2937}
2938#endif /* VBOX_WITH_RESOURCE_USAGE_API */
2939
2940/**
2941 * Returns the default machine folder from the system properties
2942 * with proper locking.
2943 * @return
2944 */
2945const Utf8Str& VirtualBox::getDefaultMachineFolder() const
2946{
2947 AutoReadLock propsLock(m->pSystemProperties);
2948 return m->pSystemProperties->m_strDefaultMachineFolder;
2949}
2950
2951/**
2952 * Returns the default hard disk folder from the system properties
2953 * with proper locking.
2954 * @return
2955 */
2956const Utf8Str& VirtualBox::getDefaultHardDiskFolder() const
2957{
2958 AutoReadLock propsLock(m->pSystemProperties);
2959 return m->pSystemProperties->m_strDefaultHardDiskFolder;
2960}
2961
2962/**
2963 * Returns the default hard disk format from the system properties
2964 * with proper locking.
2965 * @return
2966 */
2967const Utf8Str& VirtualBox::getDefaultHardDiskFormat() const
2968{
2969 AutoReadLock propsLock(m->pSystemProperties);
2970 return m->pSystemProperties->m_strDefaultHardDiskFormat;
2971}
2972
2973const Utf8Str& VirtualBox::homeDir() const
2974{
2975 return m->strHomeDir;
2976}
2977
2978/**
2979 * Calculates the absolute path of the given path taking the VirtualBox home
2980 * directory as the current directory.
2981 *
2982 * @param aPath Path to calculate the absolute path for.
2983 * @param aResult Where to put the result (used only on success, can be the
2984 * same Utf8Str instance as passed in @a aPath).
2985 * @return IPRT result.
2986 *
2987 * @note Doesn't lock any object.
2988 */
2989int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
2990{
2991 AutoCaller autoCaller(this);
2992 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
2993
2994 /* no need to lock since mHomeDir is const */
2995
2996 char folder[RTPATH_MAX];
2997 int vrc = RTPathAbsEx(m->strHomeDir.c_str(), strPath.c_str(), folder, sizeof(folder));
2998 if (RT_SUCCESS(vrc))
2999 aResult = folder;
3000
3001 return vrc;
3002}
3003
3004/**
3005 * Tries to calculate the relative path of the given absolute path using the
3006 * directory of the VirtualBox settings file as the base directory.
3007 *
3008 * @param aPath Absolute path to calculate the relative path for.
3009 * @param aResult Where to put the result (used only when it's possible to
3010 * make a relative path from the given absolute path; otherwise
3011 * left untouched).
3012 *
3013 * @note Doesn't lock any object.
3014 */
3015void VirtualBox::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
3016{
3017 AutoCaller autoCaller(this);
3018 AssertComRCReturnVoid (autoCaller.rc());
3019
3020 /* no need to lock since mHomeDir is const */
3021
3022 Utf8Str settingsDir = m->strHomeDir;
3023
3024 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
3025 {
3026 /* when assigning, we create a separate Utf8Str instance because both
3027 * aPath and aResult can point to the same memory location when this
3028 * func is called (if we just do aResult = aPath, aResult will be freed
3029 * first, and since its the same as aPath, an attempt to copy garbage
3030 * will be made. */
3031 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
3032 }
3033}
3034
3035// private methods
3036/////////////////////////////////////////////////////////////////////////////
3037
3038/**
3039 * Checks if there is a hard disk, DVD or floppy image with the given ID or
3040 * location already registered.
3041 *
3042 * On return, sets @aConflict to the string describing the conflicting medium,
3043 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3044 * either case. A failure is unexpected.
3045 *
3046 * @param aId UUID to check.
3047 * @param aLocation Location to check.
3048 * @param aConflict Where to return parameters of the conflicting medium.
3049 *
3050 * @note Locks this object and media objects for reading.
3051 */
3052HRESULT VirtualBox::checkMediaForConflicts2 (const Guid &aId,
3053 const Utf8Str &aLocation,
3054 Utf8Str &aConflict)
3055{
3056 aConflict.setNull();
3057
3058 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
3059
3060 AutoReadLock alock(this);
3061
3062 HRESULT rc = S_OK;
3063
3064 Bstr bstrLocation(aLocation);
3065
3066 {
3067 ComObjPtr<Medium> hardDisk;
3068 rc = findHardDisk(&aId, bstrLocation, false /* aSetError */, &hardDisk);
3069 if (SUCCEEDED(rc))
3070 {
3071 /* Note: no AutoCaller since bound to this */
3072 AutoReadLock mediaLock (hardDisk);
3073 aConflict = Utf8StrFmt (
3074 tr ("hard disk '%s' with UUID {%RTuuid}"),
3075 hardDisk->locationFull().raw(), hardDisk->id().raw());
3076 return S_OK;
3077 }
3078 }
3079
3080 {
3081 ComObjPtr<Medium> image;
3082 rc = findDVDImage (&aId, bstrLocation, false /* aSetError */, &image);
3083 if (SUCCEEDED(rc))
3084 {
3085 /* Note: no AutoCaller since bound to this */
3086 AutoReadLock mediaLock (image);
3087 aConflict = Utf8StrFmt (
3088 tr ("CD/DVD image '%s' with UUID {%RTuuid}"),
3089 image->locationFull().raw(), image->id().raw());
3090 return S_OK;
3091 }
3092 }
3093
3094 {
3095 ComObjPtr<Medium> image;
3096 rc = findFloppyImage(&aId, bstrLocation, false /* aSetError */, &image);
3097 if (SUCCEEDED(rc))
3098 {
3099 /* Note: no AutoCaller since bound to this */
3100 AutoReadLock mediaLock (image);
3101 aConflict = Utf8StrFmt (
3102 tr ("floppy image '%s' with UUID {%RTuuid}"),
3103 image->locationFull().raw(), image->id().raw());
3104 return S_OK;
3105 }
3106 }
3107
3108 return S_OK;
3109}
3110
3111/**
3112 * Helper function to write out the configuration tree.
3113 *
3114 * @note Locks this object for writing and child objects for reading/writing!
3115 */
3116HRESULT VirtualBox::saveSettings()
3117{
3118 AutoCaller autoCaller (this);
3119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3120
3121 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3122
3123 HRESULT rc = S_OK;
3124
3125 /* serialize file access (prevents concurrent reads and writes) */
3126 AutoWriteLock alock(this);
3127
3128 try
3129 {
3130 // machines
3131 m->pMainConfigFile->llMachines.clear();
3132 for (MachineList::iterator it = m->llMachines.begin();
3133 it != m->llMachines.end();
3134 ++it)
3135 {
3136 settings::MachineRegistryEntry mre;
3137 rc = (*it)->saveRegistryEntry(mre);
3138 m->pMainConfigFile->llMachines.push_back(mre);
3139 }
3140
3141 // hard disks
3142 m->pMainConfigFile->llHardDisks.clear();
3143 for (HardDiskList::const_iterator it = m->llHardDisks.begin();
3144 it != m->llHardDisks.end();
3145 ++it)
3146 {
3147 settings::Medium med;
3148 rc = (*it)->saveSettings(med);
3149 m->pMainConfigFile->llHardDisks.push_back(med);
3150 CheckComRCThrowRC(rc);
3151 }
3152
3153 /* CD/DVD images */
3154 m->pMainConfigFile->llDvdImages.clear();
3155 for (DVDImageList::const_iterator it = m->llDVDImages.begin();
3156 it != m->llDVDImages.end();
3157 ++it)
3158 {
3159 settings::Medium med;
3160 rc = (*it)->saveSettings(med);
3161 CheckComRCThrowRC(rc);
3162 m->pMainConfigFile->llDvdImages.push_back(med);
3163 }
3164
3165 /* floppy images */
3166 m->pMainConfigFile->llFloppyImages.clear();
3167 for (FloppyImageList::const_iterator it = m->llFloppyImages.begin();
3168 it != m->llFloppyImages.end();
3169 ++it)
3170 {
3171 settings::Medium med;
3172 rc = (*it)->saveSettings(med);
3173 CheckComRCThrowRC(rc);
3174 m->pMainConfigFile->llFloppyImages.push_back(med);
3175 }
3176
3177 m->pMainConfigFile->llDhcpServers.clear();
3178 for (DHCPServerList::const_iterator it =
3179 m->llDHCPServers.begin();
3180 it != m->llDHCPServers.end();
3181 ++ it)
3182 {
3183 settings::DHCPServer d;
3184 rc = (*it)->saveSettings(d);
3185 CheckComRCThrowRC(rc);
3186 m->pMainConfigFile->llDhcpServers.push_back(d);
3187 }
3188
3189 /* host data (USB filters) */
3190 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3191 CheckComRCThrowRC(rc);
3192
3193 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3194 CheckComRCThrowRC(rc);
3195
3196 // now write out the XML
3197 m->pMainConfigFile->write(m->strSettingsFilePath);
3198 }
3199 catch (HRESULT err)
3200 {
3201 /* we assume that error info is set by the thrower */
3202 rc = err;
3203 }
3204 catch (...)
3205 {
3206 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3207 }
3208
3209 return rc;
3210}
3211
3212/**
3213 * Helper to register the machine.
3214 *
3215 * When called during VirtualBox startup, adds the given machine to the
3216 * collection of registered machines. Otherwise tries to mark the machine
3217 * as registered, and, if succeeded, adds it to the collection and
3218 * saves global settings.
3219 *
3220 * @note The caller must have added itself as a caller of the @a aMachine
3221 * object if calls this method not on VirtualBox startup.
3222 *
3223 * @param aMachine machine to register
3224 *
3225 * @note Locks objects!
3226 */
3227HRESULT VirtualBox::registerMachine (Machine *aMachine)
3228{
3229 ComAssertRet (aMachine, E_INVALIDARG);
3230
3231 AutoCaller autoCaller(this);
3232 CheckComRCReturnRC(autoCaller.rc());
3233
3234 AutoWriteLock alock(this);
3235
3236 HRESULT rc = S_OK;
3237
3238 {
3239 ComObjPtr<Machine> m;
3240 rc = findMachine (aMachine->id(), false /* aDoSetError */, &m);
3241 if (SUCCEEDED(rc))
3242 {
3243 /* sanity */
3244 AutoLimitedCaller machCaller (m);
3245 AssertComRC (machCaller.rc());
3246
3247 return setError (E_INVALIDARG,
3248 tr ("Registered machine with UUID {%RTuuid} ('%ls') already exists"),
3249 aMachine->id().raw(), m->settingsFileFull().raw());
3250 }
3251
3252 ComAssertRet (rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3253 rc = S_OK;
3254 }
3255
3256 if (autoCaller.state() != InInit)
3257 {
3258 /* Machine::trySetRegistered() will commit and save machine settings */
3259 rc = aMachine->trySetRegistered (TRUE);
3260 CheckComRCReturnRC(rc);
3261 }
3262
3263 /* add to the collection of registered machines */
3264 m->llMachines.push_back(aMachine);
3265
3266 if (autoCaller.state() != InInit)
3267 rc = saveSettings();
3268
3269 return rc;
3270}
3271
3272/**
3273 * Remembers the given hard disk by storing it in the hard disk registry.
3274 *
3275 * @param aHardDisk Hard disk object to remember.
3276 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3277 *
3278 * When @a aSaveRegistry is @c true, this operation may fail because of the
3279 * failed #saveSettings() method it calls. In this case, the hard disk object
3280 * will not be remembered. It is therefore the responsibility of the caller to
3281 * call this method as the last step of some action that requires registration
3282 * in order to make sure that only fully functional hard disk objects get
3283 * registered.
3284 *
3285 * @note Locks this object for writing and @a aHardDisk for reading.
3286 */
3287HRESULT VirtualBox::registerHardDisk(Medium *aHardDisk,
3288 bool aSaveRegistry /*= true*/)
3289{
3290 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3291
3292 AutoCaller autoCaller(this);
3293 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3294
3295 AutoWriteLock alock(this);
3296
3297 AutoCaller hardDiskCaller (aHardDisk);
3298 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3299
3300 AutoReadLock hardDiskLock (aHardDisk);
3301
3302 Utf8Str strConflict;
3303 HRESULT rc = checkMediaForConflicts2(aHardDisk->id(),
3304 aHardDisk->locationFull(),
3305 strConflict);
3306 CheckComRCReturnRC(rc);
3307
3308 if (strConflict.length())
3309 {
3310 return setError(E_INVALIDARG,
3311 tr("Cannot register the hard disk '%ls' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3312 aHardDisk->locationFull().raw(),
3313 aHardDisk->id().raw(),
3314 strConflict.raw(),
3315 m->strSettingsFilePath.raw());
3316 }
3317
3318 if (aHardDisk->parent().isNull())
3319 {
3320 /* base (root) hard disk */
3321 m->llHardDisks.push_back (aHardDisk);
3322 }
3323
3324 m->mapHardDisks
3325 .insert (HardDiskMap::value_type (
3326 aHardDisk->id(), HardDiskMap::mapped_type (aHardDisk)));
3327
3328 if (aSaveRegistry)
3329 {
3330 rc = saveSettings();
3331 if (FAILED (rc))
3332 unregisterHardDisk(aHardDisk, false /* aSaveRegistry */);
3333 }
3334
3335 return rc;
3336}
3337
3338/**
3339 * Removes the given hard disk from the hard disk registry.
3340 *
3341 * @param aHardDisk Hard disk object to remove.
3342 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3343 *
3344 * When @a aSaveRegistry is @c true, this operation may fail because of the
3345 * failed #saveSettings() method it calls. In this case, the hard disk object
3346 * will NOT be removed from the registry when this method returns. It is
3347 * therefore the responsibility of the caller to call this method as the first
3348 * step of some action that requires unregistration, before calling uninit() on
3349 * @a aHardDisk.
3350 *
3351 * @note Locks this object for writing and @a aHardDisk for reading.
3352 */
3353HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3354 bool aSaveRegistry /*= true*/)
3355{
3356 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3357
3358 AutoCaller autoCaller(this);
3359 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3360
3361 AutoWriteLock alock(this);
3362
3363 AutoCaller hardDiskCaller (aHardDisk);
3364 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3365
3366 AutoReadLock hardDiskLock (aHardDisk);
3367
3368 size_t cnt = m->mapHardDisks.erase (aHardDisk->id());
3369 Assert (cnt == 1);
3370 NOREF(cnt);
3371
3372 if (aHardDisk->parent().isNull())
3373 {
3374 /* base (root) hard disk */
3375 m->llHardDisks.remove (aHardDisk);
3376 }
3377
3378 HRESULT rc = S_OK;
3379
3380 if (aSaveRegistry)
3381 {
3382 rc = saveSettings();
3383 if (FAILED (rc))
3384 registerHardDisk(aHardDisk, false /* aSaveRegistry */);
3385 }
3386
3387 return rc;
3388}
3389
3390/**
3391 * Remembers the given image by storing it in the CD/DVD image registry.
3392 *
3393 * @param aImage Image object to remember.
3394 * @param aSaveRegistry @c true to save the image registry to disk (default).
3395 *
3396 * When @a aSaveRegistry is @c true, this operation may fail because of the
3397 * failed #saveSettings() method it calls. In this case, the image object
3398 * will not be remembered. It is therefore the responsibility of the caller to
3399 * call this method as the last step of some action that requires registration
3400 * in order to make sure that only fully functional image objects get
3401 * registered.
3402 *
3403 * @note Locks this object for writing and @a aImage for reading.
3404 */
3405HRESULT VirtualBox::registerDVDImage (Medium *aImage,
3406 bool aSaveRegistry /*= true*/)
3407{
3408 AssertReturn(aImage != NULL, E_INVALIDARG);
3409
3410 AutoCaller autoCaller(this);
3411 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3412
3413 AutoWriteLock alock(this);
3414
3415 AutoCaller imageCaller (aImage);
3416 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3417
3418 AutoReadLock imageLock (aImage);
3419
3420 Utf8Str strConflict;
3421 HRESULT rc = checkMediaForConflicts2(aImage->id(),
3422 aImage->locationFull(),
3423 strConflict);
3424 CheckComRCReturnRC(rc);
3425
3426 if (strConflict.length())
3427 {
3428 return setError(VBOX_E_INVALID_OBJECT_STATE,
3429 tr("Cannot register the CD/DVD image '%ls' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3430 aImage->locationFull().raw(),
3431 aImage->id().raw(),
3432 strConflict.raw(),
3433 m->strSettingsFilePath.raw());
3434 }
3435
3436 /* add to the collection */
3437 m->llDVDImages.push_back (aImage);
3438
3439 if (aSaveRegistry)
3440 {
3441 rc = saveSettings();
3442 if (FAILED (rc))
3443 unregisterDVDImage (aImage, false /* aSaveRegistry */);
3444 }
3445
3446 return rc;
3447}
3448
3449/**
3450 * Removes the given image from the CD/DVD image registry registry.
3451 *
3452 * @param aImage Image object to remove.
3453 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3454 *
3455 * When @a aSaveRegistry is @c true, this operation may fail because of the
3456 * failed #saveSettings() method it calls. In this case, the image object
3457 * will NOT be removed from the registry when this method returns. It is
3458 * therefore the responsibility of the caller to call this method as the first
3459 * step of some action that requires unregistration, before calling uninit() on
3460 * @a aImage.
3461 *
3462 * @note Locks this object for writing and @a aImage for reading.
3463 */
3464HRESULT VirtualBox::unregisterDVDImage (Medium *aImage,
3465 bool aSaveRegistry /*= true*/)
3466{
3467 AssertReturn(aImage != NULL, E_INVALIDARG);
3468
3469 AutoCaller autoCaller(this);
3470 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3471
3472 AutoWriteLock alock(this);
3473
3474 AutoCaller imageCaller (aImage);
3475 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3476
3477 AutoReadLock imageLock (aImage);
3478
3479 m->llDVDImages.remove (aImage);
3480
3481 HRESULT rc = S_OK;
3482
3483 if (aSaveRegistry)
3484 {
3485 rc = saveSettings();
3486 if (FAILED (rc))
3487 registerDVDImage (aImage, false /* aSaveRegistry */);
3488 }
3489
3490 return rc;
3491}
3492
3493/**
3494 * Remembers the given image by storing it in the floppy image registry.
3495 *
3496 * @param aImage Image object to remember.
3497 * @param aSaveRegistry @c true to save the image registry to disk (default).
3498 *
3499 * When @a aSaveRegistry is @c true, this operation may fail because of the
3500 * failed #saveSettings() method it calls. In this case, the image object
3501 * will not be remembered. It is therefore the responsibility of the caller to
3502 * call this method as the last step of some action that requires registration
3503 * in order to make sure that only fully functional image objects get
3504 * registered.
3505 *
3506 * @note Locks this object for writing and @a aImage for reading.
3507 */
3508HRESULT VirtualBox::registerFloppyImage(Medium *aImage,
3509 bool aSaveRegistry /*= true*/)
3510{
3511 AssertReturn(aImage != NULL, E_INVALIDARG);
3512
3513 AutoCaller autoCaller(this);
3514 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3515
3516 AutoWriteLock alock(this);
3517
3518 AutoCaller imageCaller (aImage);
3519 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3520
3521 AutoReadLock imageLock (aImage);
3522
3523 Utf8Str strConflict;
3524 HRESULT rc = checkMediaForConflicts2(aImage->id(),
3525 aImage->locationFull(),
3526 strConflict);
3527 CheckComRCReturnRC(rc);
3528
3529 if (strConflict.length())
3530 {
3531 return setError(VBOX_E_INVALID_OBJECT_STATE,
3532 tr("Cannot register the floppy image '%ls' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3533 aImage->locationFull().raw(),
3534 aImage->id().raw(),
3535 strConflict.raw(),
3536 m->strSettingsFilePath.raw());
3537 }
3538
3539 /* add to the collection */
3540 m->llFloppyImages.push_back (aImage);
3541
3542 if (aSaveRegistry)
3543 {
3544 rc = saveSettings();
3545 if (FAILED (rc))
3546 unregisterFloppyImage(aImage, false /* aSaveRegistry */);
3547 }
3548
3549 return rc;
3550}
3551
3552/**
3553 * Removes the given image from the floppy image registry registry.
3554 *
3555 * @param aImage Image object to remove.
3556 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3557 *
3558 * When @a aSaveRegistry is @c true, this operation may fail because of the
3559 * failed #saveSettings() method it calls. In this case, the image object
3560 * will NOT be removed from the registry when this method returns. It is
3561 * therefore the responsibility of the caller to call this method as the first
3562 * step of some action that requires unregistration, before calling uninit() on
3563 * @a aImage.
3564 *
3565 * @note Locks this object for writing and @a aImage for reading.
3566 */
3567HRESULT VirtualBox::unregisterFloppyImage(Medium *aImage,
3568 bool aSaveRegistry /*= true*/)
3569{
3570 AssertReturn(aImage != NULL, E_INVALIDARG);
3571
3572 AutoCaller autoCaller(this);
3573 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3574
3575 AutoWriteLock alock(this);
3576
3577 AutoCaller imageCaller (aImage);
3578 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3579
3580 AutoReadLock imageLock (aImage);
3581
3582 m->llFloppyImages.remove (aImage);
3583
3584 HRESULT rc = S_OK;
3585
3586 if (aSaveRegistry)
3587 {
3588 rc = saveSettings();
3589 if (FAILED (rc))
3590 registerFloppyImage (aImage, false /* aSaveRegistry */);
3591 }
3592
3593 return rc;
3594}
3595
3596/**
3597 * Attempts to cast from a raw interface pointer to an underlying object.
3598 * On success, @a aTo will contain the object reference. On failure, @a aTo will
3599 * be set to @c null and an extended error info will be returned.
3600 *
3601 * @param aFrom Interface pointer to cast from.
3602 * @param aTo Where to store a reference to the underlying object.
3603 *
3604 * @note Locks #childrenLock() for reading.
3605 */
3606HRESULT VirtualBox::cast (IMedium *aFrom, ComObjPtr<Medium> &aTo)
3607{
3608 AssertReturn(aFrom != NULL, E_INVALIDARG);
3609
3610 AutoCaller autoCaller(this);
3611 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3612
3613 /* We need the children map lock here to keep the getDependentChild() result
3614 * valid until we finish */
3615 AutoReadLock chLock (childrenLock());
3616
3617 VirtualBoxBase *child = getDependentChild (aFrom);
3618 if (!child)
3619 return setError (E_FAIL, tr ("The given hard disk object is not created "
3620 "within this VirtualBox instance"));
3621
3622 /* we can safely cast child to Medium * here because only Medium
3623 * implementations of IMedium can be among our children */
3624
3625 aTo = static_cast<Medium*>(child);
3626
3627 return S_OK;
3628}
3629
3630/**
3631 * Helper to update the global settings file when the name of some machine
3632 * changes so that file and directory renaming occurs. This method ensures that
3633 * all affected paths in the disk registry are properly updated.
3634 *
3635 * @param aOldPath Old path (full).
3636 * @param aNewPath New path (full).
3637 *
3638 * @note Locks this object + DVD, Floppy and HardDisk children for writing.
3639 */
3640HRESULT VirtualBox::updateSettings (const char *aOldPath, const char *aNewPath)
3641{
3642 LogFlowThisFunc(("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
3643
3644 AssertReturn(aOldPath, E_INVALIDARG);
3645 AssertReturn(aNewPath, E_INVALIDARG);
3646
3647 AutoCaller autoCaller(this);
3648 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3649
3650 AutoWriteLock alock(this);
3651
3652 /* check DVD paths */
3653 for (DVDImageList::iterator it = m->llDVDImages.begin();
3654 it != m->llDVDImages.end();
3655 ++ it)
3656 {
3657 (*it)->updatePath (aOldPath, aNewPath);
3658 }
3659
3660 /* check Floppy paths */
3661 for (FloppyImageList::iterator it = m->llFloppyImages.begin();
3662 it != m->llFloppyImages .end();
3663 ++ it)
3664 {
3665 (*it)->updatePath (aOldPath, aNewPath);
3666 }
3667
3668 /* check HardDisk paths */
3669 for (HardDiskList::const_iterator it = m->llHardDisks.begin();
3670 it != m->llHardDisks.end();
3671 ++ it)
3672 {
3673 (*it)->updatePaths (aOldPath, aNewPath);
3674 }
3675
3676 HRESULT rc = saveSettings();
3677
3678 return rc;
3679}
3680
3681/**
3682 * Creates the path to the specified file according to the path information
3683 * present in the file name.
3684 *
3685 * Note that the given file name must contain the full path otherwise the
3686 * extracted relative path will be created based on the current working
3687 * directory which is normally unknown.
3688 *
3689 * @param aFileName Full file name which path needs to be created.
3690 *
3691 * @return Extended error information on failure to create the path.
3692 */
3693/* static */
3694HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3695{
3696 Utf8Str strDir(strFileName);
3697 strDir.stripFilename();
3698 if (!RTDirExists(strDir.c_str()))
3699 {
3700 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3701 if (RT_FAILURE(vrc))
3702 return setError(E_FAIL,
3703 tr("Could not create the directory '%s' (%Rrc)"),
3704 strDir.c_str(),
3705 vrc);
3706 }
3707
3708 return S_OK;
3709}
3710
3711/**
3712 * Handles unexpected exceptions by turning them into COM errors in release
3713 * builds or by hitting a breakpoint in the release builds.
3714 *
3715 * Usage pattern:
3716 * @code
3717 try
3718 {
3719 // ...
3720 }
3721 catch (LaLalA)
3722 {
3723 // ...
3724 }
3725 catch (...)
3726 {
3727 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3728 }
3729 * @endcode
3730 *
3731 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3732 */
3733/* static */
3734HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3735{
3736 try
3737 {
3738 /* re-throw the current exception */
3739 throw;
3740 }
3741 catch (const xml::Error &err)
3742 {
3743 return setError(E_FAIL, tr("%s.\n%s[%d] (%s)"),
3744 err.what(),
3745 pszFile, iLine, pszFunction);
3746 }
3747 catch (const std::exception &err)
3748 {
3749 return setError(E_FAIL, tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3750 err.what(), typeid(err).name(),
3751 pszFile, iLine, pszFunction);
3752 }
3753 catch (...)
3754 {
3755 return setError(E_FAIL, tr("Unknown exception\n%s[%d] (%s)"),
3756 pszFile, iLine, pszFunction);
3757 }
3758
3759 /* should not get here */
3760 AssertFailed();
3761 return E_FAIL;
3762}
3763
3764const Utf8Str& VirtualBox::settingsFilePath()
3765{
3766 return m->strSettingsFilePath;
3767}
3768
3769/**
3770 * Returns a lock handle used to protect changes to the hard disk hierarchy
3771 * (e.g. serialize access to the Medium::mParent fields and methods
3772 * adding/removing children). When using this lock, the following rules must
3773 * be obeyed:
3774 *
3775 * 1. The write lock on this handle must be either held alone on the thread
3776 * or requested *after* the VirtualBox object lock. Mixing with other
3777 * locks is prohibited.
3778 *
3779 * 2. The read lock on this handle may be intermixed with any other lock
3780 * with the exception that it must be requested *after* the VirtualBox
3781 * object lock.
3782 */
3783RWLockHandle& VirtualBox::hardDiskTreeLockHandle()
3784{
3785 return m->mtxHardDiskTree;
3786}
3787
3788/**
3789 * Reimplements VirtualBoxWithTypedChildren::childrenLock() to return a
3790 * dedicated lock instead of the main object lock. The dedicated lock for
3791 * child map operations frees callers of init() methods of these children
3792 * from acquiring a write parent (VirtualBox) lock (which would be mandatory
3793 * otherwise). Since VirtualBox has a lot of heterogenous children which
3794 * init() methods are called here and there, it definitely makes sense.
3795 */
3796RWLockHandle* VirtualBox::childrenLock()
3797{
3798 return &m->mtxChildrenMap;
3799}
3800
3801/**
3802 * Thread function that watches the termination of all client processes
3803 * that have opened sessions using IVirtualBox::OpenSession()
3804 */
3805// static
3806DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3807{
3808 LogFlowFuncEnter();
3809
3810 VirtualBox *that = (VirtualBox *) pvUser;
3811 Assert (that);
3812
3813 SessionMachineVector machines;
3814 MachineVector spawnedMachines;
3815
3816 size_t cnt = 0;
3817 size_t cntSpawned = 0;
3818
3819#if defined(RT_OS_WINDOWS)
3820
3821 HRESULT hrc = CoInitializeEx(NULL,
3822 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3823 COINIT_SPEED_OVER_MEMORY);
3824 AssertComRC (hrc);
3825
3826 /// @todo (dmik) processes reaping!
3827
3828 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3829 handles[0] = that->m->updateReq;
3830
3831 do
3832 {
3833 AutoCaller autoCaller(that);
3834 /* VirtualBox has been early uninitialized, terminate */
3835 if (!autoCaller.isOk())
3836 break;
3837
3838 do
3839 {
3840 /* release the caller to let uninit() ever proceed */
3841 autoCaller.release();
3842
3843 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3844 handles,
3845 FALSE,
3846 INFINITE);
3847
3848 /* Restore the caller before using VirtualBox. If it fails, this
3849 * means VirtualBox is being uninitialized and we must terminate. */
3850 autoCaller.add();
3851 if (!autoCaller.isOk())
3852 break;
3853
3854 bool update = false;
3855
3856 if (rc == WAIT_OBJECT_0)
3857 {
3858 /* update event is signaled */
3859 update = true;
3860 }
3861 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3862 {
3863 /* machine mutex is released */
3864 (machines [rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3865 update = true;
3866 }
3867 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3868 {
3869 /* machine mutex is abandoned due to client process termination */
3870 (machines [rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3871 update = true;
3872 }
3873 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3874 {
3875 /* spawned VM process has terminated (normally or abnormally) */
3876 (spawnedMachines [rc - WAIT_OBJECT_0 - cnt - 1])->
3877 checkForSpawnFailure();
3878 update = true;
3879 }
3880
3881 if (update)
3882 {
3883 /* close old process handles */
3884 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3885 CloseHandle (handles [i]);
3886
3887 AutoReadLock thatLock (that);
3888
3889 /* obtain a new set of opened machines */
3890 cnt = 0;
3891 machines.clear();
3892
3893 for (MachineList::iterator it = that->m->llMachines.begin();
3894 it != that->m->llMachines.end(); ++ it)
3895 {
3896 /// @todo handle situations with more than 64 objects
3897 AssertMsgBreak ((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3898 ("MAXIMUM_WAIT_OBJECTS reached"));
3899
3900 ComObjPtr<SessionMachine> sm;
3901 HANDLE ipcSem;
3902 if ((*it)->isSessionOpenOrClosing (sm, NULL, &ipcSem))
3903 {
3904 machines.push_back (sm);
3905 handles [1 + cnt] = ipcSem;
3906 ++ cnt;
3907 }
3908 }
3909
3910 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
3911
3912 /* obtain a new set of spawned machines */
3913 cntSpawned = 0;
3914 spawnedMachines.clear();
3915
3916 for (MachineList::iterator it = that->m->llMachines.begin();
3917 it != that->m->llMachines.end(); ++ it)
3918 {
3919 /// @todo handle situations with more than 64 objects
3920 AssertMsgBreak ((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
3921 ("MAXIMUM_WAIT_OBJECTS reached"));
3922
3923 RTPROCESS pid;
3924 if ((*it)->isSessionSpawning (&pid))
3925 {
3926 HANDLE ph = OpenProcess (SYNCHRONIZE, FALSE, pid);
3927 AssertMsg (ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
3928 pid, GetLastError()));
3929 if (rc == 0)
3930 {
3931 spawnedMachines.push_back (*it);
3932 handles [1 + cnt + cntSpawned] = ph;
3933 ++ cntSpawned;
3934 }
3935 }
3936 }
3937
3938 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
3939 }
3940 }
3941 while (true);
3942 }
3943 while (0);
3944
3945 /* close old process handles */
3946 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3947 CloseHandle (handles [i]);
3948
3949 /* release sets of machines if any */
3950 machines.clear();
3951 spawnedMachines.clear();
3952
3953 ::CoUninitialize();
3954
3955#elif defined (RT_OS_OS2)
3956
3957 /// @todo (dmik) processes reaping!
3958
3959 /* according to PMREF, 64 is the maximum for the muxwait list */
3960 SEMRECORD handles [64];
3961
3962 HMUX muxSem = NULLHANDLE;
3963
3964 do
3965 {
3966 AutoCaller autoCaller(that);
3967 /* VirtualBox has been early uninitialized, terminate */
3968 if (!autoCaller.isOk())
3969 break;
3970
3971 do
3972 {
3973 /* release the caller to let uninit() ever proceed */
3974 autoCaller.release();
3975
3976 int vrc = RTSemEventWait (that->m->updateReq, 500);
3977
3978 /* Restore the caller before using VirtualBox. If it fails, this
3979 * means VirtualBox is being uninitialized and we must terminate. */
3980 autoCaller.add();
3981 if (!autoCaller.isOk())
3982 break;
3983
3984 bool update = false;
3985 bool updateSpawned = false;
3986
3987 if (RT_SUCCESS(vrc))
3988 {
3989 /* update event is signaled */
3990 update = true;
3991 updateSpawned = true;
3992 }
3993 else
3994 {
3995 AssertMsg (vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
3996 ("RTSemEventWait returned %Rrc\n", vrc));
3997
3998 /* are there any mutexes? */
3999 if (cnt > 0)
4000 {
4001 /* figure out what's going on with machines */
4002
4003 unsigned long semId = 0;
4004 APIRET arc = ::DosWaitMuxWaitSem (muxSem,
4005 SEM_IMMEDIATE_RETURN, &semId);
4006
4007 if (arc == NO_ERROR)
4008 {
4009 /* machine mutex is normally released */
4010 Assert (semId >= 0 && semId < cnt);
4011 if (semId >= 0 && semId < cnt)
4012 {
4013#ifdef DEBUG
4014 {
4015 AutoReadLock machineLock (machines [semId]);
4016 LogFlowFunc (("released mutex: machine='%ls'\n",
4017 machines [semId]->name().raw()));
4018 }
4019#endif
4020 machines [semId]->checkForDeath();
4021 }
4022 update = true;
4023 }
4024 else if (arc == ERROR_SEM_OWNER_DIED)
4025 {
4026 /* machine mutex is abandoned due to client process
4027 * termination; find which mutex is in the Owner Died
4028 * state */
4029 for (size_t i = 0; i < cnt; ++ i)
4030 {
4031 PID pid; TID tid;
4032 unsigned long reqCnt;
4033 arc = DosQueryMutexSem ((HMTX) handles [i].hsemCur, &pid,
4034 &tid, &reqCnt);
4035 if (arc == ERROR_SEM_OWNER_DIED)
4036 {
4037 /* close the dead mutex as asked by PMREF */
4038 ::DosCloseMutexSem ((HMTX) handles [i].hsemCur);
4039
4040 Assert (i >= 0 && i < cnt);
4041 if (i >= 0 && i < cnt)
4042 {
4043#ifdef DEBUG
4044 {
4045 AutoReadLock machineLock (machines [semId]);
4046 LogFlowFunc (("mutex owner dead: machine='%ls'\n",
4047 machines [i]->name().raw()));
4048 }
4049#endif
4050 machines [i]->checkForDeath();
4051 }
4052 }
4053 }
4054 update = true;
4055 }
4056 else
4057 AssertMsg (arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4058 ("DosWaitMuxWaitSem returned %d\n", arc));
4059 }
4060
4061 /* are there any spawning sessions? */
4062 if (cntSpawned > 0)
4063 {
4064 for (size_t i = 0; i < cntSpawned; ++ i)
4065 updateSpawned |= (spawnedMachines [i])->
4066 checkForSpawnFailure();
4067 }
4068 }
4069
4070 if (update || updateSpawned)
4071 {
4072 AutoReadLock thatLock (that);
4073
4074 if (update)
4075 {
4076 /* close the old muxsem */
4077 if (muxSem != NULLHANDLE)
4078 ::DosCloseMuxWaitSem (muxSem);
4079
4080 /* obtain a new set of opened machines */
4081 cnt = 0;
4082 machines.clear();
4083
4084 for (MachineList::iterator it = that->m->llMachines.begin();
4085 it != that->m->llMachines.end(); ++ it)
4086 {
4087 /// @todo handle situations with more than 64 objects
4088 AssertMsg (cnt <= 64 /* according to PMREF */,
4089 ("maximum of 64 mutex semaphores reached (%d)",
4090 cnt));
4091
4092 ComObjPtr<SessionMachine> sm;
4093 HMTX ipcSem;
4094 if ((*it)->isSessionOpenOrClosing (sm, NULL, &ipcSem))
4095 {
4096 machines.push_back (sm);
4097 handles [cnt].hsemCur = (HSEM) ipcSem;
4098 handles [cnt].ulUser = cnt;
4099 ++ cnt;
4100 }
4101 }
4102
4103 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4104
4105 if (cnt > 0)
4106 {
4107 /* create a new muxsem */
4108 APIRET arc = ::DosCreateMuxWaitSem (NULL, &muxSem, cnt,
4109 handles,
4110 DCMW_WAIT_ANY);
4111 AssertMsg (arc == NO_ERROR,
4112 ("DosCreateMuxWaitSem returned %d\n", arc));
4113 NOREF(arc);
4114 }
4115 }
4116
4117 if (updateSpawned)
4118 {
4119 /* obtain a new set of spawned machines */
4120 spawnedMachines.clear();
4121
4122 for (MachineList::iterator it = that->m->llMachines.begin();
4123 it != that->m->llMachines.end(); ++ it)
4124 {
4125 if ((*it)->isSessionSpawning())
4126 spawnedMachines.push_back (*it);
4127 }
4128
4129 cntSpawned = spawnedMachines.size();
4130 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4131 }
4132 }
4133 }
4134 while (true);
4135 }
4136 while (0);
4137
4138 /* close the muxsem */
4139 if (muxSem != NULLHANDLE)
4140 ::DosCloseMuxWaitSem (muxSem);
4141
4142 /* release sets of machines if any */
4143 machines.clear();
4144 spawnedMachines.clear();
4145
4146#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4147
4148 bool update = false;
4149 bool updateSpawned = false;
4150
4151 do
4152 {
4153 AutoCaller autoCaller(that);
4154 if (!autoCaller.isOk())
4155 break;
4156
4157 do
4158 {
4159 /* release the caller to let uninit() ever proceed */
4160 autoCaller.release();
4161
4162 int rc = RTSemEventWait(that->m->updateReq, 500);
4163
4164 /*
4165 * Restore the caller before using VirtualBox. If it fails, this
4166 * means VirtualBox is being uninitialized and we must terminate.
4167 */
4168 autoCaller.add();
4169 if (!autoCaller.isOk())
4170 break;
4171
4172 if (RT_SUCCESS(rc) || update || updateSpawned)
4173 {
4174 /* RT_SUCCESS(rc) means an update event is signaled */
4175
4176 AutoReadLock thatLock (that);
4177
4178 if (RT_SUCCESS(rc) || update)
4179 {
4180 /* obtain a new set of opened machines */
4181 machines.clear();
4182
4183 for (MachineList::iterator it = that->m->llMachines.begin();
4184 it != that->m->llMachines.end(); ++ it)
4185 {
4186 ComObjPtr<SessionMachine> sm;
4187 if ((*it)->isSessionOpenOrClosing (sm))
4188 machines.push_back (sm);
4189 }
4190
4191 cnt = machines.size();
4192 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4193 }
4194
4195 if (RT_SUCCESS(rc) || updateSpawned)
4196 {
4197 /* obtain a new set of spawned machines */
4198 spawnedMachines.clear();
4199
4200 for (MachineList::iterator it = that->m->llMachines.begin();
4201 it != that->m->llMachines.end(); ++ it)
4202 {
4203 if ((*it)->isSessionSpawning())
4204 spawnedMachines.push_back (*it);
4205 }
4206
4207 cntSpawned = spawnedMachines.size();
4208 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4209 }
4210 }
4211
4212 update = false;
4213 for (size_t i = 0; i < cnt; ++ i)
4214 update |= (machines [i])->checkForDeath();
4215
4216 updateSpawned = false;
4217 for (size_t i = 0; i < cntSpawned; ++ i)
4218 updateSpawned |= (spawnedMachines [i])->checkForSpawnFailure();
4219
4220 /* reap child processes */
4221 {
4222 AutoWriteLock alock(that);
4223 if (that->m->llProcesses.size())
4224 {
4225 LogFlowFunc (("UPDATE: child process count = %d\n",
4226 that->m->llProcesses.size()));
4227 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4228 while (it != that->m->llProcesses.end())
4229 {
4230 RTPROCESS pid = *it;
4231 RTPROCSTATUS status;
4232 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4233 if (vrc == VINF_SUCCESS)
4234 {
4235 LogFlowFunc (("pid %d (%x) was reaped, "
4236 "status=%d, reason=%d\n",
4237 pid, pid, status.iStatus,
4238 status.enmReason));
4239 it = that->m->llProcesses.erase(it);
4240 }
4241 else
4242 {
4243 LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4244 pid, pid, vrc));
4245 if (vrc != VERR_PROCESS_RUNNING)
4246 {
4247 /* remove the process if it is not already running */
4248 it = that->m->llProcesses.erase(it);
4249 }
4250 else
4251 ++ it;
4252 }
4253 }
4254 }
4255 }
4256 }
4257 while (true);
4258 }
4259 while (0);
4260
4261 /* release sets of machines if any */
4262 machines.clear();
4263 spawnedMachines.clear();
4264
4265#else
4266# error "Port me!"
4267#endif
4268
4269 LogFlowFuncLeave();
4270 return 0;
4271}
4272
4273/**
4274 * Thread function that handles custom events posted using #postEvent().
4275 */
4276// static
4277DECLCALLBACK(int) VirtualBox::AsyncEventHandler (RTTHREAD thread, void *pvUser)
4278{
4279 LogFlowFuncEnter();
4280
4281 AssertReturn(pvUser, VERR_INVALID_POINTER);
4282
4283 // create an event queue for the current thread
4284 EventQueue *eventQ = new EventQueue();
4285 AssertReturn(eventQ, VERR_NO_MEMORY);
4286
4287 // return the queue to the one who created this thread
4288 *(static_cast <EventQueue **> (pvUser)) = eventQ;
4289 // signal that we're ready
4290 RTThreadUserSignal (thread);
4291
4292 BOOL ok = TRUE;
4293 Event *event = NULL;
4294
4295 while ((ok = eventQ->waitForEvent (&event)) && event)
4296 eventQ->handleEvent (event);
4297
4298 AssertReturn(ok, VERR_GENERAL_FAILURE);
4299
4300 delete eventQ;
4301
4302 LogFlowFuncLeave();
4303
4304 return 0;
4305}
4306
4307////////////////////////////////////////////////////////////////////////////////
4308
4309/**
4310 * Takes the current list of registered callbacks of the managed VirtualBox
4311 * instance, and calls #handleCallback() for every callback item from the
4312 * list, passing the item as an argument.
4313 *
4314 * @note Locks the managed VirtualBox object for reading but leaves the lock
4315 * before iterating over callbacks and calling their methods.
4316 */
4317void *VirtualBox::CallbackEvent::handler()
4318{
4319 if (mVirtualBox.isNull())
4320 return NULL;
4321
4322 AutoCaller autoCaller(mVirtualBox);
4323 if (!autoCaller.isOk())
4324 {
4325 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
4326 "the callback event is discarded!\n",
4327 autoCaller.state()));
4328 /* We don't need mVirtualBox any more, so release it */
4329 mVirtualBox.setNull();
4330 return NULL;
4331 }
4332
4333 CallbackList callbacks;
4334 {
4335 /* Make a copy to release the lock before iterating */
4336 AutoReadLock alock(mVirtualBox);
4337 callbacks = mVirtualBox->m->llCallbacks;
4338 /* We don't need mVirtualBox any more, so release it */
4339 mVirtualBox.setNull();
4340 }
4341
4342 for (CallbackList::const_iterator it = callbacks.begin();
4343 it != callbacks.end();
4344 ++it)
4345 handleCallback(*it);
4346
4347 return NULL;
4348}
4349
4350//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface (/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4351//{
4352// return E_NOTIMPL;
4353//}
4354
4355STDMETHODIMP VirtualBox::CreateDHCPServer (IN_BSTR aName, IDHCPServer ** aServer)
4356{
4357 CheckComArgNotNull(aName);
4358 CheckComArgNotNull(aServer);
4359
4360 AutoCaller autoCaller(this);
4361 CheckComRCReturnRC(autoCaller.rc());
4362
4363 ComObjPtr<DHCPServer> dhcpServer;
4364 dhcpServer.createObject();
4365 HRESULT rc = dhcpServer->init (this, aName);
4366 CheckComRCReturnRC(rc);
4367
4368 rc = registerDHCPServer(dhcpServer, true);
4369 CheckComRCReturnRC(rc);
4370
4371 dhcpServer.queryInterfaceTo(aServer);
4372
4373 return rc;
4374}
4375
4376//STDMETHODIMP VirtualBox::FindDHCPServerForInterface (IHostNetworkInterface * aIinterface, IDHCPServer ** aServer)
4377//{
4378// return E_NOTIMPL;
4379//}
4380
4381STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName (IN_BSTR aName, IDHCPServer ** aServer)
4382{
4383 CheckComArgNotNull(aName);
4384 CheckComArgNotNull(aServer);
4385
4386 AutoCaller autoCaller(this);
4387 CheckComRCReturnRC(autoCaller.rc());
4388
4389 AutoWriteLock alock(this);
4390
4391 HRESULT rc;
4392 Bstr bstr;
4393 ComPtr<DHCPServer> found;
4394
4395 for (DHCPServerList::const_iterator it =
4396 m->llDHCPServers.begin();
4397 it != m->llDHCPServers.end();
4398 ++ it)
4399 {
4400 rc = (*it)->COMGETTER(NetworkName) (bstr.asOutParam());
4401 CheckComRCThrowRC(rc);
4402
4403 if(bstr == aName)
4404 {
4405 found = *it;
4406 break;
4407 }
4408 }
4409
4410 if (!found)
4411 return E_INVALIDARG;
4412
4413 return found.queryInterfaceTo(aServer);
4414}
4415
4416STDMETHODIMP VirtualBox::RemoveDHCPServer (IDHCPServer * aServer)
4417{
4418 CheckComArgNotNull(aServer);
4419
4420 AutoCaller autoCaller(this);
4421 CheckComRCReturnRC(autoCaller.rc());
4422
4423 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4424
4425 return rc;
4426}
4427
4428/**
4429 * Remembers the given dhcp server by storing it in the hard disk registry.
4430 *
4431 * @param aDHCPServer Dhcp Server object to remember.
4432 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4433 *
4434 * When @a aSaveRegistry is @c true, this operation may fail because of the
4435 * failed #saveSettings() method it calls. In this case, the dhcp server object
4436 * will not be remembered. It is therefore the responsibility of the caller to
4437 * call this method as the last step of some action that requires registration
4438 * in order to make sure that only fully functional dhcp server objects get
4439 * registered.
4440 *
4441 * @note Locks this object for writing and @a aDHCPServer for reading.
4442 */
4443HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4444 bool aSaveRegistry /*= true*/)
4445{
4446 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4447
4448 AutoCaller autoCaller(this);
4449 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4450
4451 AutoWriteLock alock(this);
4452
4453 AutoCaller dhcpServerCaller (aDHCPServer);
4454 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4455
4456 AutoReadLock dhcpServerLock (aDHCPServer);
4457
4458 Bstr name;
4459 HRESULT rc;
4460 rc = aDHCPServer->COMGETTER(NetworkName) (name.asOutParam());
4461 CheckComRCReturnRC(rc);
4462
4463 ComPtr<IDHCPServer> existing;
4464 rc = FindDHCPServerByNetworkName(name.mutableRaw(), existing.asOutParam());
4465 if(SUCCEEDED(rc))
4466 {
4467 return E_INVALIDARG;
4468 }
4469 rc = S_OK;
4470
4471 m->llDHCPServers.push_back (aDHCPServer);
4472
4473 if (aSaveRegistry)
4474 {
4475 rc = saveSettings();
4476 if (FAILED (rc))
4477 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4478 }
4479
4480 return rc;
4481}
4482
4483/**
4484 * Removes the given hard disk from the hard disk registry.
4485 *
4486 * @param aHardDisk Hard disk object to remove.
4487 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4488 *
4489 * When @a aSaveRegistry is @c true, this operation may fail because of the
4490 * failed #saveSettings() method it calls. In this case, the hard disk object
4491 * will NOT be removed from the registry when this method returns. It is
4492 * therefore the responsibility of the caller to call this method as the first
4493 * step of some action that requires unregistration, before calling uninit() on
4494 * @a aHardDisk.
4495 *
4496 * @note Locks this object for writing and @a aHardDisk for reading.
4497 */
4498HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4499 bool aSaveRegistry /*= true*/)
4500{
4501 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4502
4503 AutoCaller autoCaller(this);
4504 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4505
4506 AutoWriteLock alock(this);
4507
4508 AutoCaller dhcpServerCaller (aDHCPServer);
4509 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4510
4511 AutoReadLock dhcpServerLock (aDHCPServer);
4512
4513 m->llDHCPServers.remove (aDHCPServer);
4514
4515 HRESULT rc = S_OK;
4516
4517 if (aSaveRegistry)
4518 {
4519 rc = saveSettings();
4520 if (FAILED (rc))
4521 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4522 }
4523
4524 return rc;
4525}
4526
4527/* 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