VirtualBox

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

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

Main: fix regression from today: assertion when creating VM

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

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