VirtualBox

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

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

Main: coding style

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

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