VirtualBox

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

Last change on this file since 10528 was 10528, checked in by vboxsync, 16 years ago

Performance API, version 0, webservice broken.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 148.4 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 10528 2008-07-11 14:27:01Z vboxsync $ */
2
3/** @file
4 * Implmentation of IVirtualBox in VBoxSVC.
5 */
6
7/*
8 * Copyright (C) 2006-2007 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 "VirtualBoxImpl.h"
24
25#include "Global.h"
26#include "MachineImpl.h"
27#include "HardDiskImpl.h"
28#include "DVDImageImpl.h"
29#include "FloppyImageImpl.h"
30#include "SharedFolderImpl.h"
31#include "ProgressImpl.h"
32#include "HostImpl.h"
33#include "USBControllerImpl.h"
34#include "SystemPropertiesImpl.h"
35#include "GuestOSTypeImpl.h"
36
37#include "VirtualBoxXMLUtil.h"
38
39#include "Logging.h"
40
41#ifdef RT_OS_WINDOWS
42# include "win/svchlp.h"
43#endif
44
45#include <stdio.h>
46#include <stdlib.h>
47
48#include <iprt/path.h>
49#include <iprt/dir.h>
50#include <iprt/file.h>
51#include <iprt/string.h>
52#include <iprt/uuid.h>
53#include <iprt/thread.h>
54#include <iprt/process.h>
55#include <iprt/env.h>
56#include <iprt/cpputils.h>
57
58#include <VBox/err.h>
59#include <VBox/param.h>
60#include <VBox/VBoxHDD.h>
61#include <VBox/VBoxHDD-new.h>
62#include <VBox/version.h>
63#include <package-generated.h>
64
65#include <VBox/com/com.h>
66#include <VBox/com/array.h>
67
68#include <algorithm>
69#include <set>
70#include <memory> // for auto_ptr
71
72#include <typeinfo>
73
74// defines
75/////////////////////////////////////////////////////////////////////////////
76
77#define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
78
79// globals
80/////////////////////////////////////////////////////////////////////////////
81
82static const char gDefaultGlobalConfig [] =
83{
84 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" RTFILE_LINEFEED
85 "<!-- Sun xVM VirtualBox Global Configuration -->" RTFILE_LINEFEED
86 "<VirtualBox xmlns=\"" VBOX_XML_NAMESPACE "\" "
87 "version=\"" VBOX_XML_VERSION_FULL "\">" RTFILE_LINEFEED
88 " <Global>"RTFILE_LINEFEED
89 " <MachineRegistry/>"RTFILE_LINEFEED
90 " <DiskRegistry/>"RTFILE_LINEFEED
91 " <USBDeviceFilters/>"RTFILE_LINEFEED
92 " <SystemProperties/>"RTFILE_LINEFEED
93 " </Global>"RTFILE_LINEFEED
94 "</VirtualBox>"RTFILE_LINEFEED
95};
96
97// static
98Bstr VirtualBox::sVersion;
99
100// static
101Bstr VirtualBox::sPackageType;
102
103// static
104Bstr VirtualBox::sSettingsFormatVersion;
105
106// constructor / destructor
107/////////////////////////////////////////////////////////////////////////////
108
109VirtualBox::VirtualBox()
110 : mAsyncEventThread (NIL_RTTHREAD)
111 , mAsyncEventQ (NULL)
112{}
113
114VirtualBox::~VirtualBox() {}
115
116HRESULT VirtualBox::FinalConstruct()
117{
118 LogFlowThisFunc (("\n"));
119
120 return init();
121}
122
123void VirtualBox::FinalRelease()
124{
125 LogFlowThisFunc (("\n"));
126
127 uninit();
128}
129
130VirtualBox::Data::Data()
131{
132}
133
134// public initializer/uninitializer for internal purposes only
135/////////////////////////////////////////////////////////////////////////////
136
137/**
138 * Initializes the VirtualBox object.
139 *
140 * @return COM result code
141 */
142HRESULT VirtualBox::init()
143{
144 /* Enclose the state transition NotReady->InInit->Ready */
145 AutoInitSpan autoInitSpan (this);
146 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
147
148 LogFlow (("===========================================================\n"));
149 LogFlowThisFuncEnter();
150
151 if (sVersion.isNull())
152 sVersion = VBOX_VERSION_STRING;
153 if (sPackageType.isNull())
154 sPackageType = VBOX_PACKAGE_STRING;
155 LogFlowThisFunc (("Version: %ls, Package: %ls\n", sVersion.raw(), sPackageType.raw()));
156
157 if (sSettingsFormatVersion.isNull())
158 sSettingsFormatVersion = VBOX_XML_VERSION_FULL;
159 LogFlowThisFunc (("Settings Format Version: %ls\n",
160 sSettingsFormatVersion.raw()));
161
162 /* Get the VirtualBox home directory. */
163 {
164 char homeDir [RTPATH_MAX];
165 int vrc = com::GetVBoxUserHomeDirectory (homeDir, sizeof (homeDir));
166 if (VBOX_FAILURE (vrc))
167 return setError (E_FAIL,
168 tr ("Could not create the VirtualBox home directory '%s'"
169 "(%Vrc)"),
170 homeDir, vrc);
171
172 unconst (mData.mHomeDir) = homeDir;
173 }
174
175 /* compose the global config file name (always full path) */
176 Utf8StrFmt vboxConfigFile ("%s%c%s", mData.mHomeDir.raw(),
177 RTPATH_DELIMITER, VBOX_GLOBAL_SETTINGS_FILE);
178
179 /* store the config file name */
180 unconst (mData.mCfgFile.mName) = vboxConfigFile;
181
182 /* lock the config file */
183 HRESULT rc = lockConfig();
184 if (SUCCEEDED (rc))
185 {
186 if (!isConfigLocked())
187 {
188 /*
189 * This means the config file not found. This is not fatal --
190 * we just create an empty one.
191 */
192 RTFILE handle = NIL_RTFILE;
193 int vrc = RTFileOpen (&handle, vboxConfigFile,
194 RTFILE_O_READWRITE | RTFILE_O_CREATE |
195 RTFILE_O_DENY_WRITE);
196 if (VBOX_SUCCESS (vrc))
197 vrc = RTFileWrite (handle,
198 (void *) gDefaultGlobalConfig,
199 sizeof (gDefaultGlobalConfig), NULL);
200 if (VBOX_FAILURE (vrc))
201 {
202 rc = setError (E_FAIL, tr ("Could not create the default settings file "
203 "'%s' (%Vrc)"),
204 vboxConfigFile.raw(), vrc);
205 }
206 else
207 {
208 mData.mCfgFile.mHandle = handle;
209 /* we do not close the file to simulate lockConfig() */
210 }
211 }
212 }
213
214 if (SUCCEEDED (rc))
215 {
216 try
217 {
218 using namespace settings;
219
220 /* no concurrent file access is possible in init() so open by handle */
221 File file (mData.mCfgFile.mHandle, vboxConfigFile);
222 XmlTreeBackend tree;
223
224 rc = VirtualBox::loadSettingsTree_FirstTime (tree, file,
225 mData.mSettingsFileVersion);
226 CheckComRCThrowRC (rc);
227
228 Key global = tree.rootKey().key ("Global");
229
230#ifdef VBOX_WITH_RESOURCE_USAGE_API
231 /* create the performance collector object BEFORE host */
232 unconst (mData.mPerformanceCollector).createObject();
233 rc = mData.mPerformanceCollector->init ();
234 ComAssertComRCThrowRC (rc);
235#endif /* VBOX_WITH_RESOURCE_USAGE_API */
236
237 /* create the host object early, machines will need it */
238 unconst (mData.mHost).createObject();
239 rc = mData.mHost->init (this);
240 ComAssertComRCThrowRC (rc);
241
242 rc = mData.mHost->loadSettings (global);
243 CheckComRCThrowRC (rc);
244
245 /* create the system properties object */
246 unconst (mData.mSystemProperties).createObject();
247 rc = mData.mSystemProperties->init (this);
248 ComAssertComRCThrowRC (rc);
249
250 rc = mData.mSystemProperties->loadSettings (global);
251 CheckComRCThrowRC (rc);
252
253 /* guest OS type objects, needed by machines */
254 for (size_t i = 0; i < RT_ELEMENTS (Global::sOSTypes); ++ i)
255 {
256 ComObjPtr <GuestOSType> guestOSTypeObj;
257 rc = guestOSTypeObj.createObject();
258 if (SUCCEEDED (rc))
259 {
260 rc = guestOSTypeObj->init (Global::sOSTypes [i].id,
261 Global::sOSTypes [i].description,
262 Global::sOSTypes [i].osType,
263 Global::sOSTypes [i].recommendedRAM,
264 Global::sOSTypes [i].recommendedVRAM,
265 Global::sOSTypes [i].recommendedHDD);
266 if (SUCCEEDED (rc))
267 mData.mGuestOSTypes.push_back (guestOSTypeObj);
268 }
269 ComAssertComRCThrowRC (rc);
270 }
271
272 /* hard disks, needed by machines */
273 rc = loadDisks (global);
274 CheckComRCThrowRC (rc);
275
276 /* machines */
277 rc = loadMachines (global);
278 CheckComRCThrowRC (rc);
279
280 /* check hard disk consistency */
281/// @todo (r=dmik) add IVirtualBox::cleanupHardDisks() instead or similar
282// for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
283// it != mData.mHardDisks.end() && SUCCEEDED (rc);
284// ++ it)
285// {
286// rc = (*it)->checkConsistency();
287// }
288// CheckComRCBreakRC ((rc));
289
290 /// @todo (dmik) if successful, check for orphan (unused) diffs
291 // that might be left because of the server crash, and remove
292 // Hmm, is it the same remark as above?..
293 }
294 catch (HRESULT err)
295 {
296 /* we assume that error info is set by the thrower */
297 rc = err;
298 }
299 catch (...)
300 {
301 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
302 }
303 }
304
305 if (SUCCEEDED (rc))
306 {
307 /* start the client watcher thread */
308#if defined(RT_OS_WINDOWS)
309 unconst (mWatcherData.mUpdateReq) = ::CreateEvent (NULL, FALSE, FALSE, NULL);
310#elif defined(RT_OS_OS2)
311 RTSemEventCreate (&unconst (mWatcherData.mUpdateReq));
312#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
313 RTSemEventCreate (&unconst (mWatcherData.mUpdateReq));
314#else
315# error "Port me!"
316#endif
317 int vrc = RTThreadCreate (&unconst (mWatcherData.mThread),
318 ClientWatcher, (void *) this,
319 0, RTTHREADTYPE_MAIN_WORKER,
320 RTTHREADFLAGS_WAITABLE, "Watcher");
321 ComAssertRC (vrc);
322 if (VBOX_FAILURE (vrc))
323 rc = E_FAIL;
324 }
325
326 if (SUCCEEDED (rc)) do
327 {
328 /* start the async event handler thread */
329 int vrc = RTThreadCreate (&unconst (mAsyncEventThread), AsyncEventHandler,
330 &unconst (mAsyncEventQ),
331 0, RTTHREADTYPE_MAIN_WORKER,
332 RTTHREADFLAGS_WAITABLE, "EventHandler");
333 ComAssertRCBreak (vrc, rc = E_FAIL);
334
335 /* wait until the thread sets mAsyncEventQ */
336 RTThreadUserWait (mAsyncEventThread, RT_INDEFINITE_WAIT);
337 ComAssertBreak (mAsyncEventQ, rc = E_FAIL);
338 }
339 while (0);
340
341 /* Confirm a successful initialization when it's the case */
342 if (SUCCEEDED (rc))
343 autoInitSpan.setSucceeded();
344
345 LogFlowThisFunc (("rc=%08X\n", rc));
346 LogFlowThisFuncLeave();
347 LogFlow (("===========================================================\n"));
348 return rc;
349}
350
351void VirtualBox::uninit()
352{
353 /* Enclose the state transition Ready->InUninit->NotReady */
354 AutoUninitSpan autoUninitSpan (this);
355 if (autoUninitSpan.uninitDone())
356 return;
357
358 LogFlow (("===========================================================\n"));
359 LogFlowThisFuncEnter();
360 LogFlowThisFunc (("initFailed()=%d\n", autoUninitSpan.initFailed()));
361
362 /* tell all our child objects we've been uninitialized */
363
364 LogFlowThisFunc (("Uninitializing machines (%d)...\n", mData.mMachines.size()));
365 if (mData.mMachines.size())
366 {
367 MachineList::iterator it = mData.mMachines.begin();
368 while (it != mData.mMachines.end())
369 (*it++)->uninit();
370 mData.mMachines.clear();
371 }
372
373 if (mData.mSystemProperties)
374 {
375 mData.mSystemProperties->uninit();
376 unconst (mData.mSystemProperties).setNull();
377 }
378
379 if (mData.mHost)
380 {
381 mData.mHost->uninit();
382 unconst (mData.mHost).setNull();
383 }
384
385#ifdef VBOX_WITH_RESOURCE_USAGE_API
386 if (mData.mPerformanceCollector)
387 {
388 mData.mPerformanceCollector->uninit();
389 unconst (mData.mPerformanceCollector).setNull();
390 }
391#endif /* VBOX_WITH_RESOURCE_USAGE_API */
392
393 /*
394 * Uninit all other children still referenced by clients
395 * (unregistered machines, hard disks, DVD/floppy images,
396 * server-side progress operations).
397 */
398 uninitDependentChildren();
399
400 mData.mFloppyImages.clear();
401 mData.mDVDImages.clear();
402 mData.mHardDisks.clear();
403
404 mData.mHardDiskMap.clear();
405
406 mData.mProgressOperations.clear();
407
408 mData.mGuestOSTypes.clear();
409
410 /* unlock the config file */
411 unlockConfig();
412
413 LogFlowThisFunc (("Releasing callbacks...\n"));
414 if (mData.mCallbacks.size())
415 {
416 /* release all callbacks */
417 LogWarningFunc (("%d unregistered callbacks!\n",
418 mData.mCallbacks.size()));
419 mData.mCallbacks.clear();
420 }
421
422 LogFlowThisFunc (("Terminating the async event handler...\n"));
423 if (mAsyncEventThread != NIL_RTTHREAD)
424 {
425 /* signal to exit the event loop */
426 if (mAsyncEventQ->postEvent (NULL))
427 {
428 /*
429 * Wait for thread termination (only if we've successfully posted
430 * a NULL event!)
431 */
432 int vrc = RTThreadWait (mAsyncEventThread, 60000, NULL);
433 if (VBOX_FAILURE (vrc))
434 LogWarningFunc (("RTThreadWait(%RTthrd) -> %Vrc\n",
435 mAsyncEventThread, vrc));
436 }
437 else
438 {
439 AssertMsgFailed (("postEvent(NULL) failed\n"));
440 RTThreadWait (mAsyncEventThread, 0, NULL);
441 }
442
443 unconst (mAsyncEventThread) = NIL_RTTHREAD;
444 unconst (mAsyncEventQ) = NULL;
445 }
446
447 LogFlowThisFunc (("Terminating the client watcher...\n"));
448 if (mWatcherData.mThread != NIL_RTTHREAD)
449 {
450 /* signal the client watcher thread */
451 updateClientWatcher();
452 /* wait for the termination */
453 RTThreadWait (mWatcherData.mThread, RT_INDEFINITE_WAIT, NULL);
454 unconst (mWatcherData.mThread) = NIL_RTTHREAD;
455 }
456 mWatcherData.mProcesses.clear();
457#if defined(RT_OS_WINDOWS)
458 if (mWatcherData.mUpdateReq != NULL)
459 {
460 ::CloseHandle (mWatcherData.mUpdateReq);
461 unconst (mWatcherData.mUpdateReq) = NULL;
462 }
463#elif defined(RT_OS_OS2)
464 if (mWatcherData.mUpdateReq != NIL_RTSEMEVENT)
465 {
466 RTSemEventDestroy (mWatcherData.mUpdateReq);
467 unconst (mWatcherData.mUpdateReq) = NIL_RTSEMEVENT;
468 }
469#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
470 if (mWatcherData.mUpdateReq != NIL_RTSEMEVENT)
471 {
472 RTSemEventDestroy (mWatcherData.mUpdateReq);
473 unconst (mWatcherData.mUpdateReq) = NIL_RTSEMEVENT;
474 }
475#else
476# error "Port me!"
477#endif
478
479 LogFlowThisFuncLeave();
480 LogFlow (("===========================================================\n"));
481}
482
483// IVirtualBox properties
484/////////////////////////////////////////////////////////////////////////////
485
486STDMETHODIMP VirtualBox::COMGETTER(Version) (BSTR *aVersion)
487{
488 if (!aVersion)
489 return E_INVALIDARG;
490
491 AutoCaller autoCaller (this);
492 CheckComRCReturnRC (autoCaller.rc());
493
494 sVersion.cloneTo (aVersion);
495 return S_OK;
496}
497
498STDMETHODIMP VirtualBox::COMGETTER(PackageType) (BSTR *aPackageType)
499{
500 if (!aPackageType)
501 return E_INVALIDARG;
502
503 AutoCaller autoCaller (this);
504 CheckComRCReturnRC (autoCaller.rc());
505
506 sPackageType.cloneTo (aPackageType);
507 return S_OK;
508}
509
510STDMETHODIMP VirtualBox::COMGETTER(HomeFolder) (BSTR *aHomeFolder)
511{
512 if (!aHomeFolder)
513 return E_POINTER;
514
515 AutoCaller autoCaller (this);
516 CheckComRCReturnRC (autoCaller.rc());
517
518 /* mHomeDir is const and doesn't need a lock */
519 mData.mHomeDir.cloneTo (aHomeFolder);
520 return S_OK;
521}
522
523STDMETHODIMP VirtualBox::COMGETTER(SettingsFilePath) (BSTR *aSettingsFilePath)
524{
525 if (!aSettingsFilePath)
526 return E_INVALIDARG;
527
528 AutoCaller autoCaller (this);
529 CheckComRCReturnRC (autoCaller.rc());
530
531 /* mCfgFile.mName is const and doesn't need a lock */
532 mData.mCfgFile.mName.cloneTo (aSettingsFilePath);
533 return S_OK;
534}
535
536STDMETHODIMP VirtualBox::
537COMGETTER(SettingsFileVersion) (BSTR *aSettingsFileVersion)
538{
539 if (!aSettingsFileVersion)
540 return E_INVALIDARG;
541
542 AutoCaller autoCaller (this);
543 CheckComRCReturnRC (autoCaller.rc());
544
545 AutoReadLock alock (this);
546
547 mData.mSettingsFileVersion.cloneTo (aSettingsFileVersion);
548 return S_OK;
549}
550
551STDMETHODIMP VirtualBox::
552COMGETTER(SettingsFormatVersion) (BSTR *aSettingsFormatVersion)
553{
554 if (!aSettingsFormatVersion)
555 return E_INVALIDARG;
556
557 AutoCaller autoCaller (this);
558 CheckComRCReturnRC (autoCaller.rc());
559
560 sSettingsFormatVersion.cloneTo (aSettingsFormatVersion);
561 return S_OK;
562}
563
564STDMETHODIMP VirtualBox::COMGETTER(Host) (IHost **aHost)
565{
566 if (!aHost)
567 return E_POINTER;
568
569 AutoCaller autoCaller (this);
570 CheckComRCReturnRC (autoCaller.rc());
571
572 mData.mHost.queryInterfaceTo (aHost);
573 return S_OK;
574}
575
576STDMETHODIMP
577VirtualBox::COMGETTER(SystemProperties) (ISystemProperties **aSystemProperties)
578{
579 if (!aSystemProperties)
580 return E_POINTER;
581
582 AutoCaller autoCaller (this);
583 CheckComRCReturnRC (autoCaller.rc());
584
585 mData.mSystemProperties.queryInterfaceTo (aSystemProperties);
586 return S_OK;
587}
588
589STDMETHODIMP
590VirtualBox::COMGETTER(PerformanceCollector) (IPerformanceCollector **aPerformanceCollector)
591{
592#ifdef VBOX_WITH_RESOURCE_USAGE_API
593 if (!aPerformanceCollector)
594 return E_POINTER;
595
596 AutoCaller autoCaller (this);
597 CheckComRCReturnRC (autoCaller.rc());
598
599 mData.mPerformanceCollector.queryInterfaceTo (aPerformanceCollector);
600
601 return S_OK;
602#else /* !VBOX_WITH_RESOURCE_USAGE_API */
603 return E_NOTIMPL;
604#endif /* !VBOX_WITH_RESOURCE_USAGE_API */
605}
606
607
608/**
609 * @note Locks this object for reading.
610 */
611STDMETHODIMP VirtualBox::COMGETTER(Machines) (IMachineCollection **aMachines)
612{
613 if (!aMachines)
614 return E_POINTER;
615
616 AutoCaller autoCaller (this);
617 CheckComRCReturnRC (autoCaller.rc());
618
619 ComObjPtr <MachineCollection> collection;
620 collection.createObject();
621
622 AutoReadLock alock (this);
623 collection->init (mData.mMachines);
624 collection.queryInterfaceTo (aMachines);
625
626 return S_OK;
627}
628
629/**
630 * @note Locks this object for reading.
631 */
632STDMETHODIMP
633VirtualBox::COMGETTER(Machines2) (ComSafeArrayOut (IMachine *, aMachines))
634{
635 if (ComSafeArrayOutIsNull (aMachines))
636 return E_POINTER;
637
638 AutoCaller autoCaller (this);
639 CheckComRCReturnRC (autoCaller.rc());
640
641 AutoReadLock alock (this);
642
643 SafeIfaceArray <IMachine> machines (mData.mMachines);
644 machines.detachTo (ComSafeArrayOutArg (aMachines));
645
646 return S_OK;
647}
648
649/**
650 * @note Locks this object for reading.
651 */
652STDMETHODIMP VirtualBox::COMGETTER(HardDisks) (IHardDiskCollection **aHardDisks)
653{
654 if (!aHardDisks)
655 return E_POINTER;
656
657 AutoCaller autoCaller (this);
658 CheckComRCReturnRC (autoCaller.rc());
659
660 ComObjPtr <HardDiskCollection> collection;
661 collection.createObject();
662
663 AutoReadLock alock (this);
664 collection->init (mData.mHardDisks);
665 collection.queryInterfaceTo (aHardDisks);
666
667 return S_OK;
668}
669
670/**
671 * @note Locks this object for reading.
672 */
673STDMETHODIMP VirtualBox::COMGETTER(DVDImages) (IDVDImageCollection **aDVDImages)
674{
675 if (!aDVDImages)
676 return E_POINTER;
677
678 AutoCaller autoCaller (this);
679 CheckComRCReturnRC (autoCaller.rc());
680
681 ComObjPtr <DVDImageCollection> collection;
682 collection.createObject();
683
684 AutoReadLock alock (this);
685 collection->init (mData.mDVDImages);
686 collection.queryInterfaceTo (aDVDImages);
687
688 return S_OK;
689}
690
691/**
692 * @note Locks this object for reading.
693 */
694STDMETHODIMP VirtualBox::COMGETTER(FloppyImages) (IFloppyImageCollection **aFloppyImages)
695{
696 if (!aFloppyImages)
697 return E_POINTER;
698
699 AutoCaller autoCaller (this);
700 CheckComRCReturnRC (autoCaller.rc());
701
702 ComObjPtr <FloppyImageCollection> collection;
703 collection.createObject();
704
705 AutoReadLock alock (this);
706 collection->init (mData.mFloppyImages);
707 collection.queryInterfaceTo (aFloppyImages);
708
709 return S_OK;
710}
711
712/**
713 * @note Locks this object for reading.
714 */
715STDMETHODIMP VirtualBox::COMGETTER(ProgressOperations) (IProgressCollection **aOperations)
716{
717 if (!aOperations)
718 return E_POINTER;
719
720 AutoCaller autoCaller (this);
721 CheckComRCReturnRC (autoCaller.rc());
722
723 ComObjPtr <ProgressCollection> collection;
724 collection.createObject();
725
726 AutoReadLock alock (this);
727 collection->init (mData.mProgressOperations);
728 collection.queryInterfaceTo (aOperations);
729
730 return S_OK;
731}
732
733/**
734 * @note Locks this object for reading.
735 */
736STDMETHODIMP VirtualBox::COMGETTER(GuestOSTypes) (IGuestOSTypeCollection **aGuestOSTypes)
737{
738 if (!aGuestOSTypes)
739 return E_POINTER;
740
741 AutoCaller autoCaller (this);
742 CheckComRCReturnRC (autoCaller.rc());
743
744 ComObjPtr <GuestOSTypeCollection> collection;
745 collection.createObject();
746
747 AutoReadLock alock (this);
748 collection->init (mData.mGuestOSTypes);
749 collection.queryInterfaceTo (aGuestOSTypes);
750
751 return S_OK;
752}
753
754STDMETHODIMP
755VirtualBox::COMGETTER(SharedFolders) (ISharedFolderCollection **aSharedFolders)
756{
757 if (!aSharedFolders)
758 return E_POINTER;
759
760 AutoCaller autoCaller (this);
761 CheckComRCReturnRC (autoCaller.rc());
762
763 return setError (E_NOTIMPL, "Not yet implemented");
764}
765
766// IVirtualBox methods
767/////////////////////////////////////////////////////////////////////////////
768
769/** @note Locks mSystemProperties object for reading. */
770STDMETHODIMP VirtualBox::CreateMachine (INPTR BSTR aBaseFolder,
771 INPTR BSTR aName,
772 INPTR GUIDPARAM aId,
773 IMachine **aMachine)
774{
775 LogFlowThisFuncEnter();
776 LogFlowThisFunc (("aBaseFolder='%ls', aName='%ls' aMachine={%p}\n",
777 aBaseFolder, aName, aMachine));
778
779 if (!aName)
780 return E_INVALIDARG;
781 if (!aMachine)
782 return E_POINTER;
783
784 if (!*aName)
785 return setError (E_INVALIDARG,
786 tr ("Machine name cannot be empty"));
787
788 AutoCaller autoCaller (this);
789 CheckComRCReturnRC (autoCaller.rc());
790
791 /* Compose the settings file name using the following scheme:
792 *
793 * <base_folder>/<machine_name>/<machine_name>.xml
794 *
795 * If a non-null and non-empty base folder is specified, the default
796 * machine folder will be used as a base folder.
797 */
798 Bstr settingsFile = aBaseFolder;
799 if (settingsFile.isEmpty())
800 {
801 AutoReadLock propsLock (systemProperties());
802 /* we use the non-full folder value below to keep the path relative */
803 settingsFile = systemProperties()->defaultMachineFolder();
804 }
805 settingsFile = Utf8StrFmt ("%ls%c%ls%c%ls.xml",
806 settingsFile.raw(), RTPATH_DELIMITER,
807 aName, RTPATH_DELIMITER, aName);
808
809 HRESULT rc = E_FAIL;
810
811 /* create a new object */
812 ComObjPtr <Machine> machine;
813 rc = machine.createObject();
814 if (SUCCEEDED (rc))
815 {
816 /* Create UUID if an empty one was specified. */
817 Guid id = aId;
818 if (id.isEmpty())
819 id.create();
820
821 /* initialize the machine object */
822 rc = machine->init (this, settingsFile, Machine::Init_New, aName, TRUE, &id);
823 if (SUCCEEDED (rc))
824 {
825 /* set the return value */
826 rc = machine.queryInterfaceTo (aMachine);
827 ComAssertComRC (rc);
828 }
829 }
830
831 LogFlowThisFunc (("rc=%08X\n", rc));
832 LogFlowThisFuncLeave();
833
834 return rc;
835}
836
837STDMETHODIMP VirtualBox::CreateLegacyMachine (INPTR BSTR aSettingsFile,
838 INPTR BSTR aName,
839 INPTR GUIDPARAM aId,
840 IMachine **aMachine)
841{
842 /* null and empty strings are not allowed as path names */
843 if (!aSettingsFile || !(*aSettingsFile))
844 return E_INVALIDARG;
845
846 if (!aName)
847 return E_INVALIDARG;
848 if (!aMachine)
849 return E_POINTER;
850
851 if (!*aName)
852 return setError (E_INVALIDARG,
853 tr ("Machine name cannot be empty"));
854
855 AutoCaller autoCaller (this);
856 CheckComRCReturnRC (autoCaller.rc());
857
858 HRESULT rc = E_FAIL;
859
860 Utf8Str settingsFile = aSettingsFile;
861 /* append the default extension if none */
862 if (!RTPathHaveExt (settingsFile))
863 settingsFile = Utf8StrFmt ("%s.xml", settingsFile.raw());
864
865 /* create a new object */
866 ComObjPtr<Machine> machine;
867 rc = machine.createObject();
868 if (SUCCEEDED (rc))
869 {
870 /* Create UUID if an empty one was specified. */
871 Guid id = aId;
872 if (id.isEmpty())
873 id.create();
874
875 /* initialize the machine object */
876 rc = machine->init (this, Bstr (settingsFile), Machine::Init_New,
877 aName, FALSE /* aNameSync */, &id);
878 if (SUCCEEDED (rc))
879 {
880 /* set the return value */
881 rc = machine.queryInterfaceTo (aMachine);
882 ComAssertComRC (rc);
883 }
884 }
885 return rc;
886}
887
888STDMETHODIMP VirtualBox::OpenMachine (INPTR BSTR aSettingsFile,
889 IMachine **aMachine)
890{
891 /* null and empty strings are not allowed as path names */
892 if (!aSettingsFile || !(*aSettingsFile))
893 return E_INVALIDARG;
894
895 if (!aMachine)
896 return E_POINTER;
897
898 AutoCaller autoCaller (this);
899 CheckComRCReturnRC (autoCaller.rc());
900
901 HRESULT rc = E_FAIL;
902
903 /* create a new object */
904 ComObjPtr<Machine> machine;
905 rc = machine.createObject();
906 if (SUCCEEDED (rc))
907 {
908 /* initialize the machine object */
909 rc = machine->init (this, aSettingsFile, Machine::Init_Existing);
910 if (SUCCEEDED (rc))
911 {
912 /* set the return value */
913 rc = machine.queryInterfaceTo (aMachine);
914 ComAssertComRC (rc);
915 }
916 }
917
918 return rc;
919}
920
921/** @note Locks objects! */
922STDMETHODIMP VirtualBox::RegisterMachine (IMachine *aMachine)
923{
924 if (!aMachine)
925 return E_INVALIDARG;
926
927 AutoCaller autoCaller (this);
928 CheckComRCReturnRC (autoCaller.rc());
929
930 HRESULT rc;
931
932 Bstr name;
933 rc = aMachine->COMGETTER(Name) (name.asOutParam());
934 CheckComRCReturnRC (rc);
935
936 /*
937 * we can safely cast child to Machine * here because only Machine
938 * implementations of IMachine can be among our children
939 */
940 Machine *machine = static_cast <Machine *> (getDependentChild (aMachine));
941 if (!machine)
942 {
943 /*
944 * this machine was not created by CreateMachine()
945 * or opened by OpenMachine() or loaded during startup
946 */
947 return setError (E_FAIL,
948 tr ("The machine named '%ls' is not created within this "
949 "VirtualBox instance"), name.raw());
950 }
951
952 AutoCaller machCaller (machine);
953 ComAssertComRCRetRC (machCaller.rc());
954
955 rc = registerMachine (machine);
956
957 /* fire an event */
958 if (SUCCEEDED (rc))
959 onMachineRegistered (machine->uuid(), TRUE);
960
961 return rc;
962}
963
964/** @note Locks objects! */
965STDMETHODIMP VirtualBox::GetMachine (INPTR GUIDPARAM aId, IMachine **aMachine)
966{
967 if (!aMachine)
968 return E_POINTER;
969
970 AutoCaller autoCaller (this);
971 CheckComRCReturnRC (autoCaller.rc());
972
973 ComObjPtr <Machine> machine;
974 HRESULT rc = findMachine (Guid (aId), true /* setError */, &machine);
975
976 /* the below will set *aMachine to NULL if machine is null */
977 machine.queryInterfaceTo (aMachine);
978
979 return rc;
980}
981
982/** @note Locks this object for reading, then some machine objects for reading. */
983STDMETHODIMP VirtualBox::FindMachine (INPTR BSTR aName, IMachine **aMachine)
984{
985 LogFlowThisFuncEnter();
986 LogFlowThisFunc (("aName=\"%ls\", aMachine={%p}\n", aName, aMachine));
987
988 if (!aName)
989 return E_INVALIDARG;
990 if (!aMachine)
991 return E_POINTER;
992
993 AutoCaller autoCaller (this);
994 CheckComRCReturnRC (autoCaller.rc());
995
996 /* start with not found */
997 ComObjPtr <Machine> machine;
998 MachineList machines;
999 {
1000 /* take a copy for safe iteration outside the lock */
1001 AutoReadLock alock (this);
1002 machines = mData.mMachines;
1003 }
1004
1005 for (MachineList::iterator it = machines.begin();
1006 !machine && it != machines.end();
1007 ++ it)
1008 {
1009 AutoLimitedCaller machCaller (*it);
1010 AssertComRC (machCaller.rc());
1011
1012 /* skip inaccessible machines */
1013 if (machCaller.state() == Machine::Ready)
1014 {
1015 AutoReadLock machLock (*it);
1016 if ((*it)->name() == aName)
1017 machine = *it;
1018 }
1019 }
1020
1021 /* this will set (*machine) to NULL if machineObj is null */
1022 machine.queryInterfaceTo (aMachine);
1023
1024 HRESULT rc = machine
1025 ? S_OK
1026 : setError (E_INVALIDARG,
1027 tr ("Could not find a registered machine named '%ls'"), aName);
1028
1029 LogFlowThisFunc (("rc=%08X\n", rc));
1030 LogFlowThisFuncLeave();
1031
1032 return rc;
1033}
1034
1035/** @note Locks objects! */
1036STDMETHODIMP VirtualBox::UnregisterMachine (INPTR GUIDPARAM aId,
1037 IMachine **aMachine)
1038{
1039 Guid id = aId;
1040 if (id.isEmpty())
1041 return E_INVALIDARG;
1042
1043 AutoCaller autoCaller (this);
1044 CheckComRCReturnRC (autoCaller.rc());
1045
1046 AutoWriteLock alock (this);
1047
1048 ComObjPtr <Machine> machine;
1049
1050 HRESULT rc = findMachine (id, true /* setError */, &machine);
1051 CheckComRCReturnRC (rc);
1052
1053 rc = machine->trySetRegistered (FALSE);
1054 CheckComRCReturnRC (rc);
1055
1056 /* remove from the collection of registered machines */
1057 mData.mMachines.remove (machine);
1058
1059 /* save the global registry */
1060 rc = saveSettings();
1061
1062 /* return the unregistered machine to the caller */
1063 machine.queryInterfaceTo (aMachine);
1064
1065 /* fire an event */
1066 onMachineRegistered (id, FALSE);
1067
1068 return rc;
1069}
1070
1071STDMETHODIMP VirtualBox::CreateHardDisk (HardDiskStorageType_T aStorageType,
1072 IHardDisk **aHardDisk)
1073{
1074 if (!aHardDisk)
1075 return E_POINTER;
1076
1077 AutoCaller autoCaller (this);
1078 CheckComRCReturnRC (autoCaller.rc());
1079
1080 HRESULT rc = E_FAIL;
1081
1082 ComObjPtr <HardDisk> hardDisk;
1083
1084 switch (aStorageType)
1085 {
1086 case HardDiskStorageType_VirtualDiskImage:
1087 {
1088 ComObjPtr <HVirtualDiskImage> vdi;
1089 vdi.createObject();
1090 rc = vdi->init (this, NULL, NULL);
1091 hardDisk = vdi;
1092 break;
1093 }
1094
1095 case HardDiskStorageType_ISCSIHardDisk:
1096 {
1097 ComObjPtr <HISCSIHardDisk> iscsi;
1098 iscsi.createObject();
1099 rc = iscsi->init (this);
1100 hardDisk = iscsi;
1101 break;
1102 }
1103
1104 case HardDiskStorageType_VMDKImage:
1105 {
1106 ComObjPtr <HVMDKImage> vmdk;
1107 vmdk.createObject();
1108 rc = vmdk->init (this, NULL, NULL);
1109 hardDisk = vmdk;
1110 break;
1111 }
1112 case HardDiskStorageType_CustomHardDisk:
1113 {
1114 ComObjPtr <HCustomHardDisk> custom;
1115 custom.createObject();
1116 rc = custom->init (this, NULL, NULL);
1117 hardDisk = custom;
1118 break;
1119 }
1120 case HardDiskStorageType_VHDImage:
1121 {
1122 ComObjPtr <HVHDImage> vhd;
1123 vhd.createObject();
1124 rc = vhd->init (this, NULL, NULL);
1125 hardDisk = vhd;
1126 break;
1127 }
1128 default:
1129 AssertFailed();
1130 };
1131
1132 if (SUCCEEDED (rc))
1133 hardDisk.queryInterfaceTo (aHardDisk);
1134
1135 return rc;
1136}
1137
1138/** @note Locks mSystemProperties object for reading. */
1139STDMETHODIMP VirtualBox::OpenHardDisk (INPTR BSTR aLocation, IHardDisk **aHardDisk)
1140{
1141 /* null and empty strings are not allowed locations */
1142 if (!aLocation || !(*aLocation))
1143 return E_INVALIDARG;
1144
1145 if (!aHardDisk)
1146 return E_POINTER;
1147
1148 AutoCaller autoCaller (this);
1149 CheckComRCReturnRC (autoCaller.rc());
1150
1151 /* Currently, the location is always a path. So, append the
1152 * default path if only a name is given. */
1153 Bstr location = aLocation;
1154 {
1155 Utf8Str loc = aLocation;
1156 if (!RTPathHavePath (loc))
1157 {
1158 AutoWriteLock propsLock (mData.mSystemProperties);
1159 location = Utf8StrFmt ("%ls%c%s",
1160 mData.mSystemProperties->defaultVDIFolder().raw(),
1161 RTPATH_DELIMITER,
1162 loc.raw());
1163 }
1164 }
1165
1166 ComObjPtr <HardDisk> hardDisk;
1167 HRESULT rc = HardDisk::openHardDisk (this, location, hardDisk);
1168 if (SUCCEEDED (rc))
1169 hardDisk.queryInterfaceTo (aHardDisk);
1170
1171 return rc;
1172}
1173
1174/** @note Locks mSystemProperties object for reading. */
1175STDMETHODIMP VirtualBox::OpenVirtualDiskImage (INPTR BSTR aFilePath,
1176 IVirtualDiskImage **aImage)
1177{
1178 /* null and empty strings are not allowed as path names here */
1179 if (!aFilePath || !(*aFilePath))
1180 return E_INVALIDARG;
1181
1182 if (!aImage)
1183 return E_POINTER;
1184
1185 AutoCaller autoCaller (this);
1186 CheckComRCReturnRC (autoCaller.rc());
1187
1188 /* append the default path if only a name is given */
1189 Bstr path = aFilePath;
1190 {
1191 Utf8Str fp = aFilePath;
1192 if (!RTPathHavePath (fp))
1193 {
1194 AutoWriteLock propsLock (mData.mSystemProperties);
1195 path = Utf8StrFmt ("%ls%c%s",
1196 mData.mSystemProperties->defaultVDIFolder().raw(),
1197 RTPATH_DELIMITER,
1198 fp.raw());
1199 }
1200 }
1201
1202 ComObjPtr <HVirtualDiskImage> vdi;
1203 vdi.createObject();
1204 HRESULT rc = vdi->init (this, NULL, path);
1205
1206 if (SUCCEEDED (rc))
1207 vdi.queryInterfaceTo (aImage);
1208
1209 return rc;
1210}
1211
1212/** @note Locks objects! */
1213STDMETHODIMP VirtualBox::RegisterHardDisk (IHardDisk *aHardDisk)
1214{
1215 if (!aHardDisk)
1216 return E_POINTER;
1217
1218 AutoCaller autoCaller (this);
1219 CheckComRCReturnRC (autoCaller.rc());
1220
1221 VirtualBoxBase *child = getDependentChild (aHardDisk);
1222 if (!child)
1223 return setError (E_FAIL, tr ("The given hard disk is not created within "
1224 "this VirtualBox instance"));
1225
1226 /*
1227 * we can safely cast child to HardDisk * here because only HardDisk
1228 * implementations of IHardDisk can be among our children
1229 */
1230
1231 return registerHardDisk (static_cast <HardDisk *> (child), RHD_External);
1232}
1233
1234/** @note Locks objects! */
1235STDMETHODIMP VirtualBox::GetHardDisk (INPTR GUIDPARAM aId, IHardDisk **aHardDisk)
1236{
1237 if (!aHardDisk)
1238 return E_POINTER;
1239
1240 AutoCaller autoCaller (this);
1241 CheckComRCReturnRC (autoCaller.rc());
1242
1243 Guid id = aId;
1244 ComObjPtr <HardDisk> hd;
1245 HRESULT rc = findHardDisk (&id, NULL, true /* setError */, &hd);
1246
1247 /* the below will set *aHardDisk to NULL if hd is null */
1248 hd.queryInterfaceTo (aHardDisk);
1249
1250 return rc;
1251}
1252
1253/** @note Locks objects! */
1254STDMETHODIMP VirtualBox::FindHardDisk (INPTR BSTR aLocation,
1255 IHardDisk **aHardDisk)
1256{
1257 if (!aLocation)
1258 return E_INVALIDARG;
1259 if (!aHardDisk)
1260 return E_POINTER;
1261
1262 AutoCaller autoCaller (this);
1263 CheckComRCReturnRC (autoCaller.rc());
1264
1265 Utf8Str location = aLocation;
1266 if (strncmp (location, "iscsi:", 6) == 0)
1267 {
1268 /* nothing special */
1269 }
1270 else
1271 {
1272 /* For locations represented by file paths, append the default path if
1273 * only a name is given, and then get the full path. */
1274 if (!RTPathHavePath (location))
1275 {
1276 AutoWriteLock propsLock (mData.mSystemProperties);
1277 location = Utf8StrFmt ("%ls%c%s",
1278 mData.mSystemProperties->defaultVDIFolder().raw(),
1279 RTPATH_DELIMITER,
1280 location.raw());
1281 }
1282
1283 /* get the full file name */
1284 char buf [RTPATH_MAX];
1285 int vrc = RTPathAbsEx (mData.mHomeDir, location, buf, sizeof (buf));
1286 if (VBOX_FAILURE (vrc))
1287 return setError (E_FAIL, tr ("Invalid hard disk location '%ls' (%Vrc)"),
1288 aLocation, vrc);
1289 location = buf;
1290 }
1291
1292 ComObjPtr <HardDisk> hardDisk;
1293 HRESULT rc = findHardDisk (NULL, Bstr (location), true /* setError */,
1294 &hardDisk);
1295
1296 /* the below will set *aHardDisk to NULL if hardDisk is null */
1297 hardDisk.queryInterfaceTo (aHardDisk);
1298
1299 return rc;
1300}
1301
1302/** @note Locks objects! */
1303STDMETHODIMP VirtualBox::FindVirtualDiskImage (INPTR BSTR aFilePath,
1304 IVirtualDiskImage **aImage)
1305{
1306 if (!aFilePath)
1307 return E_INVALIDARG;
1308 if (!aImage)
1309 return E_POINTER;
1310
1311 AutoCaller autoCaller (this);
1312 CheckComRCReturnRC (autoCaller.rc());
1313
1314 /* append the default path if only a name is given */
1315 Utf8Str path = aFilePath;
1316 {
1317 Utf8Str fp = path;
1318 if (!RTPathHavePath (fp))
1319 {
1320 AutoWriteLock propsLock (mData.mSystemProperties);
1321 path = Utf8StrFmt ("%ls%c%s",
1322 mData.mSystemProperties->defaultVDIFolder().raw(),
1323 RTPATH_DELIMITER,
1324 fp.raw());
1325 }
1326 }
1327
1328 /* get the full file name */
1329 char buf [RTPATH_MAX];
1330 int vrc = RTPathAbsEx (mData.mHomeDir, path, buf, sizeof (buf));
1331 if (VBOX_FAILURE (vrc))
1332 return setError (E_FAIL, tr ("Invalid image file path '%ls' (%Vrc)"),
1333 aFilePath, vrc);
1334
1335 ComObjPtr <HVirtualDiskImage> vdi;
1336 HRESULT rc = findVirtualDiskImage (NULL, Bstr (buf), true /* setError */,
1337 &vdi);
1338
1339 /* the below will set *aImage to NULL if vdi is null */
1340 vdi.queryInterfaceTo (aImage);
1341
1342 return rc;
1343}
1344
1345/** @note Locks objects! */
1346STDMETHODIMP VirtualBox::UnregisterHardDisk (INPTR GUIDPARAM aId, IHardDisk **aHardDisk)
1347{
1348 if (!aHardDisk)
1349 return E_POINTER;
1350
1351 AutoCaller autoCaller (this);
1352 CheckComRCReturnRC (autoCaller.rc());
1353
1354 *aHardDisk = NULL;
1355
1356 Guid id = aId;
1357 ComObjPtr <HardDisk> hd;
1358 HRESULT rc = findHardDisk (&id, NULL, true /* setError */, &hd);
1359 CheckComRCReturnRC (rc);
1360
1361 rc = unregisterHardDisk (hd);
1362 if (SUCCEEDED (rc))
1363 hd.queryInterfaceTo (aHardDisk);
1364
1365 return rc;
1366}
1367
1368/** @note Doesn't lock anything. */
1369STDMETHODIMP VirtualBox::OpenDVDImage (INPTR BSTR aFilePath, INPTR GUIDPARAM aId,
1370 IDVDImage **aDVDImage)
1371{
1372 /* null and empty strings are not allowed as path names */
1373 if (!aFilePath || !(*aFilePath))
1374 return E_INVALIDARG;
1375
1376 if (!aDVDImage)
1377 return E_POINTER;
1378
1379 AutoCaller autoCaller (this);
1380 CheckComRCReturnRC (autoCaller.rc());
1381
1382 HRESULT rc = E_FAIL;
1383
1384 Guid uuid = aId;
1385 /* generate an UUID if not specified */
1386 if (uuid.isEmpty())
1387 uuid.create();
1388
1389 ComObjPtr <DVDImage> dvdImage;
1390 dvdImage.createObject();
1391 rc = dvdImage->init (this, aFilePath, FALSE /* !isRegistered */, uuid);
1392 if (SUCCEEDED (rc))
1393 dvdImage.queryInterfaceTo (aDVDImage);
1394
1395 return rc;
1396}
1397
1398/** @note Locks objects! */
1399STDMETHODIMP VirtualBox::RegisterDVDImage (IDVDImage *aDVDImage)
1400{
1401 if (!aDVDImage)
1402 return E_POINTER;
1403
1404 AutoCaller autoCaller (this);
1405 CheckComRCReturnRC (autoCaller.rc());
1406
1407 VirtualBoxBase *child = getDependentChild (aDVDImage);
1408 if (!child)
1409 return setError (E_FAIL, tr ("The given CD/DVD image is not created within "
1410 "this VirtualBox instance"));
1411
1412 /*
1413 * we can safely cast child to DVDImage * here because only DVDImage
1414 * implementations of IDVDImage can be among our children
1415 */
1416
1417 return registerDVDImage (static_cast <DVDImage *> (child),
1418 FALSE /* aOnStartUp */);
1419}
1420
1421/** @note Locks objects! */
1422STDMETHODIMP VirtualBox::GetDVDImage (INPTR GUIDPARAM aId, IDVDImage **aDVDImage)
1423{
1424 if (!aDVDImage)
1425 return E_POINTER;
1426
1427 AutoCaller autoCaller (this);
1428 CheckComRCReturnRC (autoCaller.rc());
1429
1430 Guid uuid = aId;
1431 ComObjPtr <DVDImage> dvd;
1432 HRESULT rc = findDVDImage (&uuid, NULL, true /* setError */, &dvd);
1433
1434 /* the below will set *aDVDImage to NULL if dvd is null */
1435 dvd.queryInterfaceTo (aDVDImage);
1436
1437 return rc;
1438}
1439
1440/** @note Locks objects! */
1441STDMETHODIMP VirtualBox::FindDVDImage (INPTR BSTR aFilePath, IDVDImage **aDVDImage)
1442{
1443 if (!aFilePath)
1444 return E_INVALIDARG;
1445 if (!aDVDImage)
1446 return E_POINTER;
1447
1448 AutoCaller autoCaller (this);
1449 CheckComRCReturnRC (autoCaller.rc());
1450
1451 /* get the full file name */
1452 char buf [RTPATH_MAX];
1453 int vrc = RTPathAbsEx (mData.mHomeDir, Utf8Str (aFilePath), buf, sizeof (buf));
1454 if (VBOX_FAILURE (vrc))
1455 return setError (E_FAIL, tr ("Invalid image file path: '%ls' (%Vrc)"),
1456 aFilePath, vrc);
1457
1458 ComObjPtr <DVDImage> dvd;
1459 HRESULT rc = findDVDImage (NULL, Bstr (buf), true /* setError */, &dvd);
1460
1461 /* the below will set *dvdImage to NULL if dvd is null */
1462 dvd.queryInterfaceTo (aDVDImage);
1463
1464 return rc;
1465}
1466
1467/** @note Locks objects! */
1468STDMETHODIMP VirtualBox::GetDVDImageUsage (INPTR GUIDPARAM aId,
1469 ResourceUsage_T aUsage,
1470 BSTR *aMachineIDs)
1471{
1472 if (!aMachineIDs)
1473 return E_POINTER;
1474 if (aUsage == ResourceUsage_Null)
1475 return E_INVALIDARG;
1476
1477 AutoCaller autoCaller (this);
1478 CheckComRCReturnRC (autoCaller.rc());
1479
1480 AutoReadLock alock (this);
1481
1482 Guid uuid = Guid (aId);
1483 HRESULT rc = findDVDImage (&uuid, NULL, true /* setError */, NULL);
1484 if (FAILED (rc))
1485 return rc;
1486
1487 Bstr ids;
1488 getDVDImageUsage (uuid, aUsage, &ids);
1489 ids.cloneTo (aMachineIDs);
1490
1491 return S_OK;
1492}
1493
1494/** @note Locks objects! */
1495STDMETHODIMP VirtualBox::UnregisterDVDImage (INPTR GUIDPARAM aId,
1496 IDVDImage **aDVDImage)
1497{
1498 if (!aDVDImage)
1499 return E_POINTER;
1500
1501 AutoCaller autoCaller (this);
1502 CheckComRCReturnRC (autoCaller.rc());
1503
1504 AutoWriteLock alock (this);
1505
1506 *aDVDImage = NULL;
1507
1508 Guid uuid = aId;
1509 ComObjPtr <DVDImage> dvd;
1510 HRESULT rc = findDVDImage (&uuid, NULL, true /* setError */, &dvd);
1511 CheckComRCReturnRC (rc);
1512
1513 if (!getDVDImageUsage (aId, ResourceUsage_All))
1514 {
1515 /* remove from the collection */
1516 mData.mDVDImages.remove (dvd);
1517
1518 /* save the global config file */
1519 rc = saveSettings();
1520
1521 if (SUCCEEDED (rc))
1522 {
1523 rc = dvd.queryInterfaceTo (aDVDImage);
1524 ComAssertComRC (rc);
1525 }
1526 }
1527 else
1528 rc = setError(E_FAIL,
1529 tr ("The CD/DVD image with the UUID {%s} is currently in use"),
1530 uuid.toString().raw());
1531
1532 return rc;
1533}
1534
1535/** @note Doesn't lock anything. */
1536STDMETHODIMP VirtualBox::OpenFloppyImage (INPTR BSTR aFilePath, INPTR GUIDPARAM aId,
1537 IFloppyImage **aFloppyImage)
1538{
1539 /* null and empty strings are not allowed as path names */
1540 if (!aFilePath || !(*aFilePath))
1541 return E_INVALIDARG;
1542
1543 if (!aFloppyImage)
1544 return E_POINTER;
1545
1546 AutoCaller autoCaller (this);
1547 CheckComRCReturnRC (autoCaller.rc());
1548
1549 HRESULT rc = E_FAIL;
1550
1551 Guid uuid = aId;
1552 /* generate an UUID if not specified */
1553 if (Guid::isEmpty (aId))
1554 uuid.create();
1555
1556 ComObjPtr <FloppyImage> floppyImage;
1557 floppyImage.createObject();
1558 rc = floppyImage->init (this, aFilePath, FALSE /* !isRegistered */, uuid);
1559 if (SUCCEEDED (rc))
1560 floppyImage.queryInterfaceTo (aFloppyImage);
1561
1562 return rc;
1563}
1564
1565/** @note Locks objects! */
1566STDMETHODIMP VirtualBox::RegisterFloppyImage (IFloppyImage *aFloppyImage)
1567{
1568 if (!aFloppyImage)
1569 return E_POINTER;
1570
1571 AutoCaller autoCaller (this);
1572 CheckComRCReturnRC (autoCaller.rc());
1573
1574 VirtualBoxBase *child = getDependentChild (aFloppyImage);
1575 if (!child)
1576 return setError (E_FAIL, tr ("The given floppy image is not created within "
1577 "this VirtualBox instance"));
1578
1579 /*
1580 * we can safely cast child to FloppyImage * here because only FloppyImage
1581 * implementations of IFloppyImage can be among our children
1582 */
1583
1584 return registerFloppyImage (static_cast <FloppyImage *> (child),
1585 FALSE /* aOnStartUp */);
1586}
1587
1588/** @note Locks objects! */
1589STDMETHODIMP VirtualBox::GetFloppyImage (INPTR GUIDPARAM aId,
1590 IFloppyImage **aFloppyImage)
1591{
1592 if (!aFloppyImage)
1593 return E_POINTER;
1594
1595 AutoCaller autoCaller (this);
1596 CheckComRCReturnRC (autoCaller.rc());
1597
1598 Guid uuid = aId;
1599 ComObjPtr <FloppyImage> floppy;
1600 HRESULT rc = findFloppyImage (&uuid, NULL, true /* setError */, &floppy);
1601
1602 /* the below will set *aFloppyImage to NULL if dvd is null */
1603 floppy.queryInterfaceTo (aFloppyImage);
1604
1605 return rc;
1606}
1607
1608/** @note Locks objects! */
1609STDMETHODIMP VirtualBox::FindFloppyImage (INPTR BSTR aFilePath,
1610 IFloppyImage **aFloppyImage)
1611{
1612 if (!aFilePath)
1613 return E_INVALIDARG;
1614 if (!aFloppyImage)
1615 return E_POINTER;
1616
1617 AutoCaller autoCaller (this);
1618 CheckComRCReturnRC (autoCaller.rc());
1619
1620 /* get the full file name */
1621 char buf [RTPATH_MAX];
1622 int vrc = RTPathAbsEx (mData.mHomeDir, Utf8Str (aFilePath), buf, sizeof (buf));
1623 if (VBOX_FAILURE (vrc))
1624 return setError (E_FAIL, tr ("Invalid image file path: '%ls' (%Vrc)"),
1625 aFilePath, vrc);
1626
1627 ComObjPtr <FloppyImage> floppy;
1628 HRESULT rc = findFloppyImage (NULL, Bstr (buf), true /* setError */, &floppy);
1629
1630 /* the below will set *image to NULL if img is null */
1631 floppy.queryInterfaceTo (aFloppyImage);
1632
1633 return rc;
1634}
1635
1636/** @note Locks objects! */
1637STDMETHODIMP VirtualBox::GetFloppyImageUsage (INPTR GUIDPARAM aId,
1638 ResourceUsage_T aUsage,
1639 BSTR *aMachineIDs)
1640{
1641 if (!aMachineIDs)
1642 return E_POINTER;
1643 if (aUsage == ResourceUsage_Null)
1644 return E_INVALIDARG;
1645
1646 AutoCaller autoCaller (this);
1647 CheckComRCReturnRC (autoCaller.rc());
1648
1649 AutoReadLock alock (this);
1650
1651 Guid uuid = Guid (aId);
1652 HRESULT rc = findFloppyImage (&uuid, NULL, true /* setError */, NULL);
1653 if (FAILED (rc))
1654 return rc;
1655
1656 Bstr ids;
1657 getFloppyImageUsage (uuid, aUsage, &ids);
1658 ids.cloneTo (aMachineIDs);
1659
1660 return S_OK;
1661}
1662
1663/** @note Locks objects! */
1664STDMETHODIMP VirtualBox::UnregisterFloppyImage (INPTR GUIDPARAM aId,
1665 IFloppyImage **aFloppyImage)
1666{
1667 if (!aFloppyImage)
1668 return E_INVALIDARG;
1669
1670 AutoCaller autoCaller (this);
1671 CheckComRCReturnRC (autoCaller.rc());
1672
1673 AutoWriteLock alock (this);
1674
1675 *aFloppyImage = NULL;
1676
1677 Guid uuid = aId;
1678 ComObjPtr <FloppyImage> floppy;
1679 HRESULT rc = findFloppyImage (&uuid, NULL, true /* setError */, &floppy);
1680 CheckComRCReturnRC (rc);
1681
1682 if (!getFloppyImageUsage (aId, ResourceUsage_All))
1683 {
1684 /* remove from the collection */
1685 mData.mFloppyImages.remove (floppy);
1686
1687 /* save the global config file */
1688 rc = saveSettings();
1689 if (SUCCEEDED (rc))
1690 {
1691 rc = floppy.queryInterfaceTo (aFloppyImage);
1692 ComAssertComRC (rc);
1693 }
1694 }
1695 else
1696 rc = setError(E_FAIL,
1697 tr ("A floppy image with UUID {%s} is currently in use"),
1698 uuid.toString().raw());
1699
1700 return rc;
1701}
1702
1703/** @note Locks this object for reading. */
1704STDMETHODIMP VirtualBox::GetGuestOSType (INPTR BSTR aId, IGuestOSType **aType)
1705{
1706 if (!aType)
1707 return E_INVALIDARG;
1708
1709 AutoCaller autoCaller (this);
1710 CheckComRCReturnRC (autoCaller.rc());
1711
1712 *aType = NULL;
1713
1714 AutoReadLock alock (this);
1715
1716 for (GuestOSTypeList::iterator it = mData.mGuestOSTypes.begin();
1717 it != mData.mGuestOSTypes.end();
1718 ++ it)
1719 {
1720 const Bstr &typeId = (*it)->id();
1721 AssertMsg (!!typeId, ("ID must not be NULL"));
1722 if (typeId == aId)
1723 {
1724 (*it).queryInterfaceTo (aType);
1725 break;
1726 }
1727 }
1728
1729 return (*aType) ? S_OK :
1730 setError (E_INVALIDARG,
1731 tr ("'%ls' is not a valid Guest OS type"),
1732 aId);
1733}
1734
1735STDMETHODIMP
1736VirtualBox::CreateSharedFolder (INPTR BSTR aName, INPTR BSTR aHostPath, BOOL aWritable)
1737{
1738 if (!aName || !aHostPath)
1739 return E_INVALIDARG;
1740
1741 AutoCaller autoCaller (this);
1742 CheckComRCReturnRC (autoCaller.rc());
1743
1744 return setError (E_NOTIMPL, "Not yet implemented");
1745}
1746
1747STDMETHODIMP VirtualBox::RemoveSharedFolder (INPTR BSTR aName)
1748{
1749 if (!aName)
1750 return E_INVALIDARG;
1751
1752 AutoCaller autoCaller (this);
1753 CheckComRCReturnRC (autoCaller.rc());
1754
1755 return setError (E_NOTIMPL, "Not yet implemented");
1756}
1757
1758/**
1759 * @note Locks this object for reading.
1760 */
1761STDMETHODIMP VirtualBox::
1762GetNextExtraDataKey (INPTR BSTR aKey, BSTR *aNextKey, BSTR *aNextValue)
1763{
1764 if (!aNextKey)
1765 return E_POINTER;
1766
1767 AutoCaller autoCaller (this);
1768 CheckComRCReturnRC (autoCaller.rc());
1769
1770 /* start with nothing found */
1771 *aNextKey = NULL;
1772 if (aNextValue)
1773 *aNextValue = NULL;
1774
1775 HRESULT rc = S_OK;
1776
1777 /* serialize file access (prevent writes) */
1778 AutoReadLock alock (this);
1779
1780 try
1781 {
1782 using namespace settings;
1783
1784 /* load the settings file (we don't reuse the existing handle but
1785 * request a new one to allow for concurrent multithreaded reads) */
1786 File file (File::Mode_Read, Utf8Str (mData.mCfgFile.mName));
1787 XmlTreeBackend tree;
1788
1789 rc = VirtualBox::loadSettingsTree_Again (tree, file);
1790 CheckComRCReturnRC (rc);
1791
1792 Key globalNode = tree.rootKey().key ("Global");
1793 Key extraDataNode = globalNode.findKey ("ExtraData");
1794
1795 if (!extraDataNode.isNull())
1796 {
1797 Key::List items = extraDataNode.keys ("ExtraDataItem");
1798 if (items.size())
1799 {
1800 for (Key::List::const_iterator it = items.begin();
1801 it != items.end(); ++ it)
1802 {
1803 Bstr key = (*it).stringValue ("name");
1804
1805 /* if we're supposed to return the first one */
1806 if (aKey == NULL)
1807 {
1808 key.cloneTo (aNextKey);
1809 if (aNextValue)
1810 {
1811 Bstr val = (*it).stringValue ("value");
1812 val.cloneTo (aNextValue);
1813 }
1814 return S_OK;
1815 }
1816
1817 /* did we find the key we're looking for? */
1818 if (key == aKey)
1819 {
1820 ++ it;
1821 /* is there another item? */
1822 if (it != items.end())
1823 {
1824 Bstr key = (*it).stringValue ("name");
1825 key.cloneTo (aNextKey);
1826 if (aNextValue)
1827 {
1828 Bstr val = (*it).stringValue ("value");
1829 val.cloneTo (aNextValue);
1830 }
1831 }
1832 /* else it's the last one, arguments are already NULL */
1833 return S_OK;
1834 }
1835 }
1836 }
1837 }
1838
1839 /* Here we are when a) there are no items at all or b) there are items
1840 * but none of them equals to the requested non-NULL key. b) is an
1841 * error as well as a) if the key is non-NULL. When the key is NULL
1842 * (which is the case only when there are no items), we just fall
1843 * through to return NULLs and S_OK. */
1844
1845 if (aKey != NULL)
1846 return setError (E_FAIL,
1847 tr ("Could not find the extra data key '%ls'"), aKey);
1848 }
1849 catch (...)
1850 {
1851 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
1852 }
1853
1854 return rc;
1855}
1856
1857/**
1858 * @note Locks this object for reading.
1859 */
1860STDMETHODIMP VirtualBox::GetExtraData (INPTR BSTR aKey, BSTR *aValue)
1861{
1862 if (!aKey)
1863 return E_INVALIDARG;
1864 if (!aValue)
1865 return E_POINTER;
1866
1867 AutoCaller autoCaller (this);
1868 CheckComRCReturnRC (autoCaller.rc());
1869
1870 /* start with nothing found */
1871 *aValue = NULL;
1872
1873 HRESULT rc = S_OK;
1874
1875 /* serialize file access (prevent writes) */
1876 AutoReadLock alock (this);
1877
1878 try
1879 {
1880 using namespace settings;
1881
1882 /* load the settings file (we don't reuse the existing handle but
1883 * request a new one to allow for concurrent multithreaded reads) */
1884 File file (File::Mode_Read, Utf8Str (mData.mCfgFile.mName));
1885 XmlTreeBackend tree;
1886
1887 rc = VirtualBox::loadSettingsTree_Again (tree, file);
1888 CheckComRCReturnRC (rc);
1889
1890 const Utf8Str key = aKey;
1891
1892 Key globalNode = tree.rootKey().key ("Global");
1893 Key extraDataNode = globalNode.findKey ("ExtraData");
1894
1895 if (!extraDataNode.isNull())
1896 {
1897 /* check if the key exists */
1898 Key::List items = extraDataNode.keys ("ExtraDataItem");
1899 for (Key::List::const_iterator it = items.begin();
1900 it != items.end(); ++ it)
1901 {
1902 if (key == (*it).stringValue ("name"))
1903 {
1904 Bstr val = (*it).stringValue ("value");
1905 val.cloneTo (aValue);
1906 break;
1907 }
1908 }
1909 }
1910 }
1911 catch (...)
1912 {
1913 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
1914 }
1915
1916 return rc;
1917}
1918
1919/**
1920 * @note Locks this object for writing.
1921 */
1922STDMETHODIMP VirtualBox::SetExtraData (INPTR BSTR aKey, INPTR BSTR aValue)
1923{
1924 if (!aKey)
1925 return E_INVALIDARG;
1926
1927 AutoCaller autoCaller (this);
1928 CheckComRCReturnRC (autoCaller.rc());
1929
1930 Guid emptyGuid;
1931
1932 bool changed = false;
1933 HRESULT rc = S_OK;
1934
1935 /* serialize file access (prevent concurrent reads and writes) */
1936 AutoWriteLock alock (this);
1937
1938 try
1939 {
1940 using namespace settings;
1941
1942 /* load the settings file */
1943 File file (mData.mCfgFile.mHandle, Utf8Str (mData.mCfgFile.mName));
1944 XmlTreeBackend tree;
1945
1946 rc = VirtualBox::loadSettingsTree_ForUpdate (tree, file);
1947 CheckComRCReturnRC (rc);
1948
1949 const Utf8Str key = aKey;
1950 Bstr oldVal;
1951
1952 Key globalNode = tree.rootKey().key ("Global");
1953 Key extraDataNode = globalNode.createKey ("ExtraData");
1954 Key extraDataItemNode;
1955
1956 Key::List items = extraDataNode.keys ("ExtraDataItem");
1957 for (Key::List::const_iterator it = items.begin();
1958 it != items.end(); ++ it)
1959 {
1960 if (key == (*it).stringValue ("name"))
1961 {
1962 extraDataItemNode = *it;
1963 oldVal = (*it).stringValue ("value");
1964 break;
1965 }
1966 }
1967
1968 /* When no key is found, oldVal is null */
1969 changed = oldVal != aValue;
1970
1971 if (changed)
1972 {
1973 /* ask for permission from all listeners */
1974 Bstr error;
1975 if (!onExtraDataCanChange (Guid::Empty, aKey, aValue, error))
1976 {
1977 const char *sep = error.isEmpty() ? "" : ": ";
1978 const BSTR err = error.isNull() ? (const BSTR) L"" : error.raw();
1979 LogWarningFunc (("Someone vetoed! Change refused%s%ls\n",
1980 sep, err));
1981 return setError (E_ACCESSDENIED,
1982 tr ("Could not set extra data because someone refused "
1983 "the requested change of '%ls' to '%ls'%s%ls"),
1984 aKey, aValue, sep, err);
1985 }
1986
1987 if (aValue != NULL)
1988 {
1989 if (extraDataItemNode.isNull())
1990 {
1991 extraDataItemNode = extraDataNode.appendKey ("ExtraDataItem");
1992 extraDataItemNode.setStringValue ("name", key);
1993 }
1994 extraDataItemNode.setStringValue ("value", Utf8Str (aValue));
1995 }
1996 else
1997 {
1998 /* an old value does for sure exist here (XML schema
1999 * guarantees that "value" may not absent in the
2000 * <ExtraDataItem> element) */
2001 Assert (!extraDataItemNode.isNull());
2002 extraDataItemNode.zap();
2003 }
2004
2005 /* save settings on success */
2006 rc = VirtualBox::saveSettingsTree (tree, file,
2007 mData.mSettingsFileVersion);
2008 CheckComRCReturnRC (rc);
2009 }
2010 }
2011 catch (...)
2012 {
2013 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
2014 }
2015
2016 /* fire a notification */
2017 if (SUCCEEDED (rc) && changed)
2018 onExtraDataChange (Guid::Empty, aKey, aValue);
2019
2020 return rc;
2021}
2022
2023/**
2024 * @note Locks objects!
2025 */
2026STDMETHODIMP VirtualBox::OpenSession (ISession *aSession, INPTR GUIDPARAM aMachineId)
2027{
2028 if (!aSession)
2029 return E_INVALIDARG;
2030
2031 AutoCaller autoCaller (this);
2032 CheckComRCReturnRC (autoCaller.rc());
2033
2034 Guid id = aMachineId;
2035 ComObjPtr <Machine> machine;
2036
2037 HRESULT rc = findMachine (id, true /* setError */, &machine);
2038 CheckComRCReturnRC (rc);
2039
2040 /* check the session state */
2041 SessionState_T state;
2042 rc = aSession->COMGETTER(State) (&state);
2043 CheckComRCReturnRC (rc);
2044
2045 if (state != SessionState_Closed)
2046 return setError (E_INVALIDARG,
2047 tr ("The given session is already open or being opened"));
2048
2049 /* get the IInternalSessionControl interface */
2050 ComPtr <IInternalSessionControl> control = aSession;
2051 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
2052 E_INVALIDARG);
2053
2054 rc = machine->openSession (control);
2055
2056 if (SUCCEEDED (rc))
2057 {
2058 /*
2059 * tell the client watcher thread to update the set of
2060 * machines that have open sessions
2061 */
2062 updateClientWatcher();
2063
2064 /* fire an event */
2065 onSessionStateChange (aMachineId, SessionState_Open);
2066 }
2067
2068 return rc;
2069}
2070
2071/**
2072 * @note Locks objects!
2073 */
2074STDMETHODIMP VirtualBox::OpenRemoteSession (ISession *aSession,
2075 INPTR GUIDPARAM aMachineId,
2076 INPTR BSTR aType,
2077 INPTR BSTR aEnvironment,
2078 IProgress **aProgress)
2079{
2080 if (!aSession || !aType)
2081 return E_INVALIDARG;
2082 if (!aProgress)
2083 return E_POINTER;
2084
2085 AutoCaller autoCaller (this);
2086 CheckComRCReturnRC (autoCaller.rc());
2087
2088 Guid id = aMachineId;
2089 ComObjPtr <Machine> machine;
2090
2091 HRESULT rc = findMachine (id, true /* setError */, &machine);
2092 CheckComRCReturnRC (rc);
2093
2094 /* check the session state */
2095 SessionState_T state;
2096 rc = aSession->COMGETTER(State) (&state);
2097 CheckComRCReturnRC (rc);
2098
2099 if (state != SessionState_Closed)
2100 return setError (E_INVALIDARG,
2101 tr ("The given session is already open or being opened"));
2102
2103 /* get the IInternalSessionControl interface */
2104 ComPtr <IInternalSessionControl> control = aSession;
2105 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
2106 E_INVALIDARG);
2107
2108 /* create a progress object */
2109 ComObjPtr <Progress> progress;
2110 progress.createObject();
2111 progress->init (this, static_cast <IMachine *> (machine),
2112 Bstr (tr ("Spawning session")),
2113 FALSE /* aCancelable */);
2114
2115 rc = machine->openRemoteSession (control, aType, aEnvironment, progress);
2116
2117 if (SUCCEEDED (rc))
2118 {
2119 progress.queryInterfaceTo (aProgress);
2120
2121 /* fire an event */
2122 onSessionStateChange (aMachineId, SessionState_Spawning);
2123 }
2124
2125 return rc;
2126}
2127
2128/**
2129 * @note Locks objects!
2130 */
2131STDMETHODIMP VirtualBox::OpenExistingSession (ISession *aSession,
2132 INPTR GUIDPARAM aMachineId)
2133{
2134 if (!aSession)
2135 return E_POINTER;
2136
2137 AutoCaller autoCaller (this);
2138 CheckComRCReturnRC (autoCaller.rc());
2139
2140 Guid id = aMachineId;
2141 ComObjPtr <Machine> machine;
2142
2143 HRESULT rc = findMachine (id, true /* setError */, &machine);
2144 CheckComRCReturnRC (rc);
2145
2146 /* check the session state */
2147 SessionState_T state;
2148 rc = aSession->COMGETTER(State) (&state);
2149 CheckComRCReturnRC (rc);
2150
2151 if (state != SessionState_Closed)
2152 return setError (E_INVALIDARG,
2153 tr ("The given session is already open or being opened"));
2154
2155 /* get the IInternalSessionControl interface */
2156 ComPtr <IInternalSessionControl> control = aSession;
2157 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
2158 E_INVALIDARG);
2159
2160 rc = machine->openExistingSession (control);
2161
2162 return rc;
2163}
2164
2165/**
2166 * @note Locks this object for writing.
2167 */
2168STDMETHODIMP VirtualBox::RegisterCallback (IVirtualBoxCallback *aCallback)
2169{
2170 LogFlowThisFunc (("aCallback=%p\n", aCallback));
2171
2172 if (!aCallback)
2173 return E_INVALIDARG;
2174
2175 AutoCaller autoCaller (this);
2176 CheckComRCReturnRC (autoCaller.rc());
2177
2178 AutoWriteLock alock (this);
2179 mData.mCallbacks.push_back (CallbackList::value_type (aCallback));
2180
2181 return S_OK;
2182}
2183
2184/**
2185 * @note Locks this object for writing.
2186 */
2187STDMETHODIMP VirtualBox::UnregisterCallback (IVirtualBoxCallback *aCallback)
2188{
2189 if (!aCallback)
2190 return E_INVALIDARG;
2191
2192 AutoCaller autoCaller (this);
2193 CheckComRCReturnRC (autoCaller.rc());
2194
2195 HRESULT rc = S_OK;
2196
2197 AutoWriteLock alock (this);
2198
2199 CallbackList::iterator it;
2200 it = std::find (mData.mCallbacks.begin(),
2201 mData.mCallbacks.end(),
2202 CallbackList::value_type (aCallback));
2203 if (it == mData.mCallbacks.end())
2204 rc = E_INVALIDARG;
2205 else
2206 mData.mCallbacks.erase (it);
2207
2208 LogFlowThisFunc (("aCallback=%p, rc=%08X\n", aCallback, rc));
2209 return rc;
2210}
2211
2212STDMETHODIMP VirtualBox::WaitForPropertyChange (INPTR BSTR aWhat, ULONG aTimeout,
2213 BSTR *aChanged, BSTR *aValues)
2214{
2215 return E_NOTIMPL;
2216}
2217
2218STDMETHODIMP VirtualBox::SaveSettings()
2219{
2220 AutoCaller autoCaller (this);
2221 CheckComRCReturnRC (autoCaller.rc());
2222
2223 return saveSettings();
2224}
2225
2226STDMETHODIMP VirtualBox::SaveSettingsWithBackup (BSTR *aBakFileName)
2227{
2228 if (!aBakFileName)
2229 return E_POINTER;
2230
2231 AutoCaller autoCaller (this);
2232 CheckComRCReturnRC (autoCaller.rc());
2233
2234 /* saveSettings() needs write lock */
2235 AutoWriteLock alock (this);
2236
2237 /* perform backup only when there was auto-conversion */
2238 if (mData.mSettingsFileVersion != VBOX_XML_VERSION_FULL)
2239 {
2240 Bstr bakFileName;
2241
2242 HRESULT rc = backupSettingsFile (mData.mCfgFile.mName,
2243 mData.mSettingsFileVersion,
2244 bakFileName);
2245 CheckComRCReturnRC (rc);
2246
2247 bakFileName.cloneTo (aBakFileName);
2248 }
2249
2250 return saveSettings();
2251}
2252
2253// public methods only for internal purposes
2254/////////////////////////////////////////////////////////////////////////////
2255
2256/**
2257 * Posts an event to the event queue that is processed asynchronously
2258 * on a dedicated thread.
2259 *
2260 * Posting events to the dedicated event queue is useful to perform secondary
2261 * actions outside any object locks -- for example, to iterate over a list
2262 * of callbacks and inform them about some change caused by some object's
2263 * method call.
2264 *
2265 * @param event event to post
2266 * (must be allocated using |new|, will be deleted automatically
2267 * by the event thread after processing)
2268 *
2269 * @note Doesn't lock any object.
2270 */
2271HRESULT VirtualBox::postEvent (Event *event)
2272{
2273 AutoCaller autoCaller (this);
2274 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2275
2276 if (autoCaller.state() != Ready)
2277 {
2278 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
2279 "the event is discarded!\n",
2280 autoCaller.state()));
2281 return S_OK;
2282 }
2283
2284 AssertReturn (event, E_FAIL);
2285 AssertReturn (mAsyncEventQ, E_FAIL);
2286
2287 AutoWriteLock alock (mAsyncEventQLock);
2288 if (mAsyncEventQ->postEvent (event))
2289 return S_OK;
2290
2291 return E_FAIL;
2292}
2293
2294/**
2295 * Helper method to add a progress to the global collection of pending
2296 * operations.
2297 *
2298 * @param aProgress operation to add to the collection
2299 * @return COM status code
2300 *
2301 * @note Locks this object for writing.
2302 */
2303HRESULT VirtualBox::addProgress (IProgress *aProgress)
2304{
2305 if (!aProgress)
2306 return E_INVALIDARG;
2307
2308 AutoCaller autoCaller (this);
2309 CheckComRCReturnRC (autoCaller.rc());
2310
2311 AutoWriteLock alock (this);
2312 mData.mProgressOperations.push_back (aProgress);
2313 return S_OK;
2314}
2315
2316/**
2317 * Helper method to remove the progress from the global collection of pending
2318 * operations. Usualy gets called upon progress completion.
2319 *
2320 * @param aId UUID of the progress operation to remove
2321 * @return COM status code
2322 *
2323 * @note Locks this object for writing.
2324 */
2325HRESULT VirtualBox::removeProgress (INPTR GUIDPARAM aId)
2326{
2327 AutoCaller autoCaller (this);
2328 CheckComRCReturnRC (autoCaller.rc());
2329
2330 ComPtr <IProgress> progress;
2331
2332 AutoWriteLock alock (this);
2333
2334 for (ProgressList::iterator it = mData.mProgressOperations.begin();
2335 it != mData.mProgressOperations.end();
2336 ++ it)
2337 {
2338 Guid id;
2339 (*it)->COMGETTER(Id) (id.asOutParam());
2340 if (id == aId)
2341 {
2342 mData.mProgressOperations.erase (it);
2343 return S_OK;
2344 }
2345 }
2346
2347 AssertFailed(); /* should never happen */
2348
2349 return E_FAIL;
2350}
2351
2352#ifdef RT_OS_WINDOWS
2353
2354struct StartSVCHelperClientData
2355{
2356 ComObjPtr <VirtualBox> that;
2357 ComObjPtr <Progress> progress;
2358 bool privileged;
2359 VirtualBox::SVCHelperClientFunc func;
2360 void *user;
2361};
2362
2363/**
2364 * Helper method to that starts a worker thread that:
2365 * - creates a pipe communication channel using SVCHlpClient;
2366 * - starts a SVC Helper process that will inherit this channel;
2367 * - executes the supplied function by passing it the created SVCHlpClient
2368 * and opened instance to communicate to the Helper process and the given
2369 * Progress object.
2370 *
2371 * The user function is supposed to communicate to the helper process
2372 * using the \a aClient argument to do the requested job and optionally expose
2373 * the prgress through the \a aProgress object. The user function should never
2374 * call notifyComplete() on it: this will be done automatically using the
2375 * result code returned by the function.
2376 *
2377 * Before the user function is stared, the communication channel passed to in
2378 * the \a aClient argument, is fully set up, the function should start using
2379 * it's write() and read() methods directly.
2380 *
2381 * The \a aVrc parameter of the user function may be used to return an error
2382 * code if it is related to communication errors (for example, returned by
2383 * the SVCHlpClient members when they fail). In this case, the correct error
2384 * message using this value will be reported to the caller. Note that the
2385 * value of \a aVrc is inspected only if the user function itself returns
2386 * a success.
2387 *
2388 * If a failure happens anywhere before the user function would be normally
2389 * called, it will be called anyway in special "cleanup only" mode indicated
2390 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
2391 * all the function is supposed to do is to cleanup its aUser argument if
2392 * necessary (it's assumed that the ownership of this argument is passed to
2393 * the user function once #startSVCHelperClient() returns a success, thus
2394 * making it responsible for the cleanup).
2395 *
2396 * After the user function returns, the thread will send the SVCHlpMsg::Null
2397 * message to indicate a process termination.
2398 *
2399 * @param aPrivileged |true| to start the SVC Hepler process as a privlieged
2400 * user that can perform administrative tasks
2401 * @param aFunc user function to run
2402 * @param aUser argument to the user function
2403 * @param aProgress progress object that will track operation completion
2404 *
2405 * @note aPrivileged is currently ignored (due to some unsolved problems in
2406 * Vista) and the process will be started as a normal (unprivileged)
2407 * process.
2408 *
2409 * @note Doesn't lock anything.
2410 */
2411HRESULT VirtualBox::startSVCHelperClient (bool aPrivileged,
2412 SVCHelperClientFunc aFunc,
2413 void *aUser, Progress *aProgress)
2414{
2415 AssertReturn (aFunc, E_POINTER);
2416 AssertReturn (aProgress, E_POINTER);
2417
2418 AutoCaller autoCaller (this);
2419 CheckComRCReturnRC (autoCaller.rc());
2420
2421 /* create the SVCHelperClientThread() argument */
2422 std::auto_ptr <StartSVCHelperClientData>
2423 d (new StartSVCHelperClientData());
2424 AssertReturn (d.get(), E_OUTOFMEMORY);
2425
2426 d->that = this;
2427 d->progress = aProgress;
2428 d->privileged = aPrivileged;
2429 d->func = aFunc;
2430 d->user = aUser;
2431
2432 RTTHREAD tid = NIL_RTTHREAD;
2433 int vrc = RTThreadCreate (&tid, SVCHelperClientThread,
2434 static_cast <void *> (d.get()),
2435 0, RTTHREADTYPE_MAIN_WORKER,
2436 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2437
2438 ComAssertMsgRCRet (vrc, ("Could not create SVCHelper thread (%Vrc)\n", vrc),
2439 E_FAIL);
2440
2441 /* d is now owned by SVCHelperClientThread(), so release it */
2442 d.release();
2443
2444 return S_OK;
2445}
2446
2447/**
2448 * Worker thread for startSVCHelperClient().
2449 */
2450/* static */
2451DECLCALLBACK(int)
2452VirtualBox::SVCHelperClientThread (RTTHREAD aThread, void *aUser)
2453{
2454 LogFlowFuncEnter();
2455
2456 std::auto_ptr <StartSVCHelperClientData>
2457 d (static_cast <StartSVCHelperClientData *> (aUser));
2458
2459 HRESULT rc = S_OK;
2460 bool userFuncCalled = false;
2461
2462 do
2463 {
2464 AssertBreakStmt (d.get(), rc = E_POINTER);
2465 AssertReturn (!d->progress.isNull(), E_POINTER);
2466
2467 /* protect VirtualBox from uninitialization */
2468 AutoCaller autoCaller (d->that);
2469 if (!autoCaller.isOk())
2470 {
2471 /* it's too late */
2472 rc = autoCaller.rc();
2473 break;
2474 }
2475
2476 int vrc = VINF_SUCCESS;
2477
2478 Guid id;
2479 id.create();
2480 SVCHlpClient client;
2481 vrc = client.create (Utf8StrFmt ("VirtualBox\\SVCHelper\\{%Vuuid}",
2482 id.raw()));
2483 if (VBOX_FAILURE (vrc))
2484 {
2485 rc = setError (E_FAIL,
2486 tr ("Could not create the communication channel (%Vrc)"), vrc);
2487 break;
2488 }
2489
2490 /* get the path to the executable */
2491 char exePathBuf [RTPATH_MAX];
2492 char *exePath = RTProcGetExecutableName (exePathBuf, RTPATH_MAX);
2493 ComAssertBreak (exePath, E_FAIL);
2494
2495 Utf8Str argsStr = Utf8StrFmt ("/Helper %s", client.name().raw());
2496
2497 LogFlowFunc (("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
2498
2499 RTPROCESS pid = NIL_RTPROCESS;
2500
2501 if (d->privileged)
2502 {
2503 /* Attempt to start a privileged process using the Run As dialog */
2504
2505 Bstr file = exePath;
2506 Bstr parameters = argsStr;
2507
2508 SHELLEXECUTEINFO shExecInfo;
2509
2510 shExecInfo.cbSize = sizeof (SHELLEXECUTEINFO);
2511
2512 shExecInfo.fMask = NULL;
2513 shExecInfo.hwnd = NULL;
2514 shExecInfo.lpVerb = L"runas";
2515 shExecInfo.lpFile = file;
2516 shExecInfo.lpParameters = parameters;
2517 shExecInfo.lpDirectory = NULL;
2518 shExecInfo.nShow = SW_NORMAL;
2519 shExecInfo.hInstApp = NULL;
2520
2521 if (!ShellExecuteEx (&shExecInfo))
2522 {
2523 int vrc2 = RTErrConvertFromWin32 (GetLastError());
2524 /* hide excessive details in case of a frequent error
2525 * (pressing the Cancel button to close the Run As dialog) */
2526 if (vrc2 == VERR_CANCELLED)
2527 rc = setError (E_FAIL,
2528 tr ("Operatiion cancelled by the user"));
2529 else
2530 rc = setError (E_FAIL,
2531 tr ("Could not launch a privileged process '%s' (%Vrc)"),
2532 exePath, vrc2);
2533 break;
2534 }
2535 }
2536 else
2537 {
2538 const char *args[] = { exePath, "/Helper", client.name(), 0 };
2539 vrc = RTProcCreate (exePath, args, RTENV_DEFAULT, 0, &pid);
2540 if (VBOX_FAILURE (vrc))
2541 {
2542 rc = setError (E_FAIL,
2543 tr ("Could not launch a process '%s' (%Vrc)"), exePath, vrc);
2544 break;
2545 }
2546 }
2547
2548 /* wait for the client to connect */
2549 vrc = client.connect();
2550 if (VBOX_SUCCESS (vrc))
2551 {
2552 /* start the user supplied function */
2553 rc = d->func (&client, d->progress, d->user, &vrc);
2554 userFuncCalled = true;
2555 }
2556
2557 /* send the termination signal to the process anyway */
2558 {
2559 int vrc2 = client.write (SVCHlpMsg::Null);
2560 if (VBOX_SUCCESS (vrc))
2561 vrc = vrc2;
2562 }
2563
2564 if (SUCCEEDED (rc) && VBOX_FAILURE (vrc))
2565 {
2566 rc = setError (E_FAIL,
2567 tr ("Could not operate the communication channel (%Vrc)"), vrc);
2568 break;
2569 }
2570 }
2571 while (0);
2572
2573 if (FAILED (rc) && !userFuncCalled)
2574 {
2575 /* call the user function in the "cleanup only" mode
2576 * to let it free resources passed to in aUser */
2577 d->func (NULL, NULL, d->user, NULL);
2578 }
2579
2580 d->progress->notifyComplete (rc);
2581
2582 LogFlowFuncLeave();
2583 return 0;
2584}
2585
2586#endif /* RT_OS_WINDOWS */
2587
2588/**
2589 * Sends a signal to the client watcher thread to rescan the set of machines
2590 * that have open sessions.
2591 *
2592 * @note Doesn't lock anything.
2593 */
2594void VirtualBox::updateClientWatcher()
2595{
2596 AutoCaller autoCaller (this);
2597 AssertComRCReturn (autoCaller.rc(), (void) 0);
2598
2599 AssertReturn (mWatcherData.mThread != NIL_RTTHREAD, (void) 0);
2600
2601 /* sent an update request */
2602#if defined(RT_OS_WINDOWS)
2603 ::SetEvent (mWatcherData.mUpdateReq);
2604#elif defined(RT_OS_OS2)
2605 RTSemEventSignal (mWatcherData.mUpdateReq);
2606#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2607 RTSemEventSignal (mWatcherData.mUpdateReq);
2608#else
2609# error "Port me!"
2610#endif
2611}
2612
2613/**
2614 * Adds the given child process ID to the list of processes to be reaped.
2615 * This call should be followed by #updateClientWatcher() to take the effect.
2616 */
2617void VirtualBox::addProcessToReap (RTPROCESS pid)
2618{
2619 AutoCaller autoCaller (this);
2620 AssertComRCReturn (autoCaller.rc(), (void) 0);
2621
2622 /// @todo (dmik) Win32?
2623#ifndef RT_OS_WINDOWS
2624 AutoWriteLock alock (this);
2625 mWatcherData.mProcesses.push_back (pid);
2626#endif
2627}
2628
2629/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2630struct MachineEvent : public VirtualBox::CallbackEvent
2631{
2632 enum What { DataChanged, StateChanged, Registered };
2633
2634 MachineEvent (VirtualBox *aVB, const Guid &aId)
2635 : CallbackEvent (aVB), what (DataChanged), id (aId)
2636 {}
2637
2638 MachineEvent (VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2639 : CallbackEvent (aVB), what (StateChanged), id (aId)
2640 , state (aState)
2641 {}
2642
2643 MachineEvent (VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2644 : CallbackEvent (aVB), what (Registered), id (aId)
2645 , registered (aRegistered)
2646 {}
2647
2648 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2649 {
2650 switch (what)
2651 {
2652 case DataChanged:
2653 LogFlow (("OnMachineDataChange: id={%Vuuid}\n", id.ptr()));
2654 aCallback->OnMachineDataChange (id);
2655 break;
2656
2657 case StateChanged:
2658 LogFlow (("OnMachineStateChange: id={%Vuuid}, state=%d\n",
2659 id.ptr(), state));
2660 aCallback->OnMachineStateChange (id, state);
2661 break;
2662
2663 case Registered:
2664 LogFlow (("OnMachineRegistered: id={%Vuuid}, registered=%d\n",
2665 id.ptr(), registered));
2666 aCallback->OnMachineRegistered (id, registered);
2667 break;
2668 }
2669 }
2670
2671 const What what;
2672
2673 Guid id;
2674 MachineState_T state;
2675 BOOL registered;
2676};
2677
2678/**
2679 * @note Doesn't lock any object.
2680 */
2681void VirtualBox::onMachineStateChange (const Guid &aId, MachineState_T aState)
2682{
2683 postEvent (new MachineEvent (this, aId, aState));
2684}
2685
2686/**
2687 * @note Doesn't lock any object.
2688 */
2689void VirtualBox::onMachineDataChange (const Guid &aId)
2690{
2691 postEvent (new MachineEvent (this, aId));
2692}
2693
2694/**
2695 * @note Locks this object for reading.
2696 */
2697BOOL VirtualBox::onExtraDataCanChange (const Guid &aId, INPTR BSTR aKey, INPTR BSTR aValue,
2698 Bstr &aError)
2699{
2700 LogFlowThisFunc (("machine={%s} aKey={%ls} aValue={%ls}\n",
2701 aId.toString().raw(), aKey, aValue));
2702
2703 AutoCaller autoCaller (this);
2704 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2705
2706 CallbackList list;
2707 {
2708 AutoReadLock alock (this);
2709 list = mData.mCallbacks;
2710 }
2711
2712 BOOL allowChange = TRUE;
2713 CallbackList::iterator it = list.begin();
2714 while ((it != list.end()) && allowChange)
2715 {
2716 HRESULT rc = (*it++)->OnExtraDataCanChange (aId, aKey, aValue,
2717 aError.asOutParam(), &allowChange);
2718 if (FAILED (rc))
2719 {
2720 /* if a call to this method fails for some reason (for ex., because
2721 * the other side is dead), we ensure allowChange stays true
2722 * (MS COM RPC implementation seems to zero all output vars before
2723 * issuing an IPC call or after a failure, so it's essential
2724 * there) */
2725 allowChange = TRUE;
2726 }
2727 }
2728
2729 LogFlowThisFunc (("allowChange=%RTbool\n", allowChange));
2730 return allowChange;
2731}
2732
2733/** Event for onExtraDataChange() */
2734struct ExtraDataEvent : public VirtualBox::CallbackEvent
2735{
2736 ExtraDataEvent (VirtualBox *aVB, const Guid &aMachineId,
2737 INPTR BSTR aKey, INPTR BSTR aVal)
2738 : CallbackEvent (aVB), machineId (aMachineId)
2739 , key (aKey), val (aVal)
2740 {}
2741
2742 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2743 {
2744 LogFlow (("OnExtraDataChange: machineId={%Vuuid}, key='%ls', val='%ls'\n",
2745 machineId.ptr(), key.raw(), val.raw()));
2746 aCallback->OnExtraDataChange (machineId, key, val);
2747 }
2748
2749 Guid machineId;
2750 Bstr key, val;
2751};
2752
2753/**
2754 * @note Doesn't lock any object.
2755 */
2756void VirtualBox::onExtraDataChange (const Guid &aId, INPTR BSTR aKey, INPTR BSTR aValue)
2757{
2758 postEvent (new ExtraDataEvent (this, aId, aKey, aValue));
2759}
2760
2761/**
2762 * @note Doesn't lock any object.
2763 */
2764void VirtualBox::onMachineRegistered (const Guid &aId, BOOL aRegistered)
2765{
2766 postEvent (new MachineEvent (this, aId, aRegistered));
2767}
2768
2769/** Event for onSessionStateChange() */
2770struct SessionEvent : public VirtualBox::CallbackEvent
2771{
2772 SessionEvent (VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2773 : CallbackEvent (aVB), machineId (aMachineId), sessionState (aState)
2774 {}
2775
2776 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2777 {
2778 LogFlow (("OnSessionStateChange: machineId={%Vuuid}, sessionState=%d\n",
2779 machineId.ptr(), sessionState));
2780 aCallback->OnSessionStateChange (machineId, sessionState);
2781 }
2782
2783 Guid machineId;
2784 SessionState_T sessionState;
2785};
2786
2787/**
2788 * @note Doesn't lock any object.
2789 */
2790void VirtualBox::onSessionStateChange (const Guid &aId, SessionState_T aState)
2791{
2792 postEvent (new SessionEvent (this, aId, aState));
2793}
2794
2795/** Event for onSnapshotTaken(), onSnapshotRemoved() and onSnapshotChange() */
2796struct SnapshotEvent : public VirtualBox::CallbackEvent
2797{
2798 enum What { Taken, Discarded, Changed };
2799
2800 SnapshotEvent (VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2801 What aWhat)
2802 : CallbackEvent (aVB)
2803 , what (aWhat)
2804 , machineId (aMachineId), snapshotId (aSnapshotId)
2805 {}
2806
2807 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2808 {
2809 switch (what)
2810 {
2811 case Taken:
2812 LogFlow (("OnSnapshotTaken: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
2813 machineId.ptr(), snapshotId.ptr()));
2814 aCallback->OnSnapshotTaken (machineId, snapshotId);
2815 break;
2816
2817 case Discarded:
2818 LogFlow (("OnSnapshotDiscarded: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
2819 machineId.ptr(), snapshotId.ptr()));
2820 aCallback->OnSnapshotDiscarded (machineId, snapshotId);
2821 break;
2822
2823 case Changed:
2824 LogFlow (("OnSnapshotChange: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
2825 machineId.ptr(), snapshotId.ptr()));
2826 aCallback->OnSnapshotChange (machineId, snapshotId);
2827 break;
2828 }
2829 }
2830
2831 const What what;
2832
2833 Guid machineId;
2834 Guid snapshotId;
2835};
2836
2837/**
2838 * @note Doesn't lock any object.
2839 */
2840void VirtualBox::onSnapshotTaken (const Guid &aMachineId, const Guid &aSnapshotId)
2841{
2842 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Taken));
2843}
2844
2845/**
2846 * @note Doesn't lock any object.
2847 */
2848void VirtualBox::onSnapshotDiscarded (const Guid &aMachineId, const Guid &aSnapshotId)
2849{
2850 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Discarded));
2851}
2852
2853/**
2854 * @note Doesn't lock any object.
2855 */
2856void VirtualBox::onSnapshotChange (const Guid &aMachineId, const Guid &aSnapshotId)
2857{
2858 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Changed));
2859}
2860
2861/**
2862 * @note Locks this object for reading.
2863 */
2864ComObjPtr <GuestOSType> VirtualBox::getUnknownOSType()
2865{
2866 ComObjPtr <GuestOSType> type;
2867
2868 AutoCaller autoCaller (this);
2869 AssertComRCReturn (autoCaller.rc(), type);
2870
2871 AutoReadLock alock (this);
2872
2873 /* unknown type must always be the first */
2874 ComAssertRet (mData.mGuestOSTypes.size() > 0, type);
2875
2876 type = mData.mGuestOSTypes.front();
2877 return type;
2878}
2879
2880/**
2881 * Returns the list of opened machines (i.e. machines having direct sessions
2882 * opened by client processes).
2883 *
2884 * @note the returned list contains smart pointers. So, clear it as soon as
2885 * it becomes no more necessary to release instances.
2886 * @note it can be possible that a session machine from the list has been
2887 * already uninitialized, so a) lock the instance and b) chheck for
2888 * instance->isReady() return value before manipulating the object directly
2889 * (i.e. not through COM methods).
2890 *
2891 * @note Locks objects for reading.
2892 */
2893void VirtualBox::getOpenedMachines (SessionMachineVector &aVector)
2894{
2895 AutoCaller autoCaller (this);
2896 AssertComRCReturn (autoCaller.rc(), (void) 0);
2897
2898 std::list <ComObjPtr <SessionMachine> > list;
2899
2900 {
2901 AutoReadLock alock (this);
2902
2903 for (MachineList::iterator it = mData.mMachines.begin();
2904 it != mData.mMachines.end();
2905 ++ it)
2906 {
2907 ComObjPtr <SessionMachine> sm = (*it)->sessionMachine();
2908 /* SessionMachine is null when there are no open sessions */
2909 if (!sm.isNull())
2910 list.push_back (sm);
2911 }
2912 }
2913
2914 aVector = SessionMachineVector (list.begin(), list.end());
2915 return;
2916}
2917
2918/**
2919 * Helper to find machines that use the given DVD image.
2920 *
2921 * This method also checks whether an existing snapshot refers to the given
2922 * image. However, only the machine ID is returned in this case, not IDs of
2923 * individual snapshots.
2924 *
2925 * @param aId Image ID to get usage for.
2926 * @param aUsage Type of the check.
2927 * @param aMachineIDs Where to store the list of machine IDs (can be NULL)
2928 *
2929 * @return @c true if at least one machine or its snapshot found and @c false
2930 * otherwise.
2931 *
2932 * @note For now, we just scan all the machines. We can optimize this later
2933 * if required by adding the corresponding field to DVDImage and requiring all
2934 * IDVDImage instances to be DVDImage objects.
2935 *
2936 * @note Locks objects for reading.
2937 */
2938bool VirtualBox::getDVDImageUsage (const Guid &aId,
2939 ResourceUsage_T aUsage,
2940 Bstr *aMachineIDs)
2941{
2942 AutoCaller autoCaller (this);
2943 AssertComRCReturn (autoCaller.rc(), FALSE);
2944
2945 typedef std::set <Guid> Set;
2946 Set idSet;
2947
2948 {
2949 AutoReadLock alock (this);
2950
2951 for (MachineList::const_iterator mit = mData.mMachines.begin();
2952 mit != mData.mMachines.end();
2953 ++ mit)
2954 {
2955 ComObjPtr <Machine> m = *mit;
2956
2957 AutoLimitedCaller machCaller (m);
2958 AssertComRC (machCaller.rc());
2959
2960 /* ignore inaccessible machines */
2961 if (machCaller.state() == Machine::Ready)
2962 {
2963 if (m->isDVDImageUsed (aId, aUsage))
2964 {
2965 /* if not interested in the list, return shortly */
2966 if (aMachineIDs == NULL)
2967 return true;
2968
2969 idSet.insert (m->uuid());
2970 }
2971 }
2972 }
2973 }
2974
2975 if (aMachineIDs)
2976 {
2977 if (!idSet.empty())
2978 {
2979 /* convert to a string of UUIDs */
2980 char *idList = (char *) RTMemTmpAllocZ (RTUUID_STR_LENGTH * idSet.size());
2981 char *idListPtr = idList;
2982 for (Set::iterator it = idSet.begin(); it != idSet.end(); ++ it)
2983 {
2984 RTUuidToStr (*it, idListPtr, RTUUID_STR_LENGTH);
2985 idListPtr += RTUUID_STR_LENGTH - 1;
2986 /* replace EOS with a space char */
2987 *(idListPtr ++) = ' ';
2988 }
2989 Assert (int (idListPtr - idList) == int (RTUUID_STR_LENGTH * idSet.size()));
2990 /* remove the trailing space */
2991 *(-- idListPtr) = 0;
2992 /* copy the string */
2993 *aMachineIDs = idList;
2994 RTMemTmpFree (idList);
2995 }
2996 else
2997 {
2998 (*aMachineIDs).setNull();
2999 }
3000 }
3001
3002 return !idSet.empty();
3003}
3004
3005/**
3006 * Helper to find machines that use the given Floppy image.
3007 *
3008 * This method also checks whether an existing snapshot refers to the given
3009 * image. However, only the machine ID is returned in this case, not IDs of
3010 * individual snapshots.
3011 *
3012 * @param aId Image ID to get usage for.
3013 * @param aUsage Type of the check.
3014 * @param aMachineIDs Where to store the list of machine IDs (can be NULL)
3015 *
3016 * @return @c true if at least one machine or its snapshot found and @c false
3017 * otherwise.
3018 *
3019 * @note For now, we just scan all the machines. We can optimize this later
3020 * if required by adding the corresponding field to FloppyImage and requiring all
3021 * FloppyImage instances to be FloppyImage objects.
3022 *
3023 * @note Locks objects for reading.
3024 */
3025bool VirtualBox::getFloppyImageUsage (const Guid &aId,
3026 ResourceUsage_T aUsage,
3027 Bstr *aMachineIDs)
3028{
3029 AutoCaller autoCaller (this);
3030 AssertComRCReturn (autoCaller.rc(), FALSE);
3031
3032 typedef std::set <Guid> Set;
3033 Set idSet;
3034
3035 {
3036 AutoReadLock alock (this);
3037
3038 for (MachineList::const_iterator mit = mData.mMachines.begin();
3039 mit != mData.mMachines.end();
3040 ++ mit)
3041 {
3042 ComObjPtr <Machine> m = *mit;
3043
3044 AutoLimitedCaller machCaller (m);
3045 AssertComRC (machCaller.rc());
3046
3047 /* ignore inaccessible machines */
3048 if (machCaller.state() == Machine::Ready)
3049 {
3050 if (m->isFloppyImageUsed (aId, aUsage))
3051 {
3052 /* if not interested in the list, return shortly */
3053 if (aMachineIDs == NULL)
3054 return true;
3055
3056 idSet.insert (m->uuid());
3057 }
3058 }
3059 }
3060 }
3061
3062 if (aMachineIDs)
3063 {
3064 if (!idSet.empty())
3065 {
3066 /* convert to a string of UUIDs */
3067 char *idList = (char *) RTMemTmpAllocZ (RTUUID_STR_LENGTH * idSet.size());
3068 char *idListPtr = idList;
3069 for (Set::iterator it = idSet.begin(); it != idSet.end(); ++ it)
3070 {
3071 RTUuidToStr (*it, idListPtr, RTUUID_STR_LENGTH);
3072 idListPtr += RTUUID_STR_LENGTH - 1;
3073 /* replace EOS with a space char */
3074 *(idListPtr ++) = ' ';
3075 }
3076 Assert (int (idListPtr - idList) == int (RTUUID_STR_LENGTH * idSet.size()));
3077 /* remove the trailing space */
3078 *(-- idListPtr) = 0;
3079 /* copy the string */
3080 *aMachineIDs = idList;
3081 RTMemTmpFree (idList);
3082 }
3083 else
3084 {
3085 (*aMachineIDs).setNull();
3086 }
3087 }
3088
3089 return !idSet.empty();
3090}
3091
3092/**
3093 * Tries to calculate the relative path of the given absolute path using the
3094 * directory of the VirtualBox settings file as the base directory.
3095 *
3096 * @param aPath absolute path to calculate the relative path for
3097 * @param aResult where to put the result (used only when it's possible to
3098 * make a relative path from the given absolute path;
3099 * otherwise left untouched)
3100 *
3101 * @note Doesn't lock any object.
3102 */
3103void VirtualBox::calculateRelativePath (const char *aPath, Utf8Str &aResult)
3104{
3105 AutoCaller autoCaller (this);
3106 AssertComRCReturn (autoCaller.rc(), (void) 0);
3107
3108 /* no need to lock since mHomeDir is const */
3109
3110 Utf8Str settingsDir = mData.mHomeDir;
3111
3112 if (RTPathStartsWith (aPath, settingsDir))
3113 {
3114 /* when assigning, we create a separate Utf8Str instance because both
3115 * aPath and aResult can point to the same memory location when this
3116 * func is called (if we just do aResult = aPath, aResult will be freed
3117 * first, and since its the same as aPath, an attempt to copy garbage
3118 * will be made. */
3119 aResult = Utf8Str (aPath + settingsDir.length() + 1);
3120 }
3121}
3122
3123// private methods
3124/////////////////////////////////////////////////////////////////////////////
3125
3126/**
3127 * Searches for a Machine object with the given ID in the collection
3128 * of registered machines.
3129 *
3130 * @param id
3131 * ID of the machine
3132 * @param doSetError
3133 * if TRUE, the appropriate error info is set in case when the machine
3134 * is not found
3135 * @param machine
3136 * where to store the found machine object (can be NULL)
3137 *
3138 * @return
3139 * S_OK when found or E_INVALIDARG when not found
3140 *
3141 * @note Locks this object for reading.
3142 */
3143HRESULT VirtualBox::findMachine (const Guid &aId, bool aSetError,
3144 ComObjPtr <Machine> *aMachine /* = NULL */)
3145{
3146 AutoCaller autoCaller (this);
3147 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3148
3149 bool found = false;
3150
3151 {
3152 AutoReadLock alock (this);
3153
3154 for (MachineList::iterator it = mData.mMachines.begin();
3155 !found && it != mData.mMachines.end();
3156 ++ it)
3157 {
3158 /* sanity */
3159 AutoLimitedCaller machCaller (*it);
3160 AssertComRC (machCaller.rc());
3161
3162 found = (*it)->uuid() == aId;
3163 if (found && aMachine)
3164 *aMachine = *it;
3165 }
3166 }
3167
3168 HRESULT rc = found ? S_OK : E_INVALIDARG;
3169
3170 if (aSetError && !found)
3171 {
3172 setError (E_INVALIDARG,
3173 tr ("Could not find a registered machine with UUID {%Vuuid}"),
3174 aId.raw());
3175 }
3176
3177 return rc;
3178}
3179
3180/**
3181 * Searches for a HardDisk object with the given ID or location specification
3182 * in the collection of registered hard disks. If both ID and location are
3183 * specified, the first object that matches either of them (not necessarily
3184 * both) is returned.
3185 *
3186 * @param aId ID of the hard disk (NULL when unused)
3187 * @param aLocation full location specification (NULL when unused)
3188 * @param aSetError if TRUE, the appropriate error info is set in case when
3189 * the disk is not found and only one search criteria (ID
3190 * or file name) is specified.
3191 * @param aHardDisk where to store the found hard disk object (can be NULL)
3192 *
3193 * @return
3194 * S_OK when found or E_INVALIDARG when not found
3195 *
3196 * @note Locks objects for reading!
3197 */
3198HRESULT VirtualBox::
3199findHardDisk (const Guid *aId, const BSTR aLocation,
3200 bool aSetError, ComObjPtr <HardDisk> *aHardDisk /* = NULL */)
3201{
3202 ComAssertRet (aId || aLocation, E_INVALIDARG);
3203
3204 AutoReadLock alock (this);
3205
3206 /* first lookup the map by UUID if UUID is provided */
3207 if (aId)
3208 {
3209 HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
3210 if (it != mData.mHardDiskMap.end())
3211 {
3212 if (aHardDisk)
3213 *aHardDisk = (*it).second;
3214 return S_OK;
3215 }
3216 }
3217
3218 /* then iterate and find by location */
3219 bool found = false;
3220 if (aLocation)
3221 {
3222 Utf8Str location = aLocation;
3223
3224 for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3225 !found && it != mData.mHardDiskMap.end();
3226 ++ it)
3227 {
3228 const ComObjPtr <HardDisk> &hd = (*it).second;
3229 AutoReadLock hdLock (hd);
3230
3231 if (hd->storageType() == HardDiskStorageType_VirtualDiskImage ||
3232 hd->storageType() == HardDiskStorageType_VMDKImage ||
3233 hd->storageType() == HardDiskStorageType_VHDImage)
3234 {
3235 /* locations of VDI and VMDK hard disks for now are just
3236 * file paths */
3237 found = RTPathCompare (location,
3238 Utf8Str (hd->toString
3239 (false /* aShort */))) == 0;
3240 }
3241 else
3242 {
3243 found = aLocation == hd->toString (false /* aShort */);
3244 }
3245
3246 if (found && aHardDisk)
3247 *aHardDisk = hd;
3248 }
3249 }
3250
3251 HRESULT rc = found ? S_OK : E_INVALIDARG;
3252
3253 if (aSetError && !found)
3254 {
3255 if (aId && !aLocation)
3256 setError (rc, tr ("Could not find a registered hard disk "
3257 "with UUID {%Vuuid}"), aId->raw());
3258 else if (aLocation && !aId)
3259 setError (rc, tr ("Could not find a registered hard disk "
3260 "with location '%ls'"), aLocation);
3261 }
3262
3263 return rc;
3264}
3265
3266/**
3267 * @deprecated Use #findHardDisk() instead.
3268 *
3269 * Searches for a HVirtualDiskImage object with the given ID or file path in the
3270 * collection of registered hard disks. If both ID and file path are specified,
3271 * the first object that matches either of them (not necessarily both)
3272 * is returned.
3273 *
3274 * @param aId ID of the hard disk (NULL when unused)
3275 * @param filePathFull full path to the image file (NULL when unused)
3276 * @param aSetError if TRUE, the appropriate error info is set in case when
3277 * the disk is not found and only one search criteria (ID
3278 * or file name) is specified.
3279 * @param aHardDisk where to store the found hard disk object (can be NULL)
3280 *
3281 * @return
3282 * S_OK when found or E_INVALIDARG when not found
3283 *
3284 * @note Locks objects for reading!
3285 */
3286HRESULT VirtualBox::
3287findVirtualDiskImage (const Guid *aId, const BSTR aFilePathFull,
3288 bool aSetError, ComObjPtr <HVirtualDiskImage> *aImage /* = NULL */)
3289{
3290 ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3291
3292 AutoReadLock alock (this);
3293
3294 /* first lookup the map by UUID if UUID is provided */
3295 if (aId)
3296 {
3297 HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
3298 if (it != mData.mHardDiskMap.end())
3299 {
3300 AutoReadLock hdLock ((*it).second);
3301 if ((*it).second->storageType() == HardDiskStorageType_VirtualDiskImage)
3302 {
3303 if (aImage)
3304 *aImage = (*it).second->asVDI();
3305 return S_OK;
3306 }
3307 }
3308 }
3309
3310 /* then iterate and find by name */
3311 bool found = false;
3312 if (aFilePathFull)
3313 {
3314 for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3315 !found && it != mData.mHardDiskMap.end();
3316 ++ it)
3317 {
3318 const ComObjPtr <HardDisk> &hd = (*it).second;
3319 AutoReadLock hdLock (hd);
3320 if (hd->storageType() != HardDiskStorageType_VirtualDiskImage)
3321 continue;
3322
3323 found = RTPathCompare (Utf8Str (aFilePathFull),
3324 Utf8Str (hd->asVDI()->filePathFull())) == 0;
3325 if (found && aImage)
3326 *aImage = hd->asVDI();
3327 }
3328 }
3329
3330 HRESULT rc = found ? S_OK : E_INVALIDARG;
3331
3332 if (aSetError && !found)
3333 {
3334 if (aId && !aFilePathFull)
3335 setError (rc, tr ("Could not find a registered VDI hard disk "
3336 "with UUID {%Vuuid}"), aId->raw());
3337 else if (aFilePathFull && !aId)
3338 setError (rc, tr ("Could not find a registered VDI hard disk "
3339 "with the file path '%ls'"), aFilePathFull);
3340 }
3341
3342 return rc;
3343}
3344
3345/**
3346 * Searches for a DVDImage object with the given ID or file path in the
3347 * collection of registered DVD images. If both ID and file path are specified,
3348 * the first object that matches either of them (not necessarily both)
3349 * is returned.
3350 *
3351 * @param aId
3352 * ID of the DVD image (unused when NULL)
3353 * @param aFilePathFull
3354 * full path to the image file (unused when NULL)
3355 * @param aSetError
3356 * if TRUE, the appropriate error info is set in case when the image is not
3357 * found and only one search criteria (ID or file name) is specified.
3358 * @param aImage
3359 * where to store the found DVD image object (can be NULL)
3360 *
3361 * @return
3362 * S_OK when found or E_INVALIDARG when not found
3363 *
3364 * @note Locks this object for reading.
3365 */
3366HRESULT VirtualBox::findDVDImage (const Guid *aId, const BSTR aFilePathFull,
3367 bool aSetError,
3368 ComObjPtr <DVDImage> *aImage /* = NULL */)
3369{
3370 ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3371
3372 bool found = false;
3373
3374 {
3375 AutoReadLock alock (this);
3376
3377 for (DVDImageList::const_iterator it = mData.mDVDImages.begin();
3378 !found && it != mData.mDVDImages.end();
3379 ++ it)
3380 {
3381 /* DVDImage fields are constant, so no need to lock */
3382 found = (aId && (*it)->id() == *aId) ||
3383 (aFilePathFull &&
3384 RTPathCompare (Utf8Str (aFilePathFull),
3385 Utf8Str ((*it)->filePathFull())) == 0);
3386 if (found && aImage)
3387 *aImage = *it;
3388 }
3389 }
3390
3391 HRESULT rc = found ? S_OK : E_INVALIDARG;
3392
3393 if (aSetError && !found)
3394 {
3395 if (aId && !aFilePathFull)
3396 setError (rc, tr ("Could not find a registered CD/DVD image "
3397 "with UUID {%s}"), aId->toString().raw());
3398 else if (aFilePathFull && !aId)
3399 setError (rc, tr ("Could not find a registered CD/DVD image "
3400 "with the file path '%ls'"), aFilePathFull);
3401 }
3402
3403 return rc;
3404}
3405
3406/**
3407 * Searches for a FloppyImage object with the given ID or file path in the
3408 * collection of registered floppy images. If both ID and file path are specified,
3409 * the first object that matches either of them (not necessarily both)
3410 * is returned.
3411 *
3412 * @param aId
3413 * ID of the floppy image (unused when NULL)
3414 * @param aFilePathFull
3415 * full path to the image file (unused when NULL)
3416 * @param aSetError
3417 * if TRUE, the appropriate error info is set in case when the image is not
3418 * found and only one search criteria (ID or file name) is specified.
3419 * @param aImage
3420 * where to store the found floppy image object (can be NULL)
3421 *
3422 * @return
3423 * S_OK when found or E_INVALIDARG when not found
3424 *
3425 * @note Locks this object for reading.
3426 */
3427HRESULT VirtualBox::findFloppyImage (const Guid *aId, const BSTR aFilePathFull,
3428 bool aSetError,
3429 ComObjPtr <FloppyImage> *aImage /* = NULL */)
3430{
3431 ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3432
3433 bool found = false;
3434
3435 {
3436 AutoReadLock alock (this);
3437
3438 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3439 !found && it != mData.mFloppyImages.end();
3440 ++ it)
3441 {
3442 /* FloppyImage fields are constant, so no need to lock */
3443 found = (aId && (*it)->id() == *aId) ||
3444 (aFilePathFull &&
3445 RTPathCompare (Utf8Str (aFilePathFull),
3446 Utf8Str ((*it)->filePathFull())) == 0);
3447 if (found && aImage)
3448 *aImage = *it;
3449 }
3450 }
3451
3452 HRESULT rc = found ? S_OK : E_INVALIDARG;
3453
3454 if (aSetError && !found)
3455 {
3456 if (aId && !aFilePathFull)
3457 setError (rc, tr ("Could not find a registered floppy image "
3458 "with UUID {%s}"), aId->toString().raw());
3459 else if (aFilePathFull && !aId)
3460 setError (rc, tr ("Could not find a registered floppy image "
3461 "with the file path '%ls'"), aFilePathFull);
3462 }
3463
3464 return rc;
3465}
3466
3467/**
3468 * When \a aHardDisk is not NULL, searches for an object equal to the given
3469 * hard disk in the collection of registered hard disks, or, if the given hard
3470 * disk is HVirtualDiskImage, for an object with the given file path in the
3471 * collection of all registered non-hard disk images (DVDs and floppies).
3472 * Other parameters are unused.
3473 *
3474 * When \a aHardDisk is NULL, searches for an object with the given ID or file
3475 * path in the collection of all registered images (VDIs, DVDs and floppies).
3476 * If both ID and file path are specified, matching either of them will satisfy
3477 * the search.
3478 *
3479 * If a matching object is found, this method returns E_INVALIDARG and sets the
3480 * appropriate error info. Otherwise, S_OK is returned.
3481 *
3482 * @param aHardDisk hard disk object to check against registered media
3483 * (NULL when unused)
3484 * @param aId UUID of the media to check (NULL when unused)
3485 * @param aFilePathFull full path to the image file (NULL when unused)
3486 *
3487 * @note Locks objects!
3488 */
3489HRESULT VirtualBox::checkMediaForConflicts (HardDisk *aHardDisk,
3490 const Guid *aId,
3491 const BSTR aFilePathFull)
3492{
3493 AssertReturn (aHardDisk || aId || aFilePathFull, E_FAIL);
3494
3495 HRESULT rc = S_OK;
3496
3497 AutoReadLock alock (this);
3498
3499 if (aHardDisk)
3500 {
3501 for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3502 it != mData.mHardDiskMap.end();
3503 ++ it)
3504 {
3505 const ComObjPtr <HardDisk> &hd = (*it).second;
3506 if (hd->sameAs (aHardDisk))
3507 return setError (E_INVALIDARG,
3508 tr ("A hard disk with UUID {%Vuuid} or with the same properties "
3509 "('%ls') is already registered"),
3510 aHardDisk->id().raw(), aHardDisk->toString().raw());
3511 }
3512
3513 aId = &aHardDisk->id();
3514 if (aHardDisk->storageType() == HardDiskStorageType_VirtualDiskImage)
3515#if !defined (VBOX_WITH_XPCOM)
3516 /// @todo (dmik) stupid BSTR declaration lacks the BCSTR counterpart
3517 const_cast <BSTR> (aFilePathFull) = aHardDisk->asVDI()->filePathFull();
3518#else
3519 aFilePathFull = aHardDisk->asVDI()->filePathFull();
3520#endif
3521 }
3522
3523 bool found = false;
3524
3525 if (aId || aFilePathFull) do
3526 {
3527 if (!aHardDisk)
3528 {
3529 rc = findHardDisk (aId, aFilePathFull, false /* aSetError */);
3530 found = SUCCEEDED (rc);
3531 if (found)
3532 break;
3533 }
3534
3535 rc = findDVDImage (aId, aFilePathFull, false /* aSetError */);
3536 found = SUCCEEDED (rc);
3537 if (found)
3538 break;
3539
3540 rc = findFloppyImage (aId, aFilePathFull, false /* aSetError */);
3541 found = SUCCEEDED (rc);
3542 if (found)
3543 break;
3544 }
3545 while (0);
3546
3547 if (found)
3548 {
3549 if (aId && !aFilePathFull)
3550 rc = setError (E_INVALIDARG,
3551 tr ("A disk image with UUID {%Vuuid} is already registered"),
3552 aId->raw());
3553 else if (aFilePathFull && !aId)
3554 rc = setError (E_INVALIDARG,
3555 tr ("A disk image with file path '%ls' is already registered"),
3556 aFilePathFull);
3557 else
3558 rc = setError (E_INVALIDARG,
3559 tr ("A disk image with UUID {%Vuuid} or file path '%ls' "
3560 "is already registered"), aId->raw(), aFilePathFull);
3561 }
3562 else
3563 rc = S_OK;
3564
3565 return rc;
3566}
3567
3568/**
3569 * Reads in the machine definitions from the configuration loader
3570 * and creates the relevant objects.
3571 *
3572 * @param aGlobal <Global> node.
3573 *
3574 * @note Can be called only from #init().
3575 * @note Doesn't lock anything.
3576 */
3577HRESULT VirtualBox::loadMachines (const settings::Key &aGlobal)
3578{
3579 using namespace settings;
3580
3581 AutoCaller autoCaller (this);
3582 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3583
3584 HRESULT rc = S_OK;
3585
3586 Key::List machines = aGlobal.key ("MachineRegistry").keys ("MachineEntry");
3587 for (Key::List::const_iterator it = machines.begin();
3588 it != machines.end(); ++ it)
3589 {
3590 /* required */
3591 Guid uuid = (*it).value <Guid> ("uuid");
3592 /* required */
3593 Bstr src = (*it).stringValue ("src");
3594
3595 /* create a new machine object */
3596 ComObjPtr <Machine> machine;
3597 rc = machine.createObject();
3598 if (SUCCEEDED (rc))
3599 {
3600 /* initialize the machine object and register it */
3601 rc = machine->init (this, src, Machine::Init_Registered,
3602 NULL, FALSE, &uuid);
3603 if (SUCCEEDED (rc))
3604 rc = registerMachine (machine);
3605 }
3606 }
3607
3608 return rc;
3609}
3610
3611/**
3612 * Reads in the disk registration entries from the global settings file
3613 * and creates the relevant objects
3614 *
3615 * @param aGlobal <Global> node
3616 *
3617 * @note Can be called only from #init().
3618 * @note Doesn't lock anything.
3619 */
3620HRESULT VirtualBox::loadDisks (const settings::Key &aGlobal)
3621{
3622 using namespace settings;
3623
3624 AutoCaller autoCaller (this);
3625 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3626
3627 HRESULT rc = S_OK;
3628
3629 Key registry = aGlobal.key ("DiskRegistry");
3630
3631 const char *kMediaNodes[] = { "HardDisks", "DVDImages", "FloppyImages" };
3632
3633 for (size_t n = 0; n < ELEMENTS (kMediaNodes); ++ n)
3634 {
3635 /* All three media nodes are optional */
3636 Key node = registry.findKey (kMediaNodes [n]);
3637 if (node.isNull())
3638 continue;
3639
3640 if (n == 0)
3641 {
3642 /* HardDisks node */
3643 rc = loadHardDisks (node);
3644 continue;
3645 }
3646
3647 Key::List images = node.keys ("Image");
3648 for (Key::List::const_iterator it = images.begin();
3649 it != images.end(); ++ it)
3650 {
3651 /* required */
3652 Guid uuid = (*it).value <Guid> ("uuid");
3653 /* required */
3654 Bstr src = (*it).stringValue ("src");
3655
3656 switch (n)
3657 {
3658 case 1: /* DVDImages */
3659 {
3660 ComObjPtr <DVDImage> image;
3661 image.createObject();
3662 rc = image->init (this, src, TRUE /* isRegistered */, uuid);
3663 if (SUCCEEDED (rc))
3664 rc = registerDVDImage (image, TRUE /* aOnStartUp */);
3665
3666 break;
3667 }
3668 case 2: /* FloppyImages */
3669 {
3670 ComObjPtr <FloppyImage> image;
3671 image.createObject();
3672 rc = image->init (this, src, TRUE /* isRegistered */, uuid);
3673 if (SUCCEEDED (rc))
3674 rc = registerFloppyImage (image, TRUE /* aOnStartUp */);
3675
3676 break;
3677 }
3678 default:
3679 AssertFailed();
3680 }
3681
3682 CheckComRCBreakRC (rc);
3683 }
3684
3685 CheckComRCBreakRC (rc);
3686 }
3687
3688 return rc;
3689}
3690
3691/**
3692 * Loads all hard disks from the given <HardDisks> node.
3693 * Note that all loaded hard disks register themselves within this VirtualBox.
3694 *
3695 * @param aNode <HardDisks> node.
3696 *
3697 * @note Can be called only from #init().
3698 * @note Doesn't lock anything.
3699 */
3700HRESULT VirtualBox::loadHardDisks (const settings::Key &aNode)
3701{
3702 using namespace settings;
3703
3704 AutoCaller autoCaller (this);
3705 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3706
3707 AssertReturn (!aNode.isNull(), E_INVALIDARG);
3708
3709 HRESULT rc = S_OK;
3710
3711 Key::List disks = aNode.keys ("HardDisk");
3712 for (Key::List::const_iterator it = disks.begin();
3713 it != disks.end(); ++ it)
3714 {
3715 Key storageNode;
3716
3717 /* detect the type of the hard disk (either one of VirtualDiskImage,
3718 * ISCSIHardDisk, VMDKImage or HCustomHardDisk) */
3719
3720 do
3721 {
3722 storageNode = (*it).findKey ("VirtualDiskImage");
3723 if (!storageNode.isNull())
3724 {
3725 ComObjPtr <HVirtualDiskImage> vdi;
3726 vdi.createObject();
3727 rc = vdi->init (this, NULL, (*it), storageNode);
3728 break;
3729 }
3730
3731 storageNode = (*it).findKey ("ISCSIHardDisk");
3732 if (!storageNode.isNull())
3733 {
3734 ComObjPtr <HISCSIHardDisk> iscsi;
3735 iscsi.createObject();
3736 rc = iscsi->init (this, (*it), storageNode);
3737 break;
3738 }
3739
3740 storageNode = (*it).findKey ("VMDKImage");
3741 if (!storageNode.isNull())
3742 {
3743 ComObjPtr <HVMDKImage> vmdk;
3744 vmdk.createObject();
3745 rc = vmdk->init (this, NULL, (*it), storageNode);
3746 break;
3747 }
3748
3749 storageNode = (*it).findKey ("CustomHardDisk");
3750 if (!storageNode.isNull())
3751 {
3752 ComObjPtr <HCustomHardDisk> custom;
3753 custom.createObject();
3754 rc = custom->init (this, NULL, (*it), storageNode);
3755 break;
3756 }
3757
3758 storageNode = (*it).findKey ("VHDImage");
3759 if (!storageNode.isNull())
3760 {
3761 ComObjPtr <HVHDImage> vhd;
3762 vhd.createObject();
3763 rc = vhd->init (this, NULL, (*it), storageNode);
3764 break;
3765 }
3766
3767 ComAssertMsgFailedBreak (("No valid hard disk storage node!\n"),
3768 rc = E_FAIL);
3769 }
3770 while (0);
3771 }
3772
3773 return rc;
3774}
3775
3776/**
3777 * Helper function to write out the configuration tree.
3778 *
3779 * @note Locks this object for writing and child objects for reading/writing!
3780 */
3781HRESULT VirtualBox::saveSettings()
3782{
3783 AutoCaller autoCaller (this);
3784 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3785
3786 AssertReturn (!!mData.mCfgFile.mName, E_FAIL);
3787
3788 HRESULT rc = S_OK;
3789
3790 /* serialize file access (prevent concurrent reads and writes) */
3791 AutoWriteLock alock (this);
3792
3793 try
3794 {
3795 using namespace settings;
3796
3797 /* load the settings file */
3798 File file (mData.mCfgFile.mHandle, Utf8Str (mData.mCfgFile.mName));
3799 XmlTreeBackend tree;
3800
3801 rc = VirtualBox::loadSettingsTree_ForUpdate (tree, file);
3802 CheckComRCThrowRC (rc);
3803
3804 Key global = tree.rootKey().createKey ("Global");
3805
3806 /* machines */
3807 {
3808 /* first, delete the entire machine registry */
3809 Key registryNode = global.findKey ("MachineRegistry");
3810 if (!registryNode.isNull())
3811 registryNode.zap();
3812 /* then, recreate it */
3813 registryNode = global.createKey ("MachineRegistry");
3814
3815 /* write out the machines */
3816 for (MachineList::iterator it = mData.mMachines.begin();
3817 it != mData.mMachines.end();
3818 ++ it)
3819 {
3820 Key entryNode = registryNode.appendKey ("MachineEntry");
3821 rc = (*it)->saveRegistryEntry (entryNode);
3822 CheckComRCThrowRC (rc);
3823 }
3824 }
3825
3826 /* disk images */
3827 {
3828 /* first, delete the entire disk image registr */
3829 Key registryNode = global.findKey ("DiskRegistry");
3830 if (!registryNode.isNull())
3831 registryNode.zap();
3832 /* then, recreate it */
3833 registryNode = global.createKey ("DiskRegistry");
3834
3835 /* write out the hard disks */
3836 {
3837 Key imagesNode = registryNode.createKey ("HardDisks");
3838 rc = saveHardDisks (imagesNode);
3839 CheckComRCThrowRC (rc);
3840 }
3841
3842 /* write out the CD/DVD images */
3843 {
3844 Key imagesNode = registryNode.createKey ("DVDImages");
3845
3846 for (DVDImageList::iterator it = mData.mDVDImages.begin();
3847 it != mData.mDVDImages.end();
3848 ++ it)
3849 {
3850 ComObjPtr <DVDImage> dvd = *it;
3851 /* no need to lock: fields are constant */
3852 Key imageNode = imagesNode.appendKey ("Image");
3853 imageNode.setValue <Guid> ("uuid", dvd->id());
3854 imageNode.setValue <Bstr> ("src", dvd->filePath());
3855 }
3856 }
3857
3858 /* write out the floppy images */
3859 {
3860 Key imagesNode = registryNode.createKey ("FloppyImages");
3861
3862 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3863 it != mData.mFloppyImages.end();
3864 ++ it)
3865 {
3866 ComObjPtr <FloppyImage> fd = *it;
3867 /* no need to lock: fields are constant */
3868 Key imageNode = imagesNode.appendKey ("Image");
3869 imageNode.setValue <Guid> ("uuid", fd->id());
3870 imageNode.setValue <Bstr> ("src", fd->filePath());
3871 }
3872 }
3873 }
3874
3875 /* host data (USB filters) */
3876 rc = mData.mHost->saveSettings (global);
3877 CheckComRCThrowRC (rc);
3878
3879 rc = mData.mSystemProperties->saveSettings (global);
3880 CheckComRCThrowRC (rc);
3881
3882 /* save the settings on success */
3883 rc = VirtualBox::saveSettingsTree (tree, file,
3884 mData.mSettingsFileVersion);
3885 CheckComRCThrowRC (rc);
3886 }
3887 catch (HRESULT err)
3888 {
3889 /* we assume that error info is set by the thrower */
3890 rc = err;
3891 }
3892 catch (...)
3893 {
3894 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3895 }
3896
3897 return rc;
3898}
3899
3900/**
3901 * Saves all hard disks to the given <HardDisks> node.
3902 *
3903 * @param aNode <HardDisks> node.
3904 *
3905 * @note Locks this object for reding.
3906 */
3907HRESULT VirtualBox::saveHardDisks (settings::Key &aNode)
3908{
3909 using namespace settings;
3910
3911 AssertReturn (!aNode.isNull(), E_INVALIDARG);
3912
3913 HRESULT rc = S_OK;
3914
3915 AutoReadLock alock (this);
3916
3917 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
3918 it != mData.mHardDisks.end();
3919 ++ it)
3920 {
3921 ComObjPtr <HardDisk> hd = *it;
3922 AutoReadLock hdLock (hd);
3923
3924 Key hdNode = aNode.appendKey ("HardDisk");
3925
3926 switch (hd->storageType())
3927 {
3928 case HardDiskStorageType_VirtualDiskImage:
3929 {
3930 Key storageNode = hdNode.createKey ("VirtualDiskImage");
3931 rc = hd->saveSettings (hdNode, storageNode);
3932 break;
3933 }
3934
3935 case HardDiskStorageType_ISCSIHardDisk:
3936 {
3937 Key storageNode = hdNode.createKey ("ISCSIHardDisk");
3938 rc = hd->saveSettings (hdNode, storageNode);
3939 break;
3940 }
3941
3942 case HardDiskStorageType_VMDKImage:
3943 {
3944 Key storageNode = hdNode.createKey ("VMDKImage");
3945 rc = hd->saveSettings (hdNode, storageNode);
3946 break;
3947 }
3948 case HardDiskStorageType_CustomHardDisk:
3949 {
3950 Key storageNode = hdNode.createKey ("CustomHardDisk");
3951 rc = hd->saveSettings (hdNode, storageNode);
3952 break;
3953 }
3954
3955 case HardDiskStorageType_VHDImage:
3956 {
3957 Key storageNode = hdNode.createKey ("VHDImage");
3958 rc = hd->saveSettings (hdNode, storageNode);
3959 break;
3960 }
3961 }
3962
3963 CheckComRCBreakRC (rc);
3964 }
3965
3966 return rc;
3967}
3968
3969/**
3970 * Helper to register the machine.
3971 *
3972 * When called during VirtualBox startup, adds the given machine to the
3973 * collection of registered machines. Otherwise tries to mark the machine
3974 * as registered, and, if succeeded, adds it to the collection and
3975 * saves global settings.
3976 *
3977 * @note The caller must have added itself as a caller of the @a aMachine
3978 * object if calls this method not on VirtualBox startup.
3979 *
3980 * @param aMachine machine to register
3981 *
3982 * @note Locks objects!
3983 */
3984HRESULT VirtualBox::registerMachine (Machine *aMachine)
3985{
3986 ComAssertRet (aMachine, E_INVALIDARG);
3987
3988 AutoCaller autoCaller (this);
3989 CheckComRCReturnRC (autoCaller.rc());
3990
3991 AutoWriteLock alock (this);
3992
3993 HRESULT rc = S_OK;
3994
3995 {
3996 ComObjPtr <Machine> m;
3997 rc = findMachine (aMachine->uuid(), false /* aDoSetError */, &m);
3998 if (SUCCEEDED (rc))
3999 {
4000 /* sanity */
4001 AutoLimitedCaller machCaller (m);
4002 AssertComRC (machCaller.rc());
4003
4004 return setError (E_INVALIDARG,
4005 tr ("Registered machine with UUID {%Vuuid} ('%ls') already exists"),
4006 aMachine->uuid().raw(), m->settingsFileFull().raw());
4007 }
4008
4009 ComAssertRet (rc == E_INVALIDARG, rc);
4010 rc = S_OK;
4011 }
4012
4013 if (autoCaller.state() != InInit)
4014 {
4015 /* Machine::trySetRegistered() will commit and save machine settings */
4016 rc = aMachine->trySetRegistered (TRUE);
4017 CheckComRCReturnRC (rc);
4018 }
4019
4020 /* add to the collection of registered machines */
4021 mData.mMachines.push_back (aMachine);
4022
4023 if (autoCaller.state() != InInit)
4024 rc = saveSettings();
4025
4026 return rc;
4027}
4028
4029/**
4030 * Helper to register the hard disk.
4031 *
4032 * @param aHardDisk object to register
4033 * @param aFlags one of RHD_* values
4034 *
4035 * @note Locks objects!
4036 */
4037HRESULT VirtualBox::registerHardDisk (HardDisk *aHardDisk, RHD_Flags aFlags)
4038{
4039 ComAssertRet (aHardDisk, E_INVALIDARG);
4040
4041 AutoCaller autoCaller (this);
4042 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4043
4044 AutoWriteLock alock (this);
4045
4046 HRESULT rc = checkMediaForConflicts (aHardDisk, NULL, NULL);
4047 CheckComRCReturnRC (rc);
4048
4049 /* mark the hard disk as registered only when registration is external */
4050 if (aFlags == RHD_External)
4051 {
4052 rc = aHardDisk->trySetRegistered (TRUE);
4053 CheckComRCReturnRC (rc);
4054 }
4055
4056 if (!aHardDisk->parent())
4057 {
4058 /* add to the collection of top-level images */
4059 mData.mHardDisks.push_back (aHardDisk);
4060 }
4061
4062 /* insert to the map of hard disks */
4063 mData.mHardDiskMap
4064 .insert (HardDiskMap::value_type (aHardDisk->id(), aHardDisk));
4065
4066 /* save global config file if not on startup */
4067 /// @todo (dmik) optimize later to save only the <HardDisks> node
4068 if (aFlags != RHD_OnStartUp)
4069 rc = saveSettings();
4070
4071 return rc;
4072}
4073
4074/**
4075 * Helper to unregister the hard disk.
4076 *
4077 * If the hard disk is a differencing hard disk and if the unregistration
4078 * succeeds, the hard disk image is deleted and the object is uninitialized.
4079 *
4080 * @param aHardDisk hard disk to unregister
4081 *
4082 * @note Locks objects!
4083 */
4084HRESULT VirtualBox::unregisterHardDisk (HardDisk *aHardDisk)
4085{
4086 AssertReturn (aHardDisk, E_INVALIDARG);
4087
4088 AutoCaller autoCaller (this);
4089 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4090
4091 LogFlowThisFunc (("image='%ls'\n", aHardDisk->toString().raw()));
4092
4093 AutoWriteLock alock (this);
4094
4095 /* Lock the hard disk to ensure nobody registers it again before we delete
4096 * the differencing image (sanity check actually -- should never happen). */
4097 AutoWriteLock hdLock (aHardDisk);
4098
4099 /* try to unregister */
4100 HRESULT rc = aHardDisk->trySetRegistered (FALSE);
4101 CheckComRCReturnRC (rc);
4102
4103 /* remove from the map of hard disks */
4104 mData.mHardDiskMap.erase (aHardDisk->id());
4105
4106 if (!aHardDisk->parent())
4107 {
4108 /* non-differencing hard disk:
4109 * remove from the collection of top-level hard disks */
4110 mData.mHardDisks.remove (aHardDisk);
4111 }
4112 else
4113 {
4114 Assert (aHardDisk->isDifferencing());
4115
4116 /* differencing hard disk: delete and uninitialize
4117 *
4118 * Note that we ignore errors because this operation may be a result
4119 * of unregistering a missing (inaccessible) differencing hard disk
4120 * in which case a failure to implicitly delete the image will not
4121 * prevent it from being unregistered and therefore should not pop up
4122 * on the caller's side. */
4123 rc = aHardDisk->asVDI()->deleteImage (true /* aIgnoreErrors*/);
4124 aHardDisk->uninit();
4125 }
4126
4127 /* save the global config file anyway (already unregistered) */
4128 /// @todo (dmik) optimize later to save only the <HardDisks> node
4129 HRESULT rc2 = saveSettings();
4130 if (SUCCEEDED (rc))
4131 rc = rc2;
4132
4133 return rc;
4134}
4135
4136/**
4137 * Helper to unregister the differencing hard disk image.
4138 * Resets machine ID of the hard disk (to let the unregistration succeed)
4139 * and then calls #unregisterHardDisk().
4140 *
4141 * @param aHardDisk differencing hard disk image to unregister
4142 *
4143 * @note Locks objects!
4144 */
4145HRESULT VirtualBox::unregisterDiffHardDisk (HardDisk *aHardDisk)
4146{
4147 AssertReturn (aHardDisk, E_INVALIDARG);
4148
4149 AutoCaller autoCaller (this);
4150 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4151
4152 AutoWriteLock alock (this);
4153
4154 /*
4155 * Note: it's safe to lock aHardDisk here because the same object
4156 * will be locked by #unregisterHardDisk().
4157 */
4158 AutoWriteLock hdLock (aHardDisk);
4159
4160 AssertReturn (aHardDisk->isDifferencing(), E_INVALIDARG);
4161
4162 /*
4163 * deassociate the machine from the hard disk
4164 * (otherwise trySetRegistered() will definitely fail)
4165 */
4166 aHardDisk->setMachineId (Guid());
4167
4168 return unregisterHardDisk (aHardDisk);
4169}
4170
4171
4172/**
4173 * Helper to update the global settings file when the name of some machine
4174 * changes so that file and directory renaming occurs. This method ensures
4175 * that all affected paths in the disk registry are properly updated.
4176 *
4177 * @param aOldPath old path (full)
4178 * @param aNewPath new path (full)
4179 *
4180 * @note Locks this object + DVD, Floppy and HardDisk children for writing.
4181 */
4182HRESULT VirtualBox::updateSettings (const char *aOldPath, const char *aNewPath)
4183{
4184 LogFlowThisFunc (("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
4185
4186 AssertReturn (aOldPath, E_INVALIDARG);
4187 AssertReturn (aNewPath, E_INVALIDARG);
4188
4189 AutoCaller autoCaller (this);
4190 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4191
4192 AutoWriteLock alock (this);
4193
4194 size_t oldPathLen = strlen (aOldPath);
4195
4196 /* check DVD paths */
4197 for (DVDImageList::iterator it = mData.mDVDImages.begin();
4198 it != mData.mDVDImages.end();
4199 ++ it)
4200 {
4201 ComObjPtr <DVDImage> image = *it;
4202
4203 /* no need to lock: fields are constant */
4204 Utf8Str path = image->filePathFull();
4205 LogFlowThisFunc (("DVD.fullPath={%s}\n", path.raw()));
4206
4207 if (RTPathStartsWith (path, aOldPath))
4208 {
4209 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
4210 path.raw() + oldPathLen);
4211 path = newPath;
4212 calculateRelativePath (path, path);
4213 image->updatePath (newPath, path);
4214
4215 LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
4216 newPath.raw(), path.raw()));
4217 }
4218 }
4219
4220 /* check Floppy paths */
4221 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
4222 it != mData.mFloppyImages.end();
4223 ++ it)
4224 {
4225 ComObjPtr <FloppyImage> image = *it;
4226
4227 /* no need to lock: fields are constant */
4228 Utf8Str path = image->filePathFull();
4229 LogFlowThisFunc (("Floppy.fullPath={%s}\n", path.raw()));
4230
4231 if (RTPathStartsWith (path, aOldPath))
4232 {
4233 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
4234 path.raw() + oldPathLen);
4235 path = newPath;
4236 calculateRelativePath (path, path);
4237 image->updatePath (newPath, path);
4238
4239 LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
4240 newPath.raw(), path.raw()));
4241 }
4242 }
4243
4244 /* check HardDisk paths */
4245 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
4246 it != mData.mHardDisks.end();
4247 ++ it)
4248 {
4249 (*it)->updatePaths (aOldPath, aNewPath);
4250 }
4251
4252 HRESULT rc = saveSettings();
4253
4254 return rc;
4255}
4256
4257/**
4258 * Helper method to load the setting tree and turn expected exceptions into
4259 * COM errors, according to arguments.
4260 *
4261 * Note that this method will not catch unexpected errors so it may still
4262 * throw something.
4263 *
4264 * @param aTree Tree to load into settings.
4265 * @param aFile File to load settings from.
4266 * @param aValidate @c @true to enable tree validation.
4267 * @param aCatchLoadErrors @c true to catch exceptions caused by file
4268 * access or validation errors.
4269 * @param aAddDefaults @c true to cause the substitution of default
4270 * values for for missing attributes that have
4271 * defaults in the XML schema.
4272 * @param aFormatVersion Where to store the current format version of the
4273 * loaded settings tree (optional, may be NULL).
4274 */
4275/* static */
4276HRESULT VirtualBox::loadSettingsTree (settings::XmlTreeBackend &aTree,
4277 settings::File &aFile,
4278 bool aValidate,
4279 bool aCatchLoadErrors,
4280 bool aAddDefaults,
4281 Utf8Str *aFormatVersion /* = NULL */)
4282{
4283 using namespace settings;
4284
4285 try
4286 {
4287 SettingsTreeHelper helper = SettingsTreeHelper();
4288
4289 aTree.setInputResolver (helper);
4290 aTree.setAutoConverter (helper);
4291
4292 aTree.read (aFile, aValidate ? VBOX_XML_SCHEMA : NULL,
4293 aAddDefaults ? XmlTreeBackend::Read_AddDefaults : 0);
4294
4295 aTree.resetAutoConverter();
4296 aTree.resetInputResolver();
4297
4298 /* on success, memorize the current settings file version or set it to
4299 * the most recent version if no settings conversion took place. Note
4300 * that it's not necessary to do it every time we load the settings file
4301 * (i.e. only loadSettingsTree_FirstTime() passes a non-NULL
4302 * aFormatVersion value) because currently we keep the settings
4303 * files locked so that the only legal way to change the format version
4304 * while VirtualBox is running is saveSettingsTree(). */
4305 if (aFormatVersion != NULL)
4306 {
4307 *aFormatVersion = aTree.oldVersion();
4308 if (aFormatVersion->isNull())
4309 *aFormatVersion = VBOX_XML_VERSION_FULL;
4310 }
4311 }
4312 catch (const EIPRTFailure &err)
4313 {
4314 if (!aCatchLoadErrors)
4315 throw;
4316
4317 return setError (E_FAIL,
4318 tr ("Could not load the settings file '%s' (%Vrc)"),
4319 aFile.uri(), err.rc());
4320 }
4321 catch (const XmlTreeBackend::Error &err)
4322 {
4323 Assert (err.what() != NULL);
4324
4325 if (!aCatchLoadErrors)
4326 throw;
4327
4328 return setError (E_FAIL,
4329 tr ("Could not load the settings file '%s'.\n%s"),
4330 aFile.uri(),
4331 err.what() ? err.what() : "Unknown error");
4332 }
4333
4334 return S_OK;
4335}
4336
4337/**
4338 * Helper method to save the settings tree and turn expected exceptions to COM
4339 * errors.
4340 *
4341 * Note that this method will not catch unexpected errors so it may still
4342 * throw something.
4343 *
4344 * @param aTree Tree to save.
4345 * @param aFile File to save the tree to.
4346 * @param aFormatVersion Where to store the (recent) format version of the
4347 * saved settings tree on success.
4348 */
4349/* static */
4350HRESULT VirtualBox::saveSettingsTree (settings::TreeBackend &aTree,
4351 settings::File &aFile,
4352 Utf8Str &aFormatVersion)
4353{
4354 using namespace settings;
4355
4356 try
4357 {
4358 aTree.write (aFile);
4359
4360 /* set the current settings file version to the most recent version on
4361 * success. See also VirtualBox::loadSettingsTree(). */
4362 if (aFormatVersion != VBOX_XML_VERSION_FULL)
4363 aFormatVersion = VBOX_XML_VERSION_FULL;
4364 }
4365 catch (const EIPRTFailure &err)
4366 {
4367 /* this is the only expected exception for now */
4368 return setError (E_FAIL,
4369 tr ("Could not save the settings file '%s' (%Vrc)"),
4370 aFile.uri(), err.rc());
4371 }
4372
4373 return S_OK;
4374}
4375
4376/**
4377 * Creates a backup copy of the given settings file by suffixing it with the
4378 * supplied version format string and optionally with numbers from .0 to .9
4379 * if the backup file already exists.
4380 *
4381 * @param aFileName Orignal settings file name.
4382 * @param aOldFormat Version of the original format.
4383 * @param aBakFileName File name of the created backup copy (only on success).
4384 */
4385/* static */
4386HRESULT VirtualBox::backupSettingsFile (const Bstr &aFileName,
4387 const Utf8Str &aOldFormat,
4388 Bstr &aBakFileName)
4389{
4390 Utf8Str of = aFileName;
4391 Utf8Str nf = Utf8StrFmt ("%s.%s.bak", of.raw(), aOldFormat.raw());
4392
4393 int vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
4394 NULL, NULL);
4395
4396 /* try progressive suffix from .0 to .9 on failure */
4397 if (vrc == VERR_ALREADY_EXISTS)
4398 {
4399 Utf8Str tmp = nf;
4400 for (int i = 0; i <= 9 && RT_FAILURE (vrc); ++ i)
4401 {
4402 nf = Utf8StrFmt ("%s.%d", tmp.raw(), i);
4403 vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
4404 NULL, NULL);
4405 }
4406 }
4407
4408 if (RT_FAILURE (vrc))
4409 return setError (E_FAIL,
4410 tr ("Could not copy the settings file '%s' to '%s' (%Vrc)"),
4411 of.raw(), nf.raw(), vrc);
4412
4413 aBakFileName = nf;
4414
4415 return S_OK;
4416}
4417
4418/**
4419 * Handles unexpected exceptions by turning them into COM errors in release
4420 * builds or by hitting a breakpoint in the release builds.
4421 *
4422 * Usage pattern:
4423 * @code
4424 try
4425 {
4426 // ...
4427 }
4428 catch (LaLalA)
4429 {
4430 // ...
4431 }
4432 catch (...)
4433 {
4434 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
4435 }
4436 * @endcode
4437 *
4438 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
4439 */
4440/* static */
4441HRESULT VirtualBox::handleUnexpectedExceptions (RT_SRC_POS_DECL)
4442{
4443 try
4444 {
4445 /* rethrow the current exception */
4446 throw;
4447 }
4448 catch (const std::exception &err)
4449 {
4450 ComAssertMsgFailedPos (("Unexpected exception '%s' (%s)\n",
4451 typeid (err).name(), err.what()),
4452 pszFile, iLine, pszFunction);
4453 return E_FAIL;
4454 }
4455 catch (...)
4456 {
4457 ComAssertMsgFailedPos (("Unknown exception\n"),
4458 pszFile, iLine, pszFunction);
4459 return E_FAIL;
4460 }
4461
4462 /* should not get here */
4463 AssertFailed();
4464 return E_FAIL;
4465}
4466
4467/**
4468 * Helper to register the DVD image.
4469 *
4470 * @param aImage object to register
4471 * @param aOnStartUp whether this method called during VirtualBox init or not
4472 *
4473 * @return COM status code
4474 *
4475 * @note Locks objects!
4476 */
4477HRESULT VirtualBox::registerDVDImage (DVDImage *aImage, bool aOnStartUp)
4478{
4479 AssertReturn (aImage, E_INVALIDARG);
4480
4481 AutoCaller autoCaller (this);
4482 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4483
4484 AutoWriteLock alock (this);
4485
4486 HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
4487 aImage->filePathFull());
4488 CheckComRCReturnRC (rc);
4489
4490 /* add to the collection */
4491 mData.mDVDImages.push_back (aImage);
4492
4493 /* save global config file if we're supposed to */
4494 if (!aOnStartUp)
4495 rc = saveSettings();
4496
4497 return rc;
4498}
4499
4500/**
4501 * Helper to register the floppy image.
4502 *
4503 * @param aImage object to register
4504 * @param aOnStartUp whether this method called during VirtualBox init or not
4505 *
4506 * @return COM status code
4507 *
4508 * @note Locks objects!
4509 */
4510HRESULT VirtualBox::registerFloppyImage (FloppyImage *aImage, bool aOnStartUp)
4511{
4512 AssertReturn (aImage, E_INVALIDARG);
4513
4514 AutoCaller autoCaller (this);
4515 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4516
4517 AutoWriteLock alock (this);
4518
4519 HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
4520 aImage->filePathFull());
4521 CheckComRCReturnRC (rc);
4522
4523 /* add to the collection */
4524 mData.mFloppyImages.push_back (aImage);
4525
4526 /* save global config file if we're supposed to */
4527 if (!aOnStartUp)
4528 rc = saveSettings();
4529
4530 return rc;
4531}
4532
4533/**
4534 * Helper to lock the VirtualBox configuration for write access.
4535 *
4536 * @note This method is not thread safe (must be called only from #init()
4537 * or #uninit()).
4538 *
4539 * @note If the configuration file is not found, the method returns
4540 * S_OK, but subsequent #isConfigLocked() will return FALSE. This is used
4541 * in some places to determine the (valid) situation when no config file
4542 * exists yet, and therefore a new one should be created from scatch.
4543 */
4544HRESULT VirtualBox::lockConfig()
4545{
4546 AutoCaller autoCaller (this);
4547 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4548 AssertReturn (autoCaller.state() == InInit, E_FAIL);
4549
4550 HRESULT rc = S_OK;
4551
4552 Assert (!isConfigLocked());
4553 if (!isConfigLocked())
4554 {
4555 /* open the associated config file */
4556 int vrc = RTFileOpen (&mData.mCfgFile.mHandle,
4557 Utf8Str (mData.mCfgFile.mName),
4558 RTFILE_O_READWRITE | RTFILE_O_OPEN |
4559 RTFILE_O_DENY_WRITE);
4560 if (VBOX_FAILURE (vrc))
4561 {
4562 mData.mCfgFile.mHandle = NIL_RTFILE;
4563
4564 /*
4565 * It is ok if the file is not found, it will be created by
4566 * init(). Otherwise return an error.
4567 */
4568 if (vrc != VERR_FILE_NOT_FOUND)
4569 rc = setError (E_FAIL,
4570 tr ("Could not lock the settings file '%ls' (%Vrc)"),
4571 mData.mCfgFile.mName.raw(), vrc);
4572 }
4573
4574 LogFlowThisFunc (("mCfgFile.mName='%ls', mCfgFile.mHandle=%d, rc=%08X\n",
4575 mData.mCfgFile.mName.raw(), mData.mCfgFile.mHandle, rc));
4576 }
4577
4578 return rc;
4579}
4580
4581/**
4582 * Helper to unlock the VirtualBox configuration from write access.
4583 *
4584 * @note This method is not thread safe (must be called only from #init()
4585 * or #uninit()).
4586 */
4587HRESULT VirtualBox::unlockConfig()
4588{
4589 AutoCaller autoCaller (this);
4590 AssertComRCReturn (autoCaller.rc(), E_FAIL);
4591 AssertReturn (autoCaller.state() == InUninit, E_FAIL);
4592
4593 HRESULT rc = S_OK;
4594
4595 if (isConfigLocked())
4596 {
4597 RTFileFlush (mData.mCfgFile.mHandle);
4598 RTFileClose (mData.mCfgFile.mHandle);
4599 /** @todo flush the directory too. */
4600 mData.mCfgFile.mHandle = NIL_RTFILE;
4601 LogFlowThisFunc (("\n"));
4602 }
4603
4604 return rc;
4605}
4606
4607/**
4608 * Thread function that watches the termination of all client processes
4609 * that have opened sessions using IVirtualBox::OpenSession()
4610 */
4611// static
4612DECLCALLBACK(int) VirtualBox::ClientWatcher (RTTHREAD thread, void *pvUser)
4613{
4614 LogFlowFuncEnter();
4615
4616 VirtualBox *that = (VirtualBox *) pvUser;
4617 Assert (that);
4618
4619 SessionMachineVector machines;
4620 size_t cnt = 0;
4621
4622#if defined(RT_OS_WINDOWS)
4623
4624 HRESULT hrc = CoInitializeEx (NULL,
4625 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
4626 COINIT_SPEED_OVER_MEMORY);
4627 AssertComRC (hrc);
4628
4629 /// @todo (dmik) processes reaping!
4630
4631 HANDLE *handles = new HANDLE [1];
4632 handles [0] = that->mWatcherData.mUpdateReq;
4633
4634 do
4635 {
4636 AutoCaller autoCaller (that);
4637 /* VirtualBox has been early uninitialized, terminate */
4638 if (!autoCaller.isOk())
4639 break;
4640
4641 do
4642 {
4643 /* release the caller to let uninit() ever proceed */
4644 autoCaller.release();
4645
4646 DWORD rc = ::WaitForMultipleObjects (cnt + 1, handles, FALSE, INFINITE);
4647
4648 /*
4649 * Restore the caller before using VirtualBox. If it fails, this
4650 * means VirtualBox is being uninitialized and we must terminate.
4651 */
4652 autoCaller.add();
4653 if (!autoCaller.isOk())
4654 break;
4655
4656 bool update = false;
4657 if (rc == WAIT_OBJECT_0)
4658 {
4659 /* update event is signaled */
4660 update = true;
4661 }
4662 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4663 {
4664 /* machine mutex is released */
4665 (machines [rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4666 update = true;
4667 }
4668 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4669 {
4670 /* machine mutex is abandoned due to client process termination */
4671 (machines [rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4672 update = true;
4673 }
4674 if (update)
4675 {
4676 /* obtain a new set of opened machines */
4677 that->getOpenedMachines (machines);
4678 cnt = machines.size();
4679 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4680 AssertMsg ((cnt + 1) <= MAXIMUM_WAIT_OBJECTS,
4681 ("MAXIMUM_WAIT_OBJECTS reached"));
4682 /* renew the set of event handles */
4683 delete [] handles;
4684 handles = new HANDLE [cnt + 1];
4685 handles [0] = that->mWatcherData.mUpdateReq;
4686 for (size_t i = 0; i < cnt; ++ i)
4687 handles [i + 1] = (machines [i])->ipcSem();
4688 }
4689 }
4690 while (true);
4691 }
4692 while (0);
4693
4694 /* delete the set of event handles */
4695 delete [] handles;
4696
4697 /* delete the set of opened machines if any */
4698 machines.clear();
4699
4700 ::CoUninitialize();
4701
4702#elif defined (RT_OS_OS2)
4703
4704 /// @todo (dmik) processes reaping!
4705
4706 /* according to PMREF, 64 is the maximum for the muxwait list */
4707 SEMRECORD handles [64];
4708
4709 HMUX muxSem = NULLHANDLE;
4710
4711 do
4712 {
4713 AutoCaller autoCaller (that);
4714 /* VirtualBox has been early uninitialized, terminate */
4715 if (!autoCaller.isOk())
4716 break;
4717
4718 do
4719 {
4720 /* release the caller to let uninit() ever proceed */
4721 autoCaller.release();
4722
4723 int vrc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4724
4725 /*
4726 * Restore the caller before using VirtualBox. If it fails, this
4727 * means VirtualBox is being uninitialized and we must terminate.
4728 */
4729 autoCaller.add();
4730 if (!autoCaller.isOk())
4731 break;
4732
4733 bool update = false;
4734
4735 if (VBOX_SUCCESS (vrc))
4736 {
4737 /* update event is signaled */
4738 update = true;
4739 }
4740 else
4741 {
4742 AssertMsg (vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4743 ("RTSemEventWait returned %Vrc\n", vrc));
4744
4745 /* are there any mutexes? */
4746 if (cnt > 0)
4747 {
4748 /* figure out what's going on with machines */
4749
4750 unsigned long semId = 0;
4751 APIRET arc = ::DosWaitMuxWaitSem (muxSem,
4752 SEM_IMMEDIATE_RETURN, &semId);
4753
4754 if (arc == NO_ERROR)
4755 {
4756 /* machine mutex is normally released */
4757 Assert (semId >= 0 && semId < cnt);
4758 if (semId >= 0 && semId < cnt)
4759 {
4760#ifdef DEBUG
4761 {
4762 AutoReadLock machieLock (machines [semId]);
4763 LogFlowFunc (("released mutex: machine='%ls'\n",
4764 machines [semId]->name().raw()));
4765 }
4766#endif
4767 machines [semId]->checkForDeath();
4768 }
4769 update = true;
4770 }
4771 else if (arc == ERROR_SEM_OWNER_DIED)
4772 {
4773 /* machine mutex is abandoned due to client process
4774 * termination; find which mutex is in the Owner Died
4775 * state */
4776 for (size_t i = 0; i < cnt; ++ i)
4777 {
4778 PID pid; TID tid;
4779 unsigned long reqCnt;
4780 arc = DosQueryMutexSem ((HMTX) handles [i].hsemCur, &pid,
4781 &tid, &reqCnt);
4782 if (arc == ERROR_SEM_OWNER_DIED)
4783 {
4784 /* close the dead mutex as asked by PMREF */
4785 ::DosCloseMutexSem ((HMTX) handles [i].hsemCur);
4786
4787 Assert (i >= 0 && i < cnt);
4788 if (i >= 0 && i < cnt)
4789 {
4790#ifdef DEBUG
4791 {
4792 AutoReadLock machieLock (machines [semId]);
4793 LogFlowFunc (("mutex owner dead: machine='%ls'\n",
4794 machines [i]->name().raw()));
4795 }
4796#endif
4797 machines [i]->checkForDeath();
4798 }
4799 }
4800 }
4801 update = true;
4802 }
4803 else
4804 AssertMsg (arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4805 ("DosWaitMuxWaitSem returned %d\n", arc));
4806 }
4807 }
4808
4809 if (update)
4810 {
4811 /* close the old muxsem */
4812 if (muxSem != NULLHANDLE)
4813 ::DosCloseMuxWaitSem (muxSem);
4814 /* obtain a new set of opened machines */
4815 that->getOpenedMachines (machines);
4816 cnt = machines.size();
4817 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4818 /// @todo use several muxwait sems if cnt is > 64
4819 AssertMsg (cnt <= 64 /* according to PMREF */,
4820 ("maximum of 64 mutex semaphores reached (%d)", cnt));
4821 if (cnt > 64)
4822 cnt = 64;
4823 if (cnt > 0)
4824 {
4825 /* renew the set of event handles */
4826 for (size_t i = 0; i < cnt; ++ i)
4827 {
4828 handles [i].hsemCur = (HSEM) machines [i]->ipcSem();
4829 handles [i].ulUser = i;
4830 }
4831 /* create a new muxsem */
4832 APIRET arc = ::DosCreateMuxWaitSem (NULL, &muxSem, cnt, handles,
4833 DCMW_WAIT_ANY);
4834 AssertMsg (arc == NO_ERROR,
4835 ("DosCreateMuxWaitSem returned %d\n", arc)); NOREF(arc);
4836 }
4837 }
4838 }
4839 while (true);
4840 }
4841 while (0);
4842
4843 /* close the muxsem */
4844 if (muxSem != NULLHANDLE)
4845 ::DosCloseMuxWaitSem (muxSem);
4846
4847 /* delete the set of opened machines if any */
4848 machines.clear();
4849
4850#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4851
4852 bool need_update = false;
4853
4854 do
4855 {
4856 AutoCaller autoCaller (that);
4857 if (!autoCaller.isOk())
4858 break;
4859
4860 do
4861 {
4862 /* release the caller to let uninit() ever proceed */
4863 autoCaller.release();
4864
4865 int rc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4866
4867 /*
4868 * Restore the caller before using VirtualBox. If it fails, this
4869 * means VirtualBox is being uninitialized and we must terminate.
4870 */
4871 autoCaller.add();
4872 if (!autoCaller.isOk())
4873 break;
4874
4875 if (VBOX_SUCCESS (rc) || need_update)
4876 {
4877 /* VBOX_SUCCESS (rc) means an update event is signaled */
4878
4879 /* obtain a new set of opened machines */
4880 that->getOpenedMachines (machines);
4881 cnt = machines.size();
4882 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4883 }
4884
4885 need_update = false;
4886 for (size_t i = 0; i < cnt; ++ i)
4887 need_update |= (machines [i])->checkForDeath();
4888
4889 /* reap child processes */
4890 {
4891 AutoWriteLock alock (that);
4892 if (that->mWatcherData.mProcesses.size())
4893 {
4894 LogFlowFunc (("UPDATE: child process count = %d\n",
4895 that->mWatcherData.mProcesses.size()));
4896 ClientWatcherData::ProcessList::iterator it =
4897 that->mWatcherData.mProcesses.begin();
4898 while (it != that->mWatcherData.mProcesses.end())
4899 {
4900 RTPROCESS pid = *it;
4901 RTPROCSTATUS status;
4902 int vrc = ::RTProcWait (pid, RTPROCWAIT_FLAGS_NOBLOCK,
4903 &status);
4904 if (vrc == VINF_SUCCESS)
4905 {
4906 LogFlowFunc (("pid %d (%x) was reaped, "
4907 "status=%d, reason=%d\n",
4908 pid, pid, status.iStatus,
4909 status.enmReason));
4910 it = that->mWatcherData.mProcesses.erase (it);
4911 }
4912 else
4913 {
4914 LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Vrc\n",
4915 pid, pid, vrc));
4916 if (vrc != VERR_PROCESS_RUNNING)
4917 {
4918 /* remove the process if it is not already running */
4919 it = that->mWatcherData.mProcesses.erase (it);
4920 }
4921 else
4922 ++ it;
4923 }
4924 }
4925 }
4926 }
4927 }
4928 while (true);
4929 }
4930 while (0);
4931
4932 /* delete the set of opened machines if any */
4933 machines.clear();
4934
4935#else
4936# error "Port me!"
4937#endif
4938
4939 LogFlowFuncLeave();
4940 return 0;
4941}
4942
4943/**
4944 * Thread function that handles custom events posted using #postEvent().
4945 */
4946// static
4947DECLCALLBACK(int) VirtualBox::AsyncEventHandler (RTTHREAD thread, void *pvUser)
4948{
4949 LogFlowFuncEnter();
4950
4951 AssertReturn (pvUser, VERR_INVALID_POINTER);
4952
4953 // create an event queue for the current thread
4954 EventQueue *eventQ = new EventQueue();
4955 AssertReturn (eventQ, VERR_NO_MEMORY);
4956
4957 // return the queue to the one who created this thread
4958 *(static_cast <EventQueue **> (pvUser)) = eventQ;
4959 // signal that we're ready
4960 RTThreadUserSignal (thread);
4961
4962 BOOL ok = TRUE;
4963 Event *event = NULL;
4964
4965 while ((ok = eventQ->waitForEvent (&event)) && event)
4966 eventQ->handleEvent (event);
4967
4968 AssertReturn (ok, VERR_GENERAL_FAILURE);
4969
4970 delete eventQ;
4971
4972 LogFlowFuncLeave();
4973
4974 return 0;
4975}
4976
4977////////////////////////////////////////////////////////////////////////////////
4978
4979/**
4980 * Takes the current list of registered callbacks of the managed VirtualBox
4981 * instance, and calls #handleCallback() for every callback item from the
4982 * list, passing the item as an argument.
4983 *
4984 * @note Locks the managed VirtualBox object for reading but leaves the lock
4985 * before iterating over callbacks and calling their methods.
4986 */
4987void *VirtualBox::CallbackEvent::handler()
4988{
4989 if (mVirtualBox.isNull())
4990 return NULL;
4991
4992 AutoCaller autoCaller (mVirtualBox);
4993 if (!autoCaller.isOk())
4994 {
4995 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
4996 "the callback event is discarded!\n",
4997 autoCaller.state()));
4998 /* We don't need mVirtualBox any more, so release it */
4999 mVirtualBox.setNull();
5000 return NULL;
5001 }
5002
5003 CallbackVector callbacks;
5004 {
5005 /* Make a copy to release the lock before iterating */
5006 AutoReadLock alock (mVirtualBox);
5007 callbacks = CallbackVector (mVirtualBox->mData.mCallbacks.begin(),
5008 mVirtualBox->mData.mCallbacks.end());
5009 /* We don't need mVirtualBox any more, so release it */
5010 mVirtualBox.setNull();
5011 }
5012
5013 for (VirtualBox::CallbackVector::const_iterator it = callbacks.begin();
5014 it != callbacks.end(); ++ it)
5015 handleCallback (*it);
5016
5017 return NULL;
5018}
5019
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