VirtualBox

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

Last change on this file since 21138 was 21079, checked in by vboxsync, 15 years ago

Main: move libxml2 to IPRT unconditionally (remove VBOX_WITH_LIBXML2_IN_VBOXRT); move xml classes to IPRT; introduce IPRT ministring class as base for both Utf8Str and xml.cpp, with better performance; introduce some Utf8Str helpers to avoid string buffer hacks in Main code; remove std::auto_ptr<> from some headers

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