VirtualBox

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

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

Main: change Medium member variables from Bstr to Utf8 for better debugging and less conversions

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

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