VirtualBox

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

Last change on this file since 16927 was 16903, checked in by vboxsync, 15 years ago

Python: imporved error reporting in XPCOM, shell updates, docs

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

© 2023 Oracle
ContactPrivacy policyTerms of Use