VirtualBox

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

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

IPRT, Main: make ministring throw std::bad_alloc on allocation failure; remove isEmpty() and isNull(), change Main code to using length() instead; second try

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 149.2 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 21404 2009-07-08 15:19:42Z 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 strConflict;
3484 HRESULT rc = checkMediaForConflicts2(aHardDisk->id(),
3485 aHardDisk->locationFull(),
3486 strConflict);
3487 CheckComRCReturnRC (rc);
3488
3489 if (strConflict.length())
3490 {
3491 return setError(E_INVALIDARG,
3492 tr("Cannot register the hard disk '%ls' with UUID {%RTuuid} because a %s already exists in the media registry ('%ls')"),
3493 aHardDisk->locationFull().raw(),
3494 aHardDisk->id().raw(),
3495 strConflict.raw(),
3496 mData.mCfgFile.mName.raw());
3497 }
3498
3499 if (aHardDisk->parent().isNull())
3500 {
3501 /* base (root) hard disk */
3502 mData.mHardDisks.push_back (aHardDisk);
3503 }
3504
3505 mData.mHardDiskMap
3506 .insert (HardDiskMap::value_type (
3507 aHardDisk->id(), HardDiskMap::mapped_type (aHardDisk)));
3508
3509 if (aSaveRegistry)
3510 {
3511 rc = saveSettings();
3512 if (FAILED (rc))
3513 unregisterHardDisk(aHardDisk, false /* aSaveRegistry */);
3514 }
3515
3516 return rc;
3517}
3518
3519/**
3520 * Removes the given hard disk from the hard disk registry.
3521 *
3522 * @param aHardDisk Hard disk object to remove.
3523 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3524 *
3525 * When @a aSaveRegistry is @c true, this operation may fail because of the
3526 * failed #saveSettings() method it calls. In this case, the hard disk object
3527 * will NOT be removed from the registry when this method returns. It is
3528 * therefore the responsibility of the caller to call this method as the first
3529 * step of some action that requires unregistration, before calling uninit() on
3530 * @a aHardDisk.
3531 *
3532 * @note Locks this object for writing and @a aHardDisk for reading.
3533 */
3534HRESULT VirtualBox::unregisterHardDisk(HardDisk *aHardDisk,
3535 bool aSaveRegistry /*= true*/)
3536{
3537 AssertReturn (aHardDisk != NULL, E_INVALIDARG);
3538
3539 AutoCaller autoCaller (this);
3540 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3541
3542 AutoWriteLock alock (this);
3543
3544 AutoCaller hardDiskCaller (aHardDisk);
3545 AssertComRCReturn (hardDiskCaller.rc(), hardDiskCaller.rc());
3546
3547 AutoReadLock hardDiskLock (aHardDisk);
3548
3549 size_t cnt = mData.mHardDiskMap.erase (aHardDisk->id());
3550 Assert (cnt == 1);
3551 NOREF(cnt);
3552
3553 if (aHardDisk->parent().isNull())
3554 {
3555 /* base (root) hard disk */
3556 mData.mHardDisks.remove (aHardDisk);
3557 }
3558
3559 HRESULT rc = S_OK;
3560
3561 if (aSaveRegistry)
3562 {
3563 rc = saveSettings();
3564 if (FAILED (rc))
3565 registerHardDisk(aHardDisk, false /* aSaveRegistry */);
3566 }
3567
3568 return rc;
3569}
3570
3571/**
3572 * Remembers the given image by storing it in the CD/DVD image registry.
3573 *
3574 * @param aImage Image object to remember.
3575 * @param aSaveRegistry @c true to save the image registry to disk (default).
3576 *
3577 * When @a aSaveRegistry is @c true, this operation may fail because of the
3578 * failed #saveSettings() method it calls. In this case, the image object
3579 * will not be remembered. It is therefore the responsibility of the caller to
3580 * call this method as the last step of some action that requires registration
3581 * in order to make sure that only fully functional image objects get
3582 * registered.
3583 *
3584 * @note Locks this object for writing and @a aImage for reading.
3585 */
3586HRESULT VirtualBox::registerDVDImage (DVDImage *aImage,
3587 bool aSaveRegistry /*= true*/)
3588{
3589 AssertReturn (aImage != NULL, E_INVALIDARG);
3590
3591 AutoCaller autoCaller (this);
3592 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3593
3594 AutoWriteLock alock (this);
3595
3596 AutoCaller imageCaller (aImage);
3597 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3598
3599 AutoReadLock imageLock (aImage);
3600
3601 Utf8Str strConflict;
3602 HRESULT rc = checkMediaForConflicts2(aImage->id(),
3603 aImage->locationFull(),
3604 strConflict);
3605 CheckComRCReturnRC (rc);
3606
3607 if (strConflict.length())
3608 {
3609 return setError(VBOX_E_INVALID_OBJECT_STATE,
3610 tr("Cannot register the CD/DVD image '%ls' with UUID {%RTuuid} because a %s already exists in the media registry ('%ls')"),
3611 aImage->locationFull().raw(),
3612 aImage->id().raw(),
3613 strConflict.raw(),
3614 mData.mCfgFile.mName.raw());
3615 }
3616
3617 /* add to the collection */
3618 mData.mDVDImages.push_back (aImage);
3619
3620 if (aSaveRegistry)
3621 {
3622 rc = saveSettings();
3623 if (FAILED (rc))
3624 unregisterDVDImage (aImage, false /* aSaveRegistry */);
3625 }
3626
3627 return rc;
3628}
3629
3630/**
3631 * Removes the given image from the CD/DVD image registry registry.
3632 *
3633 * @param aImage Image object to remove.
3634 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3635 *
3636 * When @a aSaveRegistry is @c true, this operation may fail because of the
3637 * failed #saveSettings() method it calls. In this case, the image object
3638 * will NOT be removed from the registry when this method returns. It is
3639 * therefore the responsibility of the caller to call this method as the first
3640 * step of some action that requires unregistration, before calling uninit() on
3641 * @a aImage.
3642 *
3643 * @note Locks this object for writing and @a aImage for reading.
3644 */
3645HRESULT VirtualBox::unregisterDVDImage (DVDImage *aImage,
3646 bool aSaveRegistry /*= true*/)
3647{
3648 AssertReturn (aImage != NULL, E_INVALIDARG);
3649
3650 AutoCaller autoCaller (this);
3651 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3652
3653 AutoWriteLock alock (this);
3654
3655 AutoCaller imageCaller (aImage);
3656 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3657
3658 AutoReadLock imageLock (aImage);
3659
3660 mData.mDVDImages.remove (aImage);
3661
3662 HRESULT rc = S_OK;
3663
3664 if (aSaveRegistry)
3665 {
3666 rc = saveSettings();
3667 if (FAILED (rc))
3668 registerDVDImage (aImage, false /* aSaveRegistry */);
3669 }
3670
3671 return rc;
3672}
3673
3674/**
3675 * Remembers the given image by storing it in the floppy image registry.
3676 *
3677 * @param aImage Image object to remember.
3678 * @param aSaveRegistry @c true to save the image registry to disk (default).
3679 *
3680 * When @a aSaveRegistry is @c true, this operation may fail because of the
3681 * failed #saveSettings() method it calls. In this case, the image object
3682 * will not be remembered. It is therefore the responsibility of the caller to
3683 * call this method as the last step of some action that requires registration
3684 * in order to make sure that only fully functional image objects get
3685 * registered.
3686 *
3687 * @note Locks this object for writing and @a aImage for reading.
3688 */
3689HRESULT VirtualBox::registerFloppyImage(FloppyImage *aImage,
3690 bool aSaveRegistry /*= true*/)
3691{
3692 AssertReturn (aImage != NULL, E_INVALIDARG);
3693
3694 AutoCaller autoCaller (this);
3695 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3696
3697 AutoWriteLock alock (this);
3698
3699 AutoCaller imageCaller (aImage);
3700 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3701
3702 AutoReadLock imageLock (aImage);
3703
3704 Utf8Str strConflict;
3705 HRESULT rc = checkMediaForConflicts2(aImage->id(),
3706 aImage->locationFull(),
3707 strConflict);
3708 CheckComRCReturnRC (rc);
3709
3710 if (strConflict.length())
3711 {
3712 return setError(VBOX_E_INVALID_OBJECT_STATE,
3713 tr("Cannot register the floppy image '%ls' with UUID {%RTuuid} because a %s already exists in the media registry ('%ls')"),
3714 aImage->locationFull().raw(),
3715 aImage->id().raw(),
3716 strConflict.raw(),
3717 mData.mCfgFile.mName.raw());
3718 }
3719
3720 /* add to the collection */
3721 mData.mFloppyImages.push_back (aImage);
3722
3723 if (aSaveRegistry)
3724 {
3725 rc = saveSettings();
3726 if (FAILED (rc))
3727 unregisterFloppyImage (aImage, false /* aSaveRegistry */);
3728 }
3729
3730 return rc;
3731}
3732
3733/**
3734 * Removes the given image from the floppy image registry registry.
3735 *
3736 * @param aImage Image object to remove.
3737 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3738 *
3739 * When @a aSaveRegistry is @c true, this operation may fail because of the
3740 * failed #saveSettings() method it calls. In this case, the image object
3741 * will NOT be removed from the registry when this method returns. It is
3742 * therefore the responsibility of the caller to call this method as the first
3743 * step of some action that requires unregistration, before calling uninit() on
3744 * @a aImage.
3745 *
3746 * @note Locks this object for writing and @a aImage for reading.
3747 */
3748HRESULT VirtualBox::unregisterFloppyImage(FloppyImage *aImage,
3749 bool aSaveRegistry /*= true*/)
3750{
3751 AssertReturn (aImage != NULL, E_INVALIDARG);
3752
3753 AutoCaller autoCaller (this);
3754 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3755
3756 AutoWriteLock alock (this);
3757
3758 AutoCaller imageCaller (aImage);
3759 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3760
3761 AutoReadLock imageLock (aImage);
3762
3763 mData.mFloppyImages.remove (aImage);
3764
3765 HRESULT rc = S_OK;
3766
3767 if (aSaveRegistry)
3768 {
3769 rc = saveSettings();
3770 if (FAILED (rc))
3771 registerFloppyImage (aImage, false /* aSaveRegistry */);
3772 }
3773
3774 return rc;
3775}
3776
3777/**
3778 * Attempts to cast from a raw interface pointer to an underlying object.
3779 * On success, @a aTo will contain the object reference. On failure, @a aTo will
3780 * be set to @c null and an extended error info will be returned.
3781 *
3782 * @param aFrom Interface pointer to cast from.
3783 * @param aTo Where to store a reference to the underlying object.
3784 *
3785 * @note Locks #childrenLock() for reading.
3786 */
3787HRESULT VirtualBox::cast (IHardDisk *aFrom, ComObjPtr <HardDisk> &aTo)
3788{
3789 AssertReturn (aFrom != NULL, E_INVALIDARG);
3790
3791 AutoCaller autoCaller (this);
3792 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3793
3794 /* We need the children map lock here to keep the getDependentChild() result
3795 * valid until we finish */
3796 AutoReadLock chLock (childrenLock());
3797
3798 VirtualBoxBase *child = getDependentChild (aFrom);
3799 if (!child)
3800 return setError (E_FAIL, tr ("The given hard disk object is not created "
3801 "within this VirtualBox instance"));
3802
3803 /* we can safely cast child to HardDisk * here because only HardDisk
3804 * implementations of IHardDisk can be among our children */
3805
3806 aTo = static_cast<HardDisk*>(child);
3807
3808 return S_OK;
3809}
3810
3811/**
3812 * Helper to update the global settings file when the name of some machine
3813 * changes so that file and directory renaming occurs. This method ensures that
3814 * all affected paths in the disk registry are properly updated.
3815 *
3816 * @param aOldPath Old path (full).
3817 * @param aNewPath New path (full).
3818 *
3819 * @note Locks this object + DVD, Floppy and HardDisk children for writing.
3820 */
3821HRESULT VirtualBox::updateSettings (const char *aOldPath, const char *aNewPath)
3822{
3823 LogFlowThisFunc (("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
3824
3825 AssertReturn (aOldPath, E_INVALIDARG);
3826 AssertReturn (aNewPath, E_INVALIDARG);
3827
3828 AutoCaller autoCaller (this);
3829 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3830
3831 AutoWriteLock alock (this);
3832
3833 /* check DVD paths */
3834 for (DVDImageList::iterator it = mData.mDVDImages.begin();
3835 it != mData.mDVDImages.end();
3836 ++ it)
3837 {
3838 (*it)->updatePath (aOldPath, aNewPath);
3839 }
3840
3841 /* check Floppy paths */
3842 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3843 it != mData.mFloppyImages .end();
3844 ++ it)
3845 {
3846 (*it)->updatePath (aOldPath, aNewPath);
3847 }
3848
3849 /* check HardDisk paths */
3850 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
3851 it != mData.mHardDisks.end();
3852 ++ it)
3853 {
3854 (*it)->updatePaths (aOldPath, aNewPath);
3855 }
3856
3857 HRESULT rc = saveSettings();
3858
3859 return rc;
3860}
3861
3862/**
3863 * Creates the path to the specified file according to the path information
3864 * present in the file name.
3865 *
3866 * Note that the given file name must contain the full path otherwise the
3867 * extracted relative path will be created based on the current working
3868 * directory which is normally unknown.
3869 *
3870 * @param aFileName Full file name which path needs to be created.
3871 *
3872 * @return Extended error information on failure to create the path.
3873 */
3874/* static */
3875HRESULT VirtualBox::ensureFilePathExists (const char *aFileName)
3876{
3877 Utf8Str strDir(aFileName);
3878 strDir.stripFilename();
3879 if (!RTDirExists(strDir))
3880 {
3881 int vrc = RTDirCreateFullPath(strDir, 0777);
3882 if (RT_FAILURE (vrc))
3883 {
3884 return setError (E_FAIL,
3885 tr ("Could not create the directory '%s' (%Rrc)"),
3886 strDir.c_str(), vrc);
3887 }
3888 }
3889
3890 return S_OK;
3891}
3892
3893/**
3894 * Helper method to load the setting tree and turn expected exceptions into
3895 * COM errors, according to arguments.
3896 *
3897 * Note that this method will not catch unexpected errors so it may still
3898 * throw something.
3899 *
3900 * @param aTree Tree to load into settings.
3901 * @param aFile File to load settings from.
3902 * @param aValidate @c @true to enable tree validation.
3903 * @param aCatchLoadErrors @c true to catch exceptions caused by file
3904 * access or validation errors.
3905 * @param aAddDefaults @c true to cause the substitution of default
3906 * values for missing attributes that have
3907 * defaults in the XML schema.
3908 * @param aFormatVersion Where to store the current format version of the
3909 * loaded settings tree (optional, may be NULL).
3910 */
3911/* static */
3912HRESULT VirtualBox::loadSettingsTree (settings::XmlTreeBackend &aTree,
3913 xml::File &aFile,
3914 bool aValidate,
3915 bool aCatchLoadErrors,
3916 bool aAddDefaults,
3917 Utf8Str *aFormatVersion /* = NULL */)
3918{
3919 using namespace settings;
3920
3921 try
3922 {
3923 SettingsTreeHelper helper = SettingsTreeHelper();
3924
3925 aTree.setInputResolver (helper);
3926 aTree.setAutoConverter (helper);
3927
3928 aTree.read (aFile, aValidate ? VBOX_XML_SCHEMA : NULL,
3929 aAddDefaults ? XmlTreeBackend::Read_AddDefaults : 0);
3930
3931 aTree.resetAutoConverter();
3932 aTree.resetInputResolver();
3933
3934 /* on success, memorize the current settings file version or set it to
3935 * the most recent version if no settings conversion took place. Note
3936 * that it's not necessary to do it every time we load the settings file
3937 * (i.e. only loadSettingsTree_FirstTime() passes a non-NULL
3938 * aFormatVersion value) because currently we keep the settings
3939 * files locked so that the only legal way to change the format version
3940 * while VirtualBox is running is saveSettingsTree(). */
3941 if (aFormatVersion != NULL)
3942 {
3943 *aFormatVersion = aTree.oldVersion();
3944 if (!aFormatVersion->length())
3945 *aFormatVersion = VBOX_XML_VERSION_FULL;
3946 }
3947 }
3948 catch (const xml::EIPRTFailure &err)
3949 {
3950 if (!aCatchLoadErrors)
3951 throw;
3952
3953 return setError (VBOX_E_FILE_ERROR,
3954 tr ("Could not load the settings file '%s' (%Rrc)"),
3955 aFile.uri(), err.rc());
3956 }
3957 catch (const xml::RuntimeError &err)
3958 {
3959 Assert (err.what() != NULL);
3960
3961 if (!aCatchLoadErrors)
3962 throw;
3963
3964 return setError (VBOX_E_XML_ERROR,
3965 tr ("Could not load the settings file '%s'.\n%s"),
3966 aFile.uri(),
3967 err.what() ? err.what() : "Unknown error");
3968 }
3969
3970 return S_OK;
3971}
3972
3973/**
3974 * Helper method to save the settings tree and turn expected exceptions to COM
3975 * errors.
3976 *
3977 * Note that this method will not catch unexpected errors so it may still
3978 * throw something.
3979 *
3980 * @param aTree Tree to save.
3981 * @param aFile File to save the tree to.
3982 * @param aFormatVersion Where to store the (recent) format version of the
3983 * saved settings tree on success.
3984 */
3985/* static */
3986HRESULT VirtualBox::saveSettingsTree (settings::TreeBackend &aTree,
3987 xml::File &aFile,
3988 Utf8Str &aFormatVersion)
3989{
3990 using namespace settings;
3991
3992 try
3993 {
3994 aTree.write (aFile);
3995
3996 /* set the current settings file version to the most recent version on
3997 * success. See also VirtualBox::loadSettingsTree(). */
3998 if (aFormatVersion != VBOX_XML_VERSION_FULL)
3999 aFormatVersion = VBOX_XML_VERSION_FULL;
4000 }
4001 catch (const xml::EIPRTFailure &err)
4002 {
4003 /* this is the only expected exception for now */
4004 return setError (VBOX_E_FILE_ERROR,
4005 tr ("Could not save the settings file '%s' (%Rrc)"),
4006 aFile.uri(), err.rc());
4007 }
4008
4009 return S_OK;
4010}
4011
4012/**
4013 * Creates a backup copy of the given settings file by suffixing it with the
4014 * supplied version format string and optionally with numbers from .0 to .9
4015 * if the backup file already exists.
4016 *
4017 * @param aFileName Original settings file name.
4018 * @param aOldFormat Version of the original format.
4019 * @param aBakFileName File name of the created backup copy (only on success).
4020 */
4021/* static */
4022HRESULT VirtualBox::backupSettingsFile (const Bstr &aFileName,
4023 const Utf8Str &aOldFormat,
4024 Bstr &aBakFileName)
4025{
4026 Utf8Str of = aFileName;
4027 Utf8Str nf = Utf8StrFmt ("%s.%s.bak", of.raw(), aOldFormat.raw());
4028
4029 int vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
4030 NULL, NULL);
4031
4032 /* try progressive suffix from .0 to .9 on failure */
4033 if (vrc == VERR_ALREADY_EXISTS)
4034 {
4035 Utf8Str tmp = nf;
4036 for (int i = 0; i <= 9 && RT_FAILURE (vrc); ++ i)
4037 {
4038 nf = Utf8StrFmt ("%s.%d", tmp.raw(), i);
4039 vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
4040 NULL, NULL);
4041 }
4042 }
4043
4044 if (RT_FAILURE (vrc))
4045 return setError (VBOX_E_IPRT_ERROR,
4046 tr ("Could not copy the settings file '%s' to '%s' (%Rrc)"),
4047 of.raw(), nf.raw(), vrc);
4048
4049 aBakFileName = nf;
4050
4051 return S_OK;
4052}
4053
4054/**
4055 * Handles unexpected exceptions by turning them into COM errors in release
4056 * builds or by hitting a breakpoint in the release builds.
4057 *
4058 * Usage pattern:
4059 * @code
4060 try
4061 {
4062 // ...
4063 }
4064 catch (LaLalA)
4065 {
4066 // ...
4067 }
4068 catch (...)
4069 {
4070 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
4071 }
4072 * @endcode
4073 *
4074 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
4075 */
4076/* static */
4077HRESULT VirtualBox::handleUnexpectedExceptions (RT_SRC_POS_DECL)
4078{
4079 try
4080 {
4081 /* re-throw the current exception */
4082 throw;
4083 }
4084 catch (const std::exception &err)
4085 {
4086 ComAssertMsgFailedPos (("Unexpected exception '%s' (%s)",
4087 typeid (err).name(), err.what()),
4088 pszFile, iLine, pszFunction);
4089 return E_FAIL;
4090 }
4091 catch (...)
4092 {
4093 ComAssertMsgFailedPos (("Unknown exception"),
4094 pszFile, iLine, pszFunction);
4095 return E_FAIL;
4096 }
4097
4098 /* should not get here */
4099 AssertFailed();
4100 return E_FAIL;
4101}
4102
4103/**
4104 * Helper to lock the VirtualBox configuration for write access.
4105 *
4106 * @note This method is not thread safe (must be called only from #init()
4107 * or #uninit()).
4108 *
4109 * @note If the configuration file is not found, the method returns
4110 * S_OK, but subsequent #isConfigLocked() will return FALSE. This is used
4111 * in some places to determine the (valid) situation when no config file
4112 * exists yet, and therefore a new one should be created from scratch.
4113 */
4114HRESULT VirtualBox::lockConfig()
4115{
4116 AutoCaller autoCaller (this);
4117 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4118 AssertReturn (autoCaller.state() == InInit, E_FAIL);
4119
4120 HRESULT rc = S_OK;
4121
4122 Assert (!isConfigLocked());
4123 if (!isConfigLocked())
4124 {
4125 /* Open the associated config file. */
4126 int vrc = RTFileOpen (&mData.mCfgFile.mHandle,
4127 Utf8Str (mData.mCfgFile.mName),
4128 RTFILE_O_READWRITE | RTFILE_O_OPEN |
4129 RTFILE_O_DENY_WRITE);
4130 if (RT_FAILURE (vrc) && (vrc != VERR_FILE_NOT_FOUND))
4131 {
4132 /* Open the associated config file only with read access. */
4133 vrc = RTFileOpen (&mData.mCfgFile.mHandle,
4134 Utf8Str (mData.mCfgFile.mName),
4135 RTFILE_O_READ | RTFILE_O_OPEN |
4136 RTFILE_O_DENY_NONE);
4137 if (RT_FAILURE (vrc))
4138 {
4139 /* We even cannot open it in read mode, so there's seriously
4140 something wrong. */
4141 rc = setError (E_FAIL,
4142 tr ("Could not even open settings file '%ls' in read mode (%Rrc)"),
4143 mData.mCfgFile.mName.raw(), vrc);
4144 }
4145 else
4146 {
4147 mData.mCfgFile.mReadonly = TRUE;
4148 }
4149 }
4150 else
4151 {
4152 mData.mCfgFile.mReadonly = FALSE;
4153 }
4154
4155 if (RT_FAILURE(vrc))
4156 {
4157 mData.mCfgFile.mHandle = NIL_RTFILE;
4158 mData.mCfgFile.mReadonly = FALSE;
4159 }
4160
4161 LogFlowThisFunc (("mCfgFile.mName='%ls', mCfgFile.mHandle=%d, rc=%08X\n",
4162 mData.mCfgFile.mName.raw(), mData.mCfgFile.mHandle, rc));
4163 }
4164 return rc;
4165}
4166
4167/**
4168 * Helper to unlock the VirtualBox configuration from write access.
4169 *
4170 * @note This method is not thread safe (must be called only from #init()
4171 * or #uninit()).
4172 */
4173HRESULT VirtualBox::unlockConfig()
4174{
4175 AutoCaller autoCaller (this);
4176 AssertComRCReturn (autoCaller.rc(), E_FAIL);
4177 AssertReturn (autoCaller.state() == InUninit, E_FAIL);
4178
4179 HRESULT rc = S_OK;
4180
4181 if (isConfigLocked())
4182 {
4183 RTFileFlush (mData.mCfgFile.mHandle);
4184 RTFileClose (mData.mCfgFile.mHandle);
4185 /** @todo flush the directory too. */
4186 mData.mCfgFile.mHandle = NIL_RTFILE;
4187 mData.mCfgFile.mReadonly = FALSE;
4188 LogFlowThisFunc (("\n"));
4189 }
4190
4191 return rc;
4192}
4193
4194/**
4195 * Thread function that watches the termination of all client processes
4196 * that have opened sessions using IVirtualBox::OpenSession()
4197 */
4198// static
4199DECLCALLBACK(int) VirtualBox::ClientWatcher (RTTHREAD /* thread */, void *pvUser)
4200{
4201 LogFlowFuncEnter();
4202
4203 VirtualBox *that = (VirtualBox *) pvUser;
4204 Assert (that);
4205
4206 SessionMachineVector machines;
4207 MachineVector spawnedMachines;
4208
4209 size_t cnt = 0;
4210 size_t cntSpawned = 0;
4211
4212#if defined(RT_OS_WINDOWS)
4213
4214 HRESULT hrc = CoInitializeEx (NULL,
4215 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
4216 COINIT_SPEED_OVER_MEMORY);
4217 AssertComRC (hrc);
4218
4219 /// @todo (dmik) processes reaping!
4220
4221 HANDLE handles [MAXIMUM_WAIT_OBJECTS];
4222 handles [0] = that->mWatcherData.mUpdateReq;
4223
4224 do
4225 {
4226 AutoCaller autoCaller (that);
4227 /* VirtualBox has been early uninitialized, terminate */
4228 if (!autoCaller.isOk())
4229 break;
4230
4231 do
4232 {
4233 /* release the caller to let uninit() ever proceed */
4234 autoCaller.release();
4235
4236 DWORD rc = ::WaitForMultipleObjects ((DWORD)(1 + cnt + cntSpawned),
4237 handles, FALSE, INFINITE);
4238
4239 /* Restore the caller before using VirtualBox. If it fails, this
4240 * means VirtualBox is being uninitialized and we must terminate. */
4241 autoCaller.add();
4242 if (!autoCaller.isOk())
4243 break;
4244
4245 bool update = false;
4246
4247 if (rc == WAIT_OBJECT_0)
4248 {
4249 /* update event is signaled */
4250 update = true;
4251 }
4252 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4253 {
4254 /* machine mutex is released */
4255 (machines [rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4256 update = true;
4257 }
4258 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4259 {
4260 /* machine mutex is abandoned due to client process termination */
4261 (machines [rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4262 update = true;
4263 }
4264 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
4265 {
4266 /* spawned VM process has terminated (normally or abnormally) */
4267 (spawnedMachines [rc - WAIT_OBJECT_0 - cnt - 1])->
4268 checkForSpawnFailure();
4269 update = true;
4270 }
4271
4272 if (update)
4273 {
4274 /* close old process handles */
4275 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4276 CloseHandle (handles [i]);
4277
4278 AutoReadLock thatLock (that);
4279
4280 /* obtain a new set of opened machines */
4281 cnt = 0;
4282 machines.clear();
4283
4284 for (MachineList::iterator it = that->mData.mMachines.begin();
4285 it != that->mData.mMachines.end(); ++ it)
4286 {
4287 /// @todo handle situations with more than 64 objects
4288 AssertMsgBreak ((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
4289 ("MAXIMUM_WAIT_OBJECTS reached"));
4290
4291 ComObjPtr <SessionMachine> sm;
4292 HANDLE ipcSem;
4293 if ((*it)->isSessionOpenOrClosing (sm, NULL, &ipcSem))
4294 {
4295 machines.push_back (sm);
4296 handles [1 + cnt] = ipcSem;
4297 ++ cnt;
4298 }
4299 }
4300
4301 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4302
4303 /* obtain a new set of spawned machines */
4304 cntSpawned = 0;
4305 spawnedMachines.clear();
4306
4307 for (MachineList::iterator it = that->mData.mMachines.begin();
4308 it != that->mData.mMachines.end(); ++ it)
4309 {
4310 /// @todo handle situations with more than 64 objects
4311 AssertMsgBreak ((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
4312 ("MAXIMUM_WAIT_OBJECTS reached"));
4313
4314 RTPROCESS pid;
4315 if ((*it)->isSessionSpawning (&pid))
4316 {
4317 HANDLE ph = OpenProcess (SYNCHRONIZE, FALSE, pid);
4318 AssertMsg (ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
4319 pid, GetLastError()));
4320 if (rc == 0)
4321 {
4322 spawnedMachines.push_back (*it);
4323 handles [1 + cnt + cntSpawned] = ph;
4324 ++ cntSpawned;
4325 }
4326 }
4327 }
4328
4329 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4330 }
4331 }
4332 while (true);
4333 }
4334 while (0);
4335
4336 /* close old process handles */
4337 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4338 CloseHandle (handles [i]);
4339
4340 /* release sets of machines if any */
4341 machines.clear();
4342 spawnedMachines.clear();
4343
4344 ::CoUninitialize();
4345
4346#elif defined (RT_OS_OS2)
4347
4348 /// @todo (dmik) processes reaping!
4349
4350 /* according to PMREF, 64 is the maximum for the muxwait list */
4351 SEMRECORD handles [64];
4352
4353 HMUX muxSem = NULLHANDLE;
4354
4355 do
4356 {
4357 AutoCaller autoCaller (that);
4358 /* VirtualBox has been early uninitialized, terminate */
4359 if (!autoCaller.isOk())
4360 break;
4361
4362 do
4363 {
4364 /* release the caller to let uninit() ever proceed */
4365 autoCaller.release();
4366
4367 int vrc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4368
4369 /* Restore the caller before using VirtualBox. If it fails, this
4370 * means VirtualBox is being uninitialized and we must terminate. */
4371 autoCaller.add();
4372 if (!autoCaller.isOk())
4373 break;
4374
4375 bool update = false;
4376 bool updateSpawned = false;
4377
4378 if (RT_SUCCESS (vrc))
4379 {
4380 /* update event is signaled */
4381 update = true;
4382 updateSpawned = true;
4383 }
4384 else
4385 {
4386 AssertMsg (vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4387 ("RTSemEventWait returned %Rrc\n", vrc));
4388
4389 /* are there any mutexes? */
4390 if (cnt > 0)
4391 {
4392 /* figure out what's going on with machines */
4393
4394 unsigned long semId = 0;
4395 APIRET arc = ::DosWaitMuxWaitSem (muxSem,
4396 SEM_IMMEDIATE_RETURN, &semId);
4397
4398 if (arc == NO_ERROR)
4399 {
4400 /* machine mutex is normally released */
4401 Assert (semId >= 0 && semId < cnt);
4402 if (semId >= 0 && semId < cnt)
4403 {
4404#ifdef DEBUG
4405 {
4406 AutoReadLock machineLock (machines [semId]);
4407 LogFlowFunc (("released mutex: machine='%ls'\n",
4408 machines [semId]->name().raw()));
4409 }
4410#endif
4411 machines [semId]->checkForDeath();
4412 }
4413 update = true;
4414 }
4415 else if (arc == ERROR_SEM_OWNER_DIED)
4416 {
4417 /* machine mutex is abandoned due to client process
4418 * termination; find which mutex is in the Owner Died
4419 * state */
4420 for (size_t i = 0; i < cnt; ++ i)
4421 {
4422 PID pid; TID tid;
4423 unsigned long reqCnt;
4424 arc = DosQueryMutexSem ((HMTX) handles [i].hsemCur, &pid,
4425 &tid, &reqCnt);
4426 if (arc == ERROR_SEM_OWNER_DIED)
4427 {
4428 /* close the dead mutex as asked by PMREF */
4429 ::DosCloseMutexSem ((HMTX) handles [i].hsemCur);
4430
4431 Assert (i >= 0 && i < cnt);
4432 if (i >= 0 && i < cnt)
4433 {
4434#ifdef DEBUG
4435 {
4436 AutoReadLock machineLock (machines [semId]);
4437 LogFlowFunc (("mutex owner dead: machine='%ls'\n",
4438 machines [i]->name().raw()));
4439 }
4440#endif
4441 machines [i]->checkForDeath();
4442 }
4443 }
4444 }
4445 update = true;
4446 }
4447 else
4448 AssertMsg (arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4449 ("DosWaitMuxWaitSem returned %d\n", arc));
4450 }
4451
4452 /* are there any spawning sessions? */
4453 if (cntSpawned > 0)
4454 {
4455 for (size_t i = 0; i < cntSpawned; ++ i)
4456 updateSpawned |= (spawnedMachines [i])->
4457 checkForSpawnFailure();
4458 }
4459 }
4460
4461 if (update || updateSpawned)
4462 {
4463 AutoReadLock thatLock (that);
4464
4465 if (update)
4466 {
4467 /* close the old muxsem */
4468 if (muxSem != NULLHANDLE)
4469 ::DosCloseMuxWaitSem (muxSem);
4470
4471 /* obtain a new set of opened machines */
4472 cnt = 0;
4473 machines.clear();
4474
4475 for (MachineList::iterator it = that->mData.mMachines.begin();
4476 it != that->mData.mMachines.end(); ++ it)
4477 {
4478 /// @todo handle situations with more than 64 objects
4479 AssertMsg (cnt <= 64 /* according to PMREF */,
4480 ("maximum of 64 mutex semaphores reached (%d)",
4481 cnt));
4482
4483 ComObjPtr <SessionMachine> sm;
4484 HMTX ipcSem;
4485 if ((*it)->isSessionOpenOrClosing (sm, NULL, &ipcSem))
4486 {
4487 machines.push_back (sm);
4488 handles [cnt].hsemCur = (HSEM) ipcSem;
4489 handles [cnt].ulUser = cnt;
4490 ++ cnt;
4491 }
4492 }
4493
4494 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4495
4496 if (cnt > 0)
4497 {
4498 /* create a new muxsem */
4499 APIRET arc = ::DosCreateMuxWaitSem (NULL, &muxSem, cnt,
4500 handles,
4501 DCMW_WAIT_ANY);
4502 AssertMsg (arc == NO_ERROR,
4503 ("DosCreateMuxWaitSem returned %d\n", arc));
4504 NOREF(arc);
4505 }
4506 }
4507
4508 if (updateSpawned)
4509 {
4510 /* obtain a new set of spawned machines */
4511 spawnedMachines.clear();
4512
4513 for (MachineList::iterator it = that->mData.mMachines.begin();
4514 it != that->mData.mMachines.end(); ++ it)
4515 {
4516 if ((*it)->isSessionSpawning())
4517 spawnedMachines.push_back (*it);
4518 }
4519
4520 cntSpawned = spawnedMachines.size();
4521 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4522 }
4523 }
4524 }
4525 while (true);
4526 }
4527 while (0);
4528
4529 /* close the muxsem */
4530 if (muxSem != NULLHANDLE)
4531 ::DosCloseMuxWaitSem (muxSem);
4532
4533 /* release sets of machines if any */
4534 machines.clear();
4535 spawnedMachines.clear();
4536
4537#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4538
4539 bool update = false;
4540 bool updateSpawned = false;
4541
4542 do
4543 {
4544 AutoCaller autoCaller (that);
4545 if (!autoCaller.isOk())
4546 break;
4547
4548 do
4549 {
4550 /* release the caller to let uninit() ever proceed */
4551 autoCaller.release();
4552
4553 int rc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4554
4555 /*
4556 * Restore the caller before using VirtualBox. If it fails, this
4557 * means VirtualBox is being uninitialized and we must terminate.
4558 */
4559 autoCaller.add();
4560 if (!autoCaller.isOk())
4561 break;
4562
4563 if (RT_SUCCESS (rc) || update || updateSpawned)
4564 {
4565 /* RT_SUCCESS (rc) means an update event is signaled */
4566
4567 AutoReadLock thatLock (that);
4568
4569 if (RT_SUCCESS (rc) || update)
4570 {
4571 /* obtain a new set of opened machines */
4572 machines.clear();
4573
4574 for (MachineList::iterator it = that->mData.mMachines.begin();
4575 it != that->mData.mMachines.end(); ++ it)
4576 {
4577 ComObjPtr <SessionMachine> sm;
4578 if ((*it)->isSessionOpenOrClosing (sm))
4579 machines.push_back (sm);
4580 }
4581
4582 cnt = machines.size();
4583 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4584 }
4585
4586 if (RT_SUCCESS (rc) || updateSpawned)
4587 {
4588 /* obtain a new set of spawned machines */
4589 spawnedMachines.clear();
4590
4591 for (MachineList::iterator it = that->mData.mMachines.begin();
4592 it != that->mData.mMachines.end(); ++ it)
4593 {
4594 if ((*it)->isSessionSpawning())
4595 spawnedMachines.push_back (*it);
4596 }
4597
4598 cntSpawned = spawnedMachines.size();
4599 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4600 }
4601 }
4602
4603 update = false;
4604 for (size_t i = 0; i < cnt; ++ i)
4605 update |= (machines [i])->checkForDeath();
4606
4607 updateSpawned = false;
4608 for (size_t i = 0; i < cntSpawned; ++ i)
4609 updateSpawned |= (spawnedMachines [i])->checkForSpawnFailure();
4610
4611 /* reap child processes */
4612 {
4613 AutoWriteLock alock (that);
4614 if (that->mWatcherData.mProcesses.size())
4615 {
4616 LogFlowFunc (("UPDATE: child process count = %d\n",
4617 that->mWatcherData.mProcesses.size()));
4618 ClientWatcherData::ProcessList::iterator it =
4619 that->mWatcherData.mProcesses.begin();
4620 while (it != that->mWatcherData.mProcesses.end())
4621 {
4622 RTPROCESS pid = *it;
4623 RTPROCSTATUS status;
4624 int vrc = ::RTProcWait (pid, RTPROCWAIT_FLAGS_NOBLOCK,
4625 &status);
4626 if (vrc == VINF_SUCCESS)
4627 {
4628 LogFlowFunc (("pid %d (%x) was reaped, "
4629 "status=%d, reason=%d\n",
4630 pid, pid, status.iStatus,
4631 status.enmReason));
4632 it = that->mWatcherData.mProcesses.erase (it);
4633 }
4634 else
4635 {
4636 LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4637 pid, pid, vrc));
4638 if (vrc != VERR_PROCESS_RUNNING)
4639 {
4640 /* remove the process if it is not already running */
4641 it = that->mWatcherData.mProcesses.erase (it);
4642 }
4643 else
4644 ++ it;
4645 }
4646 }
4647 }
4648 }
4649 }
4650 while (true);
4651 }
4652 while (0);
4653
4654 /* release sets of machines if any */
4655 machines.clear();
4656 spawnedMachines.clear();
4657
4658#else
4659# error "Port me!"
4660#endif
4661
4662 LogFlowFuncLeave();
4663 return 0;
4664}
4665
4666/**
4667 * Thread function that handles custom events posted using #postEvent().
4668 */
4669// static
4670DECLCALLBACK(int) VirtualBox::AsyncEventHandler (RTTHREAD thread, void *pvUser)
4671{
4672 LogFlowFuncEnter();
4673
4674 AssertReturn (pvUser, VERR_INVALID_POINTER);
4675
4676 // create an event queue for the current thread
4677 EventQueue *eventQ = new EventQueue();
4678 AssertReturn (eventQ, VERR_NO_MEMORY);
4679
4680 // return the queue to the one who created this thread
4681 *(static_cast <EventQueue **> (pvUser)) = eventQ;
4682 // signal that we're ready
4683 RTThreadUserSignal (thread);
4684
4685 BOOL ok = TRUE;
4686 Event *event = NULL;
4687
4688 while ((ok = eventQ->waitForEvent (&event)) && event)
4689 eventQ->handleEvent (event);
4690
4691 AssertReturn (ok, VERR_GENERAL_FAILURE);
4692
4693 delete eventQ;
4694
4695 LogFlowFuncLeave();
4696
4697 return 0;
4698}
4699
4700////////////////////////////////////////////////////////////////////////////////
4701
4702/**
4703 * Takes the current list of registered callbacks of the managed VirtualBox
4704 * instance, and calls #handleCallback() for every callback item from the
4705 * list, passing the item as an argument.
4706 *
4707 * @note Locks the managed VirtualBox object for reading but leaves the lock
4708 * before iterating over callbacks and calling their methods.
4709 */
4710void *VirtualBox::CallbackEvent::handler()
4711{
4712 if (mVirtualBox.isNull())
4713 return NULL;
4714
4715 AutoCaller autoCaller (mVirtualBox);
4716 if (!autoCaller.isOk())
4717 {
4718 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
4719 "the callback event is discarded!\n",
4720 autoCaller.state()));
4721 /* We don't need mVirtualBox any more, so release it */
4722 mVirtualBox.setNull();
4723 return NULL;
4724 }
4725
4726 CallbackVector callbacks;
4727 {
4728 /* Make a copy to release the lock before iterating */
4729 AutoReadLock alock (mVirtualBox);
4730 callbacks = CallbackVector (mVirtualBox->mData.mCallbacks.begin(),
4731 mVirtualBox->mData.mCallbacks.end());
4732 /* We don't need mVirtualBox any more, so release it */
4733 mVirtualBox.setNull();
4734 }
4735
4736 for (VirtualBox::CallbackVector::const_iterator it = callbacks.begin();
4737 it != callbacks.end(); ++ it)
4738 handleCallback (*it);
4739
4740 return NULL;
4741}
4742
4743//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface (/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4744//{
4745// return E_NOTIMPL;
4746//}
4747
4748STDMETHODIMP VirtualBox::CreateDHCPServer (IN_BSTR aName, IDHCPServer ** aServer)
4749{
4750 CheckComArgNotNull(aName);
4751 CheckComArgNotNull(aServer);
4752
4753 AutoCaller autoCaller (this);
4754 CheckComRCReturnRC (autoCaller.rc());
4755
4756 ComObjPtr<DHCPServer> dhcpServer;
4757 dhcpServer.createObject();
4758 HRESULT rc = dhcpServer->init (this, aName);
4759 CheckComRCReturnRC (rc);
4760
4761 rc = registerDHCPServer(dhcpServer, true);
4762 CheckComRCReturnRC (rc);
4763
4764 dhcpServer.queryInterfaceTo(aServer);
4765
4766 return rc;
4767}
4768
4769//STDMETHODIMP VirtualBox::FindDHCPServerForInterface (IHostNetworkInterface * aIinterface, IDHCPServer ** aServer)
4770//{
4771// return E_NOTIMPL;
4772//}
4773
4774STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName (IN_BSTR aName, IDHCPServer ** aServer)
4775{
4776 CheckComArgNotNull(aName);
4777 CheckComArgNotNull(aServer);
4778
4779 AutoCaller autoCaller (this);
4780 CheckComRCReturnRC (autoCaller.rc());
4781
4782 AutoWriteLock alock (this);
4783
4784 HRESULT rc;
4785 Bstr bstr;
4786 ComPtr <DHCPServer> found;
4787
4788 for (DHCPServerList::const_iterator it =
4789 mData.mDHCPServers.begin();
4790 it != mData.mDHCPServers.end();
4791 ++ it)
4792 {
4793 rc = (*it)->COMGETTER(NetworkName) (bstr.asOutParam());
4794 CheckComRCThrowRC (rc);
4795
4796 if(bstr == aName)
4797 {
4798 found = *it;
4799 break;
4800 }
4801 }
4802
4803 if (!found)
4804 return E_INVALIDARG;
4805
4806 return found.queryInterfaceTo (aServer);
4807}
4808
4809STDMETHODIMP VirtualBox::RemoveDHCPServer (IDHCPServer * aServer)
4810{
4811 CheckComArgNotNull(aServer);
4812
4813 AutoCaller autoCaller (this);
4814 CheckComRCReturnRC (autoCaller.rc());
4815
4816 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4817
4818 return rc;
4819}
4820
4821/**
4822 * Remembers the given dhcp server by storing it in the hard disk registry.
4823 *
4824 * @param aDHCPServer Dhcp Server object to remember.
4825 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4826 *
4827 * When @a aSaveRegistry is @c true, this operation may fail because of the
4828 * failed #saveSettings() method it calls. In this case, the dhcp server object
4829 * will not be remembered. It is therefore the responsibility of the caller to
4830 * call this method as the last step of some action that requires registration
4831 * in order to make sure that only fully functional dhcp server objects get
4832 * registered.
4833 *
4834 * @note Locks this object for writing and @a aDHCPServer for reading.
4835 */
4836HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4837 bool aSaveRegistry /*= true*/)
4838{
4839 AssertReturn (aDHCPServer != NULL, E_INVALIDARG);
4840
4841 AutoCaller autoCaller (this);
4842 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4843
4844 AutoWriteLock alock (this);
4845
4846 AutoCaller dhcpServerCaller (aDHCPServer);
4847 AssertComRCReturn (dhcpServerCaller.rc(), dhcpServerCaller.rc());
4848
4849 AutoReadLock dhcpServerLock (aDHCPServer);
4850
4851 Bstr name;
4852 HRESULT rc;
4853 rc = aDHCPServer->COMGETTER(NetworkName) (name.asOutParam());
4854 CheckComRCReturnRC (rc);
4855
4856 ComPtr<IDHCPServer> existing;
4857 rc = FindDHCPServerByNetworkName(name.mutableRaw(), existing.asOutParam());
4858 if(SUCCEEDED(rc))
4859 {
4860 return E_INVALIDARG;
4861 }
4862 rc = S_OK;
4863
4864 mData.mDHCPServers.push_back (aDHCPServer);
4865
4866 if (aSaveRegistry)
4867 {
4868 rc = saveSettings();
4869 if (FAILED (rc))
4870 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4871 }
4872
4873 return rc;
4874}
4875
4876/**
4877 * Removes the given hard disk from the hard disk registry.
4878 *
4879 * @param aHardDisk Hard disk object to remove.
4880 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4881 *
4882 * When @a aSaveRegistry is @c true, this operation may fail because of the
4883 * failed #saveSettings() method it calls. In this case, the hard disk object
4884 * will NOT be removed from the registry when this method returns. It is
4885 * therefore the responsibility of the caller to call this method as the first
4886 * step of some action that requires unregistration, before calling uninit() on
4887 * @a aHardDisk.
4888 *
4889 * @note Locks this object for writing and @a aHardDisk for reading.
4890 */
4891HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4892 bool aSaveRegistry /*= true*/)
4893{
4894 AssertReturn (aDHCPServer != NULL, E_INVALIDARG);
4895
4896 AutoCaller autoCaller (this);
4897 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4898
4899 AutoWriteLock alock (this);
4900
4901 AutoCaller dhcpServerCaller (aDHCPServer);
4902 AssertComRCReturn (dhcpServerCaller.rc(), dhcpServerCaller.rc());
4903
4904 AutoReadLock dhcpServerLock (aDHCPServer);
4905
4906 mData.mDHCPServers.remove (aDHCPServer);
4907
4908 HRESULT rc = S_OK;
4909
4910 if (aSaveRegistry)
4911 {
4912 rc = saveSettings();
4913 if (FAILED (rc))
4914 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4915 }
4916
4917 return rc;
4918}
4919
4920/* 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