VirtualBox

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

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

Main: Performance: Typos, docs, cosmetics.

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