VirtualBox

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

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

Main: fix both SetExtraData() implementations to call onExtraDataCanChange() outside of any locks

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 129.3 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 22228 2009-08-13 10:26:00Z 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 Utf8Str strKey(aKey);
1514 Utf8Str strValue(aValue);
1515 Utf8Str strOldValue; // empty
1516
1517 // locking note: we only hold the read lock briefly to look up the old value,
1518 // then release it and call the onExtraCanChange callbacks. There is a small
1519 // chance of a race insofar as the callback might be called twice if two callers
1520 // change the same key at the same time, but that's a much better solution
1521 // than the deadlock we had here before. The actual changing of the extradata
1522 // is then performed under the write lock and race-free.
1523
1524 // look up the old value first; if nothing's changed then we need not do anything
1525 {
1526 AutoReadLock alock(this); // hold read lock only while looking up
1527 settings::ExtraDataItemsMap::const_iterator it = m_pMainConfigFile->mapExtraDataItems.find(strKey);
1528 if (it != m_pMainConfigFile->mapExtraDataItems.end())
1529 strOldValue = it->second;
1530 }
1531
1532 bool fChanged;
1533 if (fChanged = (strOldValue != strValue))
1534 {
1535 // ask for permission from all listeners outside the locks;
1536 // onExtraDataCanChange() only briefly requests the VirtualBox
1537 // lock to copy the list of callbacks to invoke
1538 Bstr error;
1539 Bstr bstrValue;
1540 if (aValue)
1541 bstrValue = aValue;
1542 else
1543 bstrValue = (const char *)"";
1544
1545 if (!onExtraDataCanChange(Guid::Empty, aKey, bstrValue, error))
1546 {
1547 const char *sep = error.isEmpty() ? "" : ": ";
1548 CBSTR err = error.isNull() ? (CBSTR) L"" : error.raw();
1549 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
1550 sep, err));
1551 return setError(E_ACCESSDENIED,
1552 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
1553 aKey,
1554 bstrValue.raw(),
1555 sep,
1556 err);
1557 }
1558
1559 // data is changing and change not vetoed: then write it out under the lock
1560
1561 AutoWriteLock alock(this);
1562
1563 m_pMainConfigFile->mapExtraDataItems[strKey] = strValue;
1564 // creates a new key if needed
1565
1566 /* save settings on success */
1567 HRESULT rc = saveSettings();
1568 CheckComRCReturnRC (rc);
1569 }
1570
1571 // fire notification outside the lock
1572 if (fChanged)
1573 onExtraDataChange(Guid::Empty, aKey, aValue);
1574
1575 return S_OK;
1576}
1577
1578/**
1579 * @note Locks objects!
1580 */
1581STDMETHODIMP VirtualBox::OpenSession (ISession *aSession, IN_BSTR aMachineId)
1582{
1583 CheckComArgNotNull(aSession);
1584
1585 AutoCaller autoCaller(this);
1586 CheckComRCReturnRC(autoCaller.rc());
1587
1588 Guid id(aMachineId);
1589 ComObjPtr<Machine> machine;
1590
1591 HRESULT rc = findMachine (id, true /* setError */, &machine);
1592 CheckComRCReturnRC(rc);
1593
1594 /* check the session state */
1595 SessionState_T state;
1596 rc = aSession->COMGETTER(State) (&state);
1597 CheckComRCReturnRC(rc);
1598
1599 if (state != SessionState_Closed)
1600 return setError (VBOX_E_INVALID_OBJECT_STATE,
1601 tr ("The given session is already open or being opened"));
1602
1603 /* get the IInternalSessionControl interface */
1604 ComPtr<IInternalSessionControl> control = aSession;
1605 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1606 E_INVALIDARG);
1607
1608 rc = machine->openSession (control);
1609
1610 if (SUCCEEDED(rc))
1611 {
1612 /*
1613 * tell the client watcher thread to update the set of
1614 * machines that have open sessions
1615 */
1616 updateClientWatcher();
1617
1618 /* fire an event */
1619 onSessionStateChange (id, SessionState_Open);
1620 }
1621
1622 return rc;
1623}
1624
1625/**
1626 * @note Locks objects!
1627 */
1628STDMETHODIMP VirtualBox::OpenRemoteSession (ISession *aSession,
1629 IN_BSTR aMachineId,
1630 IN_BSTR aType,
1631 IN_BSTR aEnvironment,
1632 IProgress **aProgress)
1633{
1634 LogRel(("remotesession=%s\n", Utf8Str(aMachineId).c_str()));
1635
1636 CheckComArgNotNull(aMachineId);
1637 CheckComArgNotNull(aSession);
1638 CheckComArgNotNull(aType);
1639 CheckComArgOutSafeArrayPointerValid(aProgress);
1640
1641 AutoCaller autoCaller(this);
1642 CheckComRCReturnRC(autoCaller.rc());
1643
1644 Guid id(aMachineId);
1645 ComObjPtr<Machine> machine;
1646
1647 HRESULT rc = findMachine (id, true /* setError */, &machine);
1648 CheckComRCReturnRC(rc);
1649
1650 /* check the session state */
1651 SessionState_T state;
1652 rc = aSession->COMGETTER(State) (&state);
1653 CheckComRCReturnRC(rc);
1654
1655 if (state != SessionState_Closed)
1656 return setError (VBOX_E_INVALID_OBJECT_STATE,
1657 tr ("The given session is already open or being opened"));
1658
1659 /* get the IInternalSessionControl interface */
1660 ComPtr<IInternalSessionControl> control = aSession;
1661 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1662 E_INVALIDARG);
1663
1664 /* create a progress object */
1665 ComObjPtr<Progress> progress;
1666 progress.createObject();
1667 progress->init (this, static_cast <IMachine *> (machine),
1668 Bstr (tr ("Spawning session")),
1669 FALSE /* aCancelable */);
1670
1671 rc = machine->openRemoteSession (control, aType, aEnvironment, progress);
1672
1673 if (SUCCEEDED(rc))
1674 {
1675 progress.queryInterfaceTo(aProgress);
1676
1677 /* signal the client watcher thread */
1678 updateClientWatcher();
1679
1680 /* fire an event */
1681 onSessionStateChange (id, SessionState_Spawning);
1682 }
1683
1684 return rc;
1685}
1686
1687/**
1688 * @note Locks objects!
1689 */
1690STDMETHODIMP VirtualBox::OpenExistingSession (ISession *aSession,
1691 IN_BSTR aMachineId)
1692{
1693 CheckComArgNotNull(aSession);
1694
1695 AutoCaller autoCaller(this);
1696 CheckComRCReturnRC(autoCaller.rc());
1697
1698 Guid id(aMachineId);
1699 ComObjPtr<Machine> machine;
1700
1701 HRESULT rc = findMachine (id, true /* setError */, &machine);
1702 CheckComRCReturnRC(rc);
1703
1704 /* check the session state */
1705 SessionState_T state;
1706 rc = aSession->COMGETTER(State) (&state);
1707 CheckComRCReturnRC(rc);
1708
1709 if (state != SessionState_Closed)
1710 return setError (VBOX_E_INVALID_OBJECT_STATE,
1711 tr ("The given session is already open or being opened"));
1712
1713 /* get the IInternalSessionControl interface */
1714 ComPtr<IInternalSessionControl> control = aSession;
1715 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1716 E_INVALIDARG);
1717
1718 rc = machine->openExistingSession (control);
1719
1720 return rc;
1721}
1722
1723/**
1724 * @note Locks this object for writing.
1725 */
1726STDMETHODIMP VirtualBox::RegisterCallback (IVirtualBoxCallback *aCallback)
1727{
1728 LogFlowThisFunc(("aCallback=%p\n", aCallback));
1729
1730 CheckComArgNotNull(aCallback);
1731
1732 AutoCaller autoCaller(this);
1733 CheckComRCReturnRC(autoCaller.rc());
1734
1735#if 0 /** @todo r=bird,r=pritesh: must check that the interface id match correct or we might screw up with old code! */
1736 void *dummy;
1737 HRESULT hrc = aCallback->QueryInterface(NS_GET_IID(IVirtualBoxCallback), &dummy);
1738 if (FAILED(hrc))
1739 return hrc;
1740 aCallback->Release();
1741#endif
1742
1743 AutoWriteLock alock(this);
1744 mData.mCallbacks.push_back (CallbackList::value_type (aCallback));
1745
1746 return S_OK;
1747}
1748
1749/**
1750 * @note Locks this object for writing.
1751 */
1752STDMETHODIMP VirtualBox::UnregisterCallback (IVirtualBoxCallback *aCallback)
1753{
1754 CheckComArgNotNull(aCallback);
1755
1756 AutoCaller autoCaller(this);
1757 CheckComRCReturnRC(autoCaller.rc());
1758
1759 HRESULT rc = S_OK;
1760
1761 AutoWriteLock alock(this);
1762
1763 CallbackList::iterator it;
1764 it = std::find (mData.mCallbacks.begin(),
1765 mData.mCallbacks.end(),
1766 CallbackList::value_type (aCallback));
1767 if (it == mData.mCallbacks.end())
1768 rc = E_INVALIDARG;
1769 else
1770 mData.mCallbacks.erase (it);
1771
1772 LogFlowThisFunc(("aCallback=%p, rc=%08X\n", aCallback, rc));
1773 return rc;
1774}
1775
1776
1777STDMETHODIMP VirtualBox::WaitForPropertyChange (IN_BSTR /* aWhat */, ULONG /* aTimeout */,
1778 BSTR * /* aChanged */, BSTR * /* aValues */)
1779{
1780 ReturnComNotImplemented();
1781}
1782
1783// public methods only for internal purposes
1784/////////////////////////////////////////////////////////////////////////////
1785
1786/**
1787 * Posts an event to the event queue that is processed asynchronously
1788 * on a dedicated thread.
1789 *
1790 * Posting events to the dedicated event queue is useful to perform secondary
1791 * actions outside any object locks -- for example, to iterate over a list
1792 * of callbacks and inform them about some change caused by some object's
1793 * method call.
1794 *
1795 * @param event event to post
1796 * (must be allocated using |new|, will be deleted automatically
1797 * by the event thread after processing)
1798 *
1799 * @note Doesn't lock any object.
1800 */
1801HRESULT VirtualBox::postEvent (Event *event)
1802{
1803 AutoCaller autoCaller(this);
1804 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
1805
1806 if (autoCaller.state() != Ready)
1807 {
1808 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
1809 "the event is discarded!\n",
1810 autoCaller.state()));
1811 return S_OK;
1812 }
1813
1814 AssertReturn(event, E_FAIL);
1815 AssertReturn(mAsyncEventQ, E_FAIL);
1816
1817 if (mAsyncEventQ->postEvent (event))
1818 return S_OK;
1819
1820 return E_FAIL;
1821}
1822
1823/**
1824 * Adds a progress to the global collection of pending operations.
1825 * Usually gets called upon progress object initialization.
1826 *
1827 * @param aProgress Operation to add to the collection.
1828 *
1829 * @note Doesn't lock objects.
1830 */
1831HRESULT VirtualBox::addProgress (IProgress *aProgress)
1832{
1833 CheckComArgNotNull(aProgress);
1834
1835 AutoCaller autoCaller(this);
1836 CheckComRCReturnRC(autoCaller.rc());
1837
1838 Bstr id;
1839 HRESULT rc = aProgress->COMGETTER(Id) (id.asOutParam());
1840 AssertComRCReturnRC(rc);
1841
1842 /* protect mProgressOperations */
1843 AutoWriteLock safeLock (mSafeLock);
1844
1845 mData.mProgressOperations.insert (ProgressMap::value_type (Guid(id), aProgress));
1846 return S_OK;
1847}
1848
1849/**
1850 * Removes the progress from the global collection of pending operations.
1851 * Usually gets called upon progress completion.
1852 *
1853 * @param aId UUID of the progress operation to remove
1854 *
1855 * @note Doesn't lock objects.
1856 */
1857HRESULT VirtualBox::removeProgress (IN_GUID aId)
1858{
1859 AutoCaller autoCaller(this);
1860 CheckComRCReturnRC(autoCaller.rc());
1861
1862 ComPtr<IProgress> progress;
1863
1864 /* protect mProgressOperations */
1865 AutoWriteLock safeLock (mSafeLock);
1866
1867 size_t cnt = mData.mProgressOperations.erase (aId);
1868 Assert (cnt == 1);
1869 NOREF(cnt);
1870
1871 return S_OK;
1872}
1873
1874#ifdef RT_OS_WINDOWS
1875
1876struct StartSVCHelperClientData
1877{
1878 ComObjPtr<VirtualBox> that;
1879 ComObjPtr<Progress> progress;
1880 bool privileged;
1881 VirtualBox::SVCHelperClientFunc func;
1882 void *user;
1883};
1884
1885/**
1886 * Helper method that starts a worker thread that:
1887 * - creates a pipe communication channel using SVCHlpClient;
1888 * - starts an SVC Helper process that will inherit this channel;
1889 * - executes the supplied function by passing it the created SVCHlpClient
1890 * and opened instance to communicate to the Helper process and the given
1891 * Progress object.
1892 *
1893 * The user function is supposed to communicate to the helper process
1894 * using the \a aClient argument to do the requested job and optionally expose
1895 * the progress through the \a aProgress object. The user function should never
1896 * call notifyComplete() on it: this will be done automatically using the
1897 * result code returned by the function.
1898 *
1899 * Before the user function is started, the communication channel passed to
1900 * the \a aClient argument is fully set up, the function should start using
1901 * its write() and read() methods directly.
1902 *
1903 * The \a aVrc parameter of the user function may be used to return an error
1904 * code if it is related to communication errors (for example, returned by
1905 * the SVCHlpClient members when they fail). In this case, the correct error
1906 * message using this value will be reported to the caller. Note that the
1907 * value of \a aVrc is inspected only if the user function itself returns
1908 * success.
1909 *
1910 * If a failure happens anywhere before the user function would be normally
1911 * called, it will be called anyway in special "cleanup only" mode indicated
1912 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
1913 * all the function is supposed to do is to cleanup its aUser argument if
1914 * necessary (it's assumed that the ownership of this argument is passed to
1915 * the user function once #startSVCHelperClient() returns a success, thus
1916 * making it responsible for the cleanup).
1917 *
1918 * After the user function returns, the thread will send the SVCHlpMsg::Null
1919 * message to indicate a process termination.
1920 *
1921 * @param aPrivileged |true| to start the SVC Helper process as a privileged
1922 * user that can perform administrative tasks
1923 * @param aFunc user function to run
1924 * @param aUser argument to the user function
1925 * @param aProgress progress object that will track operation completion
1926 *
1927 * @note aPrivileged is currently ignored (due to some unsolved problems in
1928 * Vista) and the process will be started as a normal (unprivileged)
1929 * process.
1930 *
1931 * @note Doesn't lock anything.
1932 */
1933HRESULT VirtualBox::startSVCHelperClient (bool aPrivileged,
1934 SVCHelperClientFunc aFunc,
1935 void *aUser, Progress *aProgress)
1936{
1937 AssertReturn(aFunc, E_POINTER);
1938 AssertReturn(aProgress, E_POINTER);
1939
1940 AutoCaller autoCaller(this);
1941 CheckComRCReturnRC(autoCaller.rc());
1942
1943 /* create the SVCHelperClientThread() argument */
1944 std::auto_ptr <StartSVCHelperClientData>
1945 d (new StartSVCHelperClientData());
1946 AssertReturn(d.get(), E_OUTOFMEMORY);
1947
1948 d->that = this;
1949 d->progress = aProgress;
1950 d->privileged = aPrivileged;
1951 d->func = aFunc;
1952 d->user = aUser;
1953
1954 RTTHREAD tid = NIL_RTTHREAD;
1955 int vrc = RTThreadCreate (&tid, SVCHelperClientThread,
1956 static_cast <void *> (d.get()),
1957 0, RTTHREADTYPE_MAIN_WORKER,
1958 RTTHREADFLAGS_WAITABLE, "SVCHelper");
1959
1960 ComAssertMsgRCRet (vrc, ("Could not create SVCHelper thread (%Rrc)", vrc),
1961 E_FAIL);
1962
1963 /* d is now owned by SVCHelperClientThread(), so release it */
1964 d.release();
1965
1966 return S_OK;
1967}
1968
1969/**
1970 * Worker thread for startSVCHelperClient().
1971 */
1972/* static */
1973DECLCALLBACK(int)
1974VirtualBox::SVCHelperClientThread (RTTHREAD aThread, void *aUser)
1975{
1976 LogFlowFuncEnter();
1977
1978 std::auto_ptr <StartSVCHelperClientData>
1979 d (static_cast <StartSVCHelperClientData *> (aUser));
1980
1981 HRESULT rc = S_OK;
1982 bool userFuncCalled = false;
1983
1984 do
1985 {
1986 AssertBreakStmt (d.get(), rc = E_POINTER);
1987 AssertReturn(!d->progress.isNull(), E_POINTER);
1988
1989 /* protect VirtualBox from uninitialization */
1990 AutoCaller autoCaller(d->that);
1991 if (!autoCaller.isOk())
1992 {
1993 /* it's too late */
1994 rc = autoCaller.rc();
1995 break;
1996 }
1997
1998 int vrc = VINF_SUCCESS;
1999
2000 Guid id;
2001 id.create();
2002 SVCHlpClient client;
2003 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
2004 id.raw()).c_str());
2005 if (RT_FAILURE(vrc))
2006 {
2007 rc = setError(E_FAIL,
2008 tr("Could not create the communication channel (%Rrc)"), vrc);
2009 break;
2010 }
2011
2012 /* get the path to the executable */
2013 char exePathBuf [RTPATH_MAX];
2014 char *exePath = RTProcGetExecutableName (exePathBuf, RTPATH_MAX);
2015 ComAssertBreak (exePath, E_FAIL);
2016
2017 Utf8Str argsStr = Utf8StrFmt ("/Helper %s", client.name().raw());
2018
2019 LogFlowFunc (("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
2020
2021 RTPROCESS pid = NIL_RTPROCESS;
2022
2023 if (d->privileged)
2024 {
2025 /* Attempt to start a privileged process using the Run As dialog */
2026
2027 Bstr file = exePath;
2028 Bstr parameters = argsStr;
2029
2030 SHELLEXECUTEINFO shExecInfo;
2031
2032 shExecInfo.cbSize = sizeof (SHELLEXECUTEINFO);
2033
2034 shExecInfo.fMask = NULL;
2035 shExecInfo.hwnd = NULL;
2036 shExecInfo.lpVerb = L"runas";
2037 shExecInfo.lpFile = file;
2038 shExecInfo.lpParameters = parameters;
2039 shExecInfo.lpDirectory = NULL;
2040 shExecInfo.nShow = SW_NORMAL;
2041 shExecInfo.hInstApp = NULL;
2042
2043 if (!ShellExecuteEx (&shExecInfo))
2044 {
2045 int vrc2 = RTErrConvertFromWin32 (GetLastError());
2046 /* hide excessive details in case of a frequent error
2047 * (pressing the Cancel button to close the Run As dialog) */
2048 if (vrc2 == VERR_CANCELLED)
2049 rc = setError (E_FAIL,
2050 tr ("Operation cancelled by the user"));
2051 else
2052 rc = setError (E_FAIL,
2053 tr ("Could not launch a privileged process '%s' (%Rrc)"),
2054 exePath, vrc2);
2055 break;
2056 }
2057 }
2058 else
2059 {
2060 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2061 vrc = RTProcCreate (exePath, args, RTENV_DEFAULT, 0, &pid);
2062 if (RT_FAILURE(vrc))
2063 {
2064 rc = setError (E_FAIL,
2065 tr ("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2066 break;
2067 }
2068 }
2069
2070 /* wait for the client to connect */
2071 vrc = client.connect();
2072 if (RT_SUCCESS(vrc))
2073 {
2074 /* start the user supplied function */
2075 rc = d->func (&client, d->progress, d->user, &vrc);
2076 userFuncCalled = true;
2077 }
2078
2079 /* send the termination signal to the process anyway */
2080 {
2081 int vrc2 = client.write (SVCHlpMsg::Null);
2082 if (RT_SUCCESS(vrc))
2083 vrc = vrc2;
2084 }
2085
2086 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2087 {
2088 rc = setError (E_FAIL,
2089 tr ("Could not operate the communication channel (%Rrc)"), vrc);
2090 break;
2091 }
2092 }
2093 while (0);
2094
2095 if (FAILED (rc) && !userFuncCalled)
2096 {
2097 /* call the user function in the "cleanup only" mode
2098 * to let it free resources passed to in aUser */
2099 d->func (NULL, NULL, d->user, NULL);
2100 }
2101
2102 d->progress->notifyComplete (rc);
2103
2104 LogFlowFuncLeave();
2105 return 0;
2106}
2107
2108#endif /* RT_OS_WINDOWS */
2109
2110/**
2111 * Sends a signal to the client watcher thread to rescan the set of machines
2112 * that have open sessions.
2113 *
2114 * @note Doesn't lock anything.
2115 */
2116void VirtualBox::updateClientWatcher()
2117{
2118 AutoCaller autoCaller(this);
2119 AssertComRCReturn (autoCaller.rc(), (void) 0);
2120
2121 AssertReturn(mWatcherData.mThread != NIL_RTTHREAD, (void) 0);
2122
2123 /* sent an update request */
2124#if defined(RT_OS_WINDOWS)
2125 ::SetEvent (mWatcherData.mUpdateReq);
2126#elif defined(RT_OS_OS2)
2127 RTSemEventSignal (mWatcherData.mUpdateReq);
2128#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2129 RTSemEventSignal (mWatcherData.mUpdateReq);
2130#else
2131# error "Port me!"
2132#endif
2133}
2134
2135/**
2136 * Adds the given child process ID to the list of processes to be reaped.
2137 * This call should be followed by #updateClientWatcher() to take the effect.
2138 */
2139void VirtualBox::addProcessToReap (RTPROCESS pid)
2140{
2141 AutoCaller autoCaller(this);
2142 AssertComRCReturn (autoCaller.rc(), (void) 0);
2143
2144 /// @todo (dmik) Win32?
2145#ifndef RT_OS_WINDOWS
2146 AutoWriteLock alock(this);
2147 mWatcherData.mProcesses.push_back (pid);
2148#endif
2149}
2150
2151/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2152struct MachineEvent : public VirtualBox::CallbackEvent
2153{
2154 enum What { DataChanged, StateChanged, Registered };
2155
2156 MachineEvent (VirtualBox *aVB, const Guid &aId)
2157 : CallbackEvent (aVB), what (DataChanged), id (aId)
2158 {}
2159
2160 MachineEvent (VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2161 : CallbackEvent (aVB), what (StateChanged), id (aId)
2162 , state (aState)
2163 {}
2164
2165 MachineEvent (VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2166 : CallbackEvent (aVB), what (Registered), id (aId)
2167 , registered (aRegistered)
2168 {}
2169
2170 void handleCallback (const ComPtr<IVirtualBoxCallback> &aCallback)
2171 {
2172 switch (what)
2173 {
2174 case DataChanged:
2175 LogFlow (("OnMachineDataChange: id={%RTuuid}\n", id.ptr()));
2176 aCallback->OnMachineDataChange (id.toUtf16());
2177 break;
2178
2179 case StateChanged:
2180 LogFlow (("OnMachineStateChange: id={%RTuuid}, state=%d\n",
2181 id.ptr(), state));
2182 aCallback->OnMachineStateChange (id.toUtf16(), state);
2183 break;
2184
2185 case Registered:
2186 LogFlow (("OnMachineRegistered: id={%RTuuid}, registered=%d\n",
2187 id.ptr(), registered));
2188 aCallback->OnMachineRegistered (id.toUtf16(), registered);
2189 break;
2190 }
2191 }
2192
2193 const What what;
2194
2195 Guid id;
2196 MachineState_T state;
2197 BOOL registered;
2198};
2199
2200/**
2201 * @note Doesn't lock any object.
2202 */
2203void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2204{
2205 postEvent (new MachineEvent (this, aId, aState));
2206}
2207
2208/**
2209 * @note Doesn't lock any object.
2210 */
2211void VirtualBox::onMachineDataChange(const Guid &aId)
2212{
2213 postEvent (new MachineEvent (this, aId));
2214}
2215
2216/**
2217 * @note Locks this object for reading.
2218 */
2219BOOL VirtualBox::onExtraDataCanChange (const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2220 Bstr &aError)
2221{
2222 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2223 aId.toString().raw(), aKey, aValue));
2224
2225 AutoCaller autoCaller(this);
2226 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2227
2228 CallbackList list;
2229 {
2230 AutoReadLock alock(this);
2231 list = mData.mCallbacks;
2232 }
2233
2234 BOOL allowChange = TRUE;
2235 CallbackList::iterator it = list.begin();
2236 Bstr id = aId.toUtf16();
2237 while ((it != list.end()) && allowChange)
2238 {
2239 HRESULT rc = (*it++)->OnExtraDataCanChange (id, aKey, aValue,
2240 aError.asOutParam(), &allowChange);
2241 if (FAILED (rc))
2242 {
2243 /* if a call to this method fails for some reason (for ex., because
2244 * the other side is dead), we ensure allowChange stays true
2245 * (MS COM RPC implementation seems to zero all output vars before
2246 * issuing an IPC call or after a failure, so it's essential
2247 * there) */
2248 allowChange = TRUE;
2249 }
2250 }
2251
2252 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2253 return allowChange;
2254}
2255
2256/** Event for onExtraDataChange() */
2257struct ExtraDataEvent : public VirtualBox::CallbackEvent
2258{
2259 ExtraDataEvent (VirtualBox *aVB, const Guid &aMachineId,
2260 IN_BSTR aKey, IN_BSTR aVal)
2261 : CallbackEvent (aVB), machineId (aMachineId)
2262 , key (aKey), val (aVal)
2263 {}
2264
2265 void handleCallback (const ComPtr<IVirtualBoxCallback> &aCallback)
2266 {
2267 LogFlow (("OnExtraDataChange: machineId={%RTuuid}, key='%ls', val='%ls'\n",
2268 machineId.ptr(), key.raw(), val.raw()));
2269 aCallback->OnExtraDataChange (machineId.toUtf16(), key, val);
2270 }
2271
2272 Guid machineId;
2273 Bstr key, val;
2274};
2275
2276/**
2277 * @note Doesn't lock any object.
2278 */
2279void VirtualBox::onExtraDataChange (const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2280{
2281 postEvent (new ExtraDataEvent (this, aId, aKey, aValue));
2282}
2283
2284/**
2285 * @note Doesn't lock any object.
2286 */
2287void VirtualBox::onMachineRegistered (const Guid &aId, BOOL aRegistered)
2288{
2289 postEvent (new MachineEvent (this, aId, aRegistered));
2290}
2291
2292/** Event for onSessionStateChange() */
2293struct SessionEvent : public VirtualBox::CallbackEvent
2294{
2295 SessionEvent (VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2296 : CallbackEvent (aVB), machineId (aMachineId), sessionState (aState)
2297 {}
2298
2299 void handleCallback (const ComPtr<IVirtualBoxCallback> &aCallback)
2300 {
2301 LogFlow (("OnSessionStateChange: machineId={%RTuuid}, sessionState=%d\n",
2302 machineId.ptr(), sessionState));
2303 aCallback->OnSessionStateChange (machineId.toUtf16(), sessionState);
2304 }
2305
2306 Guid machineId;
2307 SessionState_T sessionState;
2308};
2309
2310/**
2311 * @note Doesn't lock any object.
2312 */
2313void VirtualBox::onSessionStateChange (const Guid &aId, SessionState_T aState)
2314{
2315 postEvent (new SessionEvent (this, aId, aState));
2316}
2317
2318/** Event for onSnapshotTaken(), onSnapshotRemoved() and onSnapshotChange() */
2319struct SnapshotEvent : public VirtualBox::CallbackEvent
2320{
2321 enum What { Taken, Discarded, Changed };
2322
2323 SnapshotEvent (VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2324 What aWhat)
2325 : CallbackEvent (aVB)
2326 , what (aWhat)
2327 , machineId (aMachineId), snapshotId (aSnapshotId)
2328 {}
2329
2330 void handleCallback (const ComPtr<IVirtualBoxCallback> &aCallback)
2331 {
2332 Bstr mid = machineId.toUtf16();
2333 Bstr sid = snapshotId.toUtf16();
2334
2335 switch (what)
2336 {
2337 case Taken:
2338 LogFlow (("OnSnapshotTaken: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2339 machineId.ptr(), snapshotId.ptr()));
2340 aCallback->OnSnapshotTaken (mid, sid);
2341 break;
2342
2343 case Discarded:
2344 LogFlow (("OnSnapshotDiscarded: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2345 machineId.ptr(), snapshotId.ptr()));
2346 aCallback->OnSnapshotDiscarded (mid, sid);
2347 break;
2348
2349 case Changed:
2350 LogFlow (("OnSnapshotChange: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2351 machineId.ptr(), snapshotId.ptr()));
2352 aCallback->OnSnapshotChange (mid, sid);
2353 break;
2354 }
2355 }
2356
2357 const What what;
2358
2359 Guid machineId;
2360 Guid snapshotId;
2361};
2362
2363/**
2364 * @note Doesn't lock any object.
2365 */
2366void VirtualBox::onSnapshotTaken (const Guid &aMachineId, const Guid &aSnapshotId)
2367{
2368 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Taken));
2369}
2370
2371/**
2372 * @note Doesn't lock any object.
2373 */
2374void VirtualBox::onSnapshotDiscarded (const Guid &aMachineId, const Guid &aSnapshotId)
2375{
2376 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Discarded));
2377}
2378
2379/**
2380 * @note Doesn't lock any object.
2381 */
2382void VirtualBox::onSnapshotChange (const Guid &aMachineId, const Guid &aSnapshotId)
2383{
2384 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Changed));
2385}
2386
2387/** Event for onGuestPropertyChange() */
2388struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2389{
2390 GuestPropertyEvent (VirtualBox *aVBox, const Guid &aMachineId,
2391 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2392 : CallbackEvent (aVBox), machineId (aMachineId)
2393 , name (aName), value (aValue), flags(aFlags)
2394 {}
2395
2396 void handleCallback (const ComPtr<IVirtualBoxCallback> &aCallback)
2397 {
2398 LogFlow (("OnGuestPropertyChange: machineId={%RTuuid}, name='%ls', value='%ls', flags='%ls'\n",
2399 machineId.ptr(), name.raw(), value.raw(), flags.raw()));
2400 aCallback->OnGuestPropertyChange (machineId.toUtf16(), name, value, flags);
2401 }
2402
2403 Guid machineId;
2404 Bstr name, value, flags;
2405};
2406
2407/**
2408 * @note Doesn't lock any object.
2409 */
2410void VirtualBox::onGuestPropertyChange (const Guid &aMachineId, IN_BSTR aName,
2411 IN_BSTR aValue, IN_BSTR aFlags)
2412{
2413 postEvent (new GuestPropertyEvent (this, aMachineId, aName, aValue, aFlags));
2414}
2415
2416/**
2417 * @note Locks this object for reading.
2418 */
2419ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2420{
2421 ComObjPtr<GuestOSType> type;
2422
2423 AutoCaller autoCaller(this);
2424 AssertComRCReturn (autoCaller.rc(), type);
2425
2426 AutoReadLock alock(this);
2427
2428 /* unknown type must always be the first */
2429 ComAssertRet (mData.mGuestOSTypes.size() > 0, type);
2430
2431 type = mData.mGuestOSTypes.front();
2432 return type;
2433}
2434
2435/**
2436 * Returns the list of opened machines (machines having direct sessions opened
2437 * by client processes) and optionally the list of direct session controls.
2438 *
2439 * @param aMachines Where to put opened machines (will be empty if none).
2440 * @param aControls Where to put direct session controls (optional).
2441 *
2442 * @note The returned lists contain smart pointers. So, clear it as soon as
2443 * it becomes no more necessary to release instances.
2444 *
2445 * @note It can be possible that a session machine from the list has been
2446 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2447 * when accessing unprotected data directly.
2448 *
2449 * @note Locks objects for reading.
2450 */
2451void VirtualBox::getOpenedMachines (SessionMachineVector &aMachines,
2452 InternalControlVector *aControls /*= NULL*/)
2453{
2454 AutoCaller autoCaller(this);
2455 AssertComRCReturnVoid (autoCaller.rc());
2456
2457 aMachines.clear();
2458 if (aControls)
2459 aControls->clear();
2460
2461 AutoReadLock alock(this);
2462
2463 for (MachineList::iterator it = mData.mMachines.begin();
2464 it != mData.mMachines.end();
2465 ++ it)
2466 {
2467 ComObjPtr<SessionMachine> sm;
2468 ComPtr<IInternalSessionControl> ctl;
2469 if ((*it)->isSessionOpen (sm, &ctl))
2470 {
2471 aMachines.push_back (sm);
2472 if (aControls)
2473 aControls->push_back (ctl);
2474 }
2475 }
2476}
2477
2478/**
2479 * Searches for a Machine object with the given ID in the collection
2480 * of registered machines.
2481 *
2482 * @param id
2483 * ID of the machine
2484 * @param doSetError
2485 * if TRUE, the appropriate error info is set in case when the machine
2486 * is not found
2487 * @param machine
2488 * where to store the found machine object (can be NULL)
2489 *
2490 * @return
2491 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
2492 *
2493 * @note Locks this object for reading.
2494 */
2495HRESULT VirtualBox::findMachine (const Guid &aId, bool aSetError,
2496 ComObjPtr<Machine> *aMachine /* = NULL */)
2497{
2498 AutoCaller autoCaller(this);
2499 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2500
2501 bool found = false;
2502
2503 {
2504 AutoReadLock alock(this);
2505
2506 for (MachineList::iterator it = mData.mMachines.begin();
2507 !found && it != mData.mMachines.end();
2508 ++ it)
2509 {
2510 /* sanity */
2511 AutoLimitedCaller machCaller (*it);
2512 AssertComRC (machCaller.rc());
2513
2514 found = (*it)->id() == aId;
2515 if (found && aMachine)
2516 *aMachine = *it;
2517 }
2518 }
2519
2520 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2521
2522 if (aSetError && !found)
2523 {
2524 setError (VBOX_E_OBJECT_NOT_FOUND,
2525 tr ("Could not find a registered machine with UUID {%RTuuid}"),
2526 aId.raw());
2527 }
2528
2529 return rc;
2530}
2531
2532/**
2533 * Searches for a HardDisk object with the given ID or location in the list of
2534 * registered hard disks. If both ID and location are specified, the first
2535 * object that matches either of them (not necessarily both) is returned.
2536 *
2537 * @param aId ID of the hard disk (unused when NULL).
2538 * @param aLocation Full location specification (unused NULL).
2539 * @param aSetError If @c true , the appropriate error info is set in case
2540 * when the hard disk is not found.
2541 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2542 *
2543 * @return S_OK when found or E_INVALIDARG when not found.
2544 *
2545 * @note Locks this object and hard disk objects for reading.
2546 */
2547HRESULT VirtualBox::findHardDisk(const Guid *aId,
2548 CBSTR aLocation,
2549 bool aSetError,
2550 ComObjPtr<HardDisk> *aHardDisk /*= NULL*/)
2551{
2552 AssertReturn(aId || aLocation, E_INVALIDARG);
2553
2554 AutoReadLock alock(this);
2555
2556 /* first, look up by UUID in the map if UUID is provided */
2557 if (aId)
2558 {
2559 HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
2560 if (it != mData.mHardDiskMap.end())
2561 {
2562 if (aHardDisk)
2563 *aHardDisk = (*it).second;
2564 return S_OK;
2565 }
2566 }
2567
2568 /* then iterate and search by location */
2569 int result = -1;
2570 if (aLocation)
2571 {
2572 Utf8Str location = aLocation;
2573
2574 for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
2575 it != mData.mHardDiskMap.end();
2576 ++ it)
2577 {
2578 const ComObjPtr<HardDisk> &hd = (*it).second;
2579
2580 HRESULT rc = hd->compareLocationTo(location, result);
2581 CheckComRCReturnRC(rc);
2582
2583 if (result == 0)
2584 {
2585 if (aHardDisk)
2586 *aHardDisk = hd;
2587 break;
2588 }
2589 }
2590 }
2591
2592 HRESULT rc = result == 0 ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2593
2594 if (aSetError && result != 0)
2595 {
2596 if (aId)
2597 setError(rc,
2598 tr("Could not find a hard disk with UUID {%RTuuid} in the media registry ('%s')"),
2599 aId->raw(),
2600 m_strSettingsFilePath.raw());
2601 else
2602 setError(rc,
2603 tr("Could not find a hard disk with location '%ls' in the media registry ('%s')"),
2604 aLocation,
2605 m_strSettingsFilePath.raw());
2606 }
2607
2608 return rc;
2609}
2610
2611/**
2612 * Searches for a DVDImage object with the given ID or location in the list of
2613 * registered DVD images. If both ID and file path are specified, the first
2614 * object that matches either of them (not necessarily both) is returned.
2615 *
2616 * @param aId ID of the DVD image (unused when NULL).
2617 * @param aLocation Full path to the image file (unused when NULL).
2618 * @param aSetError If @c true, the appropriate error info is set in case when
2619 * the image is not found.
2620 * @param aImage Where to store the found image object (can be NULL).
2621 *
2622 * @return S_OK when found or E_INVALIDARG when not found.
2623 *
2624 * @note Locks this object and image objects for reading.
2625 */
2626HRESULT VirtualBox::findDVDImage(const Guid *aId,
2627 CBSTR aLocation,
2628 bool aSetError,
2629 ComObjPtr<DVDImage> *aImage /* = NULL */)
2630{
2631 AssertReturn(aId || aLocation, E_INVALIDARG);
2632
2633 Utf8Str location;
2634
2635 if (aLocation != NULL)
2636 {
2637 int vrc = calculateFullPath(Utf8Str(aLocation), location);
2638 if (RT_FAILURE(vrc))
2639 return setError(VBOX_E_FILE_ERROR,
2640 tr("Invalid image file location '%ls' (%Rrc)"),
2641 aLocation,
2642 vrc);
2643 }
2644
2645 AutoReadLock alock(this);
2646
2647 bool found = false;
2648
2649 for (DVDImageList::const_iterator it = mData.mDVDImages.begin();
2650 it != mData.mDVDImages.end();
2651 ++ it)
2652 {
2653 /* no AutoCaller, registered image life time is bound to this */
2654 AutoReadLock imageLock (*it);
2655
2656 found = (aId && (*it)->id() == *aId) ||
2657 (aLocation != NULL &&
2658 RTPathCompare(location.c_str(),
2659 Utf8Str((*it)->locationFull()).c_str()
2660 ) == 0);
2661 if (found)
2662 {
2663 if (aImage)
2664 *aImage = *it;
2665 break;
2666 }
2667 }
2668
2669 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2670
2671 if (aSetError && !found)
2672 {
2673 if (aId)
2674 setError(rc,
2675 tr("Could not find a CD/DVD image with UUID {%RTuuid} in the media registry ('%s')"),
2676 aId->raw(),
2677 m_strSettingsFilePath.raw());
2678 else
2679 setError(rc,
2680 tr("Could not find a CD/DVD image with location '%ls' in the media registry ('%s')"),
2681 aLocation,
2682 m_strSettingsFilePath.raw());
2683 }
2684
2685 return rc;
2686}
2687
2688/**
2689 * Searches for a FloppyImage object with the given ID or location in the
2690 * collection of registered DVD images. If both ID and file path are specified,
2691 * the first object that matches either of them (not necessarily both) is
2692 * returned.
2693 *
2694 * @param aId ID of the DVD image (unused when NULL).
2695 * @param aLocation Full path to the image file (unused when NULL).
2696 * @param aSetError If @c true, the appropriate error info is set in case when
2697 * the image is not found.
2698 * @param aImage Where to store the found image object (can be NULL).
2699 *
2700 * @return S_OK when found or E_INVALIDARG when not found.
2701 *
2702 * @note Locks this object and image objects for reading.
2703 */
2704HRESULT VirtualBox::findFloppyImage(const Guid *aId, CBSTR aLocation,
2705 bool aSetError,
2706 ComObjPtr<FloppyImage> *aImage /* = NULL */)
2707{
2708 AssertReturn(aId || aLocation, E_INVALIDARG);
2709
2710 Utf8Str location;
2711
2712 if (aLocation != NULL)
2713 {
2714 int vrc = calculateFullPath(Utf8Str(aLocation), location);
2715 if (RT_FAILURE(vrc))
2716 return setError (VBOX_E_FILE_ERROR,
2717 tr ("Invalid image file location '%ls' (%Rrc)"),
2718 aLocation, vrc);
2719 }
2720
2721 AutoReadLock alock(this);
2722
2723 bool found = false;
2724
2725 for (FloppyImageList::const_iterator it = mData.mFloppyImages.begin();
2726 it != mData.mFloppyImages.end();
2727 ++ it)
2728 {
2729 /* no AutoCaller, registered image life time is bound to this */
2730 AutoReadLock imageLock (*it);
2731
2732 found = (aId && (*it)->id() == *aId) ||
2733 (aLocation != NULL &&
2734 RTPathCompare(location.c_str(),
2735 Utf8Str((*it)->locationFull()).c_str()
2736 ) == 0);
2737 if (found)
2738 {
2739 if (aImage)
2740 *aImage = *it;
2741 break;
2742 }
2743 }
2744
2745 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2746
2747 if (aSetError && !found)
2748 {
2749 if (aId)
2750 setError(rc,
2751 tr("Could not find a floppy image with UUID {%RTuuid} in the media registry ('%s')"),
2752 aId->raw(),
2753 m_strSettingsFilePath.raw());
2754 else
2755 setError(rc,
2756 tr("Could not find a floppy image with location '%ls' in the media registry ('%s')"),
2757 aLocation,
2758 m_strSettingsFilePath.raw());
2759 }
2760
2761 return rc;
2762}
2763
2764/**
2765 * Returns the default machine folder from the system properties
2766 * with proper locking.
2767 * @return
2768 */
2769const Utf8Str& VirtualBox::getDefaultMachineFolder() const
2770{
2771 AutoReadLock propsLock(mData.mSystemProperties);
2772 return mData.mSystemProperties->m_strDefaultMachineFolder;
2773}
2774
2775/**
2776 * Returns the default hard disk folder from the system properties
2777 * with proper locking.
2778 * @return
2779 */
2780const Utf8Str& VirtualBox::getDefaultHardDiskFolder() const
2781{
2782 AutoReadLock propsLock(mData.mSystemProperties);
2783 return mData.mSystemProperties->m_strDefaultHardDiskFolder;
2784}
2785
2786/**
2787 * Returns the default hard disk format from the system properties
2788 * with proper locking.
2789 * @return
2790 */
2791const Utf8Str& VirtualBox::getDefaultHardDiskFormat() const
2792{
2793 AutoReadLock propsLock(mData.mSystemProperties);
2794 return mData.mSystemProperties->m_strDefaultHardDiskFormat;
2795}
2796
2797/**
2798 * Calculates the absolute path of the given path taking the VirtualBox home
2799 * directory as the current directory.
2800 *
2801 * @param aPath Path to calculate the absolute path for.
2802 * @param aResult Where to put the result (used only on success, can be the
2803 * same Utf8Str instance as passed in @a aPath).
2804 * @return IPRT result.
2805 *
2806 * @note Doesn't lock any object.
2807 */
2808int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
2809{
2810 AutoCaller autoCaller(this);
2811 AssertComRCReturn (autoCaller.rc(), VERR_GENERAL_FAILURE);
2812
2813 /* no need to lock since mHomeDir is const */
2814
2815 char folder[RTPATH_MAX];
2816 int vrc = RTPathAbsEx(mData.mHomeDir.c_str(), strPath.c_str(), folder, sizeof(folder));
2817 if (RT_SUCCESS(vrc))
2818 aResult = folder;
2819
2820 return vrc;
2821}
2822
2823/**
2824 * Tries to calculate the relative path of the given absolute path using the
2825 * directory of the VirtualBox settings file as the base directory.
2826 *
2827 * @param aPath Absolute path to calculate the relative path for.
2828 * @param aResult Where to put the result (used only when it's possible to
2829 * make a relative path from the given absolute path; otherwise
2830 * left untouched).
2831 *
2832 * @note Doesn't lock any object.
2833 */
2834void VirtualBox::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
2835{
2836 AutoCaller autoCaller(this);
2837 AssertComRCReturnVoid (autoCaller.rc());
2838
2839 /* no need to lock since mHomeDir is const */
2840
2841 Utf8Str settingsDir = mData.mHomeDir;
2842
2843 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
2844 {
2845 /* when assigning, we create a separate Utf8Str instance because both
2846 * aPath and aResult can point to the same memory location when this
2847 * func is called (if we just do aResult = aPath, aResult will be freed
2848 * first, and since its the same as aPath, an attempt to copy garbage
2849 * will be made. */
2850 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
2851 }
2852}
2853
2854// private methods
2855/////////////////////////////////////////////////////////////////////////////
2856
2857/**
2858 * Checks if there is a hard disk, DVD or floppy image with the given ID or
2859 * location already registered.
2860 *
2861 * On return, sets @aConflict to the string describing the conflicting medium,
2862 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
2863 * either case. A failure is unexpected.
2864 *
2865 * @param aId UUID to check.
2866 * @param aLocation Location to check.
2867 * @param aConflict Where to return parameters of the conflicting medium.
2868 *
2869 * @note Locks this object and media objects for reading.
2870 */
2871HRESULT VirtualBox::checkMediaForConflicts2 (const Guid &aId,
2872 const Bstr &aLocation,
2873 Utf8Str &aConflict)
2874{
2875 aConflict.setNull();
2876
2877 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
2878
2879 AutoReadLock alock(this);
2880
2881 HRESULT rc = S_OK;
2882
2883 {
2884 ComObjPtr<HardDisk> hardDisk;
2885 rc = findHardDisk(&aId, aLocation, false /* aSetError */, &hardDisk);
2886 if (SUCCEEDED(rc))
2887 {
2888 /* Note: no AutoCaller since bound to this */
2889 AutoReadLock mediaLock (hardDisk);
2890 aConflict = Utf8StrFmt (
2891 tr ("hard disk '%ls' with UUID {%RTuuid}"),
2892 hardDisk->locationFull().raw(), hardDisk->id().raw());
2893 return S_OK;
2894 }
2895 }
2896
2897 {
2898 ComObjPtr<DVDImage> image;
2899 rc = findDVDImage (&aId, aLocation, false /* aSetError */, &image);
2900 if (SUCCEEDED(rc))
2901 {
2902 /* Note: no AutoCaller since bound to this */
2903 AutoReadLock mediaLock (image);
2904 aConflict = Utf8StrFmt (
2905 tr ("CD/DVD image '%ls' with UUID {%RTuuid}"),
2906 image->locationFull().raw(), image->id().raw());
2907 return S_OK;
2908 }
2909 }
2910
2911 {
2912 ComObjPtr<FloppyImage> image;
2913 rc = findFloppyImage(&aId, aLocation, false /* aSetError */, &image);
2914 if (SUCCEEDED(rc))
2915 {
2916 /* Note: no AutoCaller since bound to this */
2917 AutoReadLock mediaLock (image);
2918 aConflict = Utf8StrFmt (
2919 tr ("floppy image '%ls' with UUID {%RTuuid}"),
2920 image->locationFull().raw(), image->id().raw());
2921 return S_OK;
2922 }
2923 }
2924
2925 return S_OK;
2926}
2927
2928/**
2929 * Helper function to write out the configuration tree.
2930 *
2931 * @note Locks this object for writing and child objects for reading/writing!
2932 */
2933HRESULT VirtualBox::saveSettings()
2934{
2935 AutoCaller autoCaller (this);
2936 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2937
2938 AssertReturn(!m_strSettingsFilePath.isEmpty(), E_FAIL);
2939
2940 HRESULT rc = S_OK;
2941
2942 /* serialize file access (prevents concurrent reads and writes) */
2943 AutoWriteLock alock(this);
2944
2945 try
2946 {
2947 // machines
2948 m_pMainConfigFile->llMachines.clear();
2949 for (MachineList::iterator it = mData.mMachines.begin();
2950 it != mData.mMachines.end();
2951 ++it)
2952 {
2953 settings::MachineRegistryEntry mre;
2954 rc = (*it)->saveRegistryEntry(mre);
2955 m_pMainConfigFile->llMachines.push_back(mre);
2956 }
2957
2958 // hard disks
2959 m_pMainConfigFile->llHardDisks.clear();
2960 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
2961 it != mData.mHardDisks.end();
2962 ++it)
2963 {
2964 settings::Medium m;
2965 rc = (*it)->saveSettings(m);
2966 m_pMainConfigFile->llHardDisks.push_back(m);
2967 CheckComRCThrowRC(rc);
2968 }
2969
2970 /* CD/DVD images */
2971 m_pMainConfigFile->llDvdImages.clear();
2972 for (DVDImageList::const_iterator it = mData.mDVDImages.begin();
2973 it != mData.mDVDImages.end();
2974 ++it)
2975 {
2976 settings::Medium m;
2977 rc = (*it)->saveSettings(m);
2978 CheckComRCThrowRC(rc);
2979 m_pMainConfigFile->llDvdImages.push_back(m);
2980 }
2981
2982 /* floppy images */
2983 m_pMainConfigFile->llFloppyImages.clear();
2984 for (FloppyImageList::const_iterator it = mData.mFloppyImages.begin();
2985 it != mData.mFloppyImages.end();
2986 ++it)
2987 {
2988 settings::Medium m;
2989 rc = (*it)->saveSettings(m);
2990 CheckComRCThrowRC(rc);
2991 m_pMainConfigFile->llFloppyImages.push_back(m);
2992 }
2993
2994 m_pMainConfigFile->llDhcpServers.clear();
2995 for (DHCPServerList::const_iterator it =
2996 mData.mDHCPServers.begin();
2997 it != mData.mDHCPServers.end();
2998 ++ it)
2999 {
3000 settings::DHCPServer d;
3001 rc = (*it)->saveSettings(d);
3002 CheckComRCThrowRC(rc);
3003 m_pMainConfigFile->llDhcpServers.push_back(d);
3004 }
3005
3006 /* host data (USB filters) */
3007 rc = mData.mHost->saveSettings(m_pMainConfigFile->host);
3008 CheckComRCThrowRC(rc);
3009
3010 rc = mData.mSystemProperties->saveSettings(m_pMainConfigFile->systemProperties);
3011 CheckComRCThrowRC(rc);
3012
3013 // now write out the XML
3014 m_pMainConfigFile->write(m_strSettingsFilePath);
3015 }
3016 catch (HRESULT err)
3017 {
3018 /* we assume that error info is set by the thrower */
3019 rc = err;
3020 }
3021 catch (...)
3022 {
3023 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3024 }
3025
3026 return rc;
3027}
3028
3029/**
3030 * Helper to register the machine.
3031 *
3032 * When called during VirtualBox startup, adds the given machine to the
3033 * collection of registered machines. Otherwise tries to mark the machine
3034 * as registered, and, if succeeded, adds it to the collection and
3035 * saves global settings.
3036 *
3037 * @note The caller must have added itself as a caller of the @a aMachine
3038 * object if calls this method not on VirtualBox startup.
3039 *
3040 * @param aMachine machine to register
3041 *
3042 * @note Locks objects!
3043 */
3044HRESULT VirtualBox::registerMachine (Machine *aMachine)
3045{
3046 ComAssertRet (aMachine, E_INVALIDARG);
3047
3048 AutoCaller autoCaller(this);
3049 CheckComRCReturnRC(autoCaller.rc());
3050
3051 AutoWriteLock alock(this);
3052
3053 HRESULT rc = S_OK;
3054
3055 {
3056 ComObjPtr<Machine> m;
3057 rc = findMachine (aMachine->id(), false /* aDoSetError */, &m);
3058 if (SUCCEEDED(rc))
3059 {
3060 /* sanity */
3061 AutoLimitedCaller machCaller (m);
3062 AssertComRC (machCaller.rc());
3063
3064 return setError (E_INVALIDARG,
3065 tr ("Registered machine with UUID {%RTuuid} ('%ls') already exists"),
3066 aMachine->id().raw(), m->settingsFileFull().raw());
3067 }
3068
3069 ComAssertRet (rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3070 rc = S_OK;
3071 }
3072
3073 if (autoCaller.state() != InInit)
3074 {
3075 /* Machine::trySetRegistered() will commit and save machine settings */
3076 rc = aMachine->trySetRegistered (TRUE);
3077 CheckComRCReturnRC(rc);
3078 }
3079
3080 /* add to the collection of registered machines */
3081 mData.mMachines.push_back(aMachine);
3082
3083 if (autoCaller.state() != InInit)
3084 rc = saveSettings();
3085
3086 return rc;
3087}
3088
3089/**
3090 * Remembers the given hard disk by storing it in the hard disk registry.
3091 *
3092 * @param aHardDisk Hard disk object to remember.
3093 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3094 *
3095 * When @a aSaveRegistry is @c true, this operation may fail because of the
3096 * failed #saveSettings() method it calls. In this case, the hard disk object
3097 * will not be remembered. It is therefore the responsibility of the caller to
3098 * call this method as the last step of some action that requires registration
3099 * in order to make sure that only fully functional hard disk objects get
3100 * registered.
3101 *
3102 * @note Locks this object for writing and @a aHardDisk for reading.
3103 */
3104HRESULT VirtualBox::registerHardDisk(HardDisk *aHardDisk,
3105 bool aSaveRegistry /*= true*/)
3106{
3107 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3108
3109 AutoCaller autoCaller(this);
3110 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3111
3112 AutoWriteLock alock(this);
3113
3114 AutoCaller hardDiskCaller (aHardDisk);
3115 AssertComRCReturn (hardDiskCaller.rc(), hardDiskCaller.rc());
3116
3117 AutoReadLock hardDiskLock (aHardDisk);
3118
3119 Utf8Str strConflict;
3120 HRESULT rc = checkMediaForConflicts2(aHardDisk->id(),
3121 aHardDisk->locationFull(),
3122 strConflict);
3123 CheckComRCReturnRC(rc);
3124
3125 if (strConflict.length())
3126 {
3127 return setError(E_INVALIDARG,
3128 tr("Cannot register the hard disk '%ls' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3129 aHardDisk->locationFull().raw(),
3130 aHardDisk->id().raw(),
3131 strConflict.raw(),
3132 m_strSettingsFilePath.raw());
3133 }
3134
3135 if (aHardDisk->parent().isNull())
3136 {
3137 /* base (root) hard disk */
3138 mData.mHardDisks.push_back (aHardDisk);
3139 }
3140
3141 mData.mHardDiskMap
3142 .insert (HardDiskMap::value_type (
3143 aHardDisk->id(), HardDiskMap::mapped_type (aHardDisk)));
3144
3145 if (aSaveRegistry)
3146 {
3147 rc = saveSettings();
3148 if (FAILED (rc))
3149 unregisterHardDisk(aHardDisk, false /* aSaveRegistry */);
3150 }
3151
3152 return rc;
3153}
3154
3155/**
3156 * Removes the given hard disk from the hard disk registry.
3157 *
3158 * @param aHardDisk Hard disk object to remove.
3159 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3160 *
3161 * When @a aSaveRegistry is @c true, this operation may fail because of the
3162 * failed #saveSettings() method it calls. In this case, the hard disk object
3163 * will NOT be removed from the registry when this method returns. It is
3164 * therefore the responsibility of the caller to call this method as the first
3165 * step of some action that requires unregistration, before calling uninit() on
3166 * @a aHardDisk.
3167 *
3168 * @note Locks this object for writing and @a aHardDisk for reading.
3169 */
3170HRESULT VirtualBox::unregisterHardDisk(HardDisk *aHardDisk,
3171 bool aSaveRegistry /*= true*/)
3172{
3173 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3174
3175 AutoCaller autoCaller(this);
3176 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3177
3178 AutoWriteLock alock(this);
3179
3180 AutoCaller hardDiskCaller (aHardDisk);
3181 AssertComRCReturn (hardDiskCaller.rc(), hardDiskCaller.rc());
3182
3183 AutoReadLock hardDiskLock (aHardDisk);
3184
3185 size_t cnt = mData.mHardDiskMap.erase (aHardDisk->id());
3186 Assert (cnt == 1);
3187 NOREF(cnt);
3188
3189 if (aHardDisk->parent().isNull())
3190 {
3191 /* base (root) hard disk */
3192 mData.mHardDisks.remove (aHardDisk);
3193 }
3194
3195 HRESULT rc = S_OK;
3196
3197 if (aSaveRegistry)
3198 {
3199 rc = saveSettings();
3200 if (FAILED (rc))
3201 registerHardDisk(aHardDisk, false /* aSaveRegistry */);
3202 }
3203
3204 return rc;
3205}
3206
3207/**
3208 * Remembers the given image by storing it in the CD/DVD image registry.
3209 *
3210 * @param aImage Image object to remember.
3211 * @param aSaveRegistry @c true to save the image registry to disk (default).
3212 *
3213 * When @a aSaveRegistry is @c true, this operation may fail because of the
3214 * failed #saveSettings() method it calls. In this case, the image object
3215 * will not be remembered. It is therefore the responsibility of the caller to
3216 * call this method as the last step of some action that requires registration
3217 * in order to make sure that only fully functional image objects get
3218 * registered.
3219 *
3220 * @note Locks this object for writing and @a aImage for reading.
3221 */
3222HRESULT VirtualBox::registerDVDImage (DVDImage *aImage,
3223 bool aSaveRegistry /*= true*/)
3224{
3225 AssertReturn(aImage != NULL, E_INVALIDARG);
3226
3227 AutoCaller autoCaller(this);
3228 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3229
3230 AutoWriteLock alock(this);
3231
3232 AutoCaller imageCaller (aImage);
3233 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3234
3235 AutoReadLock imageLock (aImage);
3236
3237 Utf8Str strConflict;
3238 HRESULT rc = checkMediaForConflicts2(aImage->id(),
3239 aImage->locationFull(),
3240 strConflict);
3241 CheckComRCReturnRC(rc);
3242
3243 if (strConflict.length())
3244 {
3245 return setError(VBOX_E_INVALID_OBJECT_STATE,
3246 tr("Cannot register the CD/DVD image '%ls' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3247 aImage->locationFull().raw(),
3248 aImage->id().raw(),
3249 strConflict.raw(),
3250 m_strSettingsFilePath.raw());
3251 }
3252
3253 /* add to the collection */
3254 mData.mDVDImages.push_back (aImage);
3255
3256 if (aSaveRegistry)
3257 {
3258 rc = saveSettings();
3259 if (FAILED (rc))
3260 unregisterDVDImage (aImage, false /* aSaveRegistry */);
3261 }
3262
3263 return rc;
3264}
3265
3266/**
3267 * Removes the given image from the CD/DVD image registry registry.
3268 *
3269 * @param aImage Image object to remove.
3270 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3271 *
3272 * When @a aSaveRegistry is @c true, this operation may fail because of the
3273 * failed #saveSettings() method it calls. In this case, the image object
3274 * will NOT be removed from the registry when this method returns. It is
3275 * therefore the responsibility of the caller to call this method as the first
3276 * step of some action that requires unregistration, before calling uninit() on
3277 * @a aImage.
3278 *
3279 * @note Locks this object for writing and @a aImage for reading.
3280 */
3281HRESULT VirtualBox::unregisterDVDImage (DVDImage *aImage,
3282 bool aSaveRegistry /*= true*/)
3283{
3284 AssertReturn(aImage != NULL, E_INVALIDARG);
3285
3286 AutoCaller autoCaller(this);
3287 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3288
3289 AutoWriteLock alock(this);
3290
3291 AutoCaller imageCaller (aImage);
3292 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3293
3294 AutoReadLock imageLock (aImage);
3295
3296 mData.mDVDImages.remove (aImage);
3297
3298 HRESULT rc = S_OK;
3299
3300 if (aSaveRegistry)
3301 {
3302 rc = saveSettings();
3303 if (FAILED (rc))
3304 registerDVDImage (aImage, false /* aSaveRegistry */);
3305 }
3306
3307 return rc;
3308}
3309
3310/**
3311 * Remembers the given image by storing it in the floppy image registry.
3312 *
3313 * @param aImage Image object to remember.
3314 * @param aSaveRegistry @c true to save the image registry to disk (default).
3315 *
3316 * When @a aSaveRegistry is @c true, this operation may fail because of the
3317 * failed #saveSettings() method it calls. In this case, the image object
3318 * will not be remembered. It is therefore the responsibility of the caller to
3319 * call this method as the last step of some action that requires registration
3320 * in order to make sure that only fully functional image objects get
3321 * registered.
3322 *
3323 * @note Locks this object for writing and @a aImage for reading.
3324 */
3325HRESULT VirtualBox::registerFloppyImage(FloppyImage *aImage,
3326 bool aSaveRegistry /*= true*/)
3327{
3328 AssertReturn(aImage != NULL, E_INVALIDARG);
3329
3330 AutoCaller autoCaller(this);
3331 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3332
3333 AutoWriteLock alock(this);
3334
3335 AutoCaller imageCaller (aImage);
3336 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3337
3338 AutoReadLock imageLock (aImage);
3339
3340 Utf8Str strConflict;
3341 HRESULT rc = checkMediaForConflicts2(aImage->id(),
3342 aImage->locationFull(),
3343 strConflict);
3344 CheckComRCReturnRC(rc);
3345
3346 if (strConflict.length())
3347 {
3348 return setError(VBOX_E_INVALID_OBJECT_STATE,
3349 tr("Cannot register the floppy image '%ls' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3350 aImage->locationFull().raw(),
3351 aImage->id().raw(),
3352 strConflict.raw(),
3353 m_strSettingsFilePath.raw());
3354 }
3355
3356 /* add to the collection */
3357 mData.mFloppyImages.push_back (aImage);
3358
3359 if (aSaveRegistry)
3360 {
3361 rc = saveSettings();
3362 if (FAILED (rc))
3363 unregisterFloppyImage(aImage, false /* aSaveRegistry */);
3364 }
3365
3366 return rc;
3367}
3368
3369/**
3370 * Removes the given image from the floppy image registry registry.
3371 *
3372 * @param aImage Image object to remove.
3373 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3374 *
3375 * When @a aSaveRegistry is @c true, this operation may fail because of the
3376 * failed #saveSettings() method it calls. In this case, the image object
3377 * will NOT be removed from the registry when this method returns. It is
3378 * therefore the responsibility of the caller to call this method as the first
3379 * step of some action that requires unregistration, before calling uninit() on
3380 * @a aImage.
3381 *
3382 * @note Locks this object for writing and @a aImage for reading.
3383 */
3384HRESULT VirtualBox::unregisterFloppyImage(FloppyImage *aImage,
3385 bool aSaveRegistry /*= true*/)
3386{
3387 AssertReturn(aImage != NULL, E_INVALIDARG);
3388
3389 AutoCaller autoCaller(this);
3390 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3391
3392 AutoWriteLock alock(this);
3393
3394 AutoCaller imageCaller (aImage);
3395 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3396
3397 AutoReadLock imageLock (aImage);
3398
3399 mData.mFloppyImages.remove (aImage);
3400
3401 HRESULT rc = S_OK;
3402
3403 if (aSaveRegistry)
3404 {
3405 rc = saveSettings();
3406 if (FAILED (rc))
3407 registerFloppyImage (aImage, false /* aSaveRegistry */);
3408 }
3409
3410 return rc;
3411}
3412
3413/**
3414 * Attempts to cast from a raw interface pointer to an underlying object.
3415 * On success, @a aTo will contain the object reference. On failure, @a aTo will
3416 * be set to @c null and an extended error info will be returned.
3417 *
3418 * @param aFrom Interface pointer to cast from.
3419 * @param aTo Where to store a reference to the underlying object.
3420 *
3421 * @note Locks #childrenLock() for reading.
3422 */
3423HRESULT VirtualBox::cast (IHardDisk *aFrom, ComObjPtr<HardDisk> &aTo)
3424{
3425 AssertReturn(aFrom != NULL, E_INVALIDARG);
3426
3427 AutoCaller autoCaller(this);
3428 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3429
3430 /* We need the children map lock here to keep the getDependentChild() result
3431 * valid until we finish */
3432 AutoReadLock chLock (childrenLock());
3433
3434 VirtualBoxBase *child = getDependentChild (aFrom);
3435 if (!child)
3436 return setError (E_FAIL, tr ("The given hard disk object is not created "
3437 "within this VirtualBox instance"));
3438
3439 /* we can safely cast child to HardDisk * here because only HardDisk
3440 * implementations of IHardDisk can be among our children */
3441
3442 aTo = static_cast<HardDisk*>(child);
3443
3444 return S_OK;
3445}
3446
3447/**
3448 * Helper to update the global settings file when the name of some machine
3449 * changes so that file and directory renaming occurs. This method ensures that
3450 * all affected paths in the disk registry are properly updated.
3451 *
3452 * @param aOldPath Old path (full).
3453 * @param aNewPath New path (full).
3454 *
3455 * @note Locks this object + DVD, Floppy and HardDisk children for writing.
3456 */
3457HRESULT VirtualBox::updateSettings (const char *aOldPath, const char *aNewPath)
3458{
3459 LogFlowThisFunc(("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
3460
3461 AssertReturn(aOldPath, E_INVALIDARG);
3462 AssertReturn(aNewPath, E_INVALIDARG);
3463
3464 AutoCaller autoCaller(this);
3465 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3466
3467 AutoWriteLock alock(this);
3468
3469 /* check DVD paths */
3470 for (DVDImageList::iterator it = mData.mDVDImages.begin();
3471 it != mData.mDVDImages.end();
3472 ++ it)
3473 {
3474 (*it)->updatePath (aOldPath, aNewPath);
3475 }
3476
3477 /* check Floppy paths */
3478 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3479 it != mData.mFloppyImages .end();
3480 ++ it)
3481 {
3482 (*it)->updatePath (aOldPath, aNewPath);
3483 }
3484
3485 /* check HardDisk paths */
3486 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
3487 it != mData.mHardDisks.end();
3488 ++ it)
3489 {
3490 (*it)->updatePaths (aOldPath, aNewPath);
3491 }
3492
3493 HRESULT rc = saveSettings();
3494
3495 return rc;
3496}
3497
3498/**
3499 * Creates the path to the specified file according to the path information
3500 * present in the file name.
3501 *
3502 * Note that the given file name must contain the full path otherwise the
3503 * extracted relative path will be created based on the current working
3504 * directory which is normally unknown.
3505 *
3506 * @param aFileName Full file name which path needs to be created.
3507 *
3508 * @return Extended error information on failure to create the path.
3509 */
3510/* static */
3511HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3512{
3513 Utf8Str strDir(strFileName);
3514 strDir.stripFilename();
3515 if (!RTDirExists(strDir.c_str()))
3516 {
3517 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3518 if (RT_FAILURE(vrc))
3519 return setError(E_FAIL,
3520 tr("Could not create the directory '%s' (%Rrc)"),
3521 strDir.c_str(),
3522 vrc);
3523 }
3524
3525 return S_OK;
3526}
3527
3528/**
3529 * Handles unexpected exceptions by turning them into COM errors in release
3530 * builds or by hitting a breakpoint in the release builds.
3531 *
3532 * Usage pattern:
3533 * @code
3534 try
3535 {
3536 // ...
3537 }
3538 catch (LaLalA)
3539 {
3540 // ...
3541 }
3542 catch (...)
3543 {
3544 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3545 }
3546 * @endcode
3547 *
3548 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3549 */
3550/* static */
3551HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3552{
3553 try
3554 {
3555 /* re-throw the current exception */
3556 throw;
3557 }
3558 catch (const std::exception &err)
3559 {
3560 return setError(E_FAIL, tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3561 err.what(), typeid(err).name(),
3562 pszFile, iLine, pszFunction);
3563 }
3564 catch (...)
3565 {
3566 return setError(E_FAIL, tr("Unknown exception\n%s[%d] (%s)"),
3567 pszFile, iLine, pszFunction);
3568 }
3569
3570 /* should not get here */
3571 AssertFailed();
3572 return E_FAIL;
3573}
3574
3575/**
3576 * Thread function that watches the termination of all client processes
3577 * that have opened sessions using IVirtualBox::OpenSession()
3578 */
3579// static
3580DECLCALLBACK(int) VirtualBox::ClientWatcher (RTTHREAD /* thread */, void *pvUser)
3581{
3582 LogFlowFuncEnter();
3583
3584 VirtualBox *that = (VirtualBox *) pvUser;
3585 Assert (that);
3586
3587 SessionMachineVector machines;
3588 MachineVector spawnedMachines;
3589
3590 size_t cnt = 0;
3591 size_t cntSpawned = 0;
3592
3593#if defined(RT_OS_WINDOWS)
3594
3595 HRESULT hrc = CoInitializeEx (NULL,
3596 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3597 COINIT_SPEED_OVER_MEMORY);
3598 AssertComRC (hrc);
3599
3600 /// @todo (dmik) processes reaping!
3601
3602 HANDLE handles [MAXIMUM_WAIT_OBJECTS];
3603 handles [0] = that->mWatcherData.mUpdateReq;
3604
3605 do
3606 {
3607 AutoCaller autoCaller(that);
3608 /* VirtualBox has been early uninitialized, terminate */
3609 if (!autoCaller.isOk())
3610 break;
3611
3612 do
3613 {
3614 /* release the caller to let uninit() ever proceed */
3615 autoCaller.release();
3616
3617 DWORD rc = ::WaitForMultipleObjects ((DWORD)(1 + cnt + cntSpawned),
3618 handles, FALSE, INFINITE);
3619
3620 /* Restore the caller before using VirtualBox. If it fails, this
3621 * means VirtualBox is being uninitialized and we must terminate. */
3622 autoCaller.add();
3623 if (!autoCaller.isOk())
3624 break;
3625
3626 bool update = false;
3627
3628 if (rc == WAIT_OBJECT_0)
3629 {
3630 /* update event is signaled */
3631 update = true;
3632 }
3633 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3634 {
3635 /* machine mutex is released */
3636 (machines [rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3637 update = true;
3638 }
3639 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3640 {
3641 /* machine mutex is abandoned due to client process termination */
3642 (machines [rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3643 update = true;
3644 }
3645 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3646 {
3647 /* spawned VM process has terminated (normally or abnormally) */
3648 (spawnedMachines [rc - WAIT_OBJECT_0 - cnt - 1])->
3649 checkForSpawnFailure();
3650 update = true;
3651 }
3652
3653 if (update)
3654 {
3655 /* close old process handles */
3656 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3657 CloseHandle (handles [i]);
3658
3659 AutoReadLock thatLock (that);
3660
3661 /* obtain a new set of opened machines */
3662 cnt = 0;
3663 machines.clear();
3664
3665 for (MachineList::iterator it = that->mData.mMachines.begin();
3666 it != that->mData.mMachines.end(); ++ it)
3667 {
3668 /// @todo handle situations with more than 64 objects
3669 AssertMsgBreak ((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3670 ("MAXIMUM_WAIT_OBJECTS reached"));
3671
3672 ComObjPtr<SessionMachine> sm;
3673 HANDLE ipcSem;
3674 if ((*it)->isSessionOpenOrClosing (sm, NULL, &ipcSem))
3675 {
3676 machines.push_back (sm);
3677 handles [1 + cnt] = ipcSem;
3678 ++ cnt;
3679 }
3680 }
3681
3682 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
3683
3684 /* obtain a new set of spawned machines */
3685 cntSpawned = 0;
3686 spawnedMachines.clear();
3687
3688 for (MachineList::iterator it = that->mData.mMachines.begin();
3689 it != that->mData.mMachines.end(); ++ it)
3690 {
3691 /// @todo handle situations with more than 64 objects
3692 AssertMsgBreak ((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
3693 ("MAXIMUM_WAIT_OBJECTS reached"));
3694
3695 RTPROCESS pid;
3696 if ((*it)->isSessionSpawning (&pid))
3697 {
3698 HANDLE ph = OpenProcess (SYNCHRONIZE, FALSE, pid);
3699 AssertMsg (ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
3700 pid, GetLastError()));
3701 if (rc == 0)
3702 {
3703 spawnedMachines.push_back (*it);
3704 handles [1 + cnt + cntSpawned] = ph;
3705 ++ cntSpawned;
3706 }
3707 }
3708 }
3709
3710 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
3711 }
3712 }
3713 while (true);
3714 }
3715 while (0);
3716
3717 /* close old process handles */
3718 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3719 CloseHandle (handles [i]);
3720
3721 /* release sets of machines if any */
3722 machines.clear();
3723 spawnedMachines.clear();
3724
3725 ::CoUninitialize();
3726
3727#elif defined (RT_OS_OS2)
3728
3729 /// @todo (dmik) processes reaping!
3730
3731 /* according to PMREF, 64 is the maximum for the muxwait list */
3732 SEMRECORD handles [64];
3733
3734 HMUX muxSem = NULLHANDLE;
3735
3736 do
3737 {
3738 AutoCaller autoCaller(that);
3739 /* VirtualBox has been early uninitialized, terminate */
3740 if (!autoCaller.isOk())
3741 break;
3742
3743 do
3744 {
3745 /* release the caller to let uninit() ever proceed */
3746 autoCaller.release();
3747
3748 int vrc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
3749
3750 /* Restore the caller before using VirtualBox. If it fails, this
3751 * means VirtualBox is being uninitialized and we must terminate. */
3752 autoCaller.add();
3753 if (!autoCaller.isOk())
3754 break;
3755
3756 bool update = false;
3757 bool updateSpawned = false;
3758
3759 if (RT_SUCCESS(vrc))
3760 {
3761 /* update event is signaled */
3762 update = true;
3763 updateSpawned = true;
3764 }
3765 else
3766 {
3767 AssertMsg (vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
3768 ("RTSemEventWait returned %Rrc\n", vrc));
3769
3770 /* are there any mutexes? */
3771 if (cnt > 0)
3772 {
3773 /* figure out what's going on with machines */
3774
3775 unsigned long semId = 0;
3776 APIRET arc = ::DosWaitMuxWaitSem (muxSem,
3777 SEM_IMMEDIATE_RETURN, &semId);
3778
3779 if (arc == NO_ERROR)
3780 {
3781 /* machine mutex is normally released */
3782 Assert (semId >= 0 && semId < cnt);
3783 if (semId >= 0 && semId < cnt)
3784 {
3785#ifdef DEBUG
3786 {
3787 AutoReadLock machineLock (machines [semId]);
3788 LogFlowFunc (("released mutex: machine='%ls'\n",
3789 machines [semId]->name().raw()));
3790 }
3791#endif
3792 machines [semId]->checkForDeath();
3793 }
3794 update = true;
3795 }
3796 else if (arc == ERROR_SEM_OWNER_DIED)
3797 {
3798 /* machine mutex is abandoned due to client process
3799 * termination; find which mutex is in the Owner Died
3800 * state */
3801 for (size_t i = 0; i < cnt; ++ i)
3802 {
3803 PID pid; TID tid;
3804 unsigned long reqCnt;
3805 arc = DosQueryMutexSem ((HMTX) handles [i].hsemCur, &pid,
3806 &tid, &reqCnt);
3807 if (arc == ERROR_SEM_OWNER_DIED)
3808 {
3809 /* close the dead mutex as asked by PMREF */
3810 ::DosCloseMutexSem ((HMTX) handles [i].hsemCur);
3811
3812 Assert (i >= 0 && i < cnt);
3813 if (i >= 0 && i < cnt)
3814 {
3815#ifdef DEBUG
3816 {
3817 AutoReadLock machineLock (machines [semId]);
3818 LogFlowFunc (("mutex owner dead: machine='%ls'\n",
3819 machines [i]->name().raw()));
3820 }
3821#endif
3822 machines [i]->checkForDeath();
3823 }
3824 }
3825 }
3826 update = true;
3827 }
3828 else
3829 AssertMsg (arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
3830 ("DosWaitMuxWaitSem returned %d\n", arc));
3831 }
3832
3833 /* are there any spawning sessions? */
3834 if (cntSpawned > 0)
3835 {
3836 for (size_t i = 0; i < cntSpawned; ++ i)
3837 updateSpawned |= (spawnedMachines [i])->
3838 checkForSpawnFailure();
3839 }
3840 }
3841
3842 if (update || updateSpawned)
3843 {
3844 AutoReadLock thatLock (that);
3845
3846 if (update)
3847 {
3848 /* close the old muxsem */
3849 if (muxSem != NULLHANDLE)
3850 ::DosCloseMuxWaitSem (muxSem);
3851
3852 /* obtain a new set of opened machines */
3853 cnt = 0;
3854 machines.clear();
3855
3856 for (MachineList::iterator it = that->mData.mMachines.begin();
3857 it != that->mData.mMachines.end(); ++ it)
3858 {
3859 /// @todo handle situations with more than 64 objects
3860 AssertMsg (cnt <= 64 /* according to PMREF */,
3861 ("maximum of 64 mutex semaphores reached (%d)",
3862 cnt));
3863
3864 ComObjPtr<SessionMachine> sm;
3865 HMTX ipcSem;
3866 if ((*it)->isSessionOpenOrClosing (sm, NULL, &ipcSem))
3867 {
3868 machines.push_back (sm);
3869 handles [cnt].hsemCur = (HSEM) ipcSem;
3870 handles [cnt].ulUser = cnt;
3871 ++ cnt;
3872 }
3873 }
3874
3875 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
3876
3877 if (cnt > 0)
3878 {
3879 /* create a new muxsem */
3880 APIRET arc = ::DosCreateMuxWaitSem (NULL, &muxSem, cnt,
3881 handles,
3882 DCMW_WAIT_ANY);
3883 AssertMsg (arc == NO_ERROR,
3884 ("DosCreateMuxWaitSem returned %d\n", arc));
3885 NOREF(arc);
3886 }
3887 }
3888
3889 if (updateSpawned)
3890 {
3891 /* obtain a new set of spawned machines */
3892 spawnedMachines.clear();
3893
3894 for (MachineList::iterator it = that->mData.mMachines.begin();
3895 it != that->mData.mMachines.end(); ++ it)
3896 {
3897 if ((*it)->isSessionSpawning())
3898 spawnedMachines.push_back (*it);
3899 }
3900
3901 cntSpawned = spawnedMachines.size();
3902 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
3903 }
3904 }
3905 }
3906 while (true);
3907 }
3908 while (0);
3909
3910 /* close the muxsem */
3911 if (muxSem != NULLHANDLE)
3912 ::DosCloseMuxWaitSem (muxSem);
3913
3914 /* release sets of machines if any */
3915 machines.clear();
3916 spawnedMachines.clear();
3917
3918#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
3919
3920 bool update = false;
3921 bool updateSpawned = false;
3922
3923 do
3924 {
3925 AutoCaller autoCaller(that);
3926 if (!autoCaller.isOk())
3927 break;
3928
3929 do
3930 {
3931 /* release the caller to let uninit() ever proceed */
3932 autoCaller.release();
3933
3934 int rc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
3935
3936 /*
3937 * Restore the caller before using VirtualBox. If it fails, this
3938 * means VirtualBox is being uninitialized and we must terminate.
3939 */
3940 autoCaller.add();
3941 if (!autoCaller.isOk())
3942 break;
3943
3944 if (RT_SUCCESS(rc) || update || updateSpawned)
3945 {
3946 /* RT_SUCCESS(rc) means an update event is signaled */
3947
3948 AutoReadLock thatLock (that);
3949
3950 if (RT_SUCCESS(rc) || update)
3951 {
3952 /* obtain a new set of opened machines */
3953 machines.clear();
3954
3955 for (MachineList::iterator it = that->mData.mMachines.begin();
3956 it != that->mData.mMachines.end(); ++ it)
3957 {
3958 ComObjPtr<SessionMachine> sm;
3959 if ((*it)->isSessionOpenOrClosing (sm))
3960 machines.push_back (sm);
3961 }
3962
3963 cnt = machines.size();
3964 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
3965 }
3966
3967 if (RT_SUCCESS(rc) || updateSpawned)
3968 {
3969 /* obtain a new set of spawned machines */
3970 spawnedMachines.clear();
3971
3972 for (MachineList::iterator it = that->mData.mMachines.begin();
3973 it != that->mData.mMachines.end(); ++ it)
3974 {
3975 if ((*it)->isSessionSpawning())
3976 spawnedMachines.push_back (*it);
3977 }
3978
3979 cntSpawned = spawnedMachines.size();
3980 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
3981 }
3982 }
3983
3984 update = false;
3985 for (size_t i = 0; i < cnt; ++ i)
3986 update |= (machines [i])->checkForDeath();
3987
3988 updateSpawned = false;
3989 for (size_t i = 0; i < cntSpawned; ++ i)
3990 updateSpawned |= (spawnedMachines [i])->checkForSpawnFailure();
3991
3992 /* reap child processes */
3993 {
3994 AutoWriteLock alock(that);
3995 if (that->mWatcherData.mProcesses.size())
3996 {
3997 LogFlowFunc (("UPDATE: child process count = %d\n",
3998 that->mWatcherData.mProcesses.size()));
3999 ClientWatcherData::ProcessList::iterator it =
4000 that->mWatcherData.mProcesses.begin();
4001 while (it != that->mWatcherData.mProcesses.end())
4002 {
4003 RTPROCESS pid = *it;
4004 RTPROCSTATUS status;
4005 int vrc = ::RTProcWait (pid, RTPROCWAIT_FLAGS_NOBLOCK,
4006 &status);
4007 if (vrc == VINF_SUCCESS)
4008 {
4009 LogFlowFunc (("pid %d (%x) was reaped, "
4010 "status=%d, reason=%d\n",
4011 pid, pid, status.iStatus,
4012 status.enmReason));
4013 it = that->mWatcherData.mProcesses.erase (it);
4014 }
4015 else
4016 {
4017 LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4018 pid, pid, vrc));
4019 if (vrc != VERR_PROCESS_RUNNING)
4020 {
4021 /* remove the process if it is not already running */
4022 it = that->mWatcherData.mProcesses.erase (it);
4023 }
4024 else
4025 ++ it;
4026 }
4027 }
4028 }
4029 }
4030 }
4031 while (true);
4032 }
4033 while (0);
4034
4035 /* release sets of machines if any */
4036 machines.clear();
4037 spawnedMachines.clear();
4038
4039#else
4040# error "Port me!"
4041#endif
4042
4043 LogFlowFuncLeave();
4044 return 0;
4045}
4046
4047/**
4048 * Thread function that handles custom events posted using #postEvent().
4049 */
4050// static
4051DECLCALLBACK(int) VirtualBox::AsyncEventHandler (RTTHREAD thread, void *pvUser)
4052{
4053 LogFlowFuncEnter();
4054
4055 AssertReturn(pvUser, VERR_INVALID_POINTER);
4056
4057 // create an event queue for the current thread
4058 EventQueue *eventQ = new EventQueue();
4059 AssertReturn(eventQ, VERR_NO_MEMORY);
4060
4061 // return the queue to the one who created this thread
4062 *(static_cast <EventQueue **> (pvUser)) = eventQ;
4063 // signal that we're ready
4064 RTThreadUserSignal (thread);
4065
4066 BOOL ok = TRUE;
4067 Event *event = NULL;
4068
4069 while ((ok = eventQ->waitForEvent (&event)) && event)
4070 eventQ->handleEvent (event);
4071
4072 AssertReturn(ok, VERR_GENERAL_FAILURE);
4073
4074 delete eventQ;
4075
4076 LogFlowFuncLeave();
4077
4078 return 0;
4079}
4080
4081////////////////////////////////////////////////////////////////////////////////
4082
4083/**
4084 * Takes the current list of registered callbacks of the managed VirtualBox
4085 * instance, and calls #handleCallback() for every callback item from the
4086 * list, passing the item as an argument.
4087 *
4088 * @note Locks the managed VirtualBox object for reading but leaves the lock
4089 * before iterating over callbacks and calling their methods.
4090 */
4091void *VirtualBox::CallbackEvent::handler()
4092{
4093 if (mVirtualBox.isNull())
4094 return NULL;
4095
4096 AutoCaller autoCaller(mVirtualBox);
4097 if (!autoCaller.isOk())
4098 {
4099 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
4100 "the callback event is discarded!\n",
4101 autoCaller.state()));
4102 /* We don't need mVirtualBox any more, so release it */
4103 mVirtualBox.setNull();
4104 return NULL;
4105 }
4106
4107 CallbackVector callbacks;
4108 {
4109 /* Make a copy to release the lock before iterating */
4110 AutoReadLock alock(mVirtualBox);
4111 callbacks = CallbackVector (mVirtualBox->mData.mCallbacks.begin(),
4112 mVirtualBox->mData.mCallbacks.end());
4113 /* We don't need mVirtualBox any more, so release it */
4114 mVirtualBox.setNull();
4115 }
4116
4117 for (VirtualBox::CallbackVector::const_iterator it = callbacks.begin();
4118 it != callbacks.end(); ++ it)
4119 handleCallback (*it);
4120
4121 return NULL;
4122}
4123
4124//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface (/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4125//{
4126// return E_NOTIMPL;
4127//}
4128
4129STDMETHODIMP VirtualBox::CreateDHCPServer (IN_BSTR aName, IDHCPServer ** aServer)
4130{
4131 CheckComArgNotNull(aName);
4132 CheckComArgNotNull(aServer);
4133
4134 AutoCaller autoCaller(this);
4135 CheckComRCReturnRC(autoCaller.rc());
4136
4137 ComObjPtr<DHCPServer> dhcpServer;
4138 dhcpServer.createObject();
4139 HRESULT rc = dhcpServer->init (this, aName);
4140 CheckComRCReturnRC(rc);
4141
4142 rc = registerDHCPServer(dhcpServer, true);
4143 CheckComRCReturnRC(rc);
4144
4145 dhcpServer.queryInterfaceTo(aServer);
4146
4147 return rc;
4148}
4149
4150//STDMETHODIMP VirtualBox::FindDHCPServerForInterface (IHostNetworkInterface * aIinterface, IDHCPServer ** aServer)
4151//{
4152// return E_NOTIMPL;
4153//}
4154
4155STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName (IN_BSTR aName, IDHCPServer ** aServer)
4156{
4157 CheckComArgNotNull(aName);
4158 CheckComArgNotNull(aServer);
4159
4160 AutoCaller autoCaller(this);
4161 CheckComRCReturnRC(autoCaller.rc());
4162
4163 AutoWriteLock alock(this);
4164
4165 HRESULT rc;
4166 Bstr bstr;
4167 ComPtr<DHCPServer> found;
4168
4169 for (DHCPServerList::const_iterator it =
4170 mData.mDHCPServers.begin();
4171 it != mData.mDHCPServers.end();
4172 ++ it)
4173 {
4174 rc = (*it)->COMGETTER(NetworkName) (bstr.asOutParam());
4175 CheckComRCThrowRC(rc);
4176
4177 if(bstr == aName)
4178 {
4179 found = *it;
4180 break;
4181 }
4182 }
4183
4184 if (!found)
4185 return E_INVALIDARG;
4186
4187 return found.queryInterfaceTo(aServer);
4188}
4189
4190STDMETHODIMP VirtualBox::RemoveDHCPServer (IDHCPServer * aServer)
4191{
4192 CheckComArgNotNull(aServer);
4193
4194 AutoCaller autoCaller(this);
4195 CheckComRCReturnRC(autoCaller.rc());
4196
4197 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4198
4199 return rc;
4200}
4201
4202/**
4203 * Remembers the given dhcp server by storing it in the hard disk registry.
4204 *
4205 * @param aDHCPServer Dhcp Server object to remember.
4206 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4207 *
4208 * When @a aSaveRegistry is @c true, this operation may fail because of the
4209 * failed #saveSettings() method it calls. In this case, the dhcp server object
4210 * will not be remembered. It is therefore the responsibility of the caller to
4211 * call this method as the last step of some action that requires registration
4212 * in order to make sure that only fully functional dhcp server objects get
4213 * registered.
4214 *
4215 * @note Locks this object for writing and @a aDHCPServer for reading.
4216 */
4217HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4218 bool aSaveRegistry /*= true*/)
4219{
4220 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4221
4222 AutoCaller autoCaller(this);
4223 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4224
4225 AutoWriteLock alock(this);
4226
4227 AutoCaller dhcpServerCaller (aDHCPServer);
4228 AssertComRCReturn (dhcpServerCaller.rc(), dhcpServerCaller.rc());
4229
4230 AutoReadLock dhcpServerLock (aDHCPServer);
4231
4232 Bstr name;
4233 HRESULT rc;
4234 rc = aDHCPServer->COMGETTER(NetworkName) (name.asOutParam());
4235 CheckComRCReturnRC(rc);
4236
4237 ComPtr<IDHCPServer> existing;
4238 rc = FindDHCPServerByNetworkName(name.mutableRaw(), existing.asOutParam());
4239 if(SUCCEEDED(rc))
4240 {
4241 return E_INVALIDARG;
4242 }
4243 rc = S_OK;
4244
4245 mData.mDHCPServers.push_back (aDHCPServer);
4246
4247 if (aSaveRegistry)
4248 {
4249 rc = saveSettings();
4250 if (FAILED (rc))
4251 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4252 }
4253
4254 return rc;
4255}
4256
4257/**
4258 * Removes the given hard disk from the hard disk registry.
4259 *
4260 * @param aHardDisk Hard disk object to remove.
4261 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4262 *
4263 * When @a aSaveRegistry is @c true, this operation may fail because of the
4264 * failed #saveSettings() method it calls. In this case, the hard disk object
4265 * will NOT be removed from the registry when this method returns. It is
4266 * therefore the responsibility of the caller to call this method as the first
4267 * step of some action that requires unregistration, before calling uninit() on
4268 * @a aHardDisk.
4269 *
4270 * @note Locks this object for writing and @a aHardDisk for reading.
4271 */
4272HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4273 bool aSaveRegistry /*= true*/)
4274{
4275 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4276
4277 AutoCaller autoCaller(this);
4278 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4279
4280 AutoWriteLock alock(this);
4281
4282 AutoCaller dhcpServerCaller (aDHCPServer);
4283 AssertComRCReturn (dhcpServerCaller.rc(), dhcpServerCaller.rc());
4284
4285 AutoReadLock dhcpServerLock (aDHCPServer);
4286
4287 mData.mDHCPServers.remove (aDHCPServer);
4288
4289 HRESULT rc = S_OK;
4290
4291 if (aSaveRegistry)
4292 {
4293 rc = saveSettings();
4294 if (FAILED (rc))
4295 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4296 }
4297
4298 return rc;
4299}
4300
4301/* 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