VirtualBox

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

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

Storage: Eradicated the last bits using the old VDI only backend, keeping only the testcases for now (no longer built).

Completely removed old iSCSI driver.

Added intnet option to addiscsidisk and adjusted documentation.

Made backend name comparisons case-insensitive.

Detect VMDK files not according to VMDK 1.0 and reject with clear error message.

Changed format probing logic to not fall through to the "unsupported" case if it's a known format, i.e. has valid header.

VBoxManage converthd generic format converter made official.

Added format flag to VBoxManage createhd, allows creating VMDK files.

VBoxManage convertdd reimplemented based on new framework, supporting any image format.

VBoxManage internalcommands sethduuid reimplemented based on new framework, supporting any image format.

Cleaned up error codes.

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