VirtualBox

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

Last change on this file since 23232 was 23223, checked in by vboxsync, 16 years ago

API: big medium handling change and lots of assorted other cleanups and fixes

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette