VirtualBox

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

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

Added QNX guest OS type. Cleaned up the guest OS table.

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