VirtualBox

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

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

Main: fix crash if VirtualBox.xml is not present.

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