VirtualBox

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

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

Main: less locks: use the same lock for several lists

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

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