VirtualBox

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

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

Main: make the path compare OS indepentend

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