VirtualBox

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

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

Main: do not include include/VBox/settings.h from other header files but only from implementations that need it (save compile time)

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

© 2023 Oracle
ContactPrivacy policyTerms of Use