VirtualBox

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

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

Main: Better result codes for VIrtualBox::createMachine(), documentation. Assgined new UUIDs to changed interfaces (important!).

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