VirtualBox

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

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

FE/Qt4: 2883: Structure OS list. Feature request implemented in base designed variant.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette