VirtualBox

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

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

dhcp settings uninit fix

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