VirtualBox

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

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

#3686: “Main: fix unused var warnings”

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

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