VirtualBox

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

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

API and Frontends: change IVirtualBox::openHardDisk to allow modifying the image UUID and parent UUID on open

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