VirtualBox

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

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

Main: make restoreSnapshot() work with the lock validator; take saveSettings() out of a lot of functions and instead return a flag to the caller so the caller can make that call; as a side effect, this no longer calls saveSettings multiple times in several code paths (e.g. restoreSnapshot()) and cleans up locking in medium tasks

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

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