VirtualBox

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

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

no trailing \n when using ComAssertMsg*

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