VirtualBox

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

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

Main: Treat hard disks with no Create capabilities as Created right after IVirtualBox::createHardDisk2() and don't allow IHardDisk2::create*Storage()/deleteStorage() for them.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette