VirtualBox

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

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

Main: more XML reader implemenation.

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