VirtualBox

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

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

Main: report unexpected failures when spawning a remote session

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