VirtualBox

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

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

Main: add read/write param to OpenHardDisk to allow for opening disk images during import without requiring write access

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