VirtualBox

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

最後變更 在這個檔案從34587是 34393,由 vboxsync 提交於 14 年 前

Main: better type checks when firing events

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

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