VirtualBox

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

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

Main: cosmetics

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