VirtualBox

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

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

win DHCP server de-consoled

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