VirtualBox

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

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

Main: Perform case-insensitive Guest OS type ID comparisons and support old (pre-2.1) type ID values (#3549).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 138.4 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 16216 2009-01-24 16:30:43Z 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_OBJECT_NOT_FOUND,
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 /* Old ID to new ID conversion table. See r39691 for a source */
1307 static const wchar_t *kOldNewIDs[] =
1308 {
1309 L"unknown", L"Other",
1310 L"win31", L"Windows31",
1311 L"win95", L"Windows95",
1312 L"win98", L"Windows98",
1313 L"winme", L"WindowsMe",
1314 L"winnt4", L"WindowsNT4",
1315 L"win2k", L"Windows2000",
1316 L"winxp", L"WindowsXP",
1317 L"win2k3", L"Windows2003",
1318 L"winvista", L"WindowsVista",
1319 L"win2k8", L"Windows2008",
1320 L"ecs", L"OS2eCS",
1321 L"fedoracore", L"Fedora",
1322 /* the rest is covered by the case-insensitive comparison */
1323 };
1324
1325 CheckComArgNotNull (aType);
1326
1327 AutoCaller autoCaller (this);
1328 CheckComRCReturnRC (autoCaller.rc());
1329
1330 /* first, look for a substitution */
1331 Bstr id = aId;
1332 for (size_t i = 0; i < RT_ELEMENTS (kOldNewIDs) / 2; i += 2)
1333 {
1334 if (id == kOldNewIDs [i])
1335 {
1336 id = kOldNewIDs [i + 1];
1337 break;
1338 }
1339 }
1340
1341 *aType = NULL;
1342
1343 AutoReadLock alock (this);
1344
1345 for (GuestOSTypeList::iterator it = mData.mGuestOSTypes.begin();
1346 it != mData.mGuestOSTypes.end();
1347 ++ it)
1348 {
1349 const Bstr &typeId = (*it)->id();
1350 AssertMsg (!!typeId, ("ID must not be NULL"));
1351 if (typeId.compareIgnoreCase (id) == 0)
1352 {
1353 (*it).queryInterfaceTo (aType);
1354 break;
1355 }
1356 }
1357
1358 return (*aType) ? S_OK :
1359 setError (E_INVALIDARG,
1360 tr ("'%ls' is not a valid Guest OS type"),
1361 aId);
1362}
1363
1364STDMETHODIMP
1365VirtualBox::CreateSharedFolder (IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable)
1366{
1367 CheckComArgNotNull(aName);
1368 CheckComArgNotNull(aHostPath);
1369
1370 AutoCaller autoCaller (this);
1371 CheckComRCReturnRC (autoCaller.rc());
1372
1373 return setError (E_NOTIMPL, "Not yet implemented");
1374}
1375
1376STDMETHODIMP VirtualBox::RemoveSharedFolder (IN_BSTR aName)
1377{
1378 CheckComArgNotNull(aName);
1379
1380 AutoCaller autoCaller (this);
1381 CheckComRCReturnRC (autoCaller.rc());
1382
1383 return setError (E_NOTIMPL, "Not yet implemented");
1384}
1385
1386/**
1387 * @note Locks this object for reading.
1388 */
1389STDMETHODIMP VirtualBox::
1390GetNextExtraDataKey (IN_BSTR aKey, BSTR *aNextKey, BSTR *aNextValue)
1391{
1392 CheckComArgNotNull(aNextKey);
1393
1394 AutoCaller autoCaller (this);
1395 CheckComRCReturnRC (autoCaller.rc());
1396
1397 /* start with nothing found */
1398 *aNextKey = NULL;
1399 if (aNextValue)
1400 *aNextValue = NULL;
1401
1402 HRESULT rc = S_OK;
1403
1404 /* serialize file access (prevent writes) */
1405 AutoReadLock alock (this);
1406
1407 try
1408 {
1409 using namespace settings;
1410 using namespace xml;
1411
1412 /* load the settings file (we don't reuse the existing handle but
1413 * request a new one to allow for concurrent multi-threaded reads) */
1414 File file (File::Mode_Read, Utf8Str (mData.mCfgFile.mName));
1415 XmlTreeBackend tree;
1416
1417 rc = VirtualBox::loadSettingsTree_Again (tree, file);
1418 CheckComRCReturnRC (rc);
1419
1420 Key globalNode = tree.rootKey().key ("Global");
1421 Key extraDataNode = globalNode.findKey ("ExtraData");
1422
1423 if (!extraDataNode.isNull())
1424 {
1425 Key::List items = extraDataNode.keys ("ExtraDataItem");
1426 if (items.size())
1427 {
1428 for (Key::List::const_iterator it = items.begin();
1429 it != items.end(); ++ it)
1430 {
1431 Bstr key = (*it).stringValue ("name");
1432
1433 /* if we're supposed to return the first one */
1434 if (aKey == NULL)
1435 {
1436 key.cloneTo (aNextKey);
1437 if (aNextValue)
1438 {
1439 Bstr val = (*it).stringValue ("value");
1440 val.cloneTo (aNextValue);
1441 }
1442 return S_OK;
1443 }
1444
1445 /* did we find the key we're looking for? */
1446 if (key == aKey)
1447 {
1448 ++ it;
1449 /* is there another item? */
1450 if (it != items.end())
1451 {
1452 Bstr key = (*it).stringValue ("name");
1453 key.cloneTo (aNextKey);
1454 if (aNextValue)
1455 {
1456 Bstr val = (*it).stringValue ("value");
1457 val.cloneTo (aNextValue);
1458 }
1459 }
1460 /* else it's the last one, arguments are already NULL */
1461 return S_OK;
1462 }
1463 }
1464 }
1465 }
1466
1467 /* Here we are when a) there are no items at all or b) there are items
1468 * but none of them equals to the requested non-NULL key. b) is an
1469 * error as well as a) if the key is non-NULL. When the key is NULL
1470 * (which is the case only when there are no items), we just fall
1471 * through to return NULLs and S_OK. */
1472
1473 if (aKey != NULL)
1474 return setError (VBOX_E_OBJECT_NOT_FOUND,
1475 tr ("Could not find the extra data key '%ls'"), aKey);
1476 }
1477 catch (...)
1478 {
1479 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
1480 }
1481
1482 return rc;
1483}
1484
1485/**
1486 * @note Locks this object for reading.
1487 */
1488STDMETHODIMP VirtualBox::GetExtraData (IN_BSTR aKey, BSTR *aValue)
1489{
1490 CheckComArgNotNull(aKey);
1491 CheckComArgNotNull(aValue);
1492
1493 AutoCaller autoCaller (this);
1494 CheckComRCReturnRC (autoCaller.rc());
1495
1496 /* start with nothing found */
1497 *aValue = NULL;
1498
1499 HRESULT rc = S_OK;
1500
1501 /* serialize file access (prevent writes) */
1502 AutoReadLock alock (this);
1503
1504 try
1505 {
1506 using namespace settings;
1507 using namespace xml;
1508
1509 /* load the settings file (we don't reuse the existing handle but
1510 * request a new one to allow for concurrent multi-threaded reads) */
1511 File file (File::Mode_Read, Utf8Str (mData.mCfgFile.mName));
1512 XmlTreeBackend tree;
1513
1514 rc = VirtualBox::loadSettingsTree_Again (tree, file);
1515 CheckComRCReturnRC (rc);
1516
1517 const Utf8Str key = aKey;
1518
1519 Key globalNode = tree.rootKey().key ("Global");
1520 Key extraDataNode = globalNode.findKey ("ExtraData");
1521
1522 if (!extraDataNode.isNull())
1523 {
1524 /* check if the key exists */
1525 Key::List items = extraDataNode.keys ("ExtraDataItem");
1526 for (Key::List::const_iterator it = items.begin();
1527 it != items.end(); ++ it)
1528 {
1529 if (key == (*it).stringValue ("name"))
1530 {
1531 Bstr val = (*it).stringValue ("value");
1532 val.cloneTo (aValue);
1533 break;
1534 }
1535 }
1536 }
1537 }
1538 catch (...)
1539 {
1540 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
1541 }
1542
1543 return rc;
1544}
1545
1546/**
1547 * @note Locks this object for writing.
1548 */
1549STDMETHODIMP VirtualBox::SetExtraData (IN_BSTR aKey, IN_BSTR aValue)
1550{
1551 CheckComArgNotNull(aKey);
1552
1553 AutoCaller autoCaller (this);
1554 CheckComRCReturnRC (autoCaller.rc());
1555
1556 Guid emptyGuid;
1557
1558 bool changed = false;
1559 HRESULT rc = S_OK;
1560
1561 /* serialize file access (prevent concurrent reads and writes) */
1562 AutoWriteLock alock (this);
1563
1564 try
1565 {
1566 using namespace settings;
1567 using namespace xml;
1568
1569 /* load the settings file */
1570 File file (mData.mCfgFile.mHandle, Utf8Str (mData.mCfgFile.mName));
1571 XmlTreeBackend tree;
1572
1573 rc = VirtualBox::loadSettingsTree_ForUpdate (tree, file);
1574 CheckComRCReturnRC (rc);
1575
1576 const Utf8Str key = aKey;
1577 Bstr oldVal;
1578
1579 Key globalNode = tree.rootKey().key ("Global");
1580 Key extraDataNode = globalNode.createKey ("ExtraData");
1581 Key extraDataItemNode;
1582
1583 Key::List items = extraDataNode.keys ("ExtraDataItem");
1584 for (Key::List::const_iterator it = items.begin();
1585 it != items.end(); ++ it)
1586 {
1587 if (key == (*it).stringValue ("name"))
1588 {
1589 extraDataItemNode = *it;
1590 oldVal = (*it).stringValue ("value");
1591 break;
1592 }
1593 }
1594
1595 /* When no key is found, oldVal is null */
1596 changed = oldVal != aValue;
1597
1598 if (changed)
1599 {
1600 /* ask for permission from all listeners */
1601 Bstr error;
1602 if (!onExtraDataCanChange (Guid::Empty, aKey, aValue, error))
1603 {
1604 const char *sep = error.isEmpty() ? "" : ": ";
1605 CBSTR err = error.isNull() ? (CBSTR) L"" : error.raw();
1606 LogWarningFunc (("Someone vetoed! Change refused%s%ls\n",
1607 sep, err));
1608 return setError (E_ACCESSDENIED,
1609 tr ("Could not set extra data because someone refused "
1610 "the requested change of '%ls' to '%ls'%s%ls"),
1611 aKey, aValue, sep, err);
1612 }
1613
1614 if (aValue != NULL)
1615 {
1616 if (extraDataItemNode.isNull())
1617 {
1618 extraDataItemNode = extraDataNode.appendKey ("ExtraDataItem");
1619 extraDataItemNode.setStringValue ("name", key);
1620 }
1621 extraDataItemNode.setStringValue ("value", Utf8Str (aValue));
1622 }
1623 else
1624 {
1625 /* An old value does for sure exist here (XML schema
1626 * guarantees that "value" may not be absent in the
1627 * <ExtraDataItem> element). */
1628 Assert (!extraDataItemNode.isNull());
1629 extraDataItemNode.zap();
1630 }
1631
1632 /* save settings on success */
1633 rc = VirtualBox::saveSettingsTree (tree, file,
1634 mData.mSettingsFileVersion);
1635 CheckComRCReturnRC (rc);
1636 }
1637 }
1638 catch (...)
1639 {
1640 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
1641 }
1642
1643 /* fire a notification */
1644 if (SUCCEEDED (rc) && changed)
1645 onExtraDataChange (Guid::Empty, aKey, aValue);
1646
1647 return rc;
1648}
1649
1650/**
1651 * @note Locks objects!
1652 */
1653STDMETHODIMP VirtualBox::OpenSession (ISession *aSession, IN_GUID aMachineId)
1654{
1655 CheckComArgNotNull(aSession);
1656
1657 AutoCaller autoCaller (this);
1658 CheckComRCReturnRC (autoCaller.rc());
1659
1660 Guid id = aMachineId;
1661 ComObjPtr <Machine> machine;
1662
1663 HRESULT rc = findMachine (id, true /* setError */, &machine);
1664 CheckComRCReturnRC (rc);
1665
1666 /* check the session state */
1667 SessionState_T state;
1668 rc = aSession->COMGETTER(State) (&state);
1669 CheckComRCReturnRC (rc);
1670
1671 if (state != SessionState_Closed)
1672 return setError (VBOX_E_INVALID_OBJECT_STATE,
1673 tr ("The given session is already open or being opened"));
1674
1675 /* get the IInternalSessionControl interface */
1676 ComPtr <IInternalSessionControl> control = aSession;
1677 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1678 E_INVALIDARG);
1679
1680 rc = machine->openSession (control);
1681
1682 if (SUCCEEDED (rc))
1683 {
1684 /*
1685 * tell the client watcher thread to update the set of
1686 * machines that have open sessions
1687 */
1688 updateClientWatcher();
1689
1690 /* fire an event */
1691 onSessionStateChange (aMachineId, SessionState_Open);
1692 }
1693
1694 return rc;
1695}
1696
1697/**
1698 * @note Locks objects!
1699 */
1700STDMETHODIMP VirtualBox::OpenRemoteSession (ISession *aSession,
1701 IN_GUID aMachineId,
1702 IN_BSTR aType,
1703 IN_BSTR aEnvironment,
1704 IProgress **aProgress)
1705{
1706 CheckComArgNotNull(aSession);
1707 CheckComArgNotNull(aType);
1708 CheckComArgOutSafeArrayPointerValid(aProgress);
1709
1710 AutoCaller autoCaller (this);
1711 CheckComRCReturnRC (autoCaller.rc());
1712
1713 Guid id = aMachineId;
1714 ComObjPtr <Machine> machine;
1715
1716 HRESULT rc = findMachine (id, true /* setError */, &machine);
1717 CheckComRCReturnRC (rc);
1718
1719 /* check the session state */
1720 SessionState_T state;
1721 rc = aSession->COMGETTER(State) (&state);
1722 CheckComRCReturnRC (rc);
1723
1724 if (state != SessionState_Closed)
1725 return setError (VBOX_E_INVALID_OBJECT_STATE,
1726 tr ("The given session is already open or being opened"));
1727
1728 /* get the IInternalSessionControl interface */
1729 ComPtr <IInternalSessionControl> control = aSession;
1730 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1731 E_INVALIDARG);
1732
1733 /* create a progress object */
1734 ComObjPtr <Progress> progress;
1735 progress.createObject();
1736 progress->init (this, static_cast <IMachine *> (machine),
1737 Bstr (tr ("Spawning session")),
1738 FALSE /* aCancelable */);
1739
1740 rc = machine->openRemoteSession (control, aType, aEnvironment, progress);
1741
1742 if (SUCCEEDED (rc))
1743 {
1744 progress.queryInterfaceTo (aProgress);
1745
1746 /* signal the client watcher thread */
1747 updateClientWatcher();
1748
1749 /* fire an event */
1750 onSessionStateChange (aMachineId, SessionState_Spawning);
1751 }
1752
1753 return rc;
1754}
1755
1756/**
1757 * @note Locks objects!
1758 */
1759STDMETHODIMP VirtualBox::OpenExistingSession (ISession *aSession,
1760 IN_GUID aMachineId)
1761{
1762 CheckComArgNotNull(aSession);
1763
1764 AutoCaller autoCaller (this);
1765 CheckComRCReturnRC (autoCaller.rc());
1766
1767 Guid id = aMachineId;
1768 ComObjPtr <Machine> machine;
1769
1770 HRESULT rc = findMachine (id, true /* setError */, &machine);
1771 CheckComRCReturnRC (rc);
1772
1773 /* check the session state */
1774 SessionState_T state;
1775 rc = aSession->COMGETTER(State) (&state);
1776 CheckComRCReturnRC (rc);
1777
1778 if (state != SessionState_Closed)
1779 return setError (VBOX_E_INVALID_OBJECT_STATE,
1780 tr ("The given session is already open or being opened"));
1781
1782 /* get the IInternalSessionControl interface */
1783 ComPtr <IInternalSessionControl> control = aSession;
1784 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1785 E_INVALIDARG);
1786
1787 rc = machine->openExistingSession (control);
1788
1789 return rc;
1790}
1791
1792/**
1793 * @note Locks this object for writing.
1794 */
1795STDMETHODIMP VirtualBox::RegisterCallback (IVirtualBoxCallback *aCallback)
1796{
1797 LogFlowThisFunc (("aCallback=%p\n", aCallback));
1798
1799 CheckComArgNotNull(aCallback);
1800
1801 AutoCaller autoCaller (this);
1802 CheckComRCReturnRC (autoCaller.rc());
1803
1804 AutoWriteLock alock (this);
1805 mData.mCallbacks.push_back (CallbackList::value_type (aCallback));
1806
1807 return S_OK;
1808}
1809
1810/**
1811 * @note Locks this object for writing.
1812 */
1813STDMETHODIMP VirtualBox::UnregisterCallback (IVirtualBoxCallback *aCallback)
1814{
1815 CheckComArgNotNull(aCallback);
1816
1817 AutoCaller autoCaller (this);
1818 CheckComRCReturnRC (autoCaller.rc());
1819
1820 HRESULT rc = S_OK;
1821
1822 AutoWriteLock alock (this);
1823
1824 CallbackList::iterator it;
1825 it = std::find (mData.mCallbacks.begin(),
1826 mData.mCallbacks.end(),
1827 CallbackList::value_type (aCallback));
1828 if (it == mData.mCallbacks.end())
1829 rc = E_INVALIDARG;
1830 else
1831 mData.mCallbacks.erase (it);
1832
1833 LogFlowThisFunc (("aCallback=%p, rc=%08X\n", aCallback, rc));
1834 return rc;
1835}
1836
1837STDMETHODIMP VirtualBox::WaitForPropertyChange (IN_BSTR aWhat, ULONG aTimeout,
1838 BSTR *aChanged, BSTR *aValues)
1839{
1840 ReturnComNotImplemented();
1841}
1842
1843STDMETHODIMP VirtualBox::SaveSettings()
1844{
1845 AutoCaller autoCaller (this);
1846 CheckComRCReturnRC (autoCaller.rc());
1847
1848 return saveSettings();
1849}
1850
1851STDMETHODIMP VirtualBox::SaveSettingsWithBackup (BSTR *aBakFileName)
1852{
1853 CheckComArgNotNull(aBakFileName);
1854
1855 AutoCaller autoCaller (this);
1856 CheckComRCReturnRC (autoCaller.rc());
1857
1858 /* saveSettings() needs write lock */
1859 AutoWriteLock alock (this);
1860
1861 /* perform backup only when there was auto-conversion */
1862 if (mData.mSettingsFileVersion != VBOX_XML_VERSION_FULL)
1863 {
1864 Bstr bakFileName;
1865
1866 HRESULT rc = backupSettingsFile (mData.mCfgFile.mName,
1867 mData.mSettingsFileVersion,
1868 bakFileName);
1869 CheckComRCReturnRC (rc);
1870
1871 bakFileName.cloneTo (aBakFileName);
1872 }
1873
1874 return saveSettings();
1875}
1876
1877// public methods only for internal purposes
1878/////////////////////////////////////////////////////////////////////////////
1879
1880/**
1881 * Posts an event to the event queue that is processed asynchronously
1882 * on a dedicated thread.
1883 *
1884 * Posting events to the dedicated event queue is useful to perform secondary
1885 * actions outside any object locks -- for example, to iterate over a list
1886 * of callbacks and inform them about some change caused by some object's
1887 * method call.
1888 *
1889 * @param event event to post
1890 * (must be allocated using |new|, will be deleted automatically
1891 * by the event thread after processing)
1892 *
1893 * @note Doesn't lock any object.
1894 */
1895HRESULT VirtualBox::postEvent (Event *event)
1896{
1897 AutoCaller autoCaller (this);
1898 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
1899
1900 if (autoCaller.state() != Ready)
1901 {
1902 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
1903 "the event is discarded!\n",
1904 autoCaller.state()));
1905 return S_OK;
1906 }
1907
1908 AssertReturn (event, E_FAIL);
1909 AssertReturn (mAsyncEventQ, E_FAIL);
1910
1911 if (mAsyncEventQ->postEvent (event))
1912 return S_OK;
1913
1914 return E_FAIL;
1915}
1916
1917/**
1918 * Adds a progress to the global collection of pending operations.
1919 * Usually gets called upon progress object initialization.
1920 *
1921 * @param aProgress Operation to add to the collection.
1922 *
1923 * @note Doesn't lock objects.
1924 */
1925HRESULT VirtualBox::addProgress (IProgress *aProgress)
1926{
1927 CheckComArgNotNull(aProgress);
1928
1929 AutoCaller autoCaller (this);
1930 CheckComRCReturnRC (autoCaller.rc());
1931
1932 Guid id;
1933 HRESULT rc = aProgress->COMGETTER(Id) (id.asOutParam());
1934 AssertComRCReturnRC (rc);
1935
1936 /* protect mProgressOperations */
1937 AutoWriteLock safeLock (mSafeLock);
1938
1939 mData.mProgressOperations.insert (ProgressMap::value_type (id, aProgress));
1940 return S_OK;
1941}
1942
1943/**
1944 * Removes the progress from the global collection of pending operations.
1945 * Usually gets called upon progress completion.
1946 *
1947 * @param aId UUID of the progress operation to remove
1948 *
1949 * @note Doesn't lock objects.
1950 */
1951HRESULT VirtualBox::removeProgress (IN_GUID aId)
1952{
1953 AutoCaller autoCaller (this);
1954 CheckComRCReturnRC (autoCaller.rc());
1955
1956 ComPtr <IProgress> progress;
1957
1958 /* protect mProgressOperations */
1959 AutoWriteLock safeLock (mSafeLock);
1960
1961 size_t cnt = mData.mProgressOperations.erase (aId);
1962 Assert (cnt == 1);
1963
1964 return S_OK;
1965}
1966
1967#ifdef RT_OS_WINDOWS
1968
1969struct StartSVCHelperClientData
1970{
1971 ComObjPtr <VirtualBox> that;
1972 ComObjPtr <Progress> progress;
1973 bool privileged;
1974 VirtualBox::SVCHelperClientFunc func;
1975 void *user;
1976};
1977
1978/**
1979 * Helper method that starts a worker thread that:
1980 * - creates a pipe communication channel using SVCHlpClient;
1981 * - starts an SVC Helper process that will inherit this channel;
1982 * - executes the supplied function by passing it the created SVCHlpClient
1983 * and opened instance to communicate to the Helper process and the given
1984 * Progress object.
1985 *
1986 * The user function is supposed to communicate to the helper process
1987 * using the \a aClient argument to do the requested job and optionally expose
1988 * the progress through the \a aProgress object. The user function should never
1989 * call notifyComplete() on it: this will be done automatically using the
1990 * result code returned by the function.
1991 *
1992 * Before the user function is started, the communication channel passed to
1993 * the \a aClient argument is fully set up, the function should start using
1994 * its write() and read() methods directly.
1995 *
1996 * The \a aVrc parameter of the user function may be used to return an error
1997 * code if it is related to communication errors (for example, returned by
1998 * the SVCHlpClient members when they fail). In this case, the correct error
1999 * message using this value will be reported to the caller. Note that the
2000 * value of \a aVrc is inspected only if the user function itself returns
2001 * success.
2002 *
2003 * If a failure happens anywhere before the user function would be normally
2004 * called, it will be called anyway in special "cleanup only" mode indicated
2005 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
2006 * all the function is supposed to do is to cleanup its aUser argument if
2007 * necessary (it's assumed that the ownership of this argument is passed to
2008 * the user function once #startSVCHelperClient() returns a success, thus
2009 * making it responsible for the cleanup).
2010 *
2011 * After the user function returns, the thread will send the SVCHlpMsg::Null
2012 * message to indicate a process termination.
2013 *
2014 * @param aPrivileged |true| to start the SVC Helper process as a privileged
2015 * user that can perform administrative tasks
2016 * @param aFunc user function to run
2017 * @param aUser argument to the user function
2018 * @param aProgress progress object that will track operation completion
2019 *
2020 * @note aPrivileged is currently ignored (due to some unsolved problems in
2021 * Vista) and the process will be started as a normal (unprivileged)
2022 * process.
2023 *
2024 * @note Doesn't lock anything.
2025 */
2026HRESULT VirtualBox::startSVCHelperClient (bool aPrivileged,
2027 SVCHelperClientFunc aFunc,
2028 void *aUser, Progress *aProgress)
2029{
2030 AssertReturn (aFunc, E_POINTER);
2031 AssertReturn (aProgress, E_POINTER);
2032
2033 AutoCaller autoCaller (this);
2034 CheckComRCReturnRC (autoCaller.rc());
2035
2036 /* create the SVCHelperClientThread() argument */
2037 std::auto_ptr <StartSVCHelperClientData>
2038 d (new StartSVCHelperClientData());
2039 AssertReturn (d.get(), E_OUTOFMEMORY);
2040
2041 d->that = this;
2042 d->progress = aProgress;
2043 d->privileged = aPrivileged;
2044 d->func = aFunc;
2045 d->user = aUser;
2046
2047 RTTHREAD tid = NIL_RTTHREAD;
2048 int vrc = RTThreadCreate (&tid, SVCHelperClientThread,
2049 static_cast <void *> (d.get()),
2050 0, RTTHREADTYPE_MAIN_WORKER,
2051 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2052
2053 ComAssertMsgRCRet (vrc, ("Could not create SVCHelper thread (%Rrc)", vrc),
2054 E_FAIL);
2055
2056 /* d is now owned by SVCHelperClientThread(), so release it */
2057 d.release();
2058
2059 return S_OK;
2060}
2061
2062/**
2063 * Worker thread for startSVCHelperClient().
2064 */
2065/* static */
2066DECLCALLBACK(int)
2067VirtualBox::SVCHelperClientThread (RTTHREAD aThread, void *aUser)
2068{
2069 LogFlowFuncEnter();
2070
2071 std::auto_ptr <StartSVCHelperClientData>
2072 d (static_cast <StartSVCHelperClientData *> (aUser));
2073
2074 HRESULT rc = S_OK;
2075 bool userFuncCalled = false;
2076
2077 do
2078 {
2079 AssertBreakStmt (d.get(), rc = E_POINTER);
2080 AssertReturn (!d->progress.isNull(), E_POINTER);
2081
2082 /* protect VirtualBox from uninitialization */
2083 AutoCaller autoCaller (d->that);
2084 if (!autoCaller.isOk())
2085 {
2086 /* it's too late */
2087 rc = autoCaller.rc();
2088 break;
2089 }
2090
2091 int vrc = VINF_SUCCESS;
2092
2093 Guid id;
2094 id.create();
2095 SVCHlpClient client;
2096 vrc = client.create (Utf8StrFmt ("VirtualBox\\SVCHelper\\{%RTuuid}",
2097 id.raw()));
2098 if (RT_FAILURE (vrc))
2099 {
2100 rc = setError (E_FAIL,
2101 tr ("Could not create the communication channel (%Rrc)"), vrc);
2102 break;
2103 }
2104
2105 /* get the path to the executable */
2106 char exePathBuf [RTPATH_MAX];
2107 char *exePath = RTProcGetExecutableName (exePathBuf, RTPATH_MAX);
2108 ComAssertBreak (exePath, E_FAIL);
2109
2110 Utf8Str argsStr = Utf8StrFmt ("/Helper %s", client.name().raw());
2111
2112 LogFlowFunc (("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
2113
2114 RTPROCESS pid = NIL_RTPROCESS;
2115
2116 if (d->privileged)
2117 {
2118 /* Attempt to start a privileged process using the Run As dialog */
2119
2120 Bstr file = exePath;
2121 Bstr parameters = argsStr;
2122
2123 SHELLEXECUTEINFO shExecInfo;
2124
2125 shExecInfo.cbSize = sizeof (SHELLEXECUTEINFO);
2126
2127 shExecInfo.fMask = NULL;
2128 shExecInfo.hwnd = NULL;
2129 shExecInfo.lpVerb = L"runas";
2130 shExecInfo.lpFile = file;
2131 shExecInfo.lpParameters = parameters;
2132 shExecInfo.lpDirectory = NULL;
2133 shExecInfo.nShow = SW_NORMAL;
2134 shExecInfo.hInstApp = NULL;
2135
2136 if (!ShellExecuteEx (&shExecInfo))
2137 {
2138 int vrc2 = RTErrConvertFromWin32 (GetLastError());
2139 /* hide excessive details in case of a frequent error
2140 * (pressing the Cancel button to close the Run As dialog) */
2141 if (vrc2 == VERR_CANCELLED)
2142 rc = setError (E_FAIL,
2143 tr ("Operation cancelled by the user"));
2144 else
2145 rc = setError (E_FAIL,
2146 tr ("Could not launch a privileged process '%s' (%Rrc)"),
2147 exePath, vrc2);
2148 break;
2149 }
2150 }
2151 else
2152 {
2153 const char *args[] = { exePath, "/Helper", client.name(), 0 };
2154 vrc = RTProcCreate (exePath, args, RTENV_DEFAULT, 0, &pid);
2155 if (RT_FAILURE (vrc))
2156 {
2157 rc = setError (E_FAIL,
2158 tr ("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2159 break;
2160 }
2161 }
2162
2163 /* wait for the client to connect */
2164 vrc = client.connect();
2165 if (RT_SUCCESS (vrc))
2166 {
2167 /* start the user supplied function */
2168 rc = d->func (&client, d->progress, d->user, &vrc);
2169 userFuncCalled = true;
2170 }
2171
2172 /* send the termination signal to the process anyway */
2173 {
2174 int vrc2 = client.write (SVCHlpMsg::Null);
2175 if (RT_SUCCESS (vrc))
2176 vrc = vrc2;
2177 }
2178
2179 if (SUCCEEDED (rc) && RT_FAILURE (vrc))
2180 {
2181 rc = setError (E_FAIL,
2182 tr ("Could not operate the communication channel (%Rrc)"), vrc);
2183 break;
2184 }
2185 }
2186 while (0);
2187
2188 if (FAILED (rc) && !userFuncCalled)
2189 {
2190 /* call the user function in the "cleanup only" mode
2191 * to let it free resources passed to in aUser */
2192 d->func (NULL, NULL, d->user, NULL);
2193 }
2194
2195 d->progress->notifyComplete (rc);
2196
2197 LogFlowFuncLeave();
2198 return 0;
2199}
2200
2201#endif /* RT_OS_WINDOWS */
2202
2203/**
2204 * Sends a signal to the client watcher thread to rescan the set of machines
2205 * that have open sessions.
2206 *
2207 * @note Doesn't lock anything.
2208 */
2209void VirtualBox::updateClientWatcher()
2210{
2211 AutoCaller autoCaller (this);
2212 AssertComRCReturn (autoCaller.rc(), (void) 0);
2213
2214 AssertReturn (mWatcherData.mThread != NIL_RTTHREAD, (void) 0);
2215
2216 /* sent an update request */
2217#if defined(RT_OS_WINDOWS)
2218 ::SetEvent (mWatcherData.mUpdateReq);
2219#elif defined(RT_OS_OS2)
2220 RTSemEventSignal (mWatcherData.mUpdateReq);
2221#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2222 RTSemEventSignal (mWatcherData.mUpdateReq);
2223#else
2224# error "Port me!"
2225#endif
2226}
2227
2228/**
2229 * Adds the given child process ID to the list of processes to be reaped.
2230 * This call should be followed by #updateClientWatcher() to take the effect.
2231 */
2232void VirtualBox::addProcessToReap (RTPROCESS pid)
2233{
2234 AutoCaller autoCaller (this);
2235 AssertComRCReturn (autoCaller.rc(), (void) 0);
2236
2237 /// @todo (dmik) Win32?
2238#ifndef RT_OS_WINDOWS
2239 AutoWriteLock alock (this);
2240 mWatcherData.mProcesses.push_back (pid);
2241#endif
2242}
2243
2244/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2245struct MachineEvent : public VirtualBox::CallbackEvent
2246{
2247 enum What { DataChanged, StateChanged, Registered };
2248
2249 MachineEvent (VirtualBox *aVB, const Guid &aId)
2250 : CallbackEvent (aVB), what (DataChanged), id (aId)
2251 {}
2252
2253 MachineEvent (VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2254 : CallbackEvent (aVB), what (StateChanged), id (aId)
2255 , state (aState)
2256 {}
2257
2258 MachineEvent (VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2259 : CallbackEvent (aVB), what (Registered), id (aId)
2260 , registered (aRegistered)
2261 {}
2262
2263 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2264 {
2265 switch (what)
2266 {
2267 case DataChanged:
2268 LogFlow (("OnMachineDataChange: id={%RTuuid}\n", id.ptr()));
2269 aCallback->OnMachineDataChange (id);
2270 break;
2271
2272 case StateChanged:
2273 LogFlow (("OnMachineStateChange: id={%RTuuid}, state=%d\n",
2274 id.ptr(), state));
2275 aCallback->OnMachineStateChange (id, state);
2276 break;
2277
2278 case Registered:
2279 LogFlow (("OnMachineRegistered: id={%RTuuid}, registered=%d\n",
2280 id.ptr(), registered));
2281 aCallback->OnMachineRegistered (id, registered);
2282 break;
2283 }
2284 }
2285
2286 const What what;
2287
2288 Guid id;
2289 MachineState_T state;
2290 BOOL registered;
2291};
2292
2293/**
2294 * @note Doesn't lock any object.
2295 */
2296void VirtualBox::onMachineStateChange (const Guid &aId, MachineState_T aState)
2297{
2298 postEvent (new MachineEvent (this, aId, aState));
2299}
2300
2301/**
2302 * @note Doesn't lock any object.
2303 */
2304void VirtualBox::onMachineDataChange (const Guid &aId)
2305{
2306 postEvent (new MachineEvent (this, aId));
2307}
2308
2309/**
2310 * @note Locks this object for reading.
2311 */
2312BOOL VirtualBox::onExtraDataCanChange (const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2313 Bstr &aError)
2314{
2315 LogFlowThisFunc (("machine={%s} aKey={%ls} aValue={%ls}\n",
2316 aId.toString().raw(), aKey, aValue));
2317
2318 AutoCaller autoCaller (this);
2319 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2320
2321 CallbackList list;
2322 {
2323 AutoReadLock alock (this);
2324 list = mData.mCallbacks;
2325 }
2326
2327 BOOL allowChange = TRUE;
2328 CallbackList::iterator it = list.begin();
2329 while ((it != list.end()) && allowChange)
2330 {
2331 HRESULT rc = (*it++)->OnExtraDataCanChange (aId, aKey, aValue,
2332 aError.asOutParam(), &allowChange);
2333 if (FAILED (rc))
2334 {
2335 /* if a call to this method fails for some reason (for ex., because
2336 * the other side is dead), we ensure allowChange stays true
2337 * (MS COM RPC implementation seems to zero all output vars before
2338 * issuing an IPC call or after a failure, so it's essential
2339 * there) */
2340 allowChange = TRUE;
2341 }
2342 }
2343
2344 LogFlowThisFunc (("allowChange=%RTbool\n", allowChange));
2345 return allowChange;
2346}
2347
2348/** Event for onExtraDataChange() */
2349struct ExtraDataEvent : public VirtualBox::CallbackEvent
2350{
2351 ExtraDataEvent (VirtualBox *aVB, const Guid &aMachineId,
2352 IN_BSTR aKey, IN_BSTR aVal)
2353 : CallbackEvent (aVB), machineId (aMachineId)
2354 , key (aKey), val (aVal)
2355 {}
2356
2357 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2358 {
2359 LogFlow (("OnExtraDataChange: machineId={%RTuuid}, key='%ls', val='%ls'\n",
2360 machineId.ptr(), key.raw(), val.raw()));
2361 aCallback->OnExtraDataChange (machineId, key, val);
2362 }
2363
2364 Guid machineId;
2365 Bstr key, val;
2366};
2367
2368/**
2369 * @note Doesn't lock any object.
2370 */
2371void VirtualBox::onExtraDataChange (const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2372{
2373 postEvent (new ExtraDataEvent (this, aId, aKey, aValue));
2374}
2375
2376/**
2377 * @note Doesn't lock any object.
2378 */
2379void VirtualBox::onMachineRegistered (const Guid &aId, BOOL aRegistered)
2380{
2381 postEvent (new MachineEvent (this, aId, aRegistered));
2382}
2383
2384/** Event for onSessionStateChange() */
2385struct SessionEvent : public VirtualBox::CallbackEvent
2386{
2387 SessionEvent (VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2388 : CallbackEvent (aVB), machineId (aMachineId), sessionState (aState)
2389 {}
2390
2391 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2392 {
2393 LogFlow (("OnSessionStateChange: machineId={%RTuuid}, sessionState=%d\n",
2394 machineId.ptr(), sessionState));
2395 aCallback->OnSessionStateChange (machineId, sessionState);
2396 }
2397
2398 Guid machineId;
2399 SessionState_T sessionState;
2400};
2401
2402/**
2403 * @note Doesn't lock any object.
2404 */
2405void VirtualBox::onSessionStateChange (const Guid &aId, SessionState_T aState)
2406{
2407 postEvent (new SessionEvent (this, aId, aState));
2408}
2409
2410/** Event for onSnapshotTaken(), onSnapshotRemoved() and onSnapshotChange() */
2411struct SnapshotEvent : public VirtualBox::CallbackEvent
2412{
2413 enum What { Taken, Discarded, Changed };
2414
2415 SnapshotEvent (VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2416 What aWhat)
2417 : CallbackEvent (aVB)
2418 , what (aWhat)
2419 , machineId (aMachineId), snapshotId (aSnapshotId)
2420 {}
2421
2422 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2423 {
2424 switch (what)
2425 {
2426 case Taken:
2427 LogFlow (("OnSnapshotTaken: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2428 machineId.ptr(), snapshotId.ptr()));
2429 aCallback->OnSnapshotTaken (machineId, snapshotId);
2430 break;
2431
2432 case Discarded:
2433 LogFlow (("OnSnapshotDiscarded: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2434 machineId.ptr(), snapshotId.ptr()));
2435 aCallback->OnSnapshotDiscarded (machineId, snapshotId);
2436 break;
2437
2438 case Changed:
2439 LogFlow (("OnSnapshotChange: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2440 machineId.ptr(), snapshotId.ptr()));
2441 aCallback->OnSnapshotChange (machineId, snapshotId);
2442 break;
2443 }
2444 }
2445
2446 const What what;
2447
2448 Guid machineId;
2449 Guid snapshotId;
2450};
2451
2452/**
2453 * @note Doesn't lock any object.
2454 */
2455void VirtualBox::onSnapshotTaken (const Guid &aMachineId, const Guid &aSnapshotId)
2456{
2457 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Taken));
2458}
2459
2460/**
2461 * @note Doesn't lock any object.
2462 */
2463void VirtualBox::onSnapshotDiscarded (const Guid &aMachineId, const Guid &aSnapshotId)
2464{
2465 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Discarded));
2466}
2467
2468/**
2469 * @note Doesn't lock any object.
2470 */
2471void VirtualBox::onSnapshotChange (const Guid &aMachineId, const Guid &aSnapshotId)
2472{
2473 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Changed));
2474}
2475
2476/** Event for onGuestPropertyChange() */
2477struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2478{
2479 GuestPropertyEvent (VirtualBox *aVBox, const Guid &aMachineId,
2480 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2481 : CallbackEvent (aVBox), machineId (aMachineId)
2482 , name (aName), value (aValue), flags(aFlags)
2483 {}
2484
2485 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2486 {
2487 LogFlow (("OnGuestPropertyChange: machineId={%RTuuid}, name='%ls', value='%ls', flags='%ls'\n",
2488 machineId.ptr(), name.raw(), value.raw(), flags.raw()));
2489 aCallback->OnGuestPropertyChange (machineId, name, value, flags);
2490 }
2491
2492 Guid machineId;
2493 Bstr name, value, flags;
2494};
2495
2496/**
2497 * @note Doesn't lock any object.
2498 */
2499void VirtualBox::onGuestPropertyChange (const Guid &aMachineId, IN_BSTR aName,
2500 IN_BSTR aValue, IN_BSTR aFlags)
2501{
2502 postEvent (new GuestPropertyEvent (this, aMachineId, aName, aValue, aFlags));
2503}
2504
2505/**
2506 * @note Locks this object for reading.
2507 */
2508ComObjPtr <GuestOSType> VirtualBox::getUnknownOSType()
2509{
2510 ComObjPtr <GuestOSType> type;
2511
2512 AutoCaller autoCaller (this);
2513 AssertComRCReturn (autoCaller.rc(), type);
2514
2515 AutoReadLock alock (this);
2516
2517 /* unknown type must always be the first */
2518 ComAssertRet (mData.mGuestOSTypes.size() > 0, type);
2519
2520 type = mData.mGuestOSTypes.front();
2521 return type;
2522}
2523
2524/**
2525 * Returns the list of opened machines (machines having direct sessions opened
2526 * by client processes) and optionally the list of direct session controls.
2527 *
2528 * @param aMachines Where to put opened machines (will be empty if none).
2529 * @param aControls Where to put direct session controls (optional).
2530 *
2531 * @note The returned lists contain smart pointers. So, clear it as soon as
2532 * it becomes no more necessary to release instances.
2533 *
2534 * @note It can be possible that a session machine from the list has been
2535 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2536 * when accessing unprotected data directly.
2537 *
2538 * @note Locks objects for reading.
2539 */
2540void VirtualBox::getOpenedMachines (SessionMachineVector &aMachines,
2541 InternalControlVector *aControls /*= NULL*/)
2542{
2543 AutoCaller autoCaller (this);
2544 AssertComRCReturnVoid (autoCaller.rc());
2545
2546 aMachines.clear();
2547 if (aControls)
2548 aControls->clear();
2549
2550 AutoReadLock alock (this);
2551
2552 for (MachineList::iterator it = mData.mMachines.begin();
2553 it != mData.mMachines.end();
2554 ++ it)
2555 {
2556 ComObjPtr <SessionMachine> sm;
2557 ComPtr <IInternalSessionControl> ctl;
2558 if ((*it)->isSessionOpen (sm, &ctl))
2559 {
2560 aMachines.push_back (sm);
2561 if (aControls)
2562 aControls->push_back (ctl);
2563 }
2564 }
2565}
2566
2567/**
2568 * Searches for a Machine object with the given ID in the collection
2569 * of registered machines.
2570 *
2571 * @param id
2572 * ID of the machine
2573 * @param doSetError
2574 * if TRUE, the appropriate error info is set in case when the machine
2575 * is not found
2576 * @param machine
2577 * where to store the found machine object (can be NULL)
2578 *
2579 * @return
2580 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
2581 *
2582 * @note Locks this object for reading.
2583 */
2584HRESULT VirtualBox::findMachine (const Guid &aId, bool aSetError,
2585 ComObjPtr <Machine> *aMachine /* = NULL */)
2586{
2587 AutoCaller autoCaller (this);
2588 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2589
2590 bool found = false;
2591
2592 {
2593 AutoReadLock alock (this);
2594
2595 for (MachineList::iterator it = mData.mMachines.begin();
2596 !found && it != mData.mMachines.end();
2597 ++ it)
2598 {
2599 /* sanity */
2600 AutoLimitedCaller machCaller (*it);
2601 AssertComRC (machCaller.rc());
2602
2603 found = (*it)->id() == aId;
2604 if (found && aMachine)
2605 *aMachine = *it;
2606 }
2607 }
2608
2609 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2610
2611 if (aSetError && !found)
2612 {
2613 setError (VBOX_E_OBJECT_NOT_FOUND,
2614 tr ("Could not find a registered machine with UUID {%RTuuid}"),
2615 aId.raw());
2616 }
2617
2618 return rc;
2619}
2620
2621/**
2622 * Searches for a HardDisk object with the given ID or location in the list of
2623 * registered hard disks. If both ID and location are specified, the first
2624 * object that matches either of them (not necessarily both) is returned.
2625 *
2626 * @param aId ID of the hard disk (unused when NULL).
2627 * @param aLocation Full location specification (unused NULL).
2628 * @param aSetError If @c true , the appropriate error info is set in case
2629 * when the hard disk is not found.
2630 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2631 *
2632 * @return S_OK when found or E_INVALIDARG when not found.
2633 *
2634 * @note Locks this object and hard disk objects for reading.
2635 */
2636HRESULT VirtualBox::
2637findHardDisk2 (const Guid *aId, CBSTR aLocation,
2638 bool aSetError, ComObjPtr <HardDisk2> *aHardDisk /*= NULL*/)
2639{
2640 AssertReturn (aId || aLocation, E_INVALIDARG);
2641
2642 AutoReadLock alock (this);
2643
2644 /* first, look up by UUID in the map if UUID is provided */
2645 if (aId)
2646 {
2647 HardDisk2Map::const_iterator it = mData.mHardDisk2Map.find (*aId);
2648 if (it != mData.mHardDisk2Map.end())
2649 {
2650 if (aHardDisk)
2651 *aHardDisk = (*it).second;
2652 return S_OK;
2653 }
2654 }
2655
2656 /* then iterate and search by location */
2657 int result = -1;
2658 if (aLocation)
2659 {
2660 Utf8Str location = aLocation;
2661
2662 for (HardDisk2Map::const_iterator it = mData.mHardDisk2Map.begin();
2663 it != mData.mHardDisk2Map.end();
2664 ++ it)
2665 {
2666 const ComObjPtr <HardDisk2> &hd = (*it).second;
2667
2668 HRESULT rc = hd->compareLocationTo (location, result);
2669 CheckComRCReturnRC (rc);
2670
2671 if (result == 0)
2672 {
2673 if (aHardDisk)
2674 *aHardDisk = hd;
2675 break;
2676 }
2677 }
2678 }
2679
2680 HRESULT rc = result == 0 ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2681
2682 if (aSetError && result != 0)
2683 {
2684 if (aId)
2685 setError (rc, tr ("Could not find a hard disk with UUID {%RTuuid} "
2686 "in the media registry ('%ls')"),
2687 aId->raw(), mData.mCfgFile.mName.raw());
2688 else
2689 setError (rc, tr ("Could not find a hard disk with location '%ls' "
2690 "in the media registry ('%ls')"),
2691 aLocation, mData.mCfgFile.mName.raw());
2692 }
2693
2694 return rc;
2695}
2696
2697/**
2698 * Searches for a DVDImage2 object with the given ID or location in the list of
2699 * registered DVD images. If both ID and file path are specified, the first
2700 * object that matches either of them (not necessarily both) is returned.
2701 *
2702 * @param aId ID of the DVD image (unused when NULL).
2703 * @param aLocation Full path to the image file (unused when NULL).
2704 * @param aSetError If @c true, the appropriate error info is set in case when
2705 * the image is not found.
2706 * @param aImage Where to store the found image object (can be NULL).
2707 *
2708 * @return S_OK when found or E_INVALIDARG when not found.
2709 *
2710 * @note Locks this object and image objects for reading.
2711 */
2712HRESULT VirtualBox::findDVDImage2 (const Guid *aId, CBSTR aLocation,
2713 bool aSetError,
2714 ComObjPtr <DVDImage2> *aImage /* = NULL */)
2715{
2716 AssertReturn (aId || aLocation, E_INVALIDARG);
2717
2718 Utf8Str location;
2719
2720 if (aLocation != NULL)
2721 {
2722 int vrc = calculateFullPath (Utf8Str (aLocation), location);
2723 if (RT_FAILURE (vrc))
2724 return setError (VBOX_E_FILE_ERROR,
2725 tr ("Invalid image file location '%ls' (%Rrc)"),
2726 aLocation, vrc);
2727 }
2728
2729 AutoReadLock alock (this);
2730
2731 bool found = false;
2732
2733 for (DVDImage2List::const_iterator it = mData.mDVDImages2.begin();
2734 it != mData.mDVDImages2.end();
2735 ++ it)
2736 {
2737 /* no AutoCaller, registered image life time is bound to this */
2738 AutoReadLock imageLock (*it);
2739
2740 found = (aId && (*it)->id() == *aId) ||
2741 (aLocation != NULL &&
2742 RTPathCompare (location, Utf8Str ((*it)->locationFull())) == 0);
2743 if (found)
2744 {
2745 if (aImage)
2746 *aImage = *it;
2747 break;
2748 }
2749 }
2750
2751 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2752
2753 if (aSetError && !found)
2754 {
2755 if (aId)
2756 setError (rc, tr ("Could not find a CD/DVD image with UUID {%RTuuid} "
2757 "in the media registry ('%ls')"),
2758 aId->raw(), mData.mCfgFile.mName.raw());
2759 else
2760 setError (rc, tr ("Could not find a CD/DVD image with location '%ls' "
2761 "in the media registry ('%ls')"),
2762 aLocation, mData.mCfgFile.mName.raw());
2763 }
2764
2765 return rc;
2766}
2767
2768/**
2769 * Searches for a FloppyImage2 object with the given ID or location in the
2770 * collection of registered DVD images. If both ID and file path are specified,
2771 * the first object that matches either of them (not necessarily both) is
2772 * returned.
2773 *
2774 * @param aId ID of the DVD image (unused when NULL).
2775 * @param aLocation Full path to the image file (unused when NULL).
2776 * @param aSetError If @c true, the appropriate error info is set in case when
2777 * the image is not found.
2778 * @param aImage Where to store the found image object (can be NULL).
2779 *
2780 * @return S_OK when found or E_INVALIDARG when not found.
2781 *
2782 * @note Locks this object and image objects for reading.
2783 */
2784HRESULT VirtualBox::findFloppyImage2 (const Guid *aId, CBSTR aLocation,
2785 bool aSetError,
2786 ComObjPtr <FloppyImage2> *aImage /* = NULL */)
2787{
2788 AssertReturn (aId || aLocation, E_INVALIDARG);
2789
2790 Utf8Str location;
2791
2792 if (aLocation != NULL)
2793 {
2794 int vrc = calculateFullPath (Utf8Str (aLocation), location);
2795 if (RT_FAILURE (vrc))
2796 return setError (VBOX_E_FILE_ERROR,
2797 tr ("Invalid image file location '%ls' (%Rrc)"),
2798 aLocation, vrc);
2799 }
2800
2801 AutoReadLock alock (this);
2802
2803 bool found = false;
2804
2805 for (FloppyImage2List::const_iterator it = mData.mFloppyImages2.begin();
2806 it != mData.mFloppyImages2.end();
2807 ++ it)
2808 {
2809 /* no AutoCaller, registered image life time is bound to this */
2810 AutoReadLock imageLock (*it);
2811
2812 found = (aId && (*it)->id() == *aId) ||
2813 (aLocation != NULL &&
2814 RTPathCompare (location, Utf8Str ((*it)->locationFull())) == 0);
2815 if (found)
2816 {
2817 if (aImage)
2818 *aImage = *it;
2819 break;
2820 }
2821 }
2822
2823 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2824
2825 if (aSetError && !found)
2826 {
2827 if (aId)
2828 setError (rc, tr ("Could not find a floppy image with UUID {%RTuuid} "
2829 "in the media registry ('%ls')"),
2830 aId->raw(), mData.mCfgFile.mName.raw());
2831 else
2832 setError (rc, tr ("Could not find a floppy image with location '%ls' "
2833 "in the media registry ('%ls')"),
2834 aLocation, mData.mCfgFile.mName.raw());
2835 }
2836
2837 return rc;
2838}
2839
2840/**
2841 * Calculates the absolute path of the given path taking the VirtualBox home
2842 * directory as the current directory.
2843 *
2844 * @param aPath Path to calculate the absolute path for.
2845 * @param aResult Where to put the result (used only on success, can be the
2846 * same Utf8Str instance as passed in @a aPath).
2847 * @return IPRT result.
2848 *
2849 * @note Doesn't lock any object.
2850 */
2851int VirtualBox::calculateFullPath (const char *aPath, Utf8Str &aResult)
2852{
2853 AutoCaller autoCaller (this);
2854 AssertComRCReturn (autoCaller.rc(), VERR_GENERAL_FAILURE);
2855
2856 /* no need to lock since mHomeDir is const */
2857
2858 char folder [RTPATH_MAX];
2859 int vrc = RTPathAbsEx (mData.mHomeDir, aPath, folder, sizeof (folder));
2860 if (RT_SUCCESS (vrc))
2861 aResult = folder;
2862
2863 return vrc;
2864}
2865
2866/**
2867 * Tries to calculate the relative path of the given absolute path using the
2868 * directory of the VirtualBox settings file as the base directory.
2869 *
2870 * @param aPath Absolute path to calculate the relative path for.
2871 * @param aResult Where to put the result (used only when it's possible to
2872 * make a relative path from the given absolute path; otherwise
2873 * left untouched).
2874 *
2875 * @note Doesn't lock any object.
2876 */
2877void VirtualBox::calculateRelativePath (const char *aPath, Utf8Str &aResult)
2878{
2879 AutoCaller autoCaller (this);
2880 AssertComRCReturnVoid (autoCaller.rc());
2881
2882 /* no need to lock since mHomeDir is const */
2883
2884 Utf8Str settingsDir = mData.mHomeDir;
2885
2886 if (RTPathStartsWith (aPath, settingsDir))
2887 {
2888 /* when assigning, we create a separate Utf8Str instance because both
2889 * aPath and aResult can point to the same memory location when this
2890 * func is called (if we just do aResult = aPath, aResult will be freed
2891 * first, and since its the same as aPath, an attempt to copy garbage
2892 * will be made. */
2893 aResult = Utf8Str (aPath + settingsDir.length() + 1);
2894 }
2895}
2896
2897// private methods
2898/////////////////////////////////////////////////////////////////////////////
2899
2900/**
2901 * Checks if there is a hard disk, DVD or floppy image with the given ID or
2902 * location already registered.
2903 *
2904 * On return, sets @aConflict to the string describing the conflicting medium,
2905 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
2906 * either case. A failure is unexpected.
2907 *
2908 * @param aId UUID to check.
2909 * @param aLocation Location to check.
2910 * @param aConflict Where to return parameters of the conflicting medium.
2911 *
2912 * @note Locks this object and media objects for reading.
2913 */
2914HRESULT VirtualBox::checkMediaForConflicts2 (const Guid &aId,
2915 const Bstr &aLocation,
2916 Utf8Str &aConflict)
2917{
2918 aConflict.setNull();
2919
2920 AssertReturn (!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
2921
2922 AutoReadLock alock (this);
2923
2924 HRESULT rc = S_OK;
2925
2926 {
2927 ComObjPtr <HardDisk2> hardDisk;
2928 rc = findHardDisk2 (&aId, aLocation, false /* aSetError */, &hardDisk);
2929 if (SUCCEEDED (rc))
2930 {
2931 /* Note: no AutoCaller since bound to this */
2932 AutoReadLock mediaLock (hardDisk);
2933 aConflict = Utf8StrFmt (
2934 tr ("hard disk '%ls' with UUID {%RTuuid}"),
2935 hardDisk->locationFull().raw(), hardDisk->id().raw());
2936 return S_OK;
2937 }
2938 }
2939
2940 {
2941 ComObjPtr <DVDImage2> image;
2942 rc = findDVDImage2 (&aId, aLocation, false /* aSetError */, &image);
2943 if (SUCCEEDED (rc))
2944 {
2945 /* Note: no AutoCaller since bound to this */
2946 AutoReadLock mediaLock (image);
2947 aConflict = Utf8StrFmt (
2948 tr ("CD/DVD image '%ls' with UUID {%RTuuid}"),
2949 image->locationFull().raw(), image->id().raw());
2950 return S_OK;
2951 }
2952 }
2953
2954 {
2955 ComObjPtr <FloppyImage2> image;
2956 rc = findFloppyImage2 (&aId, aLocation, false /* aSetError */, &image);
2957 if (SUCCEEDED (rc))
2958 {
2959 /* Note: no AutoCaller since bound to this */
2960 AutoReadLock mediaLock (image);
2961 aConflict = Utf8StrFmt (
2962 tr ("floppy image '%ls' with UUID {%RTuuid}"),
2963 image->locationFull().raw(), image->id().raw());
2964 return S_OK;
2965 }
2966 }
2967
2968 return S_OK;
2969}
2970
2971/**
2972 * Reads in the machine definitions from the configuration loader
2973 * and creates the relevant objects.
2974 *
2975 * @param aGlobal <Global> node.
2976 *
2977 * @note Can be called only from #init().
2978 * @note Doesn't lock anything.
2979 */
2980HRESULT VirtualBox::loadMachines (const settings::Key &aGlobal)
2981{
2982 using namespace settings;
2983
2984 AutoCaller autoCaller (this);
2985 AssertReturn (autoCaller.state() == InInit, E_FAIL);
2986
2987 HRESULT rc = S_OK;
2988
2989 Key::List machines = aGlobal.key ("MachineRegistry").keys ("MachineEntry");
2990 for (Key::List::const_iterator it = machines.begin();
2991 it != machines.end(); ++ it)
2992 {
2993 /* required */
2994 Guid uuid = (*it).value <Guid> ("uuid");
2995 /* required */
2996 Bstr src = (*it).stringValue ("src");
2997
2998 /* create a new machine object */
2999 ComObjPtr <Machine> machine;
3000 rc = machine.createObject();
3001 if (SUCCEEDED (rc))
3002 {
3003 /* initialize the machine object and register it */
3004 rc = machine->init (this, src, Machine::Init_Registered,
3005 NULL, NULL, FALSE, &uuid);
3006 if (SUCCEEDED (rc))
3007 rc = registerMachine (machine);
3008 }
3009 }
3010
3011 return rc;
3012}
3013
3014/**
3015 * Reads in the media registration entries from the global settings file
3016 * and creates the relevant objects.
3017 *
3018 * @param aGlobal <Global> node
3019 *
3020 * @note Can be called only from #init().
3021 * @note Doesn't lock anything.
3022 */
3023HRESULT VirtualBox::loadMedia (const settings::Key &aGlobal)
3024{
3025 using namespace settings;
3026
3027 AutoCaller autoCaller (this);
3028 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3029
3030 HRESULT rc = S_OK;
3031
3032 Key registry = aGlobal.key ("MediaRegistry");
3033
3034 const char *kMediaNodes[] = { "HardDisks", "DVDImages", "FloppyImages" };
3035
3036 for (size_t n = 0; n < RT_ELEMENTS (kMediaNodes); ++ n)
3037 {
3038 /* All three media nodes are optional */
3039 Key node = registry.findKey (kMediaNodes [n]);
3040 if (node.isNull())
3041 continue;
3042
3043 if (n == 0)
3044 {
3045 Key::List hardDisks = node.keys ("HardDisk");
3046 for (Key::List::const_iterator it = hardDisks.begin();
3047 it != hardDisks.end(); ++ it)
3048 {
3049 ComObjPtr <HardDisk2> hardDisk;
3050 hardDisk.createObject();
3051 rc = hardDisk->init (this, NULL, *it);
3052 CheckComRCBreakRC (rc);
3053
3054 rc = registerHardDisk2 (hardDisk, false /* aSaveRegistry */);
3055 CheckComRCBreakRC (rc);
3056 }
3057
3058 continue;
3059 }
3060
3061 CheckComRCBreakRC (rc);
3062
3063 Key::List images = node.keys ("Image");
3064 for (Key::List::const_iterator it = images.begin();
3065 it != images.end(); ++ it)
3066 {
3067 switch (n)
3068 {
3069 case 1: /* DVDImages */
3070 {
3071 ComObjPtr <DVDImage2> image;
3072 image.createObject();
3073 rc = image->init (this, *it);
3074 CheckComRCBreakRC (rc);
3075
3076 rc = registerDVDImage (image, false /* aSaveRegistry */);
3077 CheckComRCBreakRC (rc);
3078
3079 break;
3080 }
3081 case 2: /* FloppyImages */
3082 {
3083 ComObjPtr <FloppyImage2> image;
3084 image.createObject();
3085 rc = image->init (this, *it);
3086 CheckComRCBreakRC (rc);
3087
3088 rc = registerFloppyImage (image, false /* aSaveRegistry */);
3089 CheckComRCBreakRC (rc);
3090
3091 break;
3092 }
3093 default:
3094 AssertFailed();
3095 }
3096
3097 CheckComRCBreakRC (rc);
3098 }
3099
3100 CheckComRCBreakRC (rc);
3101 }
3102
3103 return rc;
3104}
3105
3106/**
3107 * Helper function to write out the configuration tree.
3108 *
3109 * @note Locks this object for writing and child objects for reading/writing!
3110 */
3111HRESULT VirtualBox::saveSettings()
3112{
3113 AutoCaller autoCaller (this);
3114 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3115
3116 AssertReturn (!!mData.mCfgFile.mName, E_FAIL);
3117
3118 HRESULT rc = S_OK;
3119
3120 /* serialize file access (prevents concurrent reads and writes) */
3121 AutoWriteLock alock (this);
3122
3123 try
3124 {
3125 using namespace settings;
3126 using namespace xml;
3127
3128 /* load the settings file */
3129 File file (mData.mCfgFile.mHandle, Utf8Str (mData.mCfgFile.mName));
3130 XmlTreeBackend tree;
3131
3132 rc = VirtualBox::loadSettingsTree_ForUpdate (tree, file);
3133 CheckComRCThrowRC (rc);
3134
3135 Key global = tree.rootKey().createKey ("Global");
3136
3137 /* machines */
3138 {
3139 /* first, delete the entire machine registry */
3140 Key registryNode = global.findKey ("MachineRegistry");
3141 if (!registryNode.isNull())
3142 registryNode.zap();
3143 /* then, recreate it */
3144 registryNode = global.createKey ("MachineRegistry");
3145
3146 /* write out the machines */
3147 for (MachineList::iterator it = mData.mMachines.begin();
3148 it != mData.mMachines.end();
3149 ++ it)
3150 {
3151 Key entryNode = registryNode.appendKey ("MachineEntry");
3152 rc = (*it)->saveRegistryEntry (entryNode);
3153 CheckComRCThrowRC (rc);
3154 }
3155 }
3156
3157 /* media */
3158 {
3159 /* first, delete the entire media registry */
3160 Key registryNode = global.findKey ("MediaRegistry");
3161 if (!registryNode.isNull())
3162 registryNode.zap();
3163 /* then, recreate it */
3164 registryNode = global.createKey ("MediaRegistry");
3165
3166 /* hard disks */
3167 {
3168 Key hardDisksNode = registryNode.createKey ("HardDisks");
3169
3170 for (HardDisk2List::const_iterator it =
3171 mData.mHardDisks2.begin();
3172 it != mData.mHardDisks2.end();
3173 ++ it)
3174 {
3175 rc = (*it)->saveSettings (hardDisksNode);
3176 CheckComRCThrowRC (rc);
3177 }
3178 }
3179
3180 /* CD/DVD images */
3181 {
3182 Key imagesNode = registryNode.createKey ("DVDImages");
3183
3184 for (DVDImage2List::const_iterator it =
3185 mData.mDVDImages2.begin();
3186 it != mData.mDVDImages2.end();
3187 ++ it)
3188 {
3189 rc = (*it)->saveSettings (imagesNode);
3190 CheckComRCThrowRC (rc);
3191 }
3192 }
3193
3194 /* floppy images */
3195 {
3196 Key imagesNode = registryNode.createKey ("FloppyImages");
3197
3198 for (FloppyImage2List::const_iterator it =
3199 mData.mFloppyImages2.begin();
3200 it != mData.mFloppyImages2.end();
3201 ++ it)
3202 {
3203 rc = (*it)->saveSettings (imagesNode);
3204 CheckComRCThrowRC (rc);
3205 }
3206 }
3207 }
3208
3209 /* host data (USB filters) */
3210 rc = mData.mHost->saveSettings (global);
3211 CheckComRCThrowRC (rc);
3212
3213 rc = mData.mSystemProperties->saveSettings (global);
3214 CheckComRCThrowRC (rc);
3215
3216 /* save the settings on success */
3217 rc = VirtualBox::saveSettingsTree (tree, file,
3218 mData.mSettingsFileVersion);
3219 CheckComRCThrowRC (rc);
3220 }
3221 catch (HRESULT err)
3222 {
3223 /* we assume that error info is set by the thrower */
3224 rc = err;
3225 }
3226 catch (...)
3227 {
3228 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3229 }
3230
3231 return rc;
3232}
3233
3234/**
3235 * Helper to register the machine.
3236 *
3237 * When called during VirtualBox startup, adds the given machine to the
3238 * collection of registered machines. Otherwise tries to mark the machine
3239 * as registered, and, if succeeded, adds it to the collection and
3240 * saves global settings.
3241 *
3242 * @note The caller must have added itself as a caller of the @a aMachine
3243 * object if calls this method not on VirtualBox startup.
3244 *
3245 * @param aMachine machine to register
3246 *
3247 * @note Locks objects!
3248 */
3249HRESULT VirtualBox::registerMachine (Machine *aMachine)
3250{
3251 ComAssertRet (aMachine, E_INVALIDARG);
3252
3253 AutoCaller autoCaller (this);
3254 CheckComRCReturnRC (autoCaller.rc());
3255
3256 AutoWriteLock alock (this);
3257
3258 HRESULT rc = S_OK;
3259
3260 {
3261 ComObjPtr <Machine> m;
3262 rc = findMachine (aMachine->id(), false /* aDoSetError */, &m);
3263 if (SUCCEEDED (rc))
3264 {
3265 /* sanity */
3266 AutoLimitedCaller machCaller (m);
3267 AssertComRC (machCaller.rc());
3268
3269 return setError (E_INVALIDARG,
3270 tr ("Registered machine with UUID {%RTuuid} ('%ls') already exists"),
3271 aMachine->id().raw(), m->settingsFileFull().raw());
3272 }
3273
3274 ComAssertRet (rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3275 rc = S_OK;
3276 }
3277
3278 if (autoCaller.state() != InInit)
3279 {
3280 /* Machine::trySetRegistered() will commit and save machine settings */
3281 rc = aMachine->trySetRegistered (TRUE);
3282 CheckComRCReturnRC (rc);
3283 }
3284
3285 /* add to the collection of registered machines */
3286 mData.mMachines.push_back (aMachine);
3287
3288 if (autoCaller.state() != InInit)
3289 rc = saveSettings();
3290
3291 return rc;
3292}
3293
3294/**
3295 * Remembers the given hard disk by storing it in the hard disk registry.
3296 *
3297 * @param aHardDisk Hard disk object to remember.
3298 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3299 *
3300 * When @a aSaveRegistry is @c true, this operation may fail because of the
3301 * failed #saveSettings() method it calls. In this case, the hard disk object
3302 * will not be remembered. It is therefore the responsibility of the caller to
3303 * call this method as the last step of some action that requires registration
3304 * in order to make sure that only fully functional hard disk objects get
3305 * registered.
3306 *
3307 * @note Locks this object for writing and @a aHardDisk for reading.
3308 */
3309HRESULT VirtualBox::registerHardDisk2 (HardDisk2 *aHardDisk,
3310 bool aSaveRegistry /*= true*/)
3311{
3312 AssertReturn (aHardDisk != NULL, E_INVALIDARG);
3313
3314 AutoCaller autoCaller (this);
3315 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3316
3317 AutoWriteLock alock (this);
3318
3319 AutoCaller hardDiskCaller (aHardDisk);
3320 AssertComRCReturn (hardDiskCaller.rc(), hardDiskCaller.rc());
3321
3322 AutoReadLock hardDiskLock (aHardDisk);
3323
3324 Utf8Str conflict;
3325 HRESULT rc = checkMediaForConflicts2 (aHardDisk->id(),
3326 aHardDisk->locationFull(),
3327 conflict);
3328 CheckComRCReturnRC (rc);
3329
3330 if (!conflict.isNull())
3331 {
3332 return setError (E_INVALIDARG,
3333 tr ("Cannot register the hard disk '%ls' with UUID {%RTuuid} "
3334 "because a %s already exists in the media registry ('%ls')"),
3335 aHardDisk->locationFull().raw(), aHardDisk->id().raw(),
3336 conflict.raw(), mData.mCfgFile.mName.raw());
3337 }
3338
3339 if (aHardDisk->parent().isNull())
3340 {
3341 /* base (root) hard disk */
3342 mData.mHardDisks2.push_back (aHardDisk);
3343 }
3344
3345 mData.mHardDisk2Map
3346 .insert (HardDisk2Map::value_type (
3347 aHardDisk->id(), HardDisk2Map::mapped_type (aHardDisk)));
3348
3349 if (aSaveRegistry)
3350 {
3351 rc = saveSettings();
3352 if (FAILED (rc))
3353 unregisterHardDisk2 (aHardDisk, false /* aSaveRegistry */);
3354 }
3355
3356 return rc;
3357}
3358
3359/**
3360 * Removes the given hard disk from the hard disk registry.
3361 *
3362 * @param aHardDisk Hard disk object to remove.
3363 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3364 *
3365 * When @a aSaveRegistry is @c true, this operation may fail because of the
3366 * failed #saveSettings() method it calls. In this case, the hard disk object
3367 * will NOT be removed from the registry when this method returns. It is
3368 * therefore the responsibility of the caller to call this method as the first
3369 * step of some action that requires unregistration, before calling uninit() on
3370 * @a aHardDisk.
3371 *
3372 * @note Locks this object for writing and @a aHardDisk for reading.
3373 */
3374HRESULT VirtualBox::unregisterHardDisk2 (HardDisk2 *aHardDisk,
3375 bool aSaveRegistry /*= true*/)
3376{
3377 AssertReturn (aHardDisk != NULL, E_INVALIDARG);
3378
3379 AutoCaller autoCaller (this);
3380 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3381
3382 AutoWriteLock alock (this);
3383
3384 AutoCaller hardDiskCaller (aHardDisk);
3385 AssertComRCReturn (hardDiskCaller.rc(), hardDiskCaller.rc());
3386
3387 AutoReadLock hardDiskLock (aHardDisk);
3388
3389 size_t cnt = mData.mHardDisk2Map.erase (aHardDisk->id());
3390 Assert (cnt == 1);
3391
3392 if (aHardDisk->parent().isNull())
3393 {
3394 /* base (root) hard disk */
3395 mData.mHardDisks2.remove (aHardDisk);
3396 }
3397
3398 HRESULT rc = S_OK;
3399
3400 if (aSaveRegistry)
3401 {
3402 rc = saveSettings();
3403 if (FAILED (rc))
3404 registerHardDisk2 (aHardDisk, false /* aSaveRegistry */);
3405 }
3406
3407 return rc;
3408}
3409
3410/**
3411 * Remembers the given image by storing it in the CD/DVD image registry.
3412 *
3413 * @param aImage Image object to remember.
3414 * @param aSaveRegistry @c true to save the image registry to disk (default).
3415 *
3416 * When @a aSaveRegistry is @c true, this operation may fail because of the
3417 * failed #saveSettings() method it calls. In this case, the image object
3418 * will not be remembered. It is therefore the responsibility of the caller to
3419 * call this method as the last step of some action that requires registration
3420 * in order to make sure that only fully functional image objects get
3421 * registered.
3422 *
3423 * @note Locks this object for writing and @a aImage for reading.
3424 */
3425HRESULT VirtualBox::registerDVDImage (DVDImage2 *aImage,
3426 bool aSaveRegistry /*= true*/)
3427{
3428 AssertReturn (aImage != NULL, E_INVALIDARG);
3429
3430 AutoCaller autoCaller (this);
3431 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3432
3433 AutoWriteLock alock (this);
3434
3435 AutoCaller imageCaller (aImage);
3436 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3437
3438 AutoReadLock imageLock (aImage);
3439
3440 Utf8Str conflict;
3441 HRESULT rc = checkMediaForConflicts2 (aImage->id(), aImage->locationFull(),
3442 conflict);
3443 CheckComRCReturnRC (rc);
3444
3445 if (!conflict.isNull())
3446 {
3447 return setError (VBOX_E_INVALID_OBJECT_STATE,
3448 tr ("Cannot register the CD/DVD image '%ls' with UUID {%RTuuid} "
3449 "because a %s already exists in the media registry ('%ls')"),
3450 aImage->locationFull().raw(), aImage->id().raw(),
3451 conflict.raw(), mData.mCfgFile.mName.raw());
3452 }
3453
3454 /* add to the collection */
3455 mData.mDVDImages2.push_back (aImage);
3456
3457 if (aSaveRegistry)
3458 {
3459 rc = saveSettings();
3460 if (FAILED (rc))
3461 unregisterDVDImage (aImage, false /* aSaveRegistry */);
3462 }
3463
3464 return rc;
3465}
3466
3467/**
3468 * Removes the given image from the CD/DVD image registry registry.
3469 *
3470 * @param aImage Image object to remove.
3471 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3472 *
3473 * When @a aSaveRegistry is @c true, this operation may fail because of the
3474 * failed #saveSettings() method it calls. In this case, the image object
3475 * will NOT be removed from the registry when this method returns. It is
3476 * therefore the responsibility of the caller to call this method as the first
3477 * step of some action that requires unregistration, before calling uninit() on
3478 * @a aImage.
3479 *
3480 * @note Locks this object for writing and @a aImage for reading.
3481 */
3482HRESULT VirtualBox::unregisterDVDImage (DVDImage2 *aImage,
3483 bool aSaveRegistry /*= true*/)
3484{
3485 AssertReturn (aImage != NULL, E_INVALIDARG);
3486
3487 AutoCaller autoCaller (this);
3488 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3489
3490 AutoWriteLock alock (this);
3491
3492 AutoCaller imageCaller (aImage);
3493 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3494
3495 AutoReadLock imageLock (aImage);
3496
3497 mData.mDVDImages2.remove (aImage);
3498
3499 HRESULT rc = S_OK;
3500
3501 if (aSaveRegistry)
3502 {
3503 rc = saveSettings();
3504 if (FAILED (rc))
3505 registerDVDImage (aImage, false /* aSaveRegistry */);
3506 }
3507
3508 return rc;
3509}
3510
3511/**
3512 * Remembers the given image by storing it in the floppy image registry.
3513 *
3514 * @param aImage Image object to remember.
3515 * @param aSaveRegistry @c true to save the image registry to disk (default).
3516 *
3517 * When @a aSaveRegistry is @c true, this operation may fail because of the
3518 * failed #saveSettings() method it calls. In this case, the image object
3519 * will not be remembered. It is therefore the responsibility of the caller to
3520 * call this method as the last step of some action that requires registration
3521 * in order to make sure that only fully functional image objects get
3522 * registered.
3523 *
3524 * @note Locks this object for writing and @a aImage for reading.
3525 */
3526HRESULT VirtualBox::registerFloppyImage (FloppyImage2 *aImage,
3527 bool aSaveRegistry /*= true*/)
3528{
3529 AssertReturn (aImage != NULL, E_INVALIDARG);
3530
3531 AutoCaller autoCaller (this);
3532 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3533
3534 AutoWriteLock alock (this);
3535
3536 AutoCaller imageCaller (aImage);
3537 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3538
3539 AutoReadLock imageLock (aImage);
3540
3541 Utf8Str conflict;
3542 HRESULT rc = checkMediaForConflicts2 (aImage->id(), aImage->locationFull(),
3543 conflict);
3544 CheckComRCReturnRC (rc);
3545
3546 if (!conflict.isNull())
3547 {
3548 return setError (VBOX_E_INVALID_OBJECT_STATE,
3549 tr ("Cannot register the floppy image '%ls' with UUID {%RTuuid} "
3550 "because a %s already exists in the media registry ('%ls')"),
3551 aImage->locationFull().raw(), aImage->id().raw(),
3552 conflict.raw(), mData.mCfgFile.mName.raw());
3553 }
3554
3555 /* add to the collection */
3556 mData.mFloppyImages2.push_back (aImage);
3557
3558 if (aSaveRegistry)
3559 {
3560 rc = saveSettings();
3561 if (FAILED (rc))
3562 unregisterFloppyImage (aImage, false /* aSaveRegistry */);
3563 }
3564
3565 return rc;
3566}
3567
3568/**
3569 * Removes the given image from the floppy image registry registry.
3570 *
3571 * @param aImage Image object to remove.
3572 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3573 *
3574 * When @a aSaveRegistry is @c true, this operation may fail because of the
3575 * failed #saveSettings() method it calls. In this case, the image object
3576 * will NOT be removed from the registry when this method returns. It is
3577 * therefore the responsibility of the caller to call this method as the first
3578 * step of some action that requires unregistration, before calling uninit() on
3579 * @a aImage.
3580 *
3581 * @note Locks this object for writing and @a aImage for reading.
3582 */
3583HRESULT VirtualBox::unregisterFloppyImage (FloppyImage2 *aImage,
3584 bool aSaveRegistry /*= true*/)
3585{
3586 AssertReturn (aImage != NULL, E_INVALIDARG);
3587
3588 AutoCaller autoCaller (this);
3589 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3590
3591 AutoWriteLock alock (this);
3592
3593 AutoCaller imageCaller (aImage);
3594 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3595
3596 AutoReadLock imageLock (aImage);
3597
3598 mData.mFloppyImages2.remove (aImage);
3599
3600 HRESULT rc = S_OK;
3601
3602 if (aSaveRegistry)
3603 {
3604 rc = saveSettings();
3605 if (FAILED (rc))
3606 registerFloppyImage (aImage, false /* aSaveRegistry */);
3607 }
3608
3609 return rc;
3610}
3611
3612/**
3613 * Attempts to cast from a raw interface pointer to an underlying object.
3614 * On success, @a aTo will contain the object reference. On failure, @a aTo will
3615 * be set to @c null and an extended error info will be returned.
3616 *
3617 * @param aFrom Interface pointer to cast from.
3618 * @param aTo Where to store a reference to the underlying object.
3619 *
3620 * @note Locks #childrenLock() for reading.
3621 */
3622HRESULT VirtualBox::cast (IHardDisk2 *aFrom, ComObjPtr <HardDisk2> &aTo)
3623{
3624 AssertReturn (aFrom != NULL, E_INVALIDARG);
3625
3626 AutoCaller autoCaller (this);
3627 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3628
3629 /* We need the children map lock here to keep the getDependentChild() result
3630 * valid until we finish */
3631 AutoReadLock chLock (childrenLock());
3632
3633 VirtualBoxBase *child = getDependentChild (aFrom);
3634 if (!child)
3635 return setError (E_FAIL, tr ("The given hard disk object is not created "
3636 "within this VirtualBox instance"));
3637
3638 /* we can safely cast child to HardDisk * here because only HardDisk
3639 * implementations of IHardDisk can be among our children */
3640
3641 aTo = static_cast <HardDisk2 *> (child);
3642
3643 return S_OK;
3644}
3645
3646/**
3647 * Helper to update the global settings file when the name of some machine
3648 * changes so that file and directory renaming occurs. This method ensures that
3649 * all affected paths in the disk registry are properly updated.
3650 *
3651 * @param aOldPath Old path (full).
3652 * @param aNewPath New path (full).
3653 *
3654 * @note Locks this object + DVD, Floppy and HardDisk children for writing.
3655 */
3656HRESULT VirtualBox::updateSettings (const char *aOldPath, const char *aNewPath)
3657{
3658 LogFlowThisFunc (("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
3659
3660 AssertReturn (aOldPath, E_INVALIDARG);
3661 AssertReturn (aNewPath, E_INVALIDARG);
3662
3663 AutoCaller autoCaller (this);
3664 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3665
3666 AutoWriteLock alock (this);
3667
3668 /* check DVD paths */
3669 for (DVDImage2List::iterator it = mData.mDVDImages2.begin();
3670 it != mData.mDVDImages2.end();
3671 ++ it)
3672 {
3673 (*it)->updatePath (aOldPath, aNewPath);
3674 }
3675
3676 /* check Floppy paths */
3677 for (FloppyImage2List::iterator it = mData.mFloppyImages2.begin();
3678 it != mData.mFloppyImages2.end();
3679 ++ it)
3680 {
3681 (*it)->updatePath (aOldPath, aNewPath);
3682 }
3683
3684 /* check HardDisk paths */
3685 for (HardDisk2List::const_iterator it = mData.mHardDisks2.begin();
3686 it != mData.mHardDisks2.end();
3687 ++ it)
3688 {
3689 (*it)->updatePaths (aOldPath, aNewPath);
3690 }
3691
3692 HRESULT rc = saveSettings();
3693
3694 return rc;
3695}
3696
3697/**
3698 * Creates the path to the specified file according to the path information
3699 * present in the file name.
3700 *
3701 * Note that the given file name must contain the full path otherwise the
3702 * extracted relative path will be created based on the current working
3703 * directory which is normally unknown.
3704 *
3705 * @param aFileName Full file name which path needs to be created.
3706 *
3707 * @return Extended error information on failure to create the path.
3708 */
3709/* static */
3710HRESULT VirtualBox::ensureFilePathExists (const char *aFileName)
3711{
3712 Utf8Str dir = aFileName;
3713 RTPathStripFilename (dir.mutableRaw());
3714 if (!RTDirExists (dir))
3715 {
3716 int vrc = RTDirCreateFullPath (dir, 0777);
3717 if (RT_FAILURE (vrc))
3718 {
3719 return setError (E_FAIL,
3720 tr ("Could not create the directory '%s' (%Rrc)"),
3721 dir.raw(), vrc);
3722 }
3723 }
3724
3725 return S_OK;
3726}
3727
3728/**
3729 * Helper method to load the setting tree and turn expected exceptions into
3730 * COM errors, according to arguments.
3731 *
3732 * Note that this method will not catch unexpected errors so it may still
3733 * throw something.
3734 *
3735 * @param aTree Tree to load into settings.
3736 * @param aFile File to load settings from.
3737 * @param aValidate @c @true to enable tree validation.
3738 * @param aCatchLoadErrors @c true to catch exceptions caused by file
3739 * access or validation errors.
3740 * @param aAddDefaults @c true to cause the substitution of default
3741 * values for missing attributes that have
3742 * defaults in the XML schema.
3743 * @param aFormatVersion Where to store the current format version of the
3744 * loaded settings tree (optional, may be NULL).
3745 */
3746/* static */
3747HRESULT VirtualBox::loadSettingsTree (settings::XmlTreeBackend &aTree,
3748 xml::File &aFile,
3749 bool aValidate,
3750 bool aCatchLoadErrors,
3751 bool aAddDefaults,
3752 Utf8Str *aFormatVersion /* = NULL */)
3753{
3754 using namespace settings;
3755
3756 try
3757 {
3758 SettingsTreeHelper helper = SettingsTreeHelper();
3759
3760 aTree.setInputResolver (helper);
3761 aTree.setAutoConverter (helper);
3762
3763 aTree.read (aFile, aValidate ? VBOX_XML_SCHEMA : NULL,
3764 aAddDefaults ? XmlTreeBackend::Read_AddDefaults : 0);
3765
3766 aTree.resetAutoConverter();
3767 aTree.resetInputResolver();
3768
3769 /* on success, memorize the current settings file version or set it to
3770 * the most recent version if no settings conversion took place. Note
3771 * that it's not necessary to do it every time we load the settings file
3772 * (i.e. only loadSettingsTree_FirstTime() passes a non-NULL
3773 * aFormatVersion value) because currently we keep the settings
3774 * files locked so that the only legal way to change the format version
3775 * while VirtualBox is running is saveSettingsTree(). */
3776 if (aFormatVersion != NULL)
3777 {
3778 *aFormatVersion = aTree.oldVersion();
3779 if (aFormatVersion->isNull())
3780 *aFormatVersion = VBOX_XML_VERSION_FULL;
3781 }
3782 }
3783 catch (const xml::EIPRTFailure &err)
3784 {
3785 if (!aCatchLoadErrors)
3786 throw;
3787
3788 return setError (VBOX_E_FILE_ERROR,
3789 tr ("Could not load the settings file '%s' (%Rrc)"),
3790 aFile.uri(), err.rc());
3791 }
3792 catch (const xml::RuntimeError &err)
3793 {
3794 Assert (err.what() != NULL);
3795
3796 if (!aCatchLoadErrors)
3797 throw;
3798
3799 return setError (VBOX_E_XML_ERROR,
3800 tr ("Could not load the settings file '%s'.\n%s"),
3801 aFile.uri(),
3802 err.what() ? err.what() : "Unknown error");
3803 }
3804
3805 return S_OK;
3806}
3807
3808/**
3809 * Helper method to save the settings tree and turn expected exceptions to COM
3810 * errors.
3811 *
3812 * Note that this method will not catch unexpected errors so it may still
3813 * throw something.
3814 *
3815 * @param aTree Tree to save.
3816 * @param aFile File to save the tree to.
3817 * @param aFormatVersion Where to store the (recent) format version of the
3818 * saved settings tree on success.
3819 */
3820/* static */
3821HRESULT VirtualBox::saveSettingsTree (settings::TreeBackend &aTree,
3822 xml::File &aFile,
3823 Utf8Str &aFormatVersion)
3824{
3825 using namespace settings;
3826
3827 try
3828 {
3829 aTree.write (aFile);
3830
3831 /* set the current settings file version to the most recent version on
3832 * success. See also VirtualBox::loadSettingsTree(). */
3833 if (aFormatVersion != VBOX_XML_VERSION_FULL)
3834 aFormatVersion = VBOX_XML_VERSION_FULL;
3835 }
3836 catch (const xml::EIPRTFailure &err)
3837 {
3838 /* this is the only expected exception for now */
3839 return setError (VBOX_E_FILE_ERROR,
3840 tr ("Could not save the settings file '%s' (%Rrc)"),
3841 aFile.uri(), err.rc());
3842 }
3843
3844 return S_OK;
3845}
3846
3847/**
3848 * Creates a backup copy of the given settings file by suffixing it with the
3849 * supplied version format string and optionally with numbers from .0 to .9
3850 * if the backup file already exists.
3851 *
3852 * @param aFileName Original settings file name.
3853 * @param aOldFormat Version of the original format.
3854 * @param aBakFileName File name of the created backup copy (only on success).
3855 */
3856/* static */
3857HRESULT VirtualBox::backupSettingsFile (const Bstr &aFileName,
3858 const Utf8Str &aOldFormat,
3859 Bstr &aBakFileName)
3860{
3861 Utf8Str of = aFileName;
3862 Utf8Str nf = Utf8StrFmt ("%s.%s.bak", of.raw(), aOldFormat.raw());
3863
3864 int vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
3865 NULL, NULL);
3866
3867 /* try progressive suffix from .0 to .9 on failure */
3868 if (vrc == VERR_ALREADY_EXISTS)
3869 {
3870 Utf8Str tmp = nf;
3871 for (int i = 0; i <= 9 && RT_FAILURE (vrc); ++ i)
3872 {
3873 nf = Utf8StrFmt ("%s.%d", tmp.raw(), i);
3874 vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
3875 NULL, NULL);
3876 }
3877 }
3878
3879 if (RT_FAILURE (vrc))
3880 return setError (VBOX_E_IPRT_ERROR,
3881 tr ("Could not copy the settings file '%s' to '%s' (%Rrc)"),
3882 of.raw(), nf.raw(), vrc);
3883
3884 aBakFileName = nf;
3885
3886 return S_OK;
3887}
3888
3889/**
3890 * Handles unexpected exceptions by turning them into COM errors in release
3891 * builds or by hitting a breakpoint in the release builds.
3892 *
3893 * Usage pattern:
3894 * @code
3895 try
3896 {
3897 // ...
3898 }
3899 catch (LaLalA)
3900 {
3901 // ...
3902 }
3903 catch (...)
3904 {
3905 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3906 }
3907 * @endcode
3908 *
3909 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3910 */
3911/* static */
3912HRESULT VirtualBox::handleUnexpectedExceptions (RT_SRC_POS_DECL)
3913{
3914 try
3915 {
3916 /* re-throw the current exception */
3917 throw;
3918 }
3919 catch (const std::exception &err)
3920 {
3921 ComAssertMsgFailedPos (("Unexpected exception '%s' (%s)",
3922 typeid (err).name(), err.what()),
3923 pszFile, iLine, pszFunction);
3924 return E_FAIL;
3925 }
3926 catch (...)
3927 {
3928 ComAssertMsgFailedPos (("Unknown exception"),
3929 pszFile, iLine, pszFunction);
3930 return E_FAIL;
3931 }
3932
3933 /* should not get here */
3934 AssertFailed();
3935 return E_FAIL;
3936}
3937
3938/**
3939 * Helper to lock the VirtualBox configuration for write access.
3940 *
3941 * @note This method is not thread safe (must be called only from #init()
3942 * or #uninit()).
3943 *
3944 * @note If the configuration file is not found, the method returns
3945 * S_OK, but subsequent #isConfigLocked() will return FALSE. This is used
3946 * in some places to determine the (valid) situation when no config file
3947 * exists yet, and therefore a new one should be created from scratch.
3948 */
3949HRESULT VirtualBox::lockConfig()
3950{
3951 AutoCaller autoCaller (this);
3952 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3953 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3954
3955 HRESULT rc = S_OK;
3956
3957 Assert (!isConfigLocked());
3958 if (!isConfigLocked())
3959 {
3960 /* open the associated config file */
3961 int vrc = RTFileOpen (&mData.mCfgFile.mHandle,
3962 Utf8Str (mData.mCfgFile.mName),
3963 RTFILE_O_READWRITE | RTFILE_O_OPEN |
3964 RTFILE_O_DENY_WRITE);
3965 if (RT_FAILURE (vrc))
3966 {
3967 mData.mCfgFile.mHandle = NIL_RTFILE;
3968
3969 /*
3970 * It is OK if the file is not found, it will be created by
3971 * init(). Otherwise return an error.
3972 */
3973 if (vrc != VERR_FILE_NOT_FOUND)
3974 rc = setError (E_FAIL,
3975 tr ("Could not lock the settings file '%ls' (%Rrc)"),
3976 mData.mCfgFile.mName.raw(), vrc);
3977 }
3978
3979 LogFlowThisFunc (("mCfgFile.mName='%ls', mCfgFile.mHandle=%d, rc=%08X\n",
3980 mData.mCfgFile.mName.raw(), mData.mCfgFile.mHandle, rc));
3981 }
3982
3983 return rc;
3984}
3985
3986/**
3987 * Helper to unlock the VirtualBox configuration from write access.
3988 *
3989 * @note This method is not thread safe (must be called only from #init()
3990 * or #uninit()).
3991 */
3992HRESULT VirtualBox::unlockConfig()
3993{
3994 AutoCaller autoCaller (this);
3995 AssertComRCReturn (autoCaller.rc(), E_FAIL);
3996 AssertReturn (autoCaller.state() == InUninit, E_FAIL);
3997
3998 HRESULT rc = S_OK;
3999
4000 if (isConfigLocked())
4001 {
4002 RTFileFlush (mData.mCfgFile.mHandle);
4003 RTFileClose (mData.mCfgFile.mHandle);
4004 /** @todo flush the directory too. */
4005 mData.mCfgFile.mHandle = NIL_RTFILE;
4006 LogFlowThisFunc (("\n"));
4007 }
4008
4009 return rc;
4010}
4011
4012/**
4013 * Thread function that watches the termination of all client processes
4014 * that have opened sessions using IVirtualBox::OpenSession()
4015 */
4016// static
4017DECLCALLBACK(int) VirtualBox::ClientWatcher (RTTHREAD thread, void *pvUser)
4018{
4019 LogFlowFuncEnter();
4020
4021 VirtualBox *that = (VirtualBox *) pvUser;
4022 Assert (that);
4023
4024 SessionMachineVector machines;
4025 MachineVector spawnedMachines;
4026
4027 size_t cnt = 0;
4028 size_t cntSpawned = 0;
4029
4030#if defined(RT_OS_WINDOWS)
4031
4032 HRESULT hrc = CoInitializeEx (NULL,
4033 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
4034 COINIT_SPEED_OVER_MEMORY);
4035 AssertComRC (hrc);
4036
4037 /// @todo (dmik) processes reaping!
4038
4039 HANDLE handles [MAXIMUM_WAIT_OBJECTS];
4040 handles [0] = that->mWatcherData.mUpdateReq;
4041
4042 do
4043 {
4044 AutoCaller autoCaller (that);
4045 /* VirtualBox has been early uninitialized, terminate */
4046 if (!autoCaller.isOk())
4047 break;
4048
4049 do
4050 {
4051 /* release the caller to let uninit() ever proceed */
4052 autoCaller.release();
4053
4054 DWORD rc = ::WaitForMultipleObjects (1 + cnt + cntSpawned,
4055 handles, FALSE, INFINITE);
4056
4057 /* Restore the caller before using VirtualBox. If it fails, this
4058 * means VirtualBox is being uninitialized and we must terminate. */
4059 autoCaller.add();
4060 if (!autoCaller.isOk())
4061 break;
4062
4063 bool update = false;
4064
4065 if (rc == WAIT_OBJECT_0)
4066 {
4067 /* update event is signaled */
4068 update = true;
4069 }
4070 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4071 {
4072 /* machine mutex is released */
4073 (machines [rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4074 update = true;
4075 }
4076 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4077 {
4078 /* machine mutex is abandoned due to client process termination */
4079 (machines [rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4080 update = true;
4081 }
4082 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
4083 {
4084 /* spawned VM process has terminated (normally or abnormally) */
4085 (spawnedMachines [rc - WAIT_OBJECT_0 - cnt - 1])->
4086 checkForSpawnFailure();
4087 update = true;
4088 }
4089
4090 if (update)
4091 {
4092 /* close old process handles */
4093 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4094 CloseHandle (handles [i]);
4095
4096 AutoReadLock thatLock (that);
4097
4098 /* obtain a new set of opened machines */
4099 cnt = 0;
4100 machines.clear();
4101
4102 for (MachineList::iterator it = that->mData.mMachines.begin();
4103 it != that->mData.mMachines.end(); ++ it)
4104 {
4105 /// @todo handle situations with more than 64 objects
4106 AssertMsgBreak ((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
4107 ("MAXIMUM_WAIT_OBJECTS reached"));
4108
4109 ComObjPtr <SessionMachine> sm;
4110 HANDLE ipcSem;
4111 if ((*it)->isSessionOpenOrClosing (sm, NULL, &ipcSem))
4112 {
4113 machines.push_back (sm);
4114 handles [1 + cnt] = ipcSem;
4115 ++ cnt;
4116 }
4117 }
4118
4119 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4120
4121 /* obtain a new set of spawned machines */
4122 cntSpawned = 0;
4123 spawnedMachines.clear();
4124
4125 for (MachineList::iterator it = that->mData.mMachines.begin();
4126 it != that->mData.mMachines.end(); ++ it)
4127 {
4128 /// @todo handle situations with more than 64 objects
4129 AssertMsgBreak ((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
4130 ("MAXIMUM_WAIT_OBJECTS reached"));
4131
4132 RTPROCESS pid;
4133 if ((*it)->isSessionSpawning (&pid))
4134 {
4135 HANDLE ph = OpenProcess (SYNCHRONIZE, FALSE, pid);
4136 AssertMsg (ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
4137 pid, GetLastError()));
4138 if (rc == 0)
4139 {
4140 spawnedMachines.push_back (*it);
4141 handles [1 + cnt + cntSpawned] = ph;
4142 ++ cntSpawned;
4143 }
4144 }
4145 }
4146
4147 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4148 }
4149 }
4150 while (true);
4151 }
4152 while (0);
4153
4154 /* close old process handles */
4155 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4156 CloseHandle (handles [i]);
4157
4158 /* release sets of machines if any */
4159 machines.clear();
4160 spawnedMachines.clear();
4161
4162 ::CoUninitialize();
4163
4164#elif defined (RT_OS_OS2)
4165
4166 /// @todo (dmik) processes reaping!
4167
4168 /* according to PMREF, 64 is the maximum for the muxwait list */
4169 SEMRECORD handles [64];
4170
4171 HMUX muxSem = NULLHANDLE;
4172
4173 do
4174 {
4175 AutoCaller autoCaller (that);
4176 /* VirtualBox has been early uninitialized, terminate */
4177 if (!autoCaller.isOk())
4178 break;
4179
4180 do
4181 {
4182 /* release the caller to let uninit() ever proceed */
4183 autoCaller.release();
4184
4185 int vrc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4186
4187 /* Restore the caller before using VirtualBox. If it fails, this
4188 * means VirtualBox is being uninitialized and we must terminate. */
4189 autoCaller.add();
4190 if (!autoCaller.isOk())
4191 break;
4192
4193 bool update = false;
4194 bool updateSpawned = false;
4195
4196 if (RT_SUCCESS (vrc))
4197 {
4198 /* update event is signaled */
4199 update = true;
4200 updateSpawned = true;
4201 }
4202 else
4203 {
4204 AssertMsg (vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4205 ("RTSemEventWait returned %Rrc\n", vrc));
4206
4207 /* are there any mutexes? */
4208 if (cnt > 0)
4209 {
4210 /* figure out what's going on with machines */
4211
4212 unsigned long semId = 0;
4213 APIRET arc = ::DosWaitMuxWaitSem (muxSem,
4214 SEM_IMMEDIATE_RETURN, &semId);
4215
4216 if (arc == NO_ERROR)
4217 {
4218 /* machine mutex is normally released */
4219 Assert (semId >= 0 && semId < cnt);
4220 if (semId >= 0 && semId < cnt)
4221 {
4222#ifdef DEBUG
4223 {
4224 AutoReadLock machineLock (machines [semId]);
4225 LogFlowFunc (("released mutex: machine='%ls'\n",
4226 machines [semId]->name().raw()));
4227 }
4228#endif
4229 machines [semId]->checkForDeath();
4230 }
4231 update = true;
4232 }
4233 else if (arc == ERROR_SEM_OWNER_DIED)
4234 {
4235 /* machine mutex is abandoned due to client process
4236 * termination; find which mutex is in the Owner Died
4237 * state */
4238 for (size_t i = 0; i < cnt; ++ i)
4239 {
4240 PID pid; TID tid;
4241 unsigned long reqCnt;
4242 arc = DosQueryMutexSem ((HMTX) handles [i].hsemCur, &pid,
4243 &tid, &reqCnt);
4244 if (arc == ERROR_SEM_OWNER_DIED)
4245 {
4246 /* close the dead mutex as asked by PMREF */
4247 ::DosCloseMutexSem ((HMTX) handles [i].hsemCur);
4248
4249 Assert (i >= 0 && i < cnt);
4250 if (i >= 0 && i < cnt)
4251 {
4252#ifdef DEBUG
4253 {
4254 AutoReadLock machineLock (machines [semId]);
4255 LogFlowFunc (("mutex owner dead: machine='%ls'\n",
4256 machines [i]->name().raw()));
4257 }
4258#endif
4259 machines [i]->checkForDeath();
4260 }
4261 }
4262 }
4263 update = true;
4264 }
4265 else
4266 AssertMsg (arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4267 ("DosWaitMuxWaitSem returned %d\n", arc));
4268 }
4269
4270 /* are there any spawning sessions? */
4271 if (cntSpawned > 0)
4272 {
4273 for (size_t i = 0; i < cntSpawned; ++ i)
4274 updateSpawned |= (spawnedMachines [i])->
4275 checkForSpawnFailure();
4276 }
4277 }
4278
4279 if (update || updateSpawned)
4280 {
4281 AutoReadLock thatLock (that);
4282
4283 if (update)
4284 {
4285 /* close the old muxsem */
4286 if (muxSem != NULLHANDLE)
4287 ::DosCloseMuxWaitSem (muxSem);
4288
4289 /* obtain a new set of opened machines */
4290 cnt = 0;
4291 machines.clear();
4292
4293 for (MachineList::iterator it = that->mData.mMachines.begin();
4294 it != that->mData.mMachines.end(); ++ it)
4295 {
4296 /// @todo handle situations with more than 64 objects
4297 AssertMsg (cnt <= 64 /* according to PMREF */,
4298 ("maximum of 64 mutex semaphores reached (%d)",
4299 cnt));
4300
4301 ComObjPtr <SessionMachine> sm;
4302 HMTX ipcSem;
4303 if ((*it)->isSessionOpenOrClosing (sm, NULL, &ipcSem))
4304 {
4305 machines.push_back (sm);
4306 handles [cnt].hsemCur = (HSEM) ipcSem;
4307 handles [cnt].ulUser = cnt;
4308 ++ cnt;
4309 }
4310 }
4311
4312 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4313
4314 if (cnt > 0)
4315 {
4316 /* create a new muxsem */
4317 APIRET arc = ::DosCreateMuxWaitSem (NULL, &muxSem, cnt,
4318 handles,
4319 DCMW_WAIT_ANY);
4320 AssertMsg (arc == NO_ERROR,
4321 ("DosCreateMuxWaitSem returned %d\n", arc));
4322 NOREF(arc);
4323 }
4324 }
4325
4326 if (updateSpawned)
4327 {
4328 /* obtain a new set of spawned machines */
4329 spawnedMachines.clear();
4330
4331 for (MachineList::iterator it = that->mData.mMachines.begin();
4332 it != that->mData.mMachines.end(); ++ it)
4333 {
4334 if ((*it)->isSessionSpawning())
4335 spawnedMachines.push_back (*it);
4336 }
4337
4338 cntSpawned = spawnedMachines.size();
4339 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4340 }
4341 }
4342 }
4343 while (true);
4344 }
4345 while (0);
4346
4347 /* close the muxsem */
4348 if (muxSem != NULLHANDLE)
4349 ::DosCloseMuxWaitSem (muxSem);
4350
4351 /* release sets of machines if any */
4352 machines.clear();
4353 spawnedMachines.clear();
4354
4355#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4356
4357 bool update = false;
4358 bool updateSpawned = false;
4359
4360 do
4361 {
4362 AutoCaller autoCaller (that);
4363 if (!autoCaller.isOk())
4364 break;
4365
4366 do
4367 {
4368 /* release the caller to let uninit() ever proceed */
4369 autoCaller.release();
4370
4371 int rc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4372
4373 /*
4374 * Restore the caller before using VirtualBox. If it fails, this
4375 * means VirtualBox is being uninitialized and we must terminate.
4376 */
4377 autoCaller.add();
4378 if (!autoCaller.isOk())
4379 break;
4380
4381 if (RT_SUCCESS (rc) || update || updateSpawned)
4382 {
4383 /* RT_SUCCESS (rc) means an update event is signaled */
4384
4385 AutoReadLock thatLock (that);
4386
4387 if (RT_SUCCESS (rc) || update)
4388 {
4389 /* obtain a new set of opened machines */
4390 machines.clear();
4391
4392 for (MachineList::iterator it = that->mData.mMachines.begin();
4393 it != that->mData.mMachines.end(); ++ it)
4394 {
4395 ComObjPtr <SessionMachine> sm;
4396 if ((*it)->isSessionOpenOrClosing (sm))
4397 machines.push_back (sm);
4398 }
4399
4400 cnt = machines.size();
4401 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4402 }
4403
4404 if (RT_SUCCESS (rc) || updateSpawned)
4405 {
4406 /* obtain a new set of spawned machines */
4407 spawnedMachines.clear();
4408
4409 for (MachineList::iterator it = that->mData.mMachines.begin();
4410 it != that->mData.mMachines.end(); ++ it)
4411 {
4412 if ((*it)->isSessionSpawning())
4413 spawnedMachines.push_back (*it);
4414 }
4415
4416 cntSpawned = spawnedMachines.size();
4417 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4418 }
4419 }
4420
4421 update = false;
4422 for (size_t i = 0; i < cnt; ++ i)
4423 update |= (machines [i])->checkForDeath();
4424
4425 updateSpawned = false;
4426 for (size_t i = 0; i < cntSpawned; ++ i)
4427 updateSpawned |= (spawnedMachines [i])->checkForSpawnFailure();
4428
4429 /* reap child processes */
4430 {
4431 AutoWriteLock alock (that);
4432 if (that->mWatcherData.mProcesses.size())
4433 {
4434 LogFlowFunc (("UPDATE: child process count = %d\n",
4435 that->mWatcherData.mProcesses.size()));
4436 ClientWatcherData::ProcessList::iterator it =
4437 that->mWatcherData.mProcesses.begin();
4438 while (it != that->mWatcherData.mProcesses.end())
4439 {
4440 RTPROCESS pid = *it;
4441 RTPROCSTATUS status;
4442 int vrc = ::RTProcWait (pid, RTPROCWAIT_FLAGS_NOBLOCK,
4443 &status);
4444 if (vrc == VINF_SUCCESS)
4445 {
4446 LogFlowFunc (("pid %d (%x) was reaped, "
4447 "status=%d, reason=%d\n",
4448 pid, pid, status.iStatus,
4449 status.enmReason));
4450 it = that->mWatcherData.mProcesses.erase (it);
4451 }
4452 else
4453 {
4454 LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4455 pid, pid, vrc));
4456 if (vrc != VERR_PROCESS_RUNNING)
4457 {
4458 /* remove the process if it is not already running */
4459 it = that->mWatcherData.mProcesses.erase (it);
4460 }
4461 else
4462 ++ it;
4463 }
4464 }
4465 }
4466 }
4467 }
4468 while (true);
4469 }
4470 while (0);
4471
4472 /* release sets of machines if any */
4473 machines.clear();
4474 spawnedMachines.clear();
4475
4476#else
4477# error "Port me!"
4478#endif
4479
4480 LogFlowFuncLeave();
4481 return 0;
4482}
4483
4484/**
4485 * Thread function that handles custom events posted using #postEvent().
4486 */
4487// static
4488DECLCALLBACK(int) VirtualBox::AsyncEventHandler (RTTHREAD thread, void *pvUser)
4489{
4490 LogFlowFuncEnter();
4491
4492 AssertReturn (pvUser, VERR_INVALID_POINTER);
4493
4494 // create an event queue for the current thread
4495 EventQueue *eventQ = new EventQueue();
4496 AssertReturn (eventQ, VERR_NO_MEMORY);
4497
4498 // return the queue to the one who created this thread
4499 *(static_cast <EventQueue **> (pvUser)) = eventQ;
4500 // signal that we're ready
4501 RTThreadUserSignal (thread);
4502
4503 BOOL ok = TRUE;
4504 Event *event = NULL;
4505
4506 while ((ok = eventQ->waitForEvent (&event)) && event)
4507 eventQ->handleEvent (event);
4508
4509 AssertReturn (ok, VERR_GENERAL_FAILURE);
4510
4511 delete eventQ;
4512
4513 LogFlowFuncLeave();
4514
4515 return 0;
4516}
4517
4518////////////////////////////////////////////////////////////////////////////////
4519
4520/**
4521 * Takes the current list of registered callbacks of the managed VirtualBox
4522 * instance, and calls #handleCallback() for every callback item from the
4523 * list, passing the item as an argument.
4524 *
4525 * @note Locks the managed VirtualBox object for reading but leaves the lock
4526 * before iterating over callbacks and calling their methods.
4527 */
4528void *VirtualBox::CallbackEvent::handler()
4529{
4530 if (mVirtualBox.isNull())
4531 return NULL;
4532
4533 AutoCaller autoCaller (mVirtualBox);
4534 if (!autoCaller.isOk())
4535 {
4536 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
4537 "the callback event is discarded!\n",
4538 autoCaller.state()));
4539 /* We don't need mVirtualBox any more, so release it */
4540 mVirtualBox.setNull();
4541 return NULL;
4542 }
4543
4544 CallbackVector callbacks;
4545 {
4546 /* Make a copy to release the lock before iterating */
4547 AutoReadLock alock (mVirtualBox);
4548 callbacks = CallbackVector (mVirtualBox->mData.mCallbacks.begin(),
4549 mVirtualBox->mData.mCallbacks.end());
4550 /* We don't need mVirtualBox any more, so release it */
4551 mVirtualBox.setNull();
4552 }
4553
4554 for (VirtualBox::CallbackVector::const_iterator it = callbacks.begin();
4555 it != callbacks.end(); ++ it)
4556 handleCallback (*it);
4557
4558 return NULL;
4559}
4560/* 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