VirtualBox

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

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

Reduce a few cases of words doubled.

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