VirtualBox

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

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

Main: get rid of isReallyChanged() voodoo in Machine and subclasses; instead check in the XML classes whether things really changed via operator==; documentation, cleanup

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

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