VirtualBox

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

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

Main: fix exception handling around VirtualBox.xml parser

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