VirtualBox

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

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

iprt/xml_cpp.h -> iprt/cpp/xml.h

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

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