VirtualBox

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

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

Main: more header cleanup

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