VirtualBox

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

Last change on this file since 21612 was 21607, checked in by vboxsync, 15 years ago

Main: Enable a serial port for OS/2 guests by default.

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

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