VirtualBox

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

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

Main: Bstr makeover (third attempt) -- make Bstr(NULL) and Bstr() behave the same; resulting cleanup; make some more internal methods use Utf8Str instead of Bstr; fix a lot of CheckComArgNotNull??() usage

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