VirtualBox

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

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

Fix "unused variable cnt" warnings for the release build.

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