VirtualBox

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

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

Main: implmented waitForEvents(aTimeout) API for XPCOM targets, added command to VBox shell using this API

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