VirtualBox

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

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

Main: Fixed: Closing sessions was not always noticed by the server which caused a hang during poweroff (regression after adding spawning session watchdog).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use