VirtualBox

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

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

Main: Bstr makeover -- 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.6 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 26550 2010-02-15 17:14:18Z 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.c_str()))
982 {
983 *aResult = TRUE;
984 if (aFile)
985 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.c_str()))
996 {
997 *aResult = TRUE;
998 if (aFile)
999 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, IN_BSTR aImageId,
1345 BOOL aSetParentId, IN_BSTR aParentId,
1346 IMedium **aHardDisk)
1347{
1348 CheckComArgStrNotEmptyOrNull(aLocation);
1349 CheckComArgOutSafeArrayPointerValid(aHardDisk);
1350
1351 AutoCaller autoCaller(this);
1352 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1353
1354 /* we don't access non-const data members so no need to lock */
1355
1356 HRESULT rc = E_FAIL;
1357
1358 ComObjPtr<Medium> hardDisk;
1359 hardDisk.createObject();
1360 Guid imageId, parentId;
1361 if (aSetImageId)
1362 {
1363 imageId = Guid(aImageId);
1364 if (imageId.isEmpty())
1365 return setError(E_INVALIDARG, tr("Argument %s is empty"), "aImageId");
1366 }
1367 if (aSetParentId)
1368 parentId = Guid(aParentId);
1369 rc = hardDisk->init(this,
1370 aLocation,
1371 (accessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
1372 DeviceType_HardDisk,
1373 aSetImageId,
1374 imageId,
1375 aSetParentId,
1376 parentId);
1377
1378 if (SUCCEEDED(rc))
1379 {
1380 bool fNeedsSaveSettings = false;
1381 rc = registerHardDisk(hardDisk, &fNeedsSaveSettings);
1382
1383 /* Note that it's important to call uninit() on failure to register
1384 * because the differencing hard disk would have been already associated
1385 * with the parent and this association needs to be broken. */
1386
1387 if (SUCCEEDED(rc))
1388 hardDisk.queryInterfaceTo(aHardDisk);
1389 else
1390 hardDisk->uninit();
1391
1392 if (fNeedsSaveSettings)
1393 {
1394 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
1395 saveSettings();
1396 }
1397 }
1398
1399 return rc;
1400}
1401
1402STDMETHODIMP VirtualBox::GetHardDisk(IN_BSTR aId,
1403 IMedium **aHardDisk)
1404{
1405 CheckComArgOutSafeArrayPointerValid(aHardDisk);
1406
1407 AutoCaller autoCaller(this);
1408 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1409
1410 Guid id(aId);
1411 ComObjPtr<Medium> hardDisk;
1412 HRESULT rc = findHardDisk(&id, NULL, true /* setError */, &hardDisk);
1413
1414 /* the below will set *aHardDisk to NULL if hardDisk is null */
1415 hardDisk.queryInterfaceTo(aHardDisk);
1416
1417 return rc;
1418}
1419
1420STDMETHODIMP VirtualBox::FindHardDisk(IN_BSTR aLocation,
1421 IMedium **aHardDisk)
1422{
1423 CheckComArgStrNotEmptyOrNull(aLocation);
1424 CheckComArgOutSafeArrayPointerValid(aHardDisk);
1425
1426 AutoCaller autoCaller(this);
1427 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1428
1429 ComObjPtr<Medium> hardDisk;
1430 Utf8Str strLocation(aLocation);
1431 HRESULT rc = findHardDisk(NULL, &strLocation, true /* setError */, &hardDisk);
1432
1433 /* the below will set *aHardDisk to NULL if hardDisk is null */
1434 hardDisk.queryInterfaceTo(aHardDisk);
1435
1436 return rc;
1437}
1438
1439/** @note Doesn't lock anything. */
1440STDMETHODIMP VirtualBox::OpenDVDImage(IN_BSTR aLocation, IN_BSTR aId,
1441 IMedium **aDVDImage)
1442{
1443 CheckComArgStrNotEmptyOrNull(aLocation);
1444 CheckComArgOutSafeArrayPointerValid(aDVDImage);
1445
1446 AutoCaller autoCaller(this);
1447 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1448
1449 HRESULT rc = VBOX_E_FILE_ERROR;
1450
1451 Guid id(aId);
1452 /* generate an UUID if not specified */
1453 if (id.isEmpty())
1454 id.create();
1455
1456 ComObjPtr<Medium> image;
1457 image.createObject();
1458 rc = image->init(this, aLocation, Medium::OpenReadOnly, DeviceType_DVD, true, id, false, Guid());
1459 if (SUCCEEDED(rc))
1460 {
1461 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1462 bool fNeedsSaveSettings = false;
1463 rc = registerImage(image, DeviceType_DVD, &fNeedsSaveSettings);
1464 treeLock.release();
1465
1466 if (SUCCEEDED(rc))
1467 image.queryInterfaceTo(aDVDImage);
1468
1469 if (fNeedsSaveSettings)
1470 {
1471 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
1472 saveSettings();
1473 }
1474 }
1475
1476 return rc;
1477}
1478
1479/** @note Locks objects! */
1480STDMETHODIMP VirtualBox::GetDVDImage(IN_BSTR aId, IMedium **aDVDImage)
1481{
1482 CheckComArgOutSafeArrayPointerValid(aDVDImage);
1483
1484 AutoCaller autoCaller(this);
1485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1486
1487 Guid id(aId);
1488 ComObjPtr<Medium> image;
1489 HRESULT rc = findDVDImage (&id, NULL, true /* setError */, &image);
1490
1491 /* the below will set *aDVDImage to NULL if image is null */
1492 image.queryInterfaceTo(aDVDImage);
1493
1494 return rc;
1495}
1496
1497/** @note Locks objects! */
1498STDMETHODIMP VirtualBox::FindDVDImage(IN_BSTR aLocation, IMedium **aDVDImage)
1499{
1500 CheckComArgStrNotEmptyOrNull(aLocation);
1501 CheckComArgOutSafeArrayPointerValid(aDVDImage);
1502
1503 AutoCaller autoCaller(this);
1504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1505
1506 ComObjPtr<Medium> image;
1507 Utf8Str strLocation(aLocation);
1508 HRESULT rc = findDVDImage(NULL, &strLocation, 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 Utf8Str strLocation(aLocation);
1588 HRESULT rc = findFloppyImage(NULL, &strLocation, true /* setError */, &image);
1589
1590 /* the below will set *aFloppyImage to NULL if img is null */
1591 image.queryInterfaceTo(aFloppyImage);
1592
1593 return rc;
1594}
1595
1596/** @note Locks this object for reading. */
1597STDMETHODIMP VirtualBox::GetGuestOSType(IN_BSTR aId, IGuestOSType **aType)
1598{
1599 /* Old ID to new ID conversion table. See r39691 for a source */
1600 static const wchar_t *kOldNewIDs[] =
1601 {
1602 L"unknown", L"Other",
1603 L"win31", L"Windows31",
1604 L"win95", L"Windows95",
1605 L"win98", L"Windows98",
1606 L"winme", L"WindowsMe",
1607 L"winnt4", L"WindowsNT4",
1608 L"win2k", L"Windows2000",
1609 L"winxp", L"WindowsXP",
1610 L"win2k3", L"Windows2003",
1611 L"winvista", L"WindowsVista",
1612 L"win2k8", L"Windows2008",
1613 L"ecs", L"OS2eCS",
1614 L"fedoracore", L"Fedora",
1615 /* the rest is covered by the case-insensitive comparison */
1616 };
1617
1618 CheckComArgNotNull(aType);
1619
1620 AutoCaller autoCaller(this);
1621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1622
1623 /* first, look for a substitution */
1624 Bstr id = aId;
1625 for (size_t i = 0; i < RT_ELEMENTS (kOldNewIDs) / 2; i += 2)
1626 {
1627 if (id == kOldNewIDs[i])
1628 {
1629 id = kOldNewIDs[i + 1];
1630 break;
1631 }
1632 }
1633
1634 *aType = NULL;
1635
1636 AutoReadLock alock(m->ollGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1637 for (GuestOSTypesOList::iterator it = m->ollGuestOSTypes.begin();
1638 it != m->ollGuestOSTypes.end();
1639 ++it)
1640 {
1641 const Bstr &typeId = (*it)->id();
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 CheckComArgNotNull(aName);
1658 CheckComArgNotNull(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 CheckComArgNotNull(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 Bstr bstrResult;
1719
1720 settings::ExtraDataItemsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
1721 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1722 // found:
1723 bstrResult = it->second; // source is a Utf8Str
1724
1725 /* return the result to caller (may be empty) */
1726 bstrResult.cloneTo(aValue);
1727
1728 return S_OK;
1729}
1730
1731/**
1732 * @note Locks this object for writing.
1733 */
1734STDMETHODIMP VirtualBox::SetExtraData(IN_BSTR aKey,
1735 IN_BSTR aValue)
1736{
1737 CheckComArgStrNotEmptyOrNull(aKey);
1738
1739 AutoCaller autoCaller(this);
1740 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1741
1742 Utf8Str strKey(aKey);
1743 Utf8Str strValue(aValue);
1744 Utf8Str strOldValue; // empty
1745
1746 // locking note: we only hold the read lock briefly to look up the old value,
1747 // then release it and call the onExtraCanChange callbacks. There is a small
1748 // chance of a race insofar as the callback might be called twice if two callers
1749 // change the same key at the same time, but that's a much better solution
1750 // than the deadlock we had here before. The actual changing of the extradata
1751 // is then performed under the write lock and race-free.
1752
1753 // look up the old value first; if nothing's changed then we need not do anything
1754 {
1755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
1756 settings::ExtraDataItemsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
1757 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1758 strOldValue = it->second;
1759 }
1760
1761 bool fChanged;
1762 if ((fChanged = (strOldValue != strValue)))
1763 {
1764 // ask for permission from all listeners outside the locks;
1765 // onExtraDataCanChange() only briefly requests the VirtualBox
1766 // lock to copy the list of callbacks to invoke
1767 Bstr error;
1768 Bstr bstrValue(aValue);
1769
1770 if (!onExtraDataCanChange(Guid::Empty, aKey, bstrValue, error))
1771 {
1772 const char *sep = error.isEmpty() ? "" : ": ";
1773 CBSTR err = error.raw();
1774 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
1775 sep, err));
1776 return setError(E_ACCESSDENIED,
1777 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
1778 aKey,
1779 bstrValue.raw(),
1780 sep,
1781 err);
1782 }
1783
1784 // data is changing and change not vetoed: then write it out under the lock
1785
1786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1787
1788 if (strValue.isEmpty())
1789 m->pMainConfigFile->mapExtraDataItems.erase(strKey);
1790 else
1791 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
1792 // creates a new key if needed
1793
1794 /* save settings on success */
1795 HRESULT rc = saveSettings();
1796 if (FAILED(rc)) return rc;
1797 }
1798
1799 // fire notification outside the lock
1800 if (fChanged)
1801 onExtraDataChange(Guid::Empty, aKey, aValue);
1802
1803 return S_OK;
1804}
1805
1806/**
1807 * @note Locks objects!
1808 */
1809STDMETHODIMP VirtualBox::OpenSession (ISession *aSession, IN_BSTR aMachineId)
1810{
1811 CheckComArgNotNull(aSession);
1812
1813 AutoCaller autoCaller(this);
1814 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1815
1816 Guid id(aMachineId);
1817 ComObjPtr<Machine> machine;
1818
1819 HRESULT rc = findMachine (id, true /* setError */, &machine);
1820 if (FAILED(rc)) return rc;
1821
1822 /* check the session state */
1823 SessionState_T state;
1824 rc = aSession->COMGETTER(State) (&state);
1825 if (FAILED(rc)) return rc;
1826
1827 if (state != SessionState_Closed)
1828 return setError(VBOX_E_INVALID_OBJECT_STATE,
1829 tr("The given session is already open or being opened"));
1830
1831 /* get the IInternalSessionControl interface */
1832 ComPtr<IInternalSessionControl> control = aSession;
1833 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1834 E_INVALIDARG);
1835
1836 rc = machine->openSession (control);
1837
1838 if (SUCCEEDED(rc))
1839 {
1840 /*
1841 * tell the client watcher thread to update the set of
1842 * machines that have open sessions
1843 */
1844 updateClientWatcher();
1845
1846 /* fire an event */
1847 onSessionStateChange (id, SessionState_Open);
1848 }
1849
1850 return rc;
1851}
1852
1853/**
1854 * @note Locks objects!
1855 */
1856STDMETHODIMP VirtualBox::OpenRemoteSession (ISession *aSession,
1857 IN_BSTR aMachineId,
1858 IN_BSTR aType,
1859 IN_BSTR aEnvironment,
1860 IProgress **aProgress)
1861{
1862 LogRel(("remotesession=%s\n", Utf8Str(aMachineId).c_str()));
1863
1864 CheckComArgStrNotEmptyOrNull(aMachineId);
1865 CheckComArgNotNull(aSession);
1866 CheckComArgOutSafeArrayPointerValid(aProgress);
1867
1868 AutoCaller autoCaller(this);
1869 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1870
1871 Guid id(aMachineId);
1872 ComObjPtr<Machine> machine;
1873
1874 HRESULT rc = findMachine (id, true /* setError */, &machine);
1875 if (FAILED(rc)) return rc;
1876
1877 /* check the session state */
1878 SessionState_T state;
1879 rc = aSession->COMGETTER(State) (&state);
1880 if (FAILED(rc)) return rc;
1881
1882 if (state != SessionState_Closed)
1883 return setError(VBOX_E_INVALID_OBJECT_STATE,
1884 tr("The given session is already open or being opened"));
1885
1886 /* get the IInternalSessionControl interface */
1887 ComPtr<IInternalSessionControl> control = aSession;
1888 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1889 E_INVALIDARG);
1890
1891 /* create a progress object */
1892 ComObjPtr<Progress> progress;
1893 progress.createObject();
1894 progress->init(this,
1895 static_cast<IMachine*>(machine),
1896 tr("Spawning session"),
1897 FALSE /* aCancelable */);
1898
1899 rc = machine->openRemoteSession (control, aType, aEnvironment, progress);
1900
1901 if (SUCCEEDED(rc))
1902 {
1903 progress.queryInterfaceTo(aProgress);
1904
1905 /* signal the client watcher thread */
1906 updateClientWatcher();
1907
1908 /* fire an event */
1909 onSessionStateChange (id, SessionState_Spawning);
1910 }
1911
1912 return rc;
1913}
1914
1915/**
1916 * @note Locks objects!
1917 */
1918STDMETHODIMP VirtualBox::OpenExistingSession (ISession *aSession,
1919 IN_BSTR aMachineId)
1920{
1921 CheckComArgNotNull(aSession);
1922
1923 AutoCaller autoCaller(this);
1924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1925
1926 Guid id(aMachineId);
1927 ComObjPtr<Machine> machine;
1928
1929 HRESULT rc = findMachine (id, true /* setError */, &machine);
1930 if (FAILED(rc)) return rc;
1931
1932 /* check the session state */
1933 SessionState_T state;
1934 rc = aSession->COMGETTER(State) (&state);
1935 if (FAILED(rc)) return rc;
1936
1937 if (state != SessionState_Closed)
1938 return setError(VBOX_E_INVALID_OBJECT_STATE,
1939 tr("The given session is already open or being opened"));
1940
1941 /* get the IInternalSessionControl interface */
1942 ComPtr<IInternalSessionControl> control = aSession;
1943 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1944 E_INVALIDARG);
1945
1946 rc = machine->openExistingSession (control);
1947
1948 return rc;
1949}
1950
1951/**
1952 * @note Locks this object for writing.
1953 */
1954STDMETHODIMP VirtualBox::RegisterCallback (IVirtualBoxCallback *aCallback)
1955{
1956 LogFlowThisFunc(("aCallback=%p\n", aCallback));
1957
1958 CheckComArgNotNull(aCallback);
1959
1960 AutoCaller autoCaller(this);
1961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1962
1963#if 0 /** @todo r=bird,r=pritesh: must check that the interface id match correct or we might screw up with old code! */
1964 void *dummy;
1965 HRESULT hrc = aCallback->QueryInterface(NS_GET_IID(IVirtualBoxCallback), &dummy);
1966 if (FAILED(hrc))
1967 return hrc;
1968 aCallback->Release();
1969#endif
1970
1971 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1972 m->llCallbacks.push_back (CallbackList::value_type (aCallback));
1973
1974 return S_OK;
1975}
1976
1977/**
1978 * @note Locks this object for writing.
1979 */
1980STDMETHODIMP VirtualBox::UnregisterCallback (IVirtualBoxCallback *aCallback)
1981{
1982 CheckComArgNotNull(aCallback);
1983
1984 AutoCaller autoCaller(this);
1985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1986
1987 HRESULT rc = S_OK;
1988
1989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1990
1991 CallbackList::iterator it;
1992 it = std::find (m->llCallbacks.begin(),
1993 m->llCallbacks.end(),
1994 CallbackList::value_type (aCallback));
1995 if (it == m->llCallbacks.end())
1996 rc = E_INVALIDARG;
1997 else
1998 m->llCallbacks.erase (it);
1999
2000 LogFlowThisFunc(("aCallback=%p, rc=%08X\n", aCallback, rc));
2001 return rc;
2002}
2003
2004
2005STDMETHODIMP VirtualBox::WaitForPropertyChange(IN_BSTR /* aWhat */,
2006 ULONG /* aTimeout */,
2007 BSTR * /* aChanged */,
2008 BSTR * /* aValues */)
2009{
2010 ReturnComNotImplemented();
2011}
2012
2013// public methods only for internal purposes
2014/////////////////////////////////////////////////////////////////////////////
2015
2016#ifdef DEBUG
2017void VirtualBox::dumpAllBackRefs()
2018{
2019 {
2020 AutoReadLock al(m->ollHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2021 for (MediaList::const_iterator mt = m->ollHardDisks.begin();
2022 mt != m->ollHardDisks.end();
2023 ++mt)
2024 {
2025 ComObjPtr<Medium> pMedium = *mt;
2026 pMedium->dumpBackRefs();
2027 }
2028 }
2029 {
2030 AutoReadLock al(m->ollDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2031 for (MediaList::const_iterator mt = m->ollDVDImages.begin();
2032 mt != m->ollDVDImages.end();
2033 ++mt)
2034 {
2035 ComObjPtr<Medium> pMedium = *mt;
2036 pMedium->dumpBackRefs();
2037 }
2038 }
2039}
2040#endif
2041
2042/**
2043 * Posts an event to the event queue that is processed asynchronously
2044 * on a dedicated thread.
2045 *
2046 * Posting events to the dedicated event queue is useful to perform secondary
2047 * actions outside any object locks -- for example, to iterate over a list
2048 * of callbacks and inform them about some change caused by some object's
2049 * method call.
2050 *
2051 * @param event event to post
2052 * (must be allocated using |new|, will be deleted automatically
2053 * by the event thread after processing)
2054 *
2055 * @note Doesn't lock any object.
2056 */
2057HRESULT VirtualBox::postEvent(Event *event)
2058{
2059 AutoCaller autoCaller(this);
2060 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2061
2062 if (autoCaller.state() != Ready)
2063 {
2064 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
2065 "the event is discarded!\n",
2066 autoCaller.state()));
2067 return S_OK;
2068 }
2069
2070 AssertReturn(event, E_FAIL);
2071 AssertReturn(m->pAsyncEventQ, E_FAIL);
2072
2073 if (m->pAsyncEventQ->postEvent(event))
2074 return S_OK;
2075
2076 return E_FAIL;
2077}
2078
2079/**
2080 * Adds a progress to the global collection of pending operations.
2081 * Usually gets called upon progress object initialization.
2082 *
2083 * @param aProgress Operation to add to the collection.
2084 *
2085 * @note Doesn't lock objects.
2086 */
2087HRESULT VirtualBox::addProgress(IProgress *aProgress)
2088{
2089 CheckComArgNotNull(aProgress);
2090
2091 AutoCaller autoCaller(this);
2092 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2093
2094 Bstr id;
2095 HRESULT rc = aProgress->COMGETTER(Id) (id.asOutParam());
2096 AssertComRCReturnRC(rc);
2097
2098 /* protect mProgressOperations */
2099 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2100
2101 m->mapProgressOperations.insert (ProgressMap::value_type (Guid(id), aProgress));
2102 return S_OK;
2103}
2104
2105/**
2106 * Removes the progress from the global collection of pending operations.
2107 * Usually gets called upon progress completion.
2108 *
2109 * @param aId UUID of the progress operation to remove
2110 *
2111 * @note Doesn't lock objects.
2112 */
2113HRESULT VirtualBox::removeProgress(IN_GUID aId)
2114{
2115 AutoCaller autoCaller(this);
2116 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2117
2118 ComPtr<IProgress> progress;
2119
2120 /* protect mProgressOperations */
2121 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2122
2123 size_t cnt = m->mapProgressOperations.erase(aId);
2124 Assert(cnt == 1);
2125 NOREF(cnt);
2126
2127 return S_OK;
2128}
2129
2130#ifdef RT_OS_WINDOWS
2131
2132struct StartSVCHelperClientData
2133{
2134 ComObjPtr<VirtualBox> that;
2135 ComObjPtr<Progress> progress;
2136 bool privileged;
2137 VirtualBox::SVCHelperClientFunc func;
2138 void *user;
2139};
2140
2141/**
2142 * Helper method that starts a worker thread that:
2143 * - creates a pipe communication channel using SVCHlpClient;
2144 * - starts an SVC Helper process that will inherit this channel;
2145 * - executes the supplied function by passing it the created SVCHlpClient
2146 * and opened instance to communicate to the Helper process and the given
2147 * Progress object.
2148 *
2149 * The user function is supposed to communicate to the helper process
2150 * using the \a aClient argument to do the requested job and optionally expose
2151 * the progress through the \a aProgress object. The user function should never
2152 * call notifyComplete() on it: this will be done automatically using the
2153 * result code returned by the function.
2154 *
2155 * Before the user function is started, the communication channel passed to
2156 * the \a aClient argument is fully set up, the function should start using
2157 * its write() and read() methods directly.
2158 *
2159 * The \a aVrc parameter of the user function may be used to return an error
2160 * code if it is related to communication errors (for example, returned by
2161 * the SVCHlpClient members when they fail). In this case, the correct error
2162 * message using this value will be reported to the caller. Note that the
2163 * value of \a aVrc is inspected only if the user function itself returns
2164 * success.
2165 *
2166 * If a failure happens anywhere before the user function would be normally
2167 * called, it will be called anyway in special "cleanup only" mode indicated
2168 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
2169 * all the function is supposed to do is to cleanup its aUser argument if
2170 * necessary (it's assumed that the ownership of this argument is passed to
2171 * the user function once #startSVCHelperClient() returns a success, thus
2172 * making it responsible for the cleanup).
2173 *
2174 * After the user function returns, the thread will send the SVCHlpMsg::Null
2175 * message to indicate a process termination.
2176 *
2177 * @param aPrivileged |true| to start the SVC Helper process as a privileged
2178 * user that can perform administrative tasks
2179 * @param aFunc user function to run
2180 * @param aUser argument to the user function
2181 * @param aProgress progress object that will track operation completion
2182 *
2183 * @note aPrivileged is currently ignored (due to some unsolved problems in
2184 * Vista) and the process will be started as a normal (unprivileged)
2185 * process.
2186 *
2187 * @note Doesn't lock anything.
2188 */
2189HRESULT VirtualBox::startSVCHelperClient (bool aPrivileged,
2190 SVCHelperClientFunc aFunc,
2191 void *aUser, Progress *aProgress)
2192{
2193 AssertReturn(aFunc, E_POINTER);
2194 AssertReturn(aProgress, E_POINTER);
2195
2196 AutoCaller autoCaller(this);
2197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2198
2199 /* create the SVCHelperClientThread() argument */
2200 std::auto_ptr <StartSVCHelperClientData>
2201 d (new StartSVCHelperClientData());
2202 AssertReturn(d.get(), E_OUTOFMEMORY);
2203
2204 d->that = this;
2205 d->progress = aProgress;
2206 d->privileged = aPrivileged;
2207 d->func = aFunc;
2208 d->user = aUser;
2209
2210 RTTHREAD tid = NIL_RTTHREAD;
2211 int vrc = RTThreadCreate (&tid, SVCHelperClientThread,
2212 static_cast <void *> (d.get()),
2213 0, RTTHREADTYPE_MAIN_WORKER,
2214 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2215
2216 ComAssertMsgRCRet (vrc, ("Could not create SVCHelper thread (%Rrc)", vrc),
2217 E_FAIL);
2218
2219 /* d is now owned by SVCHelperClientThread(), so release it */
2220 d.release();
2221
2222 return S_OK;
2223}
2224
2225/**
2226 * Worker thread for startSVCHelperClient().
2227 */
2228/* static */
2229DECLCALLBACK(int)
2230VirtualBox::SVCHelperClientThread (RTTHREAD aThread, void *aUser)
2231{
2232 LogFlowFuncEnter();
2233
2234 std::auto_ptr <StartSVCHelperClientData>
2235 d (static_cast <StartSVCHelperClientData *> (aUser));
2236
2237 HRESULT rc = S_OK;
2238 bool userFuncCalled = false;
2239
2240 do
2241 {
2242 AssertBreakStmt (d.get(), rc = E_POINTER);
2243 AssertReturn(!d->progress.isNull(), E_POINTER);
2244
2245 /* protect VirtualBox from uninitialization */
2246 AutoCaller autoCaller(d->that);
2247 if (!autoCaller.isOk())
2248 {
2249 /* it's too late */
2250 rc = autoCaller.rc();
2251 break;
2252 }
2253
2254 int vrc = VINF_SUCCESS;
2255
2256 Guid id;
2257 id.create();
2258 SVCHlpClient client;
2259 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
2260 id.raw()).c_str());
2261 if (RT_FAILURE(vrc))
2262 {
2263 rc = setError(E_FAIL,
2264 tr("Could not create the communication channel (%Rrc)"), vrc);
2265 break;
2266 }
2267
2268 /* get the path to the executable */
2269 char exePathBuf[RTPATH_MAX];
2270 char *exePath = RTProcGetExecutableName (exePathBuf, RTPATH_MAX);
2271 ComAssertBreak (exePath, E_FAIL);
2272
2273 Utf8Str argsStr = Utf8StrFmt ("/Helper %s", client.name().raw());
2274
2275 LogFlowFunc (("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
2276
2277 RTPROCESS pid = NIL_RTPROCESS;
2278
2279 if (d->privileged)
2280 {
2281 /* Attempt to start a privileged process using the Run As dialog */
2282
2283 Bstr file = exePath;
2284 Bstr parameters = argsStr;
2285
2286 SHELLEXECUTEINFO shExecInfo;
2287
2288 shExecInfo.cbSize = sizeof (SHELLEXECUTEINFO);
2289
2290 shExecInfo.fMask = NULL;
2291 shExecInfo.hwnd = NULL;
2292 shExecInfo.lpVerb = L"runas";
2293 shExecInfo.lpFile = file;
2294 shExecInfo.lpParameters = parameters;
2295 shExecInfo.lpDirectory = NULL;
2296 shExecInfo.nShow = SW_NORMAL;
2297 shExecInfo.hInstApp = NULL;
2298
2299 if (!ShellExecuteEx (&shExecInfo))
2300 {
2301 int vrc2 = RTErrConvertFromWin32 (GetLastError());
2302 /* hide excessive details in case of a frequent error
2303 * (pressing the Cancel button to close the Run As dialog) */
2304 if (vrc2 == VERR_CANCELLED)
2305 rc = setError(E_FAIL,
2306 tr("Operation cancelled by the user"));
2307 else
2308 rc = setError(E_FAIL,
2309 tr("Could not launch a privileged process '%s' (%Rrc)"),
2310 exePath, vrc2);
2311 break;
2312 }
2313 }
2314 else
2315 {
2316 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2317 vrc = RTProcCreate (exePath, args, RTENV_DEFAULT, 0, &pid);
2318 if (RT_FAILURE(vrc))
2319 {
2320 rc = setError(E_FAIL,
2321 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2322 break;
2323 }
2324 }
2325
2326 /* wait for the client to connect */
2327 vrc = client.connect();
2328 if (RT_SUCCESS(vrc))
2329 {
2330 /* start the user supplied function */
2331 rc = d->func (&client, d->progress, d->user, &vrc);
2332 userFuncCalled = true;
2333 }
2334
2335 /* send the termination signal to the process anyway */
2336 {
2337 int vrc2 = client.write (SVCHlpMsg::Null);
2338 if (RT_SUCCESS(vrc))
2339 vrc = vrc2;
2340 }
2341
2342 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2343 {
2344 rc = setError(E_FAIL,
2345 tr("Could not operate the communication channel (%Rrc)"), vrc);
2346 break;
2347 }
2348 }
2349 while (0);
2350
2351 if (FAILED(rc) && !userFuncCalled)
2352 {
2353 /* call the user function in the "cleanup only" mode
2354 * to let it free resources passed to in aUser */
2355 d->func (NULL, NULL, d->user, NULL);
2356 }
2357
2358 d->progress->notifyComplete (rc);
2359
2360 LogFlowFuncLeave();
2361 return 0;
2362}
2363
2364#endif /* RT_OS_WINDOWS */
2365
2366/**
2367 * Sends a signal to the client watcher thread to rescan the set of machines
2368 * that have open sessions.
2369 *
2370 * @note Doesn't lock anything.
2371 */
2372void VirtualBox::updateClientWatcher()
2373{
2374 AutoCaller autoCaller(this);
2375 AssertComRCReturn(autoCaller.rc(), (void) 0);
2376
2377 AssertReturn(m->threadClientWatcher != NIL_RTTHREAD, (void) 0);
2378
2379 /* sent an update request */
2380#if defined(RT_OS_WINDOWS)
2381 ::SetEvent (m->updateReq);
2382#elif defined(RT_OS_OS2)
2383 RTSemEventSignal (m->updateReq);
2384#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2385 RTSemEventSignal (m->updateReq);
2386#else
2387# error "Port me!"
2388#endif
2389}
2390
2391/**
2392 * Adds the given child process ID to the list of processes to be reaped.
2393 * This call should be followed by #updateClientWatcher() to take the effect.
2394 */
2395void VirtualBox::addProcessToReap (RTPROCESS pid)
2396{
2397 AutoCaller autoCaller(this);
2398 AssertComRCReturn(autoCaller.rc(), (void) 0);
2399
2400 /// @todo (dmik) Win32?
2401#ifndef RT_OS_WINDOWS
2402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2403 m->llProcesses.push_back (pid);
2404#endif
2405}
2406
2407/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2408struct MachineEvent : public VirtualBox::CallbackEvent
2409{
2410 enum What { DataChanged, StateChanged, Registered };
2411
2412 MachineEvent (VirtualBox *aVB, const Guid &aId)
2413 : CallbackEvent (aVB), what (DataChanged), id (aId)
2414 {}
2415
2416 MachineEvent (VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2417 : CallbackEvent (aVB), what (StateChanged), id (aId)
2418 , state (aState)
2419 {}
2420
2421 MachineEvent (VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2422 : CallbackEvent (aVB), what (Registered), id (aId)
2423 , registered (aRegistered)
2424 {}
2425
2426 void handleCallback (const ComPtr<IVirtualBoxCallback> &aCallback)
2427 {
2428 switch (what)
2429 {
2430 case DataChanged:
2431 LogFlow (("OnMachineDataChange: id={%RTuuid}\n", id.ptr()));
2432 aCallback->OnMachineDataChange(id.toUtf16());
2433 break;
2434
2435 case StateChanged:
2436 LogFlow (("OnMachineStateChange: id={%RTuuid}, state=%d\n",
2437 id.ptr(), state));
2438 aCallback->OnMachineStateChange(id.toUtf16(), state);
2439 break;
2440
2441 case Registered:
2442 LogFlow (("OnMachineRegistered: id={%RTuuid}, registered=%d\n",
2443 id.ptr(), registered));
2444 aCallback->OnMachineRegistered(id.toUtf16(), registered);
2445 break;
2446 }
2447 }
2448
2449 const What what;
2450
2451 Guid id;
2452 MachineState_T state;
2453 BOOL registered;
2454};
2455
2456/**
2457 * @note Doesn't lock any object.
2458 */
2459void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2460{
2461 postEvent (new MachineEvent (this, aId, aState));
2462}
2463
2464/**
2465 * @note Doesn't lock any object.
2466 */
2467void VirtualBox::onMachineDataChange(const Guid &aId)
2468{
2469 postEvent (new MachineEvent (this, aId));
2470}
2471
2472/**
2473 * @note Locks this object for reading.
2474 */
2475BOOL VirtualBox::onExtraDataCanChange (const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2476 Bstr &aError)
2477{
2478 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2479 aId.toString().raw(), aKey, aValue));
2480
2481 AutoCaller autoCaller(this);
2482 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2483
2484 CallbackList list;
2485 {
2486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2487 list = m->llCallbacks;
2488 }
2489
2490 BOOL allowChange = TRUE;
2491 CallbackList::iterator it = list.begin();
2492 Bstr id = aId.toUtf16();
2493 while ((it != list.end()) && allowChange)
2494 {
2495 HRESULT rc = (*it++)->OnExtraDataCanChange(id,
2496 aKey,
2497 aValue,
2498 aError.asOutParam(),
2499 &allowChange);
2500 if (FAILED(rc))
2501 {
2502 /* if a call to this method fails for some reason (for ex., because
2503 * the other side is dead), we ensure allowChange stays true
2504 * (MS COM RPC implementation seems to zero all output vars before
2505 * issuing an IPC call or after a failure, so it's essential
2506 * there) */
2507 allowChange = TRUE;
2508 }
2509 }
2510
2511 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2512 return allowChange;
2513}
2514
2515/** Event for onExtraDataChange() */
2516struct ExtraDataEvent : public VirtualBox::CallbackEvent
2517{
2518 ExtraDataEvent (VirtualBox *aVB, const Guid &aMachineId,
2519 IN_BSTR aKey, IN_BSTR aVal)
2520 : CallbackEvent (aVB), machineId (aMachineId)
2521 , key (aKey), val (aVal)
2522 {}
2523
2524 void handleCallback (const ComPtr<IVirtualBoxCallback> &aCallback)
2525 {
2526 LogFlow (("OnExtraDataChange: machineId={%RTuuid}, key='%ls', val='%ls'\n",
2527 machineId.ptr(), key.raw(), val.raw()));
2528 aCallback->OnExtraDataChange(machineId.toUtf16(), key, val);
2529 }
2530
2531 Guid machineId;
2532 Bstr key, val;
2533};
2534
2535/**
2536 * @note Doesn't lock any object.
2537 */
2538void VirtualBox::onExtraDataChange (const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2539{
2540 postEvent (new ExtraDataEvent (this, aId, aKey, aValue));
2541}
2542
2543/**
2544 * @note Doesn't lock any object.
2545 */
2546void VirtualBox::onMachineRegistered (const Guid &aId, BOOL aRegistered)
2547{
2548 postEvent (new MachineEvent (this, aId, aRegistered));
2549}
2550
2551/** Event for onSessionStateChange() */
2552struct SessionEvent : public VirtualBox::CallbackEvent
2553{
2554 SessionEvent (VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2555 : CallbackEvent (aVB), machineId(aMachineId), sessionState (aState)
2556 {}
2557
2558 void handleCallback (const ComPtr<IVirtualBoxCallback> &aCallback)
2559 {
2560 LogFlow (("OnSessionStateChange: machineId={%RTuuid}, sessionState=%d\n",
2561 machineId.ptr(), sessionState));
2562 aCallback->OnSessionStateChange(machineId.toUtf16(), sessionState);
2563 }
2564
2565 Guid machineId;
2566 SessionState_T sessionState;
2567};
2568
2569/**
2570 * @note Doesn't lock any object.
2571 */
2572void VirtualBox::onSessionStateChange (const Guid &aId, SessionState_T aState)
2573{
2574 postEvent (new SessionEvent (this, aId, aState));
2575}
2576
2577/** Event for onSnapshotTaken(), onSnapshotRemoved() and onSnapshotChange() */
2578struct SnapshotEvent : public VirtualBox::CallbackEvent
2579{
2580 enum What { Taken, Discarded, Changed };
2581
2582 SnapshotEvent (VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2583 What aWhat)
2584 : CallbackEvent (aVB)
2585 , what (aWhat)
2586 , machineId (aMachineId), snapshotId (aSnapshotId)
2587 {}
2588
2589 void handleCallback (const ComPtr<IVirtualBoxCallback> &aCallback)
2590 {
2591 Bstr mid = machineId.toUtf16();
2592 Bstr sid = snapshotId.toUtf16();
2593
2594 switch (what)
2595 {
2596 case Taken:
2597 LogFlow (("OnSnapshotTaken: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2598 machineId.ptr(), snapshotId.ptr()));
2599 aCallback->OnSnapshotTaken(mid, sid);
2600 break;
2601
2602 case Discarded:
2603 LogFlow (("OnSnapshotDiscarded: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2604 machineId.ptr(), snapshotId.ptr()));
2605 aCallback->OnSnapshotDiscarded(mid, sid);
2606 break;
2607
2608 case Changed:
2609 LogFlow (("OnSnapshotChange: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2610 machineId.ptr(), snapshotId.ptr()));
2611 aCallback->OnSnapshotChange(mid, sid);
2612 break;
2613 }
2614 }
2615
2616 const What what;
2617
2618 Guid machineId;
2619 Guid snapshotId;
2620};
2621
2622/**
2623 * @note Doesn't lock any object.
2624 */
2625void VirtualBox::onSnapshotTaken (const Guid &aMachineId, const Guid &aSnapshotId)
2626{
2627 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId, SnapshotEvent::Taken));
2628}
2629
2630/**
2631 * @note Doesn't lock any object.
2632 */
2633void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
2634{
2635 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId, SnapshotEvent::Discarded));
2636}
2637
2638/**
2639 * @note Doesn't lock any object.
2640 */
2641void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
2642{
2643 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId, SnapshotEvent::Changed));
2644}
2645
2646/** Event for onGuestPropertyChange() */
2647struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2648{
2649 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
2650 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2651 : CallbackEvent(aVBox),
2652 machineId(aMachineId),
2653 name(aName),
2654 value(aValue),
2655 flags(aFlags)
2656 {}
2657
2658 void handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2659 {
2660 LogFlow(("OnGuestPropertyChange: machineId={%RTuuid}, name='%ls', value='%ls', flags='%ls'\n",
2661 machineId.ptr(), name.raw(), value.raw(), flags.raw()));
2662 aCallback->OnGuestPropertyChange(machineId.toUtf16(), name, value, flags);
2663 }
2664
2665 Guid machineId;
2666 Bstr name, value, flags;
2667};
2668
2669/**
2670 * @note Doesn't lock any object.
2671 */
2672void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
2673 IN_BSTR aValue, IN_BSTR aFlags)
2674{
2675 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
2676}
2677
2678/**
2679 * @note Locks this object for reading.
2680 */
2681ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2682{
2683 ComObjPtr<GuestOSType> type;
2684 AutoCaller autoCaller(this);
2685 AssertComRCReturn(autoCaller.rc(), type);
2686
2687 /* unknown type must always be the first */
2688 ComAssertRet(m->ollGuestOSTypes.size() > 0, type);
2689
2690 return m->ollGuestOSTypes.front();
2691}
2692
2693/**
2694 * Returns the list of opened machines (machines having direct sessions opened
2695 * by client processes) and optionally the list of direct session controls.
2696 *
2697 * @param aMachines Where to put opened machines (will be empty if none).
2698 * @param aControls Where to put direct session controls (optional).
2699 *
2700 * @note The returned lists contain smart pointers. So, clear it as soon as
2701 * it becomes no more necessary to release instances.
2702 *
2703 * @note It can be possible that a session machine from the list has been
2704 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2705 * when accessing unprotected data directly.
2706 *
2707 * @note Locks objects for reading.
2708 */
2709void VirtualBox::getOpenedMachines(SessionMachineList &aMachines,
2710 InternalControlList *aControls /*= NULL*/)
2711{
2712 AutoCaller autoCaller(this);
2713 AssertComRCReturnVoid (autoCaller.rc());
2714
2715 aMachines.clear();
2716 if (aControls)
2717 aControls->clear();
2718
2719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2720
2721 for (MachinesOList::iterator it = m->ollMachines.begin();
2722 it != m->ollMachines.end();
2723 ++it)
2724 {
2725 ComObjPtr<SessionMachine> sm;
2726 ComPtr<IInternalSessionControl> ctl;
2727 if ((*it)->isSessionOpen(sm, &ctl))
2728 {
2729 aMachines.push_back(sm);
2730 if (aControls)
2731 aControls->push_back(ctl);
2732 }
2733 }
2734}
2735
2736/**
2737 * Searches for a Machine object with the given ID in the collection
2738 * of registered machines.
2739 *
2740 * @param id
2741 * ID of the machine
2742 * @param doSetError
2743 * if TRUE, the appropriate error info is set in case when the machine
2744 * is not found
2745 * @param machine
2746 * where to store the found machine object (can be NULL)
2747 *
2748 * @return
2749 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
2750 *
2751 * @note Locks this object for reading.
2752 */
2753HRESULT VirtualBox::findMachine(const Guid &aId,
2754 bool aSetError,
2755 ComObjPtr<Machine> *aMachine /* = NULL */)
2756{
2757 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
2758
2759 AutoCaller autoCaller(this);
2760 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2761
2762 {
2763 AutoReadLock al(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2764
2765 for (MachinesOList::iterator it = m->ollMachines.begin();
2766 it != m->ollMachines.end();
2767 ++it)
2768 {
2769 ComObjPtr<Machine> pMachine2 = *it;
2770 /* sanity */
2771 AutoLimitedCaller machCaller(pMachine2);
2772 AssertComRC(machCaller.rc());
2773
2774 if (pMachine2->getId() == aId)
2775 {
2776 rc = S_OK;
2777 if (aMachine)
2778 *aMachine = pMachine2;
2779 break;
2780 }
2781 }
2782 }
2783
2784 if (aSetError && FAILED(rc))
2785 setError(VBOX_E_OBJECT_NOT_FOUND,
2786 tr("Could not find a registered machine with UUID {%RTuuid}"),
2787 aId.raw());
2788
2789 return rc;
2790}
2791
2792/**
2793 * Searches for a Medium object with the given ID or location in the list of
2794 * registered hard disks. If both ID and location are specified, the first
2795 * object that matches either of them (not necessarily both) is returned.
2796 *
2797 * @param aId ID of the hard disk (unused when NULL).
2798 * @param pstrLocation Full location specification (unused NULL).
2799 * @param aSetError If @c true , the appropriate error info is set in case
2800 * when the hard disk is not found.
2801 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2802 *
2803 * @return S_OK when found or E_INVALIDARG when not found.
2804 *
2805 * @note Locks the media tree for reading.
2806 */
2807HRESULT VirtualBox::findHardDisk(const Guid *aId,
2808 const Utf8Str *pstrLocation,
2809 bool aSetError,
2810 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2811{
2812 AssertReturn(aId || pstrLocation, E_INVALIDARG);
2813
2814 // we use the hard disks map, but it is protected by the
2815 // hard disk _list_ lock handle
2816 AutoReadLock alock(m->ollHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2817
2818 /* first, look up by UUID in the map if UUID is provided */
2819 if (aId)
2820 {
2821 HardDiskMap::const_iterator it = m->mapHardDisks.find(*aId);
2822 if (it != m->mapHardDisks.end())
2823 {
2824 if (aHardDisk)
2825 *aHardDisk = (*it).second;
2826 return S_OK;
2827 }
2828 }
2829
2830 /* then iterate and search by location */
2831 int result = -1;
2832 if (pstrLocation)
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(pstrLocation->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 '%s' in the media registry ('%s')"),
2864 pstrLocation->c_str(),
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 const Utf8Str *pstrLocation,
2888 bool aSetError,
2889 ComObjPtr<Medium> *aImage /* = NULL */)
2890{
2891 AssertReturn(aId || pstrLocation, E_INVALIDARG);
2892
2893 Utf8Str location;
2894
2895 if (pstrLocation != NULL)
2896 {
2897 int vrc = calculateFullPath(*pstrLocation, location);
2898 if (RT_FAILURE(vrc))
2899 return setError(VBOX_E_FILE_ERROR,
2900 tr("Invalid image file location '%s' (%Rrc)"),
2901 pstrLocation->c_str(),
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 || ( pstrLocation != 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 '%s' in the media registry ('%s')"),
2941 pstrLocation->c_str(),
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 const Utf8Str *pstrLocation,
2966 bool aSetError,
2967 ComObjPtr<Medium> *aImage /* = NULL */)
2968{
2969 AssertReturn(aId || pstrLocation, E_INVALIDARG);
2970
2971 Utf8Str location;
2972
2973 if (pstrLocation != NULL)
2974 {
2975 int vrc = calculateFullPath(*pstrLocation, location);
2976 if (RT_FAILURE(vrc))
2977 return setError(VBOX_E_FILE_ERROR,
2978 tr("Invalid image file location '%s' (%Rrc)"),
2979 pstrLocation->c_str(),
2980 vrc);
2981 }
2982
2983 AutoReadLock alock(m->ollFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2984
2985 bool found = false;
2986
2987 for (MediaList::const_iterator it = m->ollFloppyImages.begin();
2988 it != m->ollFloppyImages.end();
2989 ++ it)
2990 {
2991 /* no AutoCaller, registered image life time is bound to this */
2992 AutoReadLock imageLock(*it COMMA_LOCKVAL_SRC_POS);
2993
2994 found = (aId && (*it)->getId() == *aId)
2995 || ( pstrLocation != NULL
2996 && RTPathCompare(location.c_str(),
2997 (*it)->getLocationFull().c_str()
2998 ) == 0);
2999 if (found)
3000 {
3001 if (aImage)
3002 *aImage = *it;
3003 break;
3004 }
3005 }
3006
3007 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3008
3009 if (aSetError && !found)
3010 {
3011 if (aId)
3012 setError(rc,
3013 tr("Could not find a floppy image with UUID {%RTuuid} in the media registry ('%s')"),
3014 aId->raw(),
3015 m->strSettingsFilePath.raw());
3016 else
3017 setError(rc,
3018 tr("Could not find a floppy image with location '%s' in the media registry ('%s')"),
3019 pstrLocation->c_str(),
3020 m->strSettingsFilePath.raw());
3021 }
3022
3023 return rc;
3024}
3025
3026HRESULT VirtualBox::findGuestOSType(CBSTR bstrOSType,
3027 GuestOSType*& pGuestOSType)
3028{
3029 /* Look for a GuestOSType object */
3030 AssertMsg(m->ollGuestOSTypes.size() != 0,
3031 ("Guest OS types array must be filled"));
3032
3033 if (bstrOSType == NULL)
3034 {
3035 pGuestOSType = NULL;
3036 return S_OK;
3037 }
3038
3039 AutoReadLock alock(m->ollGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3040 for (GuestOSTypesOList::const_iterator it = m->ollGuestOSTypes.begin();
3041 it != m->ollGuestOSTypes.end();
3042 ++it)
3043 {
3044 if ((*it)->id() == bstrOSType)
3045 {
3046 pGuestOSType = *it;
3047 return S_OK;
3048 }
3049 }
3050
3051 return setError(VBOX_E_OBJECT_NOT_FOUND,
3052 tr("Guest OS type '%ls' is invalid"),
3053 bstrOSType);
3054}
3055
3056const ComObjPtr<Host>& VirtualBox::host() const
3057{
3058 return m->pHost;
3059}
3060
3061const ComObjPtr<SystemProperties>& VirtualBox::systemProperties() const
3062{
3063 return m->pSystemProperties;
3064}
3065
3066#ifdef VBOX_WITH_RESOURCE_USAGE_API
3067const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
3068{
3069 return m->pPerformanceCollector;
3070}
3071#endif /* VBOX_WITH_RESOURCE_USAGE_API */
3072
3073/**
3074 * Returns the default machine folder from the system properties
3075 * with proper locking.
3076 * @return
3077 */
3078const Utf8Str& VirtualBox::getDefaultMachineFolder() const
3079{
3080 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3081 return m->pSystemProperties->m_strDefaultMachineFolder;
3082}
3083
3084/**
3085 * Returns the default hard disk folder from the system properties
3086 * with proper locking.
3087 * @return
3088 */
3089const Utf8Str& VirtualBox::getDefaultHardDiskFolder() const
3090{
3091 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3092 return m->pSystemProperties->m_strDefaultHardDiskFolder;
3093}
3094
3095/**
3096 * Returns the default hard disk format from the system properties
3097 * with proper locking.
3098 * @return
3099 */
3100const Utf8Str& VirtualBox::getDefaultHardDiskFormat() const
3101{
3102 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3103 return m->pSystemProperties->m_strDefaultHardDiskFormat;
3104}
3105
3106const Utf8Str& VirtualBox::homeDir() const
3107{
3108 return m->strHomeDir;
3109}
3110
3111/**
3112 * Calculates the absolute path of the given path taking the VirtualBox home
3113 * directory as the current directory.
3114 *
3115 * @param aPath Path to calculate the absolute path for.
3116 * @param aResult Where to put the result (used only on success, can be the
3117 * same Utf8Str instance as passed in @a aPath).
3118 * @return IPRT result.
3119 *
3120 * @note Doesn't lock any object.
3121 */
3122int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
3123{
3124 AutoCaller autoCaller(this);
3125 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
3126
3127 /* no need to lock since mHomeDir is const */
3128
3129 char folder[RTPATH_MAX];
3130 int vrc = RTPathAbsEx(m->strHomeDir.c_str(), strPath.c_str(), folder, sizeof(folder));
3131 if (RT_SUCCESS(vrc))
3132 aResult = folder;
3133
3134 return vrc;
3135}
3136
3137/**
3138 * Tries to calculate the relative path of the given absolute path using the
3139 * directory of the VirtualBox settings file as the base directory.
3140 *
3141 * @param aPath Absolute path to calculate the relative path for.
3142 * @param aResult Where to put the result (used only when it's possible to
3143 * make a relative path from the given absolute path; otherwise
3144 * left untouched).
3145 *
3146 * @note Doesn't lock any object.
3147 */
3148void VirtualBox::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
3149{
3150 AutoCaller autoCaller(this);
3151 AssertComRCReturnVoid (autoCaller.rc());
3152
3153 /* no need to lock since mHomeDir is const */
3154
3155 Utf8Str settingsDir = m->strHomeDir;
3156
3157 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
3158 {
3159 /* when assigning, we create a separate Utf8Str instance because both
3160 * aPath and aResult can point to the same memory location when this
3161 * func is called (if we just do aResult = aPath, aResult will be freed
3162 * first, and since its the same as aPath, an attempt to copy garbage
3163 * will be made. */
3164 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
3165 }
3166}
3167
3168// private methods
3169/////////////////////////////////////////////////////////////////////////////
3170
3171/**
3172 * Checks if there is a hard disk, DVD or floppy image with the given ID or
3173 * location already registered.
3174 *
3175 * On return, sets @a aConflict to the string describing the conflicting medium,
3176 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3177 * either case. A failure is unexpected.
3178 *
3179 * @param aId UUID to check.
3180 * @param aLocation Location to check.
3181 * @param aConflict Where to return parameters of the conflicting medium.
3182 *
3183 * @note Locks the media tree and media objects for reading.
3184 */
3185HRESULT VirtualBox::checkMediaForConflicts2(const Guid &aId,
3186 const Utf8Str &aLocation,
3187 Utf8Str &aConflict)
3188{
3189 aConflict.setNull();
3190
3191 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
3192
3193 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3194
3195 HRESULT rc = S_OK;
3196
3197 {
3198 ComObjPtr<Medium> hardDisk;
3199 rc = findHardDisk(&aId, &aLocation, false /* aSetError */, &hardDisk);
3200 if (SUCCEEDED(rc))
3201 {
3202 /* Note: no AutoCaller since bound to this */
3203 AutoReadLock mediaLock(hardDisk COMMA_LOCKVAL_SRC_POS);
3204 aConflict = Utf8StrFmt(tr("hard disk '%s' with UUID {%RTuuid}"),
3205 hardDisk->getLocationFull().raw(),
3206 hardDisk->getId().raw());
3207 return S_OK;
3208 }
3209 }
3210
3211 {
3212 ComObjPtr<Medium> image;
3213 rc = findDVDImage(&aId, &aLocation, false /* aSetError */, &image);
3214 if (SUCCEEDED(rc))
3215 {
3216 /* Note: no AutoCaller since bound to this */
3217 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
3218 aConflict = Utf8StrFmt(tr("CD/DVD image '%s' with UUID {%RTuuid}"),
3219 image->getLocationFull().raw(),
3220 image->getId().raw());
3221 return S_OK;
3222 }
3223 }
3224
3225 {
3226 ComObjPtr<Medium> image;
3227 rc = findFloppyImage(&aId, &aLocation, false /* aSetError */, &image);
3228 if (SUCCEEDED(rc))
3229 {
3230 /* Note: no AutoCaller since bound to this */
3231 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
3232 aConflict = Utf8StrFmt(tr("floppy image '%s' with UUID {%RTuuid}"),
3233 image->getLocationFull().raw(),
3234 image->getId().raw());
3235 return S_OK;
3236 }
3237 }
3238
3239 return S_OK;
3240}
3241
3242/**
3243 * Helper function to write out the configuration tree.
3244 *
3245 * @note Locks this object for writing and child objects for reading/writing!
3246 */
3247HRESULT VirtualBox::saveSettings()
3248{
3249 AutoCaller autoCaller(this);
3250 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3251
3252 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3253
3254 HRESULT rc = S_OK;
3255
3256 try
3257 {
3258 // machines
3259 settings::MachinesRegistry machinesTemp;
3260 {
3261 AutoReadLock al(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3262 for (MachinesOList::iterator it = m->ollMachines.begin();
3263 it != m->ollMachines.end();
3264 ++it)
3265 {
3266 settings::MachineRegistryEntry mre;
3267 rc = (*it)->saveRegistryEntry(mre);
3268 machinesTemp.push_back(mre);
3269 }
3270 }
3271
3272 // hard disks
3273 settings::MediaList hardDisksTemp;
3274 {
3275 AutoReadLock al(m->ollHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3276 for (MediaList::const_iterator it = m->ollHardDisks.begin();
3277 it != m->ollHardDisks.end();
3278 ++it)
3279 {
3280 settings::Medium med;
3281 rc = (*it)->saveSettings(med);
3282 if (FAILED(rc)) throw rc;
3283 hardDisksTemp.push_back(med);
3284 }
3285 }
3286
3287 /* CD/DVD images */
3288 settings::MediaList dvdsTemp;
3289 {
3290 AutoReadLock al(m->ollDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3291 for (MediaList::const_iterator it = m->ollDVDImages.begin();
3292 it != m->ollDVDImages.end();
3293 ++it)
3294 {
3295 settings::Medium med;
3296 rc = (*it)->saveSettings(med);
3297 if (FAILED(rc)) throw rc;
3298 dvdsTemp.push_back(med);
3299 }
3300 }
3301
3302 /* floppy images */
3303 settings::MediaList floppiesTemp;
3304 {
3305 AutoReadLock al(m->ollFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3306 for (MediaList::const_iterator it = m->ollFloppyImages.begin();
3307 it != m->ollFloppyImages.end();
3308 ++it)
3309 {
3310 settings::Medium med;
3311 rc = (*it)->saveSettings(med);
3312 if (FAILED(rc)) throw rc;
3313 floppiesTemp.push_back(med);
3314 }
3315 }
3316
3317 settings::DHCPServersList dhcpServersTemp;
3318 {
3319 AutoReadLock al(m->ollDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3320 for (DHCPServersOList::const_iterator it = m->ollDHCPServers.begin();
3321 it != m->ollDHCPServers.end();
3322 ++it)
3323 {
3324 settings::DHCPServer d;
3325 rc = (*it)->saveSettings(d);
3326 if (FAILED(rc)) throw rc;
3327 dhcpServersTemp.push_back(d);
3328 }
3329 }
3330
3331 /* finally, lock VirtualBox object for writing */
3332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3333
3334 /* now copy the temp data to the config file under the VirtualBox lock */
3335 m->pMainConfigFile->llMachines = machinesTemp;
3336 m->pMainConfigFile->llHardDisks = hardDisksTemp;
3337 m->pMainConfigFile->llDvdImages = dvdsTemp;
3338 m->pMainConfigFile->llFloppyImages = floppiesTemp;
3339 m->pMainConfigFile->llDhcpServers = dhcpServersTemp;
3340
3341 // leave extra data alone, it's still in the config file
3342
3343 /* host data (USB filters) */
3344 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3345 if (FAILED(rc)) throw rc;
3346
3347 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3348 if (FAILED(rc)) throw rc;
3349
3350 // and write out the XML, still under the lock
3351 m->pMainConfigFile->write(m->strSettingsFilePath);
3352 }
3353 catch (HRESULT err)
3354 {
3355 /* we assume that error info is set by the thrower */
3356 rc = err;
3357 }
3358 catch (...)
3359 {
3360 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3361 }
3362
3363 return rc;
3364}
3365
3366/**
3367 * Helper to register the machine.
3368 *
3369 * When called during VirtualBox startup, adds the given machine to the
3370 * collection of registered machines. Otherwise tries to mark the machine
3371 * as registered, and, if succeeded, adds it to the collection and
3372 * saves global settings.
3373 *
3374 * @note The caller must have added itself as a caller of the @a aMachine
3375 * object if calls this method not on VirtualBox startup.
3376 *
3377 * @param aMachine machine to register
3378 *
3379 * @note Locks objects!
3380 */
3381HRESULT VirtualBox::registerMachine(Machine *aMachine)
3382{
3383 ComAssertRet(aMachine, E_INVALIDARG);
3384
3385 AutoCaller autoCaller(this);
3386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3387
3388 HRESULT rc = S_OK;
3389
3390 {
3391 ComObjPtr<Machine> pMachine;
3392 rc = findMachine(aMachine->getId(), false /* aDoSetError */, &pMachine);
3393 if (SUCCEEDED(rc))
3394 {
3395 /* sanity */
3396 AutoLimitedCaller machCaller(pMachine);
3397 AssertComRC(machCaller.rc());
3398
3399 return setError(E_INVALIDARG,
3400 tr("Registered machine with UUID {%RTuuid} ('%ls') already exists"),
3401 aMachine->getId().raw(),
3402 pMachine->getSettingsFileFull().raw());
3403 }
3404
3405 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3406 rc = S_OK;
3407 }
3408
3409 if (autoCaller.state() != InInit)
3410 {
3411 // trySetRegistered needs VirtualBox object write lock;
3412 // it will commit and save machine settings
3413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3414 rc = aMachine->trySetRegistered(TRUE);
3415 if (FAILED(rc)) return rc;
3416 }
3417
3418 /* add to the collection of registered machines */
3419 m->ollMachines.addChild(aMachine);
3420
3421 if (autoCaller.state() != InInit)
3422 rc = saveSettings();
3423
3424 return rc;
3425}
3426
3427/**
3428 * Remembers the given hard disk by storing it in the hard disk registry.
3429 *
3430 * @param aHardDisk Hard disk object to remember.
3431 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3432 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3433 *
3434 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3435 */
3436HRESULT VirtualBox::registerHardDisk(Medium *aHardDisk,
3437 bool *pfNeedsSaveSettings)
3438{
3439 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3440
3441 AutoCaller autoCaller(this);
3442 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3443
3444 AutoCaller hardDiskCaller(aHardDisk);
3445 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3446
3447 // caller must hold the media tree write lock
3448 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3449
3450 Guid id;
3451 Utf8Str strLocationFull;
3452 ComObjPtr<Medium> pParent;
3453 {
3454 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3455 id = aHardDisk->getId();
3456 strLocationFull = aHardDisk->getLocationFull();
3457 pParent = aHardDisk->getParent();
3458 }
3459
3460 HRESULT rc;
3461
3462 Utf8Str strConflict;
3463 rc = checkMediaForConflicts2(id,
3464 strLocationFull,
3465 strConflict);
3466 if (FAILED(rc)) return rc;
3467
3468 if (strConflict.length())
3469 return setError(E_INVALIDARG,
3470 tr("Cannot register the hard disk '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3471 strLocationFull.raw(),
3472 id.raw(),
3473 strConflict.raw(),
3474 m->strSettingsFilePath.raw());
3475
3476 // store base (root) hard disks in the list
3477 if (pParent.isNull())
3478 m->ollHardDisks.getList().push_back(aHardDisk);
3479 // access the list directly because we already locked the list above
3480
3481 // store all hard disks (even differencing images) in the map
3482 m->mapHardDisks[id] = aHardDisk;
3483
3484 if (pfNeedsSaveSettings)
3485 *pfNeedsSaveSettings = true;
3486
3487 return rc;
3488}
3489
3490/**
3491 * Removes the given hard disk from the hard disk registry.
3492 *
3493 * @param aHardDisk Hard disk object to remove.
3494 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3495 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3496 *
3497 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3498 */
3499HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3500 bool *pfNeedsSaveSettings)
3501{
3502 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3503
3504 AutoCaller autoCaller(this);
3505 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3506
3507 AutoCaller hardDiskCaller (aHardDisk);
3508 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3509
3510 // caller must hold the media tree write lock
3511 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3512
3513 Guid id;
3514 ComObjPtr<Medium> pParent;
3515 {
3516 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3517 id = aHardDisk->getId();
3518 pParent = aHardDisk->getParent();
3519 }
3520
3521 // remove base (root) hard disks from the list
3522 if (pParent.isNull())
3523 m->ollHardDisks.getList().remove(aHardDisk);
3524 // access the list directly because caller must have locked the list
3525
3526 // remove all hard disks (even differencing images) from map
3527 size_t cnt = m->mapHardDisks.erase(id);
3528 Assert(cnt == 1);
3529 NOREF(cnt);
3530
3531 if (pfNeedsSaveSettings)
3532 *pfNeedsSaveSettings = true;
3533
3534 return S_OK;
3535}
3536
3537/**
3538 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3539 *
3540 * @param argImage Image object to remember.
3541 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3542 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3543 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3544 *
3545 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3546 */
3547HRESULT VirtualBox::registerImage(Medium *argImage,
3548 DeviceType_T argType,
3549 bool *pfNeedsSaveSettings)
3550{
3551 AssertReturn(argImage != NULL, E_INVALIDARG);
3552
3553 AutoCaller autoCaller(this);
3554 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3555
3556 AutoCaller imageCaller(argImage);
3557 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3558
3559 // caller must hold the media tree write lock
3560 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3561
3562 Guid id;
3563 Utf8Str strLocationFull;
3564 ComObjPtr<Medium> pParent;
3565 {
3566 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3567 id = argImage->getId();
3568 strLocationFull = argImage->getLocationFull();
3569 pParent = argImage->getParent();
3570 }
3571
3572 // work on DVDs or floppies list?
3573 ObjectsList<Medium> &oll = (argType == DeviceType_DVD) ? m->ollDVDImages : m->ollFloppyImages;
3574
3575 HRESULT rc;
3576 // lock the images lists (list + map) while checking for conflicts
3577 AutoWriteLock al(oll.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3578
3579 Utf8Str strConflict;
3580 rc = checkMediaForConflicts2(id,
3581 strLocationFull,
3582 strConflict);
3583 if (FAILED(rc)) return rc;
3584
3585 if (strConflict.length())
3586 return setError(VBOX_E_INVALID_OBJECT_STATE,
3587 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3588 strLocationFull.raw(),
3589 id.raw(),
3590 strConflict.raw(),
3591 m->strSettingsFilePath.raw());
3592
3593 // add to the collection
3594 oll.getList().push_back(argImage);
3595 // access the list directly because we already locked the list above
3596
3597 if (pfNeedsSaveSettings)
3598 *pfNeedsSaveSettings = true;
3599
3600 return rc;
3601}
3602
3603/**
3604 * Removes the given image from the CD/DVD or floppy image registry.
3605 *
3606 * @param argImage Image object to remove.
3607 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3608 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3609 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3610 *
3611 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3612 */
3613HRESULT VirtualBox::unregisterImage(Medium *argImage,
3614 DeviceType_T argType,
3615 bool *pfNeedsSaveSettings)
3616{
3617 AssertReturn(argImage != NULL, E_INVALIDARG);
3618
3619 AutoCaller autoCaller(this);
3620 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3621
3622 AutoCaller imageCaller(argImage);
3623 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3624
3625 // caller must hold the media tree write lock
3626 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3627
3628 Guid id;
3629 ComObjPtr<Medium> pParent;
3630 {
3631 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3632 id = argImage->getId();
3633 pParent = argImage->getParent();
3634 }
3635
3636 // work on DVDs or floppies list?
3637 ObjectsList<Medium> &oll = (argType == DeviceType_DVD) ? m->ollDVDImages : m->ollFloppyImages;
3638
3639 // access the list directly because the caller must have requested the lock
3640 oll.getList().remove(argImage);
3641
3642 HRESULT rc = S_OK;
3643
3644 if (pfNeedsSaveSettings)
3645 *pfNeedsSaveSettings = true;
3646
3647 return rc;
3648}
3649
3650/**
3651 * Helper to update the global settings file when the name of some machine
3652 * changes so that file and directory renaming occurs. This method ensures that
3653 * all affected paths in the disk registry are properly updated.
3654 *
3655 * @param aOldPath Old path (full).
3656 * @param aNewPath New path (full).
3657 *
3658 * @note Locks this object + DVD, Floppy and HardDisk children for writing.
3659 */
3660HRESULT VirtualBox::updateSettings(const char *aOldPath, const char *aNewPath)
3661{
3662 LogFlowThisFunc(("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
3663
3664 AssertReturn(aOldPath, E_INVALIDARG);
3665 AssertReturn(aNewPath, E_INVALIDARG);
3666
3667 AutoCaller autoCaller(this);
3668 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3669
3670 RWLockHandle tmpLock(LOCKCLASS_LISTOFMEDIA); // not really necessary but the ObjectsList<> constructor wants one
3671 ObjectsList<Medium> ollAll(tmpLock);
3672 ollAll.appendOtherList(m->ollDVDImages);
3673 ollAll.appendOtherList(m->ollFloppyImages);
3674 ollAll.appendOtherList(m->ollHardDisks);
3675
3676 for (MediaList::iterator it = ollAll.begin();
3677 it != ollAll.end();
3678 ++ it)
3679 {
3680 (*it)->updatePath(aOldPath, aNewPath);
3681 }
3682
3683 HRESULT rc = saveSettings();
3684
3685 return rc;
3686}
3687
3688/**
3689 * Creates the path to the specified file according to the path information
3690 * present in the file name.
3691 *
3692 * Note that the given file name must contain the full path otherwise the
3693 * extracted relative path will be created based on the current working
3694 * directory which is normally unknown.
3695 *
3696 * @param aFileName Full file name which path needs to be created.
3697 *
3698 * @return Extended error information on failure to create the path.
3699 */
3700/* static */
3701HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3702{
3703 Utf8Str strDir(strFileName);
3704 strDir.stripFilename();
3705 if (!RTDirExists(strDir.c_str()))
3706 {
3707 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3708 if (RT_FAILURE(vrc))
3709 return setError(E_FAIL,
3710 tr("Could not create the directory '%s' (%Rrc)"),
3711 strDir.c_str(),
3712 vrc);
3713 }
3714
3715 return S_OK;
3716}
3717
3718/**
3719 * Handles unexpected exceptions by turning them into COM errors in release
3720 * builds or by hitting a breakpoint in the release builds.
3721 *
3722 * Usage pattern:
3723 * @code
3724 try
3725 {
3726 // ...
3727 }
3728 catch (LaLalA)
3729 {
3730 // ...
3731 }
3732 catch (...)
3733 {
3734 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3735 }
3736 * @endcode
3737 *
3738 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3739 */
3740/* static */
3741HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3742{
3743 try
3744 {
3745 /* re-throw the current exception */
3746 throw;
3747 }
3748 catch (const xml::Error &err)
3749 {
3750 return setError(E_FAIL, tr("%s.\n%s[%d] (%s)"),
3751 err.what(),
3752 pszFile, iLine, pszFunction);
3753 }
3754 catch (const std::exception &err)
3755 {
3756 return setError(E_FAIL, tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3757 err.what(), typeid(err).name(),
3758 pszFile, iLine, pszFunction);
3759 }
3760 catch (...)
3761 {
3762 return setError(E_FAIL, tr("Unknown exception\n%s[%d] (%s)"),
3763 pszFile, iLine, pszFunction);
3764 }
3765
3766 /* should not get here */
3767 AssertFailed();
3768 return E_FAIL;
3769}
3770
3771const Utf8Str& VirtualBox::settingsFilePath()
3772{
3773 return m->strSettingsFilePath;
3774}
3775
3776/**
3777 * Returns the lock handle which protects the media trees (hard disks,
3778 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3779 * are no longer protected by the VirtualBox lock, but by this more
3780 * specialized lock. Mind the locking order: always request this lock
3781 * after the VirtualBox object lock but before the locks of the media
3782 * objects contained in these lists. See AutoLock.h.
3783 */
3784RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3785{
3786 return m->lockMedia;
3787}
3788
3789/**
3790 * Thread function that watches the termination of all client processes
3791 * that have opened sessions using IVirtualBox::OpenSession()
3792 */
3793// static
3794DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3795{
3796 LogFlowFuncEnter();
3797
3798 VirtualBox *that = (VirtualBox*)pvUser;
3799 Assert(that);
3800
3801 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3802 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3803
3804 SessionMachineVector machines;
3805 MachineVector spawnedMachines;
3806
3807 size_t cnt = 0;
3808 size_t cntSpawned = 0;
3809
3810#if defined(RT_OS_WINDOWS)
3811
3812 HRESULT hrc = CoInitializeEx(NULL,
3813 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3814 COINIT_SPEED_OVER_MEMORY);
3815 AssertComRC (hrc);
3816
3817 /// @todo (dmik) processes reaping!
3818
3819 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3820 handles[0] = that->m->updateReq;
3821
3822 do
3823 {
3824 AutoCaller autoCaller(that);
3825 /* VirtualBox has been early uninitialized, terminate */
3826 if (!autoCaller.isOk())
3827 break;
3828
3829 do
3830 {
3831 /* release the caller to let uninit() ever proceed */
3832 autoCaller.release();
3833
3834 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3835 handles,
3836 FALSE,
3837 INFINITE);
3838
3839 /* Restore the caller before using VirtualBox. If it fails, this
3840 * means VirtualBox is being uninitialized and we must terminate. */
3841 autoCaller.add();
3842 if (!autoCaller.isOk())
3843 break;
3844
3845 bool update = false;
3846
3847 if (rc == WAIT_OBJECT_0)
3848 {
3849 /* update event is signaled */
3850 update = true;
3851 }
3852 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3853 {
3854 /* machine mutex is released */
3855 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3856 update = true;
3857 }
3858 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3859 {
3860 /* machine mutex is abandoned due to client process termination */
3861 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3862 update = true;
3863 }
3864 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3865 {
3866 /* spawned VM process has terminated (normally or abnormally) */
3867 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
3868 checkForSpawnFailure();
3869 update = true;
3870 }
3871
3872 if (update)
3873 {
3874 /* close old process handles */
3875 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
3876 CloseHandle(handles[i]);
3877
3878 // lock the machines list for reading
3879 AutoReadLock thatLock(that->m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3880
3881 /* obtain a new set of opened machines */
3882 cnt = 0;
3883 machines.clear();
3884
3885 for (MachinesOList::iterator it = that->m->ollMachines.begin();
3886 it != that->m->ollMachines.end();
3887 ++it)
3888 {
3889 /// @todo handle situations with more than 64 objects
3890 AssertMsgBreak ((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3891 ("MAXIMUM_WAIT_OBJECTS reached"));
3892
3893 ComObjPtr<SessionMachine> sm;
3894 HANDLE ipcSem;
3895 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
3896 {
3897 machines.push_back(sm);
3898 handles[1 + cnt] = ipcSem;
3899 ++cnt;
3900 }
3901 }
3902
3903 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
3904
3905 /* obtain a new set of spawned machines */
3906 cntSpawned = 0;
3907 spawnedMachines.clear();
3908
3909 for (MachinesOList::iterator it = that->m->ollMachines.begin();
3910 it != that->m->ollMachines.end();
3911 ++it)
3912 {
3913 /// @todo handle situations with more than 64 objects
3914 AssertMsgBreak ((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
3915 ("MAXIMUM_WAIT_OBJECTS reached"));
3916
3917 RTPROCESS pid;
3918 if ((*it)->isSessionSpawning(&pid))
3919 {
3920 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
3921 AssertMsg (ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
3922 pid, GetLastError()));
3923 if (rc == 0)
3924 {
3925 spawnedMachines.push_back(*it);
3926 handles[1 + cnt + cntSpawned] = ph;
3927 ++cntSpawned;
3928 }
3929 }
3930 }
3931
3932 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
3933
3934 // machines lock unwinds here
3935 }
3936 }
3937 while (true);
3938 }
3939 while (0);
3940
3941 /* close old process handles */
3942 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3943 CloseHandle (handles[i]);
3944
3945 /* release sets of machines if any */
3946 machines.clear();
3947 spawnedMachines.clear();
3948
3949 ::CoUninitialize();
3950
3951#elif defined (RT_OS_OS2)
3952
3953 /// @todo (dmik) processes reaping!
3954
3955 /* according to PMREF, 64 is the maximum for the muxwait list */
3956 SEMRECORD handles[64];
3957
3958 HMUX muxSem = NULLHANDLE;
3959
3960 do
3961 {
3962 AutoCaller autoCaller(that);
3963 /* VirtualBox has been early uninitialized, terminate */
3964 if (!autoCaller.isOk())
3965 break;
3966
3967 do
3968 {
3969 /* release the caller to let uninit() ever proceed */
3970 autoCaller.release();
3971
3972 int vrc = RTSemEventWait(that->m->updateReq, 500);
3973
3974 /* Restore the caller before using VirtualBox. If it fails, this
3975 * means VirtualBox is being uninitialized and we must terminate. */
3976 autoCaller.add();
3977 if (!autoCaller.isOk())
3978 break;
3979
3980 bool update = false;
3981 bool updateSpawned = false;
3982
3983 if (RT_SUCCESS(vrc))
3984 {
3985 /* update event is signaled */
3986 update = true;
3987 updateSpawned = true;
3988 }
3989 else
3990 {
3991 AssertMsg (vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
3992 ("RTSemEventWait returned %Rrc\n", vrc));
3993
3994 /* are there any mutexes? */
3995 if (cnt > 0)
3996 {
3997 /* figure out what's going on with machines */
3998
3999 unsigned long semId = 0;
4000 APIRET arc = ::DosWaitMuxWaitSem (muxSem,
4001 SEM_IMMEDIATE_RETURN, &semId);
4002
4003 if (arc == NO_ERROR)
4004 {
4005 /* machine mutex is normally released */
4006 Assert(semId >= 0 && semId < cnt);
4007 if (semId >= 0 && semId < cnt)
4008 {
4009#ifdef DEBUG
4010 {
4011 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4012 LogFlowFunc (("released mutex: machine='%ls'\n",
4013 machines[semId]->name().raw()));
4014 }
4015#endif
4016 machines[semId]->checkForDeath();
4017 }
4018 update = true;
4019 }
4020 else if (arc == ERROR_SEM_OWNER_DIED)
4021 {
4022 /* machine mutex is abandoned due to client process
4023 * termination; find which mutex is in the Owner Died
4024 * state */
4025 for (size_t i = 0; i < cnt; ++ i)
4026 {
4027 PID pid; TID tid;
4028 unsigned long reqCnt;
4029 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
4030 if (arc == ERROR_SEM_OWNER_DIED)
4031 {
4032 /* close the dead mutex as asked by PMREF */
4033 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
4034
4035 Assert(i >= 0 && i < cnt);
4036 if (i >= 0 && i < cnt)
4037 {
4038#ifdef DEBUG
4039 {
4040 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4041 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4042 machines[i]->name().raw()));
4043 }
4044#endif
4045 machines[i]->checkForDeath();
4046 }
4047 }
4048 }
4049 update = true;
4050 }
4051 else
4052 AssertMsg (arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4053 ("DosWaitMuxWaitSem returned %d\n", arc));
4054 }
4055
4056 /* are there any spawning sessions? */
4057 if (cntSpawned > 0)
4058 {
4059 for (size_t i = 0; i < cntSpawned; ++ i)
4060 updateSpawned |= (spawnedMachines[i])->
4061 checkForSpawnFailure();
4062 }
4063 }
4064
4065 if (update || updateSpawned)
4066 {
4067 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4068
4069 if (update)
4070 {
4071 /* close the old muxsem */
4072 if (muxSem != NULLHANDLE)
4073 ::DosCloseMuxWaitSem (muxSem);
4074
4075 /* obtain a new set of opened machines */
4076 cnt = 0;
4077 machines.clear();
4078
4079 for (MachinesOList::iterator it = that->m->llMachines.begin();
4080 it != that->m->llMachines.end(); ++ it)
4081 {
4082 /// @todo handle situations with more than 64 objects
4083 AssertMsg (cnt <= 64 /* according to PMREF */,
4084 ("maximum of 64 mutex semaphores reached (%d)",
4085 cnt));
4086
4087 ComObjPtr<SessionMachine> sm;
4088 HMTX ipcSem;
4089 if ((*it)->isSessionOpenOrClosing (sm, NULL, &ipcSem))
4090 {
4091 machines.push_back (sm);
4092 handles[cnt].hsemCur = (HSEM) ipcSem;
4093 handles[cnt].ulUser = cnt;
4094 ++ cnt;
4095 }
4096 }
4097
4098 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4099
4100 if (cnt > 0)
4101 {
4102 /* create a new muxsem */
4103 APIRET arc = ::DosCreateMuxWaitSem (NULL, &muxSem, cnt,
4104 handles,
4105 DCMW_WAIT_ANY);
4106 AssertMsg (arc == NO_ERROR,
4107 ("DosCreateMuxWaitSem returned %d\n", arc));
4108 NOREF(arc);
4109 }
4110 }
4111
4112 if (updateSpawned)
4113 {
4114 /* obtain a new set of spawned machines */
4115 spawnedMachines.clear();
4116
4117 for (MachinesOList::iterator it = that->m->llMachines.begin();
4118 it != that->m->llMachines.end(); ++ it)
4119 {
4120 if ((*it)->isSessionSpawning())
4121 spawnedMachines.push_back (*it);
4122 }
4123
4124 cntSpawned = spawnedMachines.size();
4125 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4126 }
4127 }
4128 }
4129 while (true);
4130 }
4131 while (0);
4132
4133 /* close the muxsem */
4134 if (muxSem != NULLHANDLE)
4135 ::DosCloseMuxWaitSem (muxSem);
4136
4137 /* release sets of machines if any */
4138 machines.clear();
4139 spawnedMachines.clear();
4140
4141#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4142
4143 bool update = false;
4144 bool updateSpawned = false;
4145
4146 do
4147 {
4148 AutoCaller autoCaller(that);
4149 if (!autoCaller.isOk())
4150 break;
4151
4152 do
4153 {
4154 /* release the caller to let uninit() ever proceed */
4155 autoCaller.release();
4156
4157 int rc = RTSemEventWait(that->m->updateReq, 500);
4158
4159 /*
4160 * Restore the caller before using VirtualBox. If it fails, this
4161 * means VirtualBox is being uninitialized and we must terminate.
4162 */
4163 autoCaller.add();
4164 if (!autoCaller.isOk())
4165 break;
4166
4167 if (RT_SUCCESS(rc) || update || updateSpawned)
4168 {
4169 /* RT_SUCCESS(rc) means an update event is signaled */
4170
4171 // lock the machines list for reading
4172 AutoReadLock thatLock(that->m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4173
4174 if (RT_SUCCESS(rc) || update)
4175 {
4176 /* obtain a new set of opened machines */
4177 machines.clear();
4178
4179 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4180 it != that->m->ollMachines.end();
4181 ++it)
4182 {
4183 ComObjPtr<SessionMachine> sm;
4184 if ((*it)->isSessionOpenOrClosing(sm))
4185 machines.push_back(sm);
4186 }
4187
4188 cnt = machines.size();
4189 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4190 }
4191
4192 if (RT_SUCCESS(rc) || updateSpawned)
4193 {
4194 /* obtain a new set of spawned machines */
4195 spawnedMachines.clear();
4196
4197 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4198 it != that->m->ollMachines.end();
4199 ++it)
4200 {
4201 if ((*it)->isSessionSpawning())
4202 spawnedMachines.push_back(*it);
4203 }
4204
4205 cntSpawned = spawnedMachines.size();
4206 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4207 }
4208
4209 // machines lock unwinds here
4210 }
4211
4212 update = false;
4213 for (size_t i = 0; i < cnt; ++ i)
4214 update |= (machines[i])->checkForDeath();
4215
4216 updateSpawned = false;
4217 for (size_t i = 0; i < cntSpawned; ++ i)
4218 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4219
4220 /* reap child processes */
4221 {
4222 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4223 if (that->m->llProcesses.size())
4224 {
4225 LogFlowFunc (("UPDATE: child process count = %d\n",
4226 that->m->llProcesses.size()));
4227 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4228 while (it != that->m->llProcesses.end())
4229 {
4230 RTPROCESS pid = *it;
4231 RTPROCSTATUS status;
4232 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4233 if (vrc == VINF_SUCCESS)
4234 {
4235 LogFlowFunc (("pid %d (%x) was reaped, "
4236 "status=%d, reason=%d\n",
4237 pid, pid, status.iStatus,
4238 status.enmReason));
4239 it = that->m->llProcesses.erase(it);
4240 }
4241 else
4242 {
4243 LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4244 pid, pid, vrc));
4245 if (vrc != VERR_PROCESS_RUNNING)
4246 {
4247 /* remove the process if it is not already running */
4248 it = that->m->llProcesses.erase(it);
4249 }
4250 else
4251 ++ it;
4252 }
4253 }
4254 }
4255 }
4256 }
4257 while (true);
4258 }
4259 while (0);
4260
4261 /* release sets of machines if any */
4262 machines.clear();
4263 spawnedMachines.clear();
4264
4265#else
4266# error "Port me!"
4267#endif
4268
4269 LogFlowFuncLeave();
4270 return 0;
4271}
4272
4273/**
4274 * Thread function that handles custom events posted using #postEvent().
4275 */
4276// static
4277DECLCALLBACK(int) VirtualBox::AsyncEventHandler (RTTHREAD thread, void *pvUser)
4278{
4279 LogFlowFuncEnter();
4280
4281 AssertReturn(pvUser, VERR_INVALID_POINTER);
4282
4283 // create an event queue for the current thread
4284 EventQueue *eventQ = new EventQueue();
4285 AssertReturn(eventQ, VERR_NO_MEMORY);
4286
4287 // return the queue to the one who created this thread
4288 *(static_cast <EventQueue **> (pvUser)) = eventQ;
4289 // signal that we're ready
4290 RTThreadUserSignal (thread);
4291
4292 BOOL ok = TRUE;
4293 Event *event = NULL;
4294
4295 while ((ok = eventQ->waitForEvent (&event)) && event)
4296 eventQ->handleEvent (event);
4297
4298 AssertReturn(ok, VERR_GENERAL_FAILURE);
4299
4300 delete eventQ;
4301
4302 LogFlowFuncLeave();
4303
4304 return 0;
4305}
4306
4307////////////////////////////////////////////////////////////////////////////////
4308
4309/**
4310 * Takes the current list of registered callbacks of the managed VirtualBox
4311 * instance, and calls #handleCallback() for every callback item from the
4312 * list, passing the item as an argument.
4313 *
4314 * @note Locks the managed VirtualBox object for reading but leaves the lock
4315 * before iterating over callbacks and calling their methods.
4316 */
4317void *VirtualBox::CallbackEvent::handler()
4318{
4319 if (mVirtualBox.isNull())
4320 return NULL;
4321
4322 AutoCaller autoCaller(mVirtualBox);
4323 if (!autoCaller.isOk())
4324 {
4325 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
4326 "the callback event is discarded!\n",
4327 autoCaller.state()));
4328 /* We don't need mVirtualBox any more, so release it */
4329 mVirtualBox.setNull();
4330 return NULL;
4331 }
4332
4333 CallbackList callbacks;
4334 {
4335 /* Make a copy to release the lock before iterating */
4336 AutoReadLock alock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
4337 callbacks = mVirtualBox->m->llCallbacks;
4338 /* We don't need mVirtualBox any more, so release it */
4339 mVirtualBox.setNull();
4340 }
4341
4342 for (CallbackList::const_iterator it = callbacks.begin();
4343 it != callbacks.end();
4344 ++it)
4345 handleCallback(*it);
4346
4347 return NULL;
4348}
4349
4350//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface (/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4351//{
4352// return E_NOTIMPL;
4353//}
4354
4355STDMETHODIMP VirtualBox::CreateDHCPServer (IN_BSTR aName, IDHCPServer ** aServer)
4356{
4357 CheckComArgStrNotEmptyOrNull(aName);
4358 CheckComArgNotNull(aServer);
4359
4360 AutoCaller autoCaller(this);
4361 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4362
4363 ComObjPtr<DHCPServer> dhcpServer;
4364 dhcpServer.createObject();
4365 HRESULT rc = dhcpServer->init (this, aName);
4366 if (FAILED(rc)) return rc;
4367
4368 rc = registerDHCPServer(dhcpServer, true);
4369 if (FAILED(rc)) return rc;
4370
4371 dhcpServer.queryInterfaceTo(aServer);
4372
4373 return rc;
4374}
4375
4376STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4377{
4378 CheckComArgStrNotEmptyOrNull(aName);
4379 CheckComArgNotNull(aServer);
4380
4381 AutoCaller autoCaller(this);
4382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4383
4384 HRESULT rc;
4385 Bstr bstr;
4386 ComPtr<DHCPServer> found;
4387
4388 AutoReadLock alock(m->ollDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4389
4390 for (DHCPServersOList::const_iterator it = m->ollDHCPServers.begin();
4391 it != m->ollDHCPServers.end();
4392 ++it)
4393 {
4394 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4395 if (FAILED(rc)) throw rc;
4396
4397 if (bstr == aName)
4398 {
4399 found = *it;
4400 break;
4401 }
4402 }
4403
4404 if (!found)
4405 return E_INVALIDARG;
4406
4407 return found.queryInterfaceTo(aServer);
4408}
4409
4410STDMETHODIMP VirtualBox::RemoveDHCPServer (IDHCPServer * aServer)
4411{
4412 CheckComArgNotNull(aServer);
4413
4414 AutoCaller autoCaller(this);
4415 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4416
4417 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4418
4419 return rc;
4420}
4421
4422/**
4423 * Remembers the given dhcp server by storing it in the hard disk registry.
4424 *
4425 * @param aDHCPServer Dhcp Server object to remember.
4426 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4427 *
4428 * When @a aSaveRegistry is @c true, this operation may fail because of the
4429 * failed #saveSettings() method it calls. In this case, the dhcp server object
4430 * will not be remembered. It is therefore the responsibility of the caller to
4431 * call this method as the last step of some action that requires registration
4432 * in order to make sure that only fully functional dhcp server objects get
4433 * registered.
4434 *
4435 * @note Locks this object for writing and @a aDHCPServer for reading.
4436 */
4437HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4438 bool aSaveRegistry /*= true*/)
4439{
4440 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4441
4442 AutoCaller autoCaller(this);
4443 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4444
4445 AutoCaller dhcpServerCaller(aDHCPServer);
4446 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4447
4448 Bstr name;
4449 HRESULT rc;
4450 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4451 if (FAILED(rc)) return rc;
4452
4453 ComPtr<IDHCPServer> existing;
4454 rc = FindDHCPServerByNetworkName(name, existing.asOutParam());
4455 if (SUCCEEDED(rc))
4456 {
4457 return E_INVALIDARG;
4458 }
4459
4460 rc = S_OK;
4461
4462 m->ollDHCPServers.addChild(aDHCPServer);
4463
4464 if (aSaveRegistry)
4465 {
4466 rc = saveSettings();
4467 if (FAILED(rc))
4468 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4469 }
4470
4471 return rc;
4472}
4473
4474/**
4475 * Removes the given hard disk from the hard disk registry.
4476 *
4477 * @param aHardDisk Hard disk object to remove.
4478 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4479 *
4480 * When @a aSaveRegistry is @c true, this operation may fail because of the
4481 * failed #saveSettings() method it calls. In this case, the hard disk object
4482 * will NOT be removed from the registry when this method returns. It is
4483 * therefore the responsibility of the caller to call this method as the first
4484 * step of some action that requires unregistration, before calling uninit() on
4485 * @a aHardDisk.
4486 *
4487 * @note Locks this object for writing and @a aHardDisk for reading.
4488 */
4489HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4490 bool aSaveRegistry /*= true*/)
4491{
4492 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4493
4494 AutoCaller autoCaller(this);
4495 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4496
4497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4498
4499 AutoCaller dhcpServerCaller (aDHCPServer);
4500 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4501
4502 m->ollDHCPServers.removeChild(aDHCPServer);
4503
4504 HRESULT rc = S_OK;
4505
4506 if (aSaveRegistry)
4507 {
4508 rc = saveSettings();
4509 if (FAILED(rc))
4510 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4511 }
4512
4513 return rc;
4514}
4515
4516/* 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