VirtualBox

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

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

Main: turn read/write param in OpenHardDisk into an enum

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

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