VirtualBox

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

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

Main: Implemented detection of the unexpected spawning session termination for Windows (#3128), optimized for other platforms.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 154.8 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 13457 2008-10-21 17:46:36Z 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/** Event for onGuestPropertyChange() */
2884struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2885{
2886 GuestPropertyEvent (VirtualBox *aVBox, const Guid &aMachineId,
2887 INPTR BSTR aName, INPTR BSTR aValue, INPTR BSTR aFlags)
2888 : CallbackEvent (aVBox), machineId (aMachineId)
2889 , name (aName), value (aValue), flags(aFlags)
2890 {}
2891
2892 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2893 {
2894 LogFlow (("OnGuestPropertyChange: machineId={%Vuuid}, name='%ls', value='%ls', flags='%ls'\n",
2895 machineId.ptr(), name.raw(), value.raw(), flags.raw()));
2896 aCallback->OnGuestPropertyChange (machineId, name, value, flags);
2897 }
2898
2899 Guid machineId;
2900 Bstr name, value, flags;
2901};
2902
2903/**
2904 * @note Doesn't lock any object.
2905 */
2906void VirtualBox::onGuestPropertyChange (const Guid &aMachineId, INPTR BSTR aName,
2907 INPTR BSTR aValue, INPTR BSTR aFlags)
2908{
2909 postEvent (new GuestPropertyEvent (this, aMachineId, aName, aValue, aFlags));
2910}
2911
2912/**
2913 * @note Locks this object for reading.
2914 */
2915ComObjPtr <GuestOSType> VirtualBox::getUnknownOSType()
2916{
2917 ComObjPtr <GuestOSType> type;
2918
2919 AutoCaller autoCaller (this);
2920 AssertComRCReturn (autoCaller.rc(), type);
2921
2922 AutoReadLock alock (this);
2923
2924 /* unknown type must always be the first */
2925 ComAssertRet (mData.mGuestOSTypes.size() > 0, type);
2926
2927 type = mData.mGuestOSTypes.front();
2928 return type;
2929}
2930
2931/**
2932 * Returns the list of opened machines (machines having direct sessionsopened by
2933 * client processes).
2934 *
2935 * @note The returned list contains smart pointers. So, clear it as soon as
2936 * it becomes no more necessary to release instances.
2937 *
2938 * @note It can be possible that a session machine from the list has been
2939 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2940 * whenaccessing unprotected data directly.
2941 *
2942 * @note Locks objects for reading.
2943 */
2944void VirtualBox::getOpenedMachines (SessionMachineVector &aVector)
2945{
2946 AutoCaller autoCaller (this);
2947 AssertComRCReturnVoid (autoCaller.rc());
2948
2949 aVector.clear();
2950
2951 AutoReadLock alock (this);
2952
2953 for (MachineList::iterator it = mData.mMachines.begin();
2954 it != mData.mMachines.end();
2955 ++ it)
2956 {
2957 ComObjPtr <SessionMachine> sm;
2958 if ((*it)->isSessionOpen (sm))
2959 aVector.push_back (sm);
2960 }
2961}
2962
2963/**
2964 * Helper to find machines that use the given DVD image.
2965 *
2966 * This method also checks whether an existing snapshot refers to the given
2967 * image. However, only the machine ID is returned in this case, not IDs of
2968 * individual snapshots.
2969 *
2970 * @param aId Image ID to get usage for.
2971 * @param aUsage Type of the check.
2972 * @param aMachineIDs Where to store the list of machine IDs (can be NULL)
2973 *
2974 * @return @c true if at least one machine or its snapshot found and @c false
2975 * otherwise.
2976 *
2977 * @note For now, we just scan all the machines. We can optimize this later
2978 * if required by adding the corresponding field to DVDImage and requiring all
2979 * IDVDImage instances to be DVDImage objects.
2980 *
2981 * @note Locks objects for reading.
2982 */
2983bool VirtualBox::getDVDImageUsage (const Guid &aId,
2984 ResourceUsage_T aUsage,
2985 Bstr *aMachineIDs)
2986{
2987 AutoCaller autoCaller (this);
2988 AssertComRCReturn (autoCaller.rc(), FALSE);
2989
2990 typedef std::set <Guid> Set;
2991 Set idSet;
2992
2993 {
2994 AutoReadLock alock (this);
2995
2996 for (MachineList::const_iterator mit = mData.mMachines.begin();
2997 mit != mData.mMachines.end();
2998 ++ mit)
2999 {
3000 ComObjPtr <Machine> m = *mit;
3001
3002 AutoLimitedCaller machCaller (m);
3003 AssertComRC (machCaller.rc());
3004
3005 /* ignore inaccessible machines */
3006 if (machCaller.state() == Machine::Ready)
3007 {
3008 if (m->isDVDImageUsed (aId, aUsage))
3009 {
3010 /* if not interested in the list, return shortly */
3011 if (aMachineIDs == NULL)
3012 return true;
3013
3014 idSet.insert (m->uuid());
3015 }
3016 }
3017 }
3018 }
3019
3020 if (aMachineIDs)
3021 {
3022 if (!idSet.empty())
3023 {
3024 /* convert to a string of UUIDs */
3025 char *idList = (char *) RTMemTmpAllocZ (RTUUID_STR_LENGTH * idSet.size());
3026 char *idListPtr = idList;
3027 for (Set::iterator it = idSet.begin(); it != idSet.end(); ++ it)
3028 {
3029 RTUuidToStr (*it, idListPtr, RTUUID_STR_LENGTH);
3030 idListPtr += RTUUID_STR_LENGTH - 1;
3031 /* replace EOS with a space char */
3032 *(idListPtr ++) = ' ';
3033 }
3034 Assert (int (idListPtr - idList) == int (RTUUID_STR_LENGTH * idSet.size()));
3035 /* remove the trailing space */
3036 *(-- idListPtr) = 0;
3037 /* copy the string */
3038 *aMachineIDs = idList;
3039 RTMemTmpFree (idList);
3040 }
3041 else
3042 {
3043 (*aMachineIDs).setNull();
3044 }
3045 }
3046
3047 return !idSet.empty();
3048}
3049
3050/**
3051 * Helper to find machines that use the given Floppy image.
3052 *
3053 * This method also checks whether an existing snapshot refers to the given
3054 * image. However, only the machine ID is returned in this case, not IDs of
3055 * individual snapshots.
3056 *
3057 * @param aId Image ID to get usage for.
3058 * @param aUsage Type of the check.
3059 * @param aMachineIDs Where to store the list of machine IDs (can be NULL)
3060 *
3061 * @return @c true if at least one machine or its snapshot found and @c false
3062 * otherwise.
3063 *
3064 * @note For now, we just scan all the machines. We can optimize this later
3065 * if required by adding the corresponding field to FloppyImage and requiring all
3066 * FloppyImage instances to be FloppyImage objects.
3067 *
3068 * @note Locks objects for reading.
3069 */
3070bool VirtualBox::getFloppyImageUsage (const Guid &aId,
3071 ResourceUsage_T aUsage,
3072 Bstr *aMachineIDs)
3073{
3074 AutoCaller autoCaller (this);
3075 AssertComRCReturn (autoCaller.rc(), FALSE);
3076
3077 typedef std::set <Guid> Set;
3078 Set idSet;
3079
3080 {
3081 AutoReadLock alock (this);
3082
3083 for (MachineList::const_iterator mit = mData.mMachines.begin();
3084 mit != mData.mMachines.end();
3085 ++ mit)
3086 {
3087 ComObjPtr <Machine> m = *mit;
3088
3089 AutoLimitedCaller machCaller (m);
3090 AssertComRC (machCaller.rc());
3091
3092 /* ignore inaccessible machines */
3093 if (machCaller.state() == Machine::Ready)
3094 {
3095 if (m->isFloppyImageUsed (aId, aUsage))
3096 {
3097 /* if not interested in the list, return shortly */
3098 if (aMachineIDs == NULL)
3099 return true;
3100
3101 idSet.insert (m->uuid());
3102 }
3103 }
3104 }
3105 }
3106
3107 if (aMachineIDs)
3108 {
3109 if (!idSet.empty())
3110 {
3111 /* convert to a string of UUIDs */
3112 char *idList = (char *) RTMemTmpAllocZ (RTUUID_STR_LENGTH * idSet.size());
3113 char *idListPtr = idList;
3114 for (Set::iterator it = idSet.begin(); it != idSet.end(); ++ it)
3115 {
3116 RTUuidToStr (*it, idListPtr, RTUUID_STR_LENGTH);
3117 idListPtr += RTUUID_STR_LENGTH - 1;
3118 /* replace EOS with a space char */
3119 *(idListPtr ++) = ' ';
3120 }
3121 Assert (int (idListPtr - idList) == int (RTUUID_STR_LENGTH * idSet.size()));
3122 /* remove the trailing space */
3123 *(-- idListPtr) = 0;
3124 /* copy the string */
3125 *aMachineIDs = idList;
3126 RTMemTmpFree (idList);
3127 }
3128 else
3129 {
3130 (*aMachineIDs).setNull();
3131 }
3132 }
3133
3134 return !idSet.empty();
3135}
3136
3137/**
3138 * Tries to calculate the relative path of the given absolute path using the
3139 * directory of the VirtualBox settings file as the base directory.
3140 *
3141 * @param aPath absolute path to calculate the relative path for
3142 * @param aResult where to put the result (used only when it's possible to
3143 * make a relative path from the given absolute path;
3144 * otherwise left untouched)
3145 *
3146 * @note Doesn't lock any object.
3147 */
3148void VirtualBox::calculateRelativePath (const char *aPath, Utf8Str &aResult)
3149{
3150 AutoCaller autoCaller (this);
3151 AssertComRCReturn (autoCaller.rc(), (void) 0);
3152
3153 /* no need to lock since mHomeDir is const */
3154
3155 Utf8Str settingsDir = mData.mHomeDir;
3156
3157 if (RTPathStartsWith (aPath, settingsDir))
3158 {
3159 /* when assigning, we create a separate Utf8Str instance because both
3160 * aPath and aResult can point to the same memory location when this
3161 * func is called (if we just do aResult = aPath, aResult will be freed
3162 * first, and since its the same as aPath, an attempt to copy garbage
3163 * will be made. */
3164 aResult = Utf8Str (aPath + settingsDir.length() + 1);
3165 }
3166}
3167
3168// private methods
3169/////////////////////////////////////////////////////////////////////////////
3170
3171/**
3172 * Searches for a Machine object with the given ID in the collection
3173 * of registered machines.
3174 *
3175 * @param id
3176 * ID of the machine
3177 * @param doSetError
3178 * if TRUE, the appropriate error info is set in case when the machine
3179 * is not found
3180 * @param machine
3181 * where to store the found machine object (can be NULL)
3182 *
3183 * @return
3184 * S_OK when found or E_INVALIDARG when not found
3185 *
3186 * @note Locks this object for reading.
3187 */
3188HRESULT VirtualBox::findMachine (const Guid &aId, bool aSetError,
3189 ComObjPtr <Machine> *aMachine /* = NULL */)
3190{
3191 AutoCaller autoCaller (this);
3192 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3193
3194 bool found = false;
3195
3196 {
3197 AutoReadLock alock (this);
3198
3199 for (MachineList::iterator it = mData.mMachines.begin();
3200 !found && it != mData.mMachines.end();
3201 ++ it)
3202 {
3203 /* sanity */
3204 AutoLimitedCaller machCaller (*it);
3205 AssertComRC (machCaller.rc());
3206
3207 found = (*it)->uuid() == aId;
3208 if (found && aMachine)
3209 *aMachine = *it;
3210 }
3211 }
3212
3213 HRESULT rc = found ? S_OK : E_INVALIDARG;
3214
3215 if (aSetError && !found)
3216 {
3217 setError (E_INVALIDARG,
3218 tr ("Could not find a registered machine with UUID {%Vuuid}"),
3219 aId.raw());
3220 }
3221
3222 return rc;
3223}
3224
3225/**
3226 * Searches for a HardDisk object with the given ID or location specification
3227 * in the collection of registered hard disks. If both ID and location are
3228 * specified, the first object that matches either of them (not necessarily
3229 * both) is returned.
3230 *
3231 * @param aId ID of the hard disk (NULL when unused)
3232 * @param aLocation full location specification (NULL when unused)
3233 * @param aSetError if TRUE, the appropriate error info is set in case when
3234 * the disk is not found and only one search criteria (ID
3235 * or file name) is specified.
3236 * @param aHardDisk where to store the found hard disk object (can be NULL)
3237 *
3238 * @return
3239 * S_OK when found or E_INVALIDARG when not found
3240 *
3241 * @note Locks objects for reading!
3242 */
3243HRESULT VirtualBox::
3244findHardDisk (const Guid *aId, const BSTR aLocation,
3245 bool aSetError, ComObjPtr <HardDisk> *aHardDisk /* = NULL */)
3246{
3247 ComAssertRet (aId || aLocation, E_INVALIDARG);
3248
3249 AutoReadLock alock (this);
3250
3251 /* first lookup the map by UUID if UUID is provided */
3252 if (aId)
3253 {
3254 HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
3255 if (it != mData.mHardDiskMap.end())
3256 {
3257 if (aHardDisk)
3258 *aHardDisk = (*it).second;
3259 return S_OK;
3260 }
3261 }
3262
3263 /* then iterate and find by location */
3264 bool found = false;
3265 if (aLocation)
3266 {
3267 Utf8Str location = aLocation;
3268
3269 for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3270 !found && it != mData.mHardDiskMap.end();
3271 ++ it)
3272 {
3273 const ComObjPtr <HardDisk> &hd = (*it).second;
3274 AutoReadLock hdLock (hd);
3275
3276 if (hd->storageType() == HardDiskStorageType_VirtualDiskImage ||
3277 hd->storageType() == HardDiskStorageType_VMDKImage ||
3278 hd->storageType() == HardDiskStorageType_VHDImage)
3279 {
3280 /* locations of VDI and VMDK hard disks for now are just
3281 * file paths */
3282 found = RTPathCompare (location,
3283 Utf8Str (hd->toString
3284 (false /* aShort */))) == 0;
3285 }
3286 else
3287 {
3288 found = aLocation == hd->toString (false /* aShort */);
3289 }
3290
3291 if (found && aHardDisk)
3292 *aHardDisk = hd;
3293 }
3294 }
3295
3296 HRESULT rc = found ? S_OK : E_INVALIDARG;
3297
3298 if (aSetError && !found)
3299 {
3300 if (aId && !aLocation)
3301 setError (rc, tr ("Could not find a registered hard disk "
3302 "with UUID {%Vuuid}"), aId->raw());
3303 else if (aLocation && !aId)
3304 setError (rc, tr ("Could not find a registered hard disk "
3305 "with location '%ls'"), aLocation);
3306 }
3307
3308 return rc;
3309}
3310
3311/**
3312 * @deprecated Use #findHardDisk() instead.
3313 *
3314 * Searches for a HVirtualDiskImage object with the given ID or file path in the
3315 * collection of registered hard disks. If both ID and file path are specified,
3316 * the first object that matches either of them (not necessarily both)
3317 * is returned.
3318 *
3319 * @param aId ID of the hard disk (NULL when unused)
3320 * @param filePathFull full path to the image file (NULL when unused)
3321 * @param aSetError if TRUE, the appropriate error info is set in case when
3322 * the disk is not found and only one search criteria (ID
3323 * or file name) is specified.
3324 * @param aHardDisk where to store the found hard disk object (can be NULL)
3325 *
3326 * @return
3327 * S_OK when found or E_INVALIDARG when not found
3328 *
3329 * @note Locks objects for reading!
3330 */
3331HRESULT VirtualBox::
3332findVirtualDiskImage (const Guid *aId, const BSTR aFilePathFull,
3333 bool aSetError, ComObjPtr <HVirtualDiskImage> *aImage /* = NULL */)
3334{
3335 ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3336
3337 AutoReadLock alock (this);
3338
3339 /* first lookup the map by UUID if UUID is provided */
3340 if (aId)
3341 {
3342 HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
3343 if (it != mData.mHardDiskMap.end())
3344 {
3345 AutoReadLock hdLock ((*it).second);
3346 if ((*it).second->storageType() == HardDiskStorageType_VirtualDiskImage)
3347 {
3348 if (aImage)
3349 *aImage = (*it).second->asVDI();
3350 return S_OK;
3351 }
3352 }
3353 }
3354
3355 /* then iterate and find by name */
3356 bool found = false;
3357 if (aFilePathFull)
3358 {
3359 for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3360 !found && it != mData.mHardDiskMap.end();
3361 ++ it)
3362 {
3363 const ComObjPtr <HardDisk> &hd = (*it).second;
3364 AutoReadLock hdLock (hd);
3365 if (hd->storageType() != HardDiskStorageType_VirtualDiskImage)
3366 continue;
3367
3368 found = RTPathCompare (Utf8Str (aFilePathFull),
3369 Utf8Str (hd->asVDI()->filePathFull())) == 0;
3370 if (found && aImage)
3371 *aImage = hd->asVDI();
3372 }
3373 }
3374
3375 HRESULT rc = found ? S_OK : E_INVALIDARG;
3376
3377 if (aSetError && !found)
3378 {
3379 if (aId && !aFilePathFull)
3380 setError (rc, tr ("Could not find a registered VDI hard disk "
3381 "with UUID {%Vuuid}"), aId->raw());
3382 else if (aFilePathFull && !aId)
3383 setError (rc, tr ("Could not find a registered VDI hard disk "
3384 "with the file path '%ls'"), aFilePathFull);
3385 }
3386
3387 return rc;
3388}
3389
3390/**
3391 * Searches for a DVDImage object with the given ID or file path in the
3392 * collection of registered DVD images. If both ID and file path are specified,
3393 * the first object that matches either of them (not necessarily both)
3394 * is returned.
3395 *
3396 * @param aId
3397 * ID of the DVD image (unused when NULL)
3398 * @param aFilePathFull
3399 * full path to the image file (unused when NULL)
3400 * @param aSetError
3401 * if TRUE, the appropriate error info is set in case when the image is not
3402 * found and only one search criteria (ID or file name) is specified.
3403 * @param aImage
3404 * where to store the found DVD image object (can be NULL)
3405 *
3406 * @return
3407 * S_OK when found or E_INVALIDARG when not found
3408 *
3409 * @note Locks this object for reading.
3410 */
3411HRESULT VirtualBox::findDVDImage (const Guid *aId, const BSTR aFilePathFull,
3412 bool aSetError,
3413 ComObjPtr <DVDImage> *aImage /* = NULL */)
3414{
3415 ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3416
3417 bool found = false;
3418
3419 {
3420 AutoReadLock alock (this);
3421
3422 for (DVDImageList::const_iterator it = mData.mDVDImages.begin();
3423 !found && it != mData.mDVDImages.end();
3424 ++ it)
3425 {
3426 /* DVDImage fields are constant, so no need to lock */
3427 found = (aId && (*it)->id() == *aId) ||
3428 (aFilePathFull &&
3429 RTPathCompare (Utf8Str (aFilePathFull),
3430 Utf8Str ((*it)->filePathFull())) == 0);
3431 if (found && aImage)
3432 *aImage = *it;
3433 }
3434 }
3435
3436 HRESULT rc = found ? S_OK : E_INVALIDARG;
3437
3438 if (aSetError && !found)
3439 {
3440 if (aId && !aFilePathFull)
3441 setError (rc, tr ("Could not find a registered CD/DVD image "
3442 "with UUID {%s}"), aId->toString().raw());
3443 else if (aFilePathFull && !aId)
3444 setError (rc, tr ("Could not find a registered CD/DVD image "
3445 "with the file path '%ls'"), aFilePathFull);
3446 }
3447
3448 return rc;
3449}
3450
3451/**
3452 * Searches for a FloppyImage object with the given ID or file path in the
3453 * collection of registered floppy images. If both ID and file path are specified,
3454 * the first object that matches either of them (not necessarily both)
3455 * is returned.
3456 *
3457 * @param aId
3458 * ID of the floppy image (unused when NULL)
3459 * @param aFilePathFull
3460 * full path to the image file (unused when NULL)
3461 * @param aSetError
3462 * if TRUE, the appropriate error info is set in case when the image is not
3463 * found and only one search criteria (ID or file name) is specified.
3464 * @param aImage
3465 * where to store the found floppy image object (can be NULL)
3466 *
3467 * @return
3468 * S_OK when found or E_INVALIDARG when not found
3469 *
3470 * @note Locks this object for reading.
3471 */
3472HRESULT VirtualBox::findFloppyImage (const Guid *aId, const BSTR aFilePathFull,
3473 bool aSetError,
3474 ComObjPtr <FloppyImage> *aImage /* = NULL */)
3475{
3476 ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3477
3478 bool found = false;
3479
3480 {
3481 AutoReadLock alock (this);
3482
3483 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3484 !found && it != mData.mFloppyImages.end();
3485 ++ it)
3486 {
3487 /* FloppyImage fields are constant, so no need to lock */
3488 found = (aId && (*it)->id() == *aId) ||
3489 (aFilePathFull &&
3490 RTPathCompare (Utf8Str (aFilePathFull),
3491 Utf8Str ((*it)->filePathFull())) == 0);
3492 if (found && aImage)
3493 *aImage = *it;
3494 }
3495 }
3496
3497 HRESULT rc = found ? S_OK : E_INVALIDARG;
3498
3499 if (aSetError && !found)
3500 {
3501 if (aId && !aFilePathFull)
3502 setError (rc, tr ("Could not find a registered floppy image "
3503 "with UUID {%s}"), aId->toString().raw());
3504 else if (aFilePathFull && !aId)
3505 setError (rc, tr ("Could not find a registered floppy image "
3506 "with the file path '%ls'"), aFilePathFull);
3507 }
3508
3509 return rc;
3510}
3511
3512/**
3513 * When \a aHardDisk is not NULL, searches for an object equal to the given
3514 * hard disk in the collection of registered hard disks, or, if the given hard
3515 * disk is HVirtualDiskImage, for an object with the given file path in the
3516 * collection of all registered non-hard disk images (DVDs and floppies).
3517 * Other parameters are unused.
3518 *
3519 * When \a aHardDisk is NULL, searches for an object with the given ID or file
3520 * path in the collection of all registered images (VDIs, DVDs and floppies).
3521 * If both ID and file path are specified, matching either of them will satisfy
3522 * the search.
3523 *
3524 * If a matching object is found, this method returns E_INVALIDARG and sets the
3525 * appropriate error info. Otherwise, S_OK is returned.
3526 *
3527 * @param aHardDisk hard disk object to check against registered media
3528 * (NULL when unused)
3529 * @param aId UUID of the media to check (NULL when unused)
3530 * @param aFilePathFull full path to the image file (NULL when unused)
3531 *
3532 * @note Locks objects!
3533 */
3534HRESULT VirtualBox::checkMediaForConflicts (HardDisk *aHardDisk,
3535 const Guid *aId,
3536 const BSTR aFilePathFull)
3537{
3538 AssertReturn (aHardDisk || aId || aFilePathFull, E_FAIL);
3539
3540 HRESULT rc = S_OK;
3541
3542 AutoReadLock alock (this);
3543
3544 if (aHardDisk)
3545 {
3546 for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3547 it != mData.mHardDiskMap.end();
3548 ++ it)
3549 {
3550 const ComObjPtr <HardDisk> &hd = (*it).second;
3551 if (hd->sameAs (aHardDisk))
3552 return setError (E_INVALIDARG,
3553 tr ("A hard disk with UUID {%Vuuid} or with the same properties "
3554 "('%ls') is already registered"),
3555 aHardDisk->id().raw(), aHardDisk->toString().raw());
3556 }
3557
3558 aId = &aHardDisk->id();
3559 if (aHardDisk->storageType() == HardDiskStorageType_VirtualDiskImage)
3560#if !defined (VBOX_WITH_XPCOM)
3561 /// @todo (dmik) stupid BSTR declaration lacks the BCSTR counterpart
3562 const_cast <BSTR> (aFilePathFull) = aHardDisk->asVDI()->filePathFull();
3563#else
3564 aFilePathFull = aHardDisk->asVDI()->filePathFull();
3565#endif
3566 }
3567
3568 bool found = false;
3569
3570 if (aId || aFilePathFull) do
3571 {
3572 if (!aHardDisk)
3573 {
3574 rc = findHardDisk (aId, aFilePathFull, false /* aSetError */);
3575 found = SUCCEEDED (rc);
3576 if (found)
3577 break;
3578 }
3579
3580 rc = findDVDImage (aId, aFilePathFull, false /* aSetError */);
3581 found = SUCCEEDED (rc);
3582 if (found)
3583 break;
3584
3585 rc = findFloppyImage (aId, aFilePathFull, false /* aSetError */);
3586 found = SUCCEEDED (rc);
3587 if (found)
3588 break;
3589 }
3590 while (0);
3591
3592 if (found)
3593 {
3594 if (aId && !aFilePathFull)
3595 rc = setError (E_INVALIDARG,
3596 tr ("A disk image with UUID {%Vuuid} is already registered"),
3597 aId->raw());
3598 else if (aFilePathFull && !aId)
3599 rc = setError (E_INVALIDARG,
3600 tr ("A disk image with file path '%ls' is already registered"),
3601 aFilePathFull);
3602 else
3603 rc = setError (E_INVALIDARG,
3604 tr ("A disk image with UUID {%Vuuid} or file path '%ls' "
3605 "is already registered"), aId->raw(), aFilePathFull);
3606 }
3607 else
3608 rc = S_OK;
3609
3610 return rc;
3611}
3612
3613/**
3614 * Reads in the machine definitions from the configuration loader
3615 * and creates the relevant objects.
3616 *
3617 * @param aGlobal <Global> node.
3618 *
3619 * @note Can be called only from #init().
3620 * @note Doesn't lock anything.
3621 */
3622HRESULT VirtualBox::loadMachines (const settings::Key &aGlobal)
3623{
3624 using namespace settings;
3625
3626 AutoCaller autoCaller (this);
3627 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3628
3629 HRESULT rc = S_OK;
3630
3631 Key::List machines = aGlobal.key ("MachineRegistry").keys ("MachineEntry");
3632 for (Key::List::const_iterator it = machines.begin();
3633 it != machines.end(); ++ it)
3634 {
3635 /* required */
3636 Guid uuid = (*it).value <Guid> ("uuid");
3637 /* required */
3638 Bstr src = (*it).stringValue ("src");
3639
3640 /* create a new machine object */
3641 ComObjPtr <Machine> machine;
3642 rc = machine.createObject();
3643 if (SUCCEEDED (rc))
3644 {
3645 /* initialize the machine object and register it */
3646 rc = machine->init (this, src, Machine::Init_Registered,
3647 NULL, FALSE, &uuid);
3648 if (SUCCEEDED (rc))
3649 rc = registerMachine (machine);
3650 }
3651 }
3652
3653 return rc;
3654}
3655
3656/**
3657 * Reads in the disk registration entries from the global settings file
3658 * and creates the relevant objects
3659 *
3660 * @param aGlobal <Global> node
3661 *
3662 * @note Can be called only from #init().
3663 * @note Doesn't lock anything.
3664 */
3665HRESULT VirtualBox::loadDisks (const settings::Key &aGlobal)
3666{
3667 using namespace settings;
3668
3669 AutoCaller autoCaller (this);
3670 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3671
3672 HRESULT rc = S_OK;
3673
3674 Key registry = aGlobal.key ("DiskRegistry");
3675
3676 const char *kMediaNodes[] = { "HardDisks", "DVDImages", "FloppyImages" };
3677
3678 for (size_t n = 0; n < ELEMENTS (kMediaNodes); ++ n)
3679 {
3680 /* All three media nodes are optional */
3681 Key node = registry.findKey (kMediaNodes [n]);
3682 if (node.isNull())
3683 continue;
3684
3685 if (n == 0)
3686 {
3687 /* HardDisks node */
3688 rc = loadHardDisks (node);
3689 continue;
3690 }
3691
3692 Key::List images = node.keys ("Image");
3693 for (Key::List::const_iterator it = images.begin();
3694 it != images.end(); ++ it)
3695 {
3696 /* required */
3697 Guid uuid = (*it).value <Guid> ("uuid");
3698 /* required */
3699 Bstr src = (*it).stringValue ("src");
3700
3701 switch (n)
3702 {
3703 case 1: /* DVDImages */
3704 {
3705 ComObjPtr <DVDImage> image;
3706 image.createObject();
3707 rc = image->init (this, src, TRUE /* isRegistered */, uuid);
3708 if (SUCCEEDED (rc))
3709 rc = registerDVDImage (image, TRUE /* aOnStartUp */);
3710
3711 break;
3712 }
3713 case 2: /* FloppyImages */
3714 {
3715 ComObjPtr <FloppyImage> image;
3716 image.createObject();
3717 rc = image->init (this, src, TRUE /* isRegistered */, uuid);
3718 if (SUCCEEDED (rc))
3719 rc = registerFloppyImage (image, TRUE /* aOnStartUp */);
3720
3721 break;
3722 }
3723 default:
3724 AssertFailed();
3725 }
3726
3727 CheckComRCBreakRC (rc);
3728 }
3729
3730 CheckComRCBreakRC (rc);
3731 }
3732
3733 return rc;
3734}
3735
3736/**
3737 * Loads all hard disks from the given <HardDisks> node.
3738 * Note that all loaded hard disks register themselves within this VirtualBox.
3739 *
3740 * @param aNode <HardDisks> node.
3741 *
3742 * @note Can be called only from #init().
3743 * @note Doesn't lock anything.
3744 */
3745HRESULT VirtualBox::loadHardDisks (const settings::Key &aNode)
3746{
3747 using namespace settings;
3748
3749 AutoCaller autoCaller (this);
3750 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3751
3752 AssertReturn (!aNode.isNull(), E_INVALIDARG);
3753
3754 HRESULT rc = S_OK;
3755
3756 Key::List disks = aNode.keys ("HardDisk");
3757 for (Key::List::const_iterator it = disks.begin();
3758 it != disks.end(); ++ it)
3759 {
3760 Key storageNode;
3761
3762 /* detect the type of the hard disk (either one of VirtualDiskImage,
3763 * ISCSIHardDisk, VMDKImage or HCustomHardDisk) */
3764
3765 do
3766 {
3767 storageNode = (*it).findKey ("VirtualDiskImage");
3768 if (!storageNode.isNull())
3769 {
3770 ComObjPtr <HVirtualDiskImage> vdi;
3771 vdi.createObject();
3772 rc = vdi->init (this, NULL, (*it), storageNode);
3773 break;
3774 }
3775
3776 storageNode = (*it).findKey ("ISCSIHardDisk");
3777 if (!storageNode.isNull())
3778 {
3779 ComObjPtr <HISCSIHardDisk> iscsi;
3780 iscsi.createObject();
3781 rc = iscsi->init (this, (*it), storageNode);
3782 break;
3783 }
3784
3785 storageNode = (*it).findKey ("VMDKImage");
3786 if (!storageNode.isNull())
3787 {
3788 ComObjPtr <HVMDKImage> vmdk;
3789 vmdk.createObject();
3790 rc = vmdk->init (this, NULL, (*it), storageNode);
3791 break;
3792 }
3793
3794 storageNode = (*it).findKey ("CustomHardDisk");
3795 if (!storageNode.isNull())
3796 {
3797 ComObjPtr <HCustomHardDisk> custom;
3798 custom.createObject();
3799 rc = custom->init (this, NULL, (*it), storageNode);
3800 break;
3801 }
3802
3803 storageNode = (*it).findKey ("VHDImage");
3804 if (!storageNode.isNull())
3805 {
3806 ComObjPtr <HVHDImage> vhd;
3807 vhd.createObject();
3808 rc = vhd->init (this, NULL, (*it), storageNode);
3809 break;
3810 }
3811
3812 ComAssertMsgFailedBreak (("No valid hard disk storage node!\n"),
3813 rc = E_FAIL);
3814 }
3815 while (0);
3816 }
3817
3818 return rc;
3819}
3820
3821/**
3822 * Helper function to write out the configuration tree.
3823 *
3824 * @note Locks this object for writing and child objects for reading/writing!
3825 */
3826HRESULT VirtualBox::saveSettings()
3827{
3828 AutoCaller autoCaller (this);
3829 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3830
3831 AssertReturn (!!mData.mCfgFile.mName, E_FAIL);
3832
3833 HRESULT rc = S_OK;
3834
3835 /* serialize file access (prevent concurrent reads and writes) */
3836 AutoWriteLock alock (this);
3837
3838 try
3839 {
3840 using namespace settings;
3841
3842 /* load the settings file */
3843 File file (mData.mCfgFile.mHandle, Utf8Str (mData.mCfgFile.mName));
3844 XmlTreeBackend tree;
3845
3846 rc = VirtualBox::loadSettingsTree_ForUpdate (tree, file);
3847 CheckComRCThrowRC (rc);
3848
3849 Key global = tree.rootKey().createKey ("Global");
3850
3851 /* machines */
3852 {
3853 /* first, delete the entire machine registry */
3854 Key registryNode = global.findKey ("MachineRegistry");
3855 if (!registryNode.isNull())
3856 registryNode.zap();
3857 /* then, recreate it */
3858 registryNode = global.createKey ("MachineRegistry");
3859
3860 /* write out the machines */
3861 for (MachineList::iterator it = mData.mMachines.begin();
3862 it != mData.mMachines.end();
3863 ++ it)
3864 {
3865 Key entryNode = registryNode.appendKey ("MachineEntry");
3866 rc = (*it)->saveRegistryEntry (entryNode);
3867 CheckComRCThrowRC (rc);
3868 }
3869 }
3870
3871 /* disk images */
3872 {
3873 /* first, delete the entire disk image registr */
3874 Key registryNode = global.findKey ("DiskRegistry");
3875 if (!registryNode.isNull())
3876 registryNode.zap();
3877 /* then, recreate it */
3878 registryNode = global.createKey ("DiskRegistry");
3879
3880 /* write out the hard disks */
3881 {
3882 Key imagesNode = registryNode.createKey ("HardDisks");
3883 rc = saveHardDisks (imagesNode);
3884 CheckComRCThrowRC (rc);
3885 }
3886
3887 /* write out the CD/DVD images */
3888 {
3889 Key imagesNode = registryNode.createKey ("DVDImages");
3890
3891 for (DVDImageList::iterator it = mData.mDVDImages.begin();
3892 it != mData.mDVDImages.end();
3893 ++ it)
3894 {
3895 ComObjPtr <DVDImage> dvd = *it;
3896 /* no need to lock: fields are constant */
3897 Key imageNode = imagesNode.appendKey ("Image");
3898 imageNode.setValue <Guid> ("uuid", dvd->id());
3899 imageNode.setValue <Bstr> ("src", dvd->filePath());
3900 }
3901 }
3902
3903 /* write out the floppy images */
3904 {
3905 Key imagesNode = registryNode.createKey ("FloppyImages");
3906
3907 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3908 it != mData.mFloppyImages.end();
3909 ++ it)
3910 {
3911 ComObjPtr <FloppyImage> fd = *it;
3912 /* no need to lock: fields are constant */
3913 Key imageNode = imagesNode.appendKey ("Image");
3914 imageNode.setValue <Guid> ("uuid", fd->id());
3915 imageNode.setValue <Bstr> ("src", fd->filePath());
3916 }
3917 }
3918 }
3919
3920 /* host data (USB filters) */
3921 rc = mData.mHost->saveSettings (global);
3922 CheckComRCThrowRC (rc);
3923
3924 rc = mData.mSystemProperties->saveSettings (global);
3925 CheckComRCThrowRC (rc);
3926
3927 /* save the settings on success */
3928 rc = VirtualBox::saveSettingsTree (tree, file,
3929 mData.mSettingsFileVersion);
3930 CheckComRCThrowRC (rc);
3931 }
3932 catch (HRESULT err)
3933 {
3934 /* we assume that error info is set by the thrower */
3935 rc = err;
3936 }
3937 catch (...)
3938 {
3939 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3940 }
3941
3942 return rc;
3943}
3944
3945/**
3946 * Saves all hard disks to the given <HardDisks> node.
3947 *
3948 * @param aNode <HardDisks> node.
3949 *
3950 * @note Locks this object for reding.
3951 */
3952HRESULT VirtualBox::saveHardDisks (settings::Key &aNode)
3953{
3954 using namespace settings;
3955
3956 AssertReturn (!aNode.isNull(), E_INVALIDARG);
3957
3958 HRESULT rc = S_OK;
3959
3960 AutoReadLock alock (this);
3961
3962 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
3963 it != mData.mHardDisks.end();
3964 ++ it)
3965 {
3966 ComObjPtr <HardDisk> hd = *it;
3967 AutoReadLock hdLock (hd);
3968
3969 Key hdNode = aNode.appendKey ("HardDisk");
3970
3971 switch (hd->storageType())
3972 {
3973 case HardDiskStorageType_VirtualDiskImage:
3974 {
3975 Key storageNode = hdNode.createKey ("VirtualDiskImage");
3976 rc = hd->saveSettings (hdNode, storageNode);
3977 break;
3978 }
3979
3980 case HardDiskStorageType_ISCSIHardDisk:
3981 {
3982 Key storageNode = hdNode.createKey ("ISCSIHardDisk");
3983 rc = hd->saveSettings (hdNode, storageNode);
3984 break;
3985 }
3986
3987 case HardDiskStorageType_VMDKImage:
3988 {
3989 Key storageNode = hdNode.createKey ("VMDKImage");
3990 rc = hd->saveSettings (hdNode, storageNode);
3991 break;
3992 }
3993 case HardDiskStorageType_CustomHardDisk:
3994 {
3995 Key storageNode = hdNode.createKey ("CustomHardDisk");
3996 rc = hd->saveSettings (hdNode, storageNode);
3997 break;
3998 }
3999
4000 case HardDiskStorageType_VHDImage:
4001 {
4002 Key storageNode = hdNode.createKey ("VHDImage");
4003 rc = hd->saveSettings (hdNode, storageNode);
4004 break;
4005 }
4006 }
4007
4008 CheckComRCBreakRC (rc);
4009 }
4010
4011 return rc;
4012}
4013
4014/**
4015 * Helper to register the machine.
4016 *
4017 * When called during VirtualBox startup, adds the given machine to the
4018 * collection of registered machines. Otherwise tries to mark the machine
4019 * as registered, and, if succeeded, adds it to the collection and
4020 * saves global settings.
4021 *
4022 * @note The caller must have added itself as a caller of the @a aMachine
4023 * object if calls this method not on VirtualBox startup.
4024 *
4025 * @param aMachine machine to register
4026 *
4027 * @note Locks objects!
4028 */
4029HRESULT VirtualBox::registerMachine (Machine *aMachine)
4030{
4031 ComAssertRet (aMachine, E_INVALIDARG);
4032
4033 AutoCaller autoCaller (this);
4034 CheckComRCReturnRC (autoCaller.rc());
4035
4036 AutoWriteLock alock (this);
4037
4038 HRESULT rc = S_OK;
4039
4040 {
4041 ComObjPtr <Machine> m;
4042 rc = findMachine (aMachine->uuid(), false /* aDoSetError */, &m);
4043 if (SUCCEEDED (rc))
4044 {
4045 /* sanity */
4046 AutoLimitedCaller machCaller (m);
4047 AssertComRC (machCaller.rc());
4048
4049 return setError (E_INVALIDARG,
4050 tr ("Registered machine with UUID {%Vuuid} ('%ls') already exists"),
4051 aMachine->uuid().raw(), m->settingsFileFull().raw());
4052 }
4053
4054 ComAssertRet (rc == E_INVALIDARG, rc);
4055 rc = S_OK;
4056 }
4057
4058 if (autoCaller.state() != InInit)
4059 {
4060 /* Machine::trySetRegistered() will commit and save machine settings */
4061 rc = aMachine->trySetRegistered (TRUE);
4062 CheckComRCReturnRC (rc);
4063 }
4064
4065 /* add to the collection of registered machines */
4066 mData.mMachines.push_back (aMachine);
4067
4068 if (autoCaller.state() != InInit)
4069 rc = saveSettings();
4070
4071 return rc;
4072}
4073
4074/**
4075 * Helper to register the hard disk.
4076 *
4077 * @param aHardDisk object to register
4078 * @param aFlags one of RHD_* values
4079 *
4080 * @note Locks objects!
4081 */
4082HRESULT VirtualBox::registerHardDisk (HardDisk *aHardDisk, RHD_Flags aFlags)
4083{
4084 ComAssertRet (aHardDisk, E_INVALIDARG);
4085
4086 AutoCaller autoCaller (this);
4087 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4088
4089 AutoWriteLock alock (this);
4090
4091 HRESULT rc = checkMediaForConflicts (aHardDisk, NULL, NULL);
4092 CheckComRCReturnRC (rc);
4093
4094 /* mark the hard disk as registered only when registration is external */
4095 if (aFlags == RHD_External)
4096 {
4097 rc = aHardDisk->trySetRegistered (TRUE);
4098 CheckComRCReturnRC (rc);
4099 }
4100
4101 if (!aHardDisk->parent())
4102 {
4103 /* add to the collection of top-level images */
4104 mData.mHardDisks.push_back (aHardDisk);
4105 }
4106
4107 /* insert to the map of hard disks */
4108 mData.mHardDiskMap
4109 .insert (HardDiskMap::value_type (aHardDisk->id(), aHardDisk));
4110
4111 /* save global config file if not on startup */
4112 /// @todo (dmik) optimize later to save only the <HardDisks> node
4113 if (aFlags != RHD_OnStartUp)
4114 rc = saveSettings();
4115
4116 return rc;
4117}
4118
4119/**
4120 * Helper to unregister the hard disk.
4121 *
4122 * If the hard disk is a differencing hard disk and if the unregistration
4123 * succeeds, the hard disk image is deleted and the object is uninitialized.
4124 *
4125 * @param aHardDisk hard disk to unregister
4126 *
4127 * @note Locks objects!
4128 */
4129HRESULT VirtualBox::unregisterHardDisk (HardDisk *aHardDisk)
4130{
4131 AssertReturn (aHardDisk, E_INVALIDARG);
4132
4133 AutoCaller autoCaller (this);
4134 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4135
4136 LogFlowThisFunc (("image='%ls'\n", aHardDisk->toString().raw()));
4137
4138 AutoWriteLock alock (this);
4139
4140 /* Lock the hard disk to ensure nobody registers it again before we delete
4141 * the differencing image (sanity check actually -- should never happen). */
4142 AutoWriteLock hdLock (aHardDisk);
4143
4144 /* try to unregister */
4145 HRESULT rc = aHardDisk->trySetRegistered (FALSE);
4146 CheckComRCReturnRC (rc);
4147
4148 /* remove from the map of hard disks */
4149 mData.mHardDiskMap.erase (aHardDisk->id());
4150
4151 if (!aHardDisk->parent())
4152 {
4153 /* non-differencing hard disk:
4154 * remove from the collection of top-level hard disks */
4155 mData.mHardDisks.remove (aHardDisk);
4156 }
4157 else
4158 {
4159 Assert (aHardDisk->isDifferencing());
4160
4161 /* differencing hard disk: delete and uninitialize
4162 *
4163 * Note that we ignore errors because this operation may be a result
4164 * of unregistering a missing (inaccessible) differencing hard disk
4165 * in which case a failure to implicitly delete the image will not
4166 * prevent it from being unregistered and therefore should not pop up
4167 * on the caller's side. */
4168 rc = aHardDisk->asVDI()->deleteImage (true /* aIgnoreErrors*/);
4169 aHardDisk->uninit();
4170 }
4171
4172 /* save the global config file anyway (already unregistered) */
4173 /// @todo (dmik) optimize later to save only the <HardDisks> node
4174 HRESULT rc2 = saveSettings();
4175 if (SUCCEEDED (rc))
4176 rc = rc2;
4177
4178 return rc;
4179}
4180
4181/**
4182 * Helper to unregister the differencing hard disk image.
4183 * Resets machine ID of the hard disk (to let the unregistration succeed)
4184 * and then calls #unregisterHardDisk().
4185 *
4186 * @param aHardDisk differencing hard disk image to unregister
4187 *
4188 * @note Locks objects!
4189 */
4190HRESULT VirtualBox::unregisterDiffHardDisk (HardDisk *aHardDisk)
4191{
4192 AssertReturn (aHardDisk, E_INVALIDARG);
4193
4194 AutoCaller autoCaller (this);
4195 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4196
4197 AutoWriteLock alock (this);
4198
4199 /*
4200 * Note: it's safe to lock aHardDisk here because the same object
4201 * will be locked by #unregisterHardDisk().
4202 */
4203 AutoWriteLock hdLock (aHardDisk);
4204
4205 AssertReturn (aHardDisk->isDifferencing(), E_INVALIDARG);
4206
4207 /*
4208 * deassociate the machine from the hard disk
4209 * (otherwise trySetRegistered() will definitely fail)
4210 */
4211 aHardDisk->setMachineId (Guid());
4212
4213 return unregisterHardDisk (aHardDisk);
4214}
4215
4216
4217/**
4218 * Helper to update the global settings file when the name of some machine
4219 * changes so that file and directory renaming occurs. This method ensures
4220 * that all affected paths in the disk registry are properly updated.
4221 *
4222 * @param aOldPath old path (full)
4223 * @param aNewPath new path (full)
4224 *
4225 * @note Locks this object + DVD, Floppy and HardDisk children for writing.
4226 */
4227HRESULT VirtualBox::updateSettings (const char *aOldPath, const char *aNewPath)
4228{
4229 LogFlowThisFunc (("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
4230
4231 AssertReturn (aOldPath, E_INVALIDARG);
4232 AssertReturn (aNewPath, E_INVALIDARG);
4233
4234 AutoCaller autoCaller (this);
4235 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4236
4237 AutoWriteLock alock (this);
4238
4239 size_t oldPathLen = strlen (aOldPath);
4240
4241 /* check DVD paths */
4242 for (DVDImageList::iterator it = mData.mDVDImages.begin();
4243 it != mData.mDVDImages.end();
4244 ++ it)
4245 {
4246 ComObjPtr <DVDImage> image = *it;
4247
4248 /* no need to lock: fields are constant */
4249 Utf8Str path = image->filePathFull();
4250 LogFlowThisFunc (("DVD.fullPath={%s}\n", path.raw()));
4251
4252 if (RTPathStartsWith (path, aOldPath))
4253 {
4254 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
4255 path.raw() + oldPathLen);
4256 path = newPath;
4257 calculateRelativePath (path, path);
4258 image->updatePath (newPath, path);
4259
4260 LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
4261 newPath.raw(), path.raw()));
4262 }
4263 }
4264
4265 /* check Floppy paths */
4266 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
4267 it != mData.mFloppyImages.end();
4268 ++ it)
4269 {
4270 ComObjPtr <FloppyImage> image = *it;
4271
4272 /* no need to lock: fields are constant */
4273 Utf8Str path = image->filePathFull();
4274 LogFlowThisFunc (("Floppy.fullPath={%s}\n", path.raw()));
4275
4276 if (RTPathStartsWith (path, aOldPath))
4277 {
4278 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
4279 path.raw() + oldPathLen);
4280 path = newPath;
4281 calculateRelativePath (path, path);
4282 image->updatePath (newPath, path);
4283
4284 LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
4285 newPath.raw(), path.raw()));
4286 }
4287 }
4288
4289 /* check HardDisk paths */
4290 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
4291 it != mData.mHardDisks.end();
4292 ++ it)
4293 {
4294 (*it)->updatePaths (aOldPath, aNewPath);
4295 }
4296
4297 HRESULT rc = saveSettings();
4298
4299 return rc;
4300}
4301
4302/**
4303 * Helper method to load the setting tree and turn expected exceptions into
4304 * COM errors, according to arguments.
4305 *
4306 * Note that this method will not catch unexpected errors so it may still
4307 * throw something.
4308 *
4309 * @param aTree Tree to load into settings.
4310 * @param aFile File to load settings from.
4311 * @param aValidate @c @true to enable tree validation.
4312 * @param aCatchLoadErrors @c true to catch exceptions caused by file
4313 * access or validation errors.
4314 * @param aAddDefaults @c true to cause the substitution of default
4315 * values for for missing attributes that have
4316 * defaults in the XML schema.
4317 * @param aFormatVersion Where to store the current format version of the
4318 * loaded settings tree (optional, may be NULL).
4319 */
4320/* static */
4321HRESULT VirtualBox::loadSettingsTree (settings::XmlTreeBackend &aTree,
4322 settings::File &aFile,
4323 bool aValidate,
4324 bool aCatchLoadErrors,
4325 bool aAddDefaults,
4326 Utf8Str *aFormatVersion /* = NULL */)
4327{
4328 using namespace settings;
4329
4330 try
4331 {
4332 SettingsTreeHelper helper = SettingsTreeHelper();
4333
4334 aTree.setInputResolver (helper);
4335 aTree.setAutoConverter (helper);
4336
4337 aTree.read (aFile, aValidate ? VBOX_XML_SCHEMA : NULL,
4338 aAddDefaults ? XmlTreeBackend::Read_AddDefaults : 0);
4339
4340 aTree.resetAutoConverter();
4341 aTree.resetInputResolver();
4342
4343 /* on success, memorize the current settings file version or set it to
4344 * the most recent version if no settings conversion took place. Note
4345 * that it's not necessary to do it every time we load the settings file
4346 * (i.e. only loadSettingsTree_FirstTime() passes a non-NULL
4347 * aFormatVersion value) because currently we keep the settings
4348 * files locked so that the only legal way to change the format version
4349 * while VirtualBox is running is saveSettingsTree(). */
4350 if (aFormatVersion != NULL)
4351 {
4352 *aFormatVersion = aTree.oldVersion();
4353 if (aFormatVersion->isNull())
4354 *aFormatVersion = VBOX_XML_VERSION_FULL;
4355 }
4356 }
4357 catch (const EIPRTFailure &err)
4358 {
4359 if (!aCatchLoadErrors)
4360 throw;
4361
4362 return setError (E_FAIL,
4363 tr ("Could not load the settings file '%s' (%Vrc)"),
4364 aFile.uri(), err.rc());
4365 }
4366 catch (const XmlTreeBackend::Error &err)
4367 {
4368 Assert (err.what() != NULL);
4369
4370 if (!aCatchLoadErrors)
4371 throw;
4372
4373 return setError (E_FAIL,
4374 tr ("Could not load the settings file '%s'.\n%s"),
4375 aFile.uri(),
4376 err.what() ? err.what() : "Unknown error");
4377 }
4378
4379 return S_OK;
4380}
4381
4382/**
4383 * Helper method to save the settings tree and turn expected exceptions to COM
4384 * errors.
4385 *
4386 * Note that this method will not catch unexpected errors so it may still
4387 * throw something.
4388 *
4389 * @param aTree Tree to save.
4390 * @param aFile File to save the tree to.
4391 * @param aFormatVersion Where to store the (recent) format version of the
4392 * saved settings tree on success.
4393 */
4394/* static */
4395HRESULT VirtualBox::saveSettingsTree (settings::TreeBackend &aTree,
4396 settings::File &aFile,
4397 Utf8Str &aFormatVersion)
4398{
4399 using namespace settings;
4400
4401 try
4402 {
4403 aTree.write (aFile);
4404
4405 /* set the current settings file version to the most recent version on
4406 * success. See also VirtualBox::loadSettingsTree(). */
4407 if (aFormatVersion != VBOX_XML_VERSION_FULL)
4408 aFormatVersion = VBOX_XML_VERSION_FULL;
4409 }
4410 catch (const EIPRTFailure &err)
4411 {
4412 /* this is the only expected exception for now */
4413 return setError (E_FAIL,
4414 tr ("Could not save the settings file '%s' (%Vrc)"),
4415 aFile.uri(), err.rc());
4416 }
4417
4418 return S_OK;
4419}
4420
4421/**
4422 * Creates a backup copy of the given settings file by suffixing it with the
4423 * supplied version format string and optionally with numbers from .0 to .9
4424 * if the backup file already exists.
4425 *
4426 * @param aFileName Orignal settings file name.
4427 * @param aOldFormat Version of the original format.
4428 * @param aBakFileName File name of the created backup copy (only on success).
4429 */
4430/* static */
4431HRESULT VirtualBox::backupSettingsFile (const Bstr &aFileName,
4432 const Utf8Str &aOldFormat,
4433 Bstr &aBakFileName)
4434{
4435 Utf8Str of = aFileName;
4436 Utf8Str nf = Utf8StrFmt ("%s.%s.bak", of.raw(), aOldFormat.raw());
4437
4438 int vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
4439 NULL, NULL);
4440
4441 /* try progressive suffix from .0 to .9 on failure */
4442 if (vrc == VERR_ALREADY_EXISTS)
4443 {
4444 Utf8Str tmp = nf;
4445 for (int i = 0; i <= 9 && RT_FAILURE (vrc); ++ i)
4446 {
4447 nf = Utf8StrFmt ("%s.%d", tmp.raw(), i);
4448 vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
4449 NULL, NULL);
4450 }
4451 }
4452
4453 if (RT_FAILURE (vrc))
4454 return setError (E_FAIL,
4455 tr ("Could not copy the settings file '%s' to '%s' (%Vrc)"),
4456 of.raw(), nf.raw(), vrc);
4457
4458 aBakFileName = nf;
4459
4460 return S_OK;
4461}
4462
4463/**
4464 * Handles unexpected exceptions by turning them into COM errors in release
4465 * builds or by hitting a breakpoint in the release builds.
4466 *
4467 * Usage pattern:
4468 * @code
4469 try
4470 {
4471 // ...
4472 }
4473 catch (LaLalA)
4474 {
4475 // ...
4476 }
4477 catch (...)
4478 {
4479 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
4480 }
4481 * @endcode
4482 *
4483 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
4484 */
4485/* static */
4486HRESULT VirtualBox::handleUnexpectedExceptions (RT_SRC_POS_DECL)
4487{
4488 try
4489 {
4490 /* rethrow the current exception */
4491 throw;
4492 }
4493 catch (const std::exception &err)
4494 {
4495 ComAssertMsgFailedPos (("Unexpected exception '%s' (%s)\n",
4496 typeid (err).name(), err.what()),
4497 pszFile, iLine, pszFunction);
4498 return E_FAIL;
4499 }
4500 catch (...)
4501 {
4502 ComAssertMsgFailedPos (("Unknown exception\n"),
4503 pszFile, iLine, pszFunction);
4504 return E_FAIL;
4505 }
4506
4507 /* should not get here */
4508 AssertFailed();
4509 return E_FAIL;
4510}
4511
4512/**
4513 * Helper to register the DVD image.
4514 *
4515 * @param aImage object to register
4516 * @param aOnStartUp whether this method called during VirtualBox init or not
4517 *
4518 * @return COM status code
4519 *
4520 * @note Locks objects!
4521 */
4522HRESULT VirtualBox::registerDVDImage (DVDImage *aImage, bool aOnStartUp)
4523{
4524 AssertReturn (aImage, E_INVALIDARG);
4525
4526 AutoCaller autoCaller (this);
4527 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4528
4529 AutoWriteLock alock (this);
4530
4531 HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
4532 aImage->filePathFull());
4533 CheckComRCReturnRC (rc);
4534
4535 /* add to the collection */
4536 mData.mDVDImages.push_back (aImage);
4537
4538 /* save global config file if we're supposed to */
4539 if (!aOnStartUp)
4540 rc = saveSettings();
4541
4542 return rc;
4543}
4544
4545/**
4546 * Helper to register the floppy image.
4547 *
4548 * @param aImage object to register
4549 * @param aOnStartUp whether this method called during VirtualBox init or not
4550 *
4551 * @return COM status code
4552 *
4553 * @note Locks objects!
4554 */
4555HRESULT VirtualBox::registerFloppyImage (FloppyImage *aImage, bool aOnStartUp)
4556{
4557 AssertReturn (aImage, E_INVALIDARG);
4558
4559 AutoCaller autoCaller (this);
4560 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4561
4562 AutoWriteLock alock (this);
4563
4564 HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
4565 aImage->filePathFull());
4566 CheckComRCReturnRC (rc);
4567
4568 /* add to the collection */
4569 mData.mFloppyImages.push_back (aImage);
4570
4571 /* save global config file if we're supposed to */
4572 if (!aOnStartUp)
4573 rc = saveSettings();
4574
4575 return rc;
4576}
4577
4578/**
4579 * Helper to lock the VirtualBox configuration for write access.
4580 *
4581 * @note This method is not thread safe (must be called only from #init()
4582 * or #uninit()).
4583 *
4584 * @note If the configuration file is not found, the method returns
4585 * S_OK, but subsequent #isConfigLocked() will return FALSE. This is used
4586 * in some places to determine the (valid) situation when no config file
4587 * exists yet, and therefore a new one should be created from scatch.
4588 */
4589HRESULT VirtualBox::lockConfig()
4590{
4591 AutoCaller autoCaller (this);
4592 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4593 AssertReturn (autoCaller.state() == InInit, E_FAIL);
4594
4595 HRESULT rc = S_OK;
4596
4597 Assert (!isConfigLocked());
4598 if (!isConfigLocked())
4599 {
4600 /* open the associated config file */
4601 int vrc = RTFileOpen (&mData.mCfgFile.mHandle,
4602 Utf8Str (mData.mCfgFile.mName),
4603 RTFILE_O_READWRITE | RTFILE_O_OPEN |
4604 RTFILE_O_DENY_WRITE);
4605 if (VBOX_FAILURE (vrc))
4606 {
4607 mData.mCfgFile.mHandle = NIL_RTFILE;
4608
4609 /*
4610 * It is ok if the file is not found, it will be created by
4611 * init(). Otherwise return an error.
4612 */
4613 if (vrc != VERR_FILE_NOT_FOUND)
4614 rc = setError (E_FAIL,
4615 tr ("Could not lock the settings file '%ls' (%Vrc)"),
4616 mData.mCfgFile.mName.raw(), vrc);
4617 }
4618
4619 LogFlowThisFunc (("mCfgFile.mName='%ls', mCfgFile.mHandle=%d, rc=%08X\n",
4620 mData.mCfgFile.mName.raw(), mData.mCfgFile.mHandle, rc));
4621 }
4622
4623 return rc;
4624}
4625
4626/**
4627 * Helper to unlock the VirtualBox configuration from write access.
4628 *
4629 * @note This method is not thread safe (must be called only from #init()
4630 * or #uninit()).
4631 */
4632HRESULT VirtualBox::unlockConfig()
4633{
4634 AutoCaller autoCaller (this);
4635 AssertComRCReturn (autoCaller.rc(), E_FAIL);
4636 AssertReturn (autoCaller.state() == InUninit, E_FAIL);
4637
4638 HRESULT rc = S_OK;
4639
4640 if (isConfigLocked())
4641 {
4642 RTFileFlush (mData.mCfgFile.mHandle);
4643 RTFileClose (mData.mCfgFile.mHandle);
4644 /** @todo flush the directory too. */
4645 mData.mCfgFile.mHandle = NIL_RTFILE;
4646 LogFlowThisFunc (("\n"));
4647 }
4648
4649 return rc;
4650}
4651
4652/**
4653 * Thread function that watches the termination of all client processes
4654 * that have opened sessions using IVirtualBox::OpenSession()
4655 */
4656// static
4657DECLCALLBACK(int) VirtualBox::ClientWatcher (RTTHREAD thread, void *pvUser)
4658{
4659 LogFlowFuncEnter();
4660
4661 VirtualBox *that = (VirtualBox *) pvUser;
4662 Assert (that);
4663
4664 SessionMachineVector machines;
4665 MachineVector spawnedMachines;
4666
4667 size_t cnt = 0;
4668 size_t cntSpawned = 0;
4669
4670#if defined(RT_OS_WINDOWS)
4671
4672 HRESULT hrc = CoInitializeEx (NULL,
4673 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
4674 COINIT_SPEED_OVER_MEMORY);
4675 AssertComRC (hrc);
4676
4677 /// @todo (dmik) processes reaping!
4678
4679 HANDLE handles [MAXIMUM_WAIT_OBJECTS];
4680 handles [0] = that->mWatcherData.mUpdateReq;
4681
4682 do
4683 {
4684 AutoCaller autoCaller (that);
4685 /* VirtualBox has been early uninitialized, terminate */
4686 if (!autoCaller.isOk())
4687 break;
4688
4689 do
4690 {
4691 /* release the caller to let uninit() ever proceed */
4692 autoCaller.release();
4693
4694 DWORD rc = ::WaitForMultipleObjects (1 + cnt + cntSpawned,
4695 handles, FALSE, INFINITE);
4696
4697 /* Restore the caller before using VirtualBox. If it fails, this
4698 * means VirtualBox is being uninitialized and we must terminate. */
4699 autoCaller.add();
4700 if (!autoCaller.isOk())
4701 break;
4702
4703 bool update = false;
4704
4705 if (rc == WAIT_OBJECT_0)
4706 {
4707 /* update event is signaled */
4708 update = true;
4709 }
4710 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4711 {
4712 /* machine mutex is released */
4713 (machines [rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4714 update = true;
4715 }
4716 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4717 {
4718 /* machine mutex is abandoned due to client process termination */
4719 (machines [rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4720 update = true;
4721 }
4722 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
4723 {
4724 /* spawned VM process has terminated (normally or abnormally) */
4725 (spawnedMachines [rc - WAIT_OBJECT_0 - cnt - 1])->
4726 checkForSpawnFailure();
4727 update = true;
4728 }
4729
4730 if (update)
4731 {
4732 /* close old process handles */
4733 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4734 CloseHandle (handles [i]);
4735
4736 AutoReadLock thatLock (that);
4737
4738 /* obtain a new set of opened machines */
4739 cnt = 0;
4740 machines.clear();
4741
4742 for (MachineList::iterator it = that->mData.mMachines.begin();
4743 it != that->mData.mMachines.end(); ++ it)
4744 {
4745 /// @todo handle situations with more than 64 objects
4746 AssertMsgBreak ((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
4747 ("MAXIMUM_WAIT_OBJECTS reached"));
4748
4749 ComObjPtr <SessionMachine> sm;
4750 HANDLE ipcSem;
4751 if ((*it)->isSessionOpen (sm, &ipcSem))
4752 {
4753 machines.push_back (sm);
4754 handles [1 + cnt] = ipcSem;
4755 ++ cnt;
4756 }
4757 }
4758
4759 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4760
4761 /* obtain a new set of spawned machines */
4762 cntSpawned = 0;
4763 spawnedMachines.clear();
4764
4765 for (MachineList::iterator it = that->mData.mMachines.begin();
4766 it != that->mData.mMachines.end(); ++ it)
4767 {
4768 /// @todo handle situations with more than 64 objects
4769 AssertMsgBreak ((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
4770 ("MAXIMUM_WAIT_OBJECTS reached"));
4771
4772 RTPROCESS pid;
4773 if ((*it)->isSessionSpawning (&pid))
4774 {
4775 HANDLE ph = OpenProcess (SYNCHRONIZE, FALSE, pid);
4776 AssertMsg (ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
4777 pid, GetLastError()));
4778 if (rc == 0)
4779 {
4780 spawnedMachines.push_back (*it);
4781 handles [1 + cnt + cntSpawned] = ph;
4782 ++ cntSpawned;
4783 }
4784 }
4785 }
4786
4787 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4788 }
4789 }
4790 while (true);
4791 }
4792 while (0);
4793
4794 /* close old process handles */
4795 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4796 CloseHandle (handles [i]);
4797
4798 /* release sets of machines if any */
4799 machines.clear();
4800 spawnedMachines.clear();
4801
4802 ::CoUninitialize();
4803
4804#elif defined (RT_OS_OS2)
4805
4806 /// @todo (dmik) processes reaping!
4807
4808 /* according to PMREF, 64 is the maximum for the muxwait list */
4809 SEMRECORD handles [64];
4810
4811 HMUX muxSem = NULLHANDLE;
4812
4813 do
4814 {
4815 AutoCaller autoCaller (that);
4816 /* VirtualBox has been early uninitialized, terminate */
4817 if (!autoCaller.isOk())
4818 break;
4819
4820 do
4821 {
4822 /* release the caller to let uninit() ever proceed */
4823 autoCaller.release();
4824
4825 int vrc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4826
4827 /* Restore the caller before using VirtualBox. If it fails, this
4828 * means VirtualBox is being uninitialized and we must terminate. */
4829 autoCaller.add();
4830 if (!autoCaller.isOk())
4831 break;
4832
4833 bool update = false;
4834 bool updateSpawned = false;
4835
4836 if (VBOX_SUCCESS (vrc))
4837 {
4838 /* update event is signaled */
4839 update = true;
4840 updateSpawned = true;
4841 }
4842 else
4843 {
4844 AssertMsg (vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4845 ("RTSemEventWait returned %Vrc\n", vrc));
4846
4847 /* are there any mutexes? */
4848 if (cnt > 0)
4849 {
4850 /* figure out what's going on with machines */
4851
4852 unsigned long semId = 0;
4853 APIRET arc = ::DosWaitMuxWaitSem (muxSem,
4854 SEM_IMMEDIATE_RETURN, &semId);
4855
4856 if (arc == NO_ERROR)
4857 {
4858 /* machine mutex is normally released */
4859 Assert (semId >= 0 && semId < cnt);
4860 if (semId >= 0 && semId < cnt)
4861 {
4862#ifdef DEBUG
4863 {
4864 AutoReadLock machieLock (machines [semId]);
4865 LogFlowFunc (("released mutex: machine='%ls'\n",
4866 machines [semId]->name().raw()));
4867 }
4868#endif
4869 machines [semId]->checkForDeath();
4870 }
4871 update = true;
4872 }
4873 else if (arc == ERROR_SEM_OWNER_DIED)
4874 {
4875 /* machine mutex is abandoned due to client process
4876 * termination; find which mutex is in the Owner Died
4877 * state */
4878 for (size_t i = 0; i < cnt; ++ i)
4879 {
4880 PID pid; TID tid;
4881 unsigned long reqCnt;
4882 arc = DosQueryMutexSem ((HMTX) handles [i].hsemCur, &pid,
4883 &tid, &reqCnt);
4884 if (arc == ERROR_SEM_OWNER_DIED)
4885 {
4886 /* close the dead mutex as asked by PMREF */
4887 ::DosCloseMutexSem ((HMTX) handles [i].hsemCur);
4888
4889 Assert (i >= 0 && i < cnt);
4890 if (i >= 0 && i < cnt)
4891 {
4892#ifdef DEBUG
4893 {
4894 AutoReadLock machieLock (machines [semId]);
4895 LogFlowFunc (("mutex owner dead: machine='%ls'\n",
4896 machines [i]->name().raw()));
4897 }
4898#endif
4899 machines [i]->checkForDeath();
4900 }
4901 }
4902 }
4903 update = true;
4904 }
4905 else
4906 AssertMsg (arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4907 ("DosWaitMuxWaitSem returned %d\n", arc));
4908 }
4909
4910 /* are there any spawning sessions? */
4911 if (cntSpawned > 0)
4912 {
4913 for (size_t i = 0; i < cntSpawned; ++ i)
4914 updateSpawned |= (spawnedMachines [i])->
4915 checkForSpawnFailure();
4916 }
4917 }
4918
4919 if (update || updateSpawned)
4920 {
4921 AutoReadLock thatLock (that);
4922
4923 if (update)
4924 {
4925 /* close the old muxsem */
4926 if (muxSem != NULLHANDLE)
4927 ::DosCloseMuxWaitSem (muxSem);
4928
4929 /* obtain a new set of opened machines */
4930 cnt = 0;
4931 machines.clear();
4932
4933 for (MachineList::iterator it = that->mData.mMachines.begin();
4934 it != that->mData.mMachines.end(); ++ it)
4935 {
4936 /// @todo handle situations with more than 64 objects
4937 AssertMsg (cnt <= 64 /* according to PMREF */,
4938 ("maximum of 64 mutex semaphores reached (%d)",
4939 cnt));
4940
4941 ComObjPtr <SessionMachine> sm;
4942 HMTX ipcSem;
4943 if ((*it)->isSessionOpen (sm, &ipcSem))
4944 {
4945 machines.push_back (sm);
4946 handles [cnt].hsemCur = (HSEM) ipcSem;
4947 handles [cnt].ulUser = cnt;
4948 ++ cnt;
4949 }
4950 }
4951
4952 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4953
4954 if (cnt > 0)
4955 {
4956 /* create a new muxsem */
4957 APIRET arc = ::DosCreateMuxWaitSem (NULL, &muxSem, cnt,
4958 handles,
4959 DCMW_WAIT_ANY);
4960 AssertMsg (arc == NO_ERROR,
4961 ("DosCreateMuxWaitSem returned %d\n", arc));
4962 NOREF(arc);
4963 }
4964 }
4965
4966 if (updateSpawned)
4967 {
4968 /* obtain a new set of spawned machines */
4969 spawnedMachines.clear();
4970
4971 for (MachineList::iterator it = that->mData.mMachines.begin();
4972 it != that->mData.mMachines.end(); ++ it)
4973 {
4974 if ((*it)->isSessionSpawning())
4975 spawnedMachines.push_back (*it);
4976 }
4977
4978 cntSpawned = spawnedMachines.size();
4979 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4980 }
4981 }
4982 }
4983 while (true);
4984 }
4985 while (0);
4986
4987 /* close the muxsem */
4988 if (muxSem != NULLHANDLE)
4989 ::DosCloseMuxWaitSem (muxSem);
4990
4991 /* release sets of machines if any */
4992 machines.clear();
4993 spawnedMachines.clear();
4994
4995#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4996
4997 bool update = false;
4998 bool updateSpawned = false;
4999
5000 do
5001 {
5002 AutoCaller autoCaller (that);
5003 if (!autoCaller.isOk())
5004 break;
5005
5006 do
5007 {
5008 /* release the caller to let uninit() ever proceed */
5009 autoCaller.release();
5010
5011 int rc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
5012
5013 /*
5014 * Restore the caller before using VirtualBox. If it fails, this
5015 * means VirtualBox is being uninitialized and we must terminate.
5016 */
5017 autoCaller.add();
5018 if (!autoCaller.isOk())
5019 break;
5020
5021 if (VBOX_SUCCESS (rc) || update || updateSpawned)
5022 {
5023 /* VBOX_SUCCESS (rc) means an update event is signaled */
5024
5025 AutoReadLock thatLock (that);
5026
5027 if (VBOX_SUCCESS (rc) || update)
5028 {
5029 /* obtain a new set of opened machines */
5030 machines.clear();
5031
5032 for (MachineList::iterator it = that->mData.mMachines.begin();
5033 it != that->mData.mMachines.end(); ++ it)
5034 {
5035 ComObjPtr <SessionMachine> sm;
5036 if ((*it)->isSessionOpen (sm))
5037 machines.push_back (sm);
5038 }
5039
5040 cnt = machines.size();
5041 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
5042 }
5043
5044 if (VBOX_SUCCESS (rc) || updateSpawned)
5045 {
5046 /* obtain a new set of spawned machines */
5047 spawnedMachines.clear();
5048
5049 for (MachineList::iterator it = that->mData.mMachines.begin();
5050 it != that->mData.mMachines.end(); ++ it)
5051 {
5052 if ((*it)->isSessionSpawning())
5053 spawnedMachines.push_back (*it);
5054 }
5055
5056 cntSpawned = spawnedMachines.size();
5057 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
5058 }
5059 }
5060
5061 update = false;
5062 for (size_t i = 0; i < cnt; ++ i)
5063 update |= (machines [i])->checkForDeath();
5064
5065 updateSpawned = false;
5066 for (size_t i = 0; i < cntSpawned; ++ i)
5067 updateSpawned |= (spawnedMachines [i])->checkForSpawnFailure();
5068
5069 /* reap child processes */
5070 {
5071 AutoWriteLock alock (that);
5072 if (that->mWatcherData.mProcesses.size())
5073 {
5074 LogFlowFunc (("UPDATE: child process count = %d\n",
5075 that->mWatcherData.mProcesses.size()));
5076 ClientWatcherData::ProcessList::iterator it =
5077 that->mWatcherData.mProcesses.begin();
5078 while (it != that->mWatcherData.mProcesses.end())
5079 {
5080 RTPROCESS pid = *it;
5081 RTPROCSTATUS status;
5082 int vrc = ::RTProcWait (pid, RTPROCWAIT_FLAGS_NOBLOCK,
5083 &status);
5084 if (vrc == VINF_SUCCESS)
5085 {
5086 LogFlowFunc (("pid %d (%x) was reaped, "
5087 "status=%d, reason=%d\n",
5088 pid, pid, status.iStatus,
5089 status.enmReason));
5090 it = that->mWatcherData.mProcesses.erase (it);
5091 }
5092 else
5093 {
5094 LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Vrc\n",
5095 pid, pid, vrc));
5096 if (vrc != VERR_PROCESS_RUNNING)
5097 {
5098 /* remove the process if it is not already running */
5099 it = that->mWatcherData.mProcesses.erase (it);
5100 }
5101 else
5102 ++ it;
5103 }
5104 }
5105 }
5106 }
5107 }
5108 while (true);
5109 }
5110 while (0);
5111
5112 /* release sets of machines if any */
5113 machines.clear();
5114 spawnedMachines.clear();
5115
5116#else
5117# error "Port me!"
5118#endif
5119
5120 LogFlowFuncLeave();
5121 return 0;
5122}
5123
5124/**
5125 * Thread function that handles custom events posted using #postEvent().
5126 */
5127// static
5128DECLCALLBACK(int) VirtualBox::AsyncEventHandler (RTTHREAD thread, void *pvUser)
5129{
5130 LogFlowFuncEnter();
5131
5132 AssertReturn (pvUser, VERR_INVALID_POINTER);
5133
5134 // create an event queue for the current thread
5135 EventQueue *eventQ = new EventQueue();
5136 AssertReturn (eventQ, VERR_NO_MEMORY);
5137
5138 // return the queue to the one who created this thread
5139 *(static_cast <EventQueue **> (pvUser)) = eventQ;
5140 // signal that we're ready
5141 RTThreadUserSignal (thread);
5142
5143 BOOL ok = TRUE;
5144 Event *event = NULL;
5145
5146 while ((ok = eventQ->waitForEvent (&event)) && event)
5147 eventQ->handleEvent (event);
5148
5149 AssertReturn (ok, VERR_GENERAL_FAILURE);
5150
5151 delete eventQ;
5152
5153 LogFlowFuncLeave();
5154
5155 return 0;
5156}
5157
5158////////////////////////////////////////////////////////////////////////////////
5159
5160/**
5161 * Takes the current list of registered callbacks of the managed VirtualBox
5162 * instance, and calls #handleCallback() for every callback item from the
5163 * list, passing the item as an argument.
5164 *
5165 * @note Locks the managed VirtualBox object for reading but leaves the lock
5166 * before iterating over callbacks and calling their methods.
5167 */
5168void *VirtualBox::CallbackEvent::handler()
5169{
5170 if (mVirtualBox.isNull())
5171 return NULL;
5172
5173 AutoCaller autoCaller (mVirtualBox);
5174 if (!autoCaller.isOk())
5175 {
5176 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
5177 "the callback event is discarded!\n",
5178 autoCaller.state()));
5179 /* We don't need mVirtualBox any more, so release it */
5180 mVirtualBox.setNull();
5181 return NULL;
5182 }
5183
5184 CallbackVector callbacks;
5185 {
5186 /* Make a copy to release the lock before iterating */
5187 AutoReadLock alock (mVirtualBox);
5188 callbacks = CallbackVector (mVirtualBox->mData.mCallbacks.begin(),
5189 mVirtualBox->mData.mCallbacks.end());
5190 /* We don't need mVirtualBox any more, so release it */
5191 mVirtualBox.setNull();
5192 }
5193
5194 for (VirtualBox::CallbackVector::const_iterator it = callbacks.begin();
5195 it != callbacks.end(); ++ it)
5196 handleCallback (*it);
5197
5198 return NULL;
5199}
5200
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use