VirtualBox

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

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

Main: Don't write a zero char to the end of a virgin global or machine XML settings file when creating it.

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