VirtualBox

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

Last change on this file since 35263 was 35139, checked in by vboxsync, 13 years ago

Main: use VirtualBox::saveRegistries() instead of plain VirtualBox::saveSettings() on media property changes to make sure the correct registry gets saved

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 146.1 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 35139 2010-12-15 15:13:43Z 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.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/asm.h>
20#include <iprt/buildconfig.h>
21#include <iprt/cpp/utils.h>
22#include <iprt/dir.h>
23#include <iprt/env.h>
24#include <iprt/file.h>
25#include <iprt/path.h>
26#include <iprt/process.h>
27#include <iprt/string.h>
28#include <iprt/stream.h>
29#include <iprt/thread.h>
30#include <iprt/uuid.h>
31#include <iprt/cpp/xml.h>
32
33#include <VBox/com/com.h>
34#include <VBox/com/array.h>
35#include "VBox/com/EventQueue.h"
36
37#include <VBox/err.h>
38#include <VBox/param.h>
39#include <VBox/settings.h>
40#include <VBox/version.h>
41
42#include <package-generated.h>
43
44#include <algorithm>
45#include <set>
46#include <vector>
47#include <memory> // for auto_ptr
48
49#include <typeinfo>
50
51#include "VirtualBoxImpl.h"
52
53#include "Global.h"
54#include "MachineImpl.h"
55#include "MediumImpl.h"
56#include "SharedFolderImpl.h"
57#include "ProgressImpl.h"
58#include "ProgressProxyImpl.h"
59#include "HostImpl.h"
60#include "USBControllerImpl.h"
61#include "SystemPropertiesImpl.h"
62#include "GuestOSTypeImpl.h"
63#include "DHCPServerRunner.h"
64#include "DHCPServerImpl.h"
65#ifdef VBOX_WITH_RESOURCE_USAGE_API
66# include "PerformanceImpl.h"
67#endif /* VBOX_WITH_RESOURCE_USAGE_API */
68#include "EventImpl.h"
69#include "VBoxEvents.h"
70#ifdef VBOX_WITH_EXTPACK
71# include "ExtPackManagerImpl.h"
72#endif
73
74#include "AutoCaller.h"
75#include "Logging.h"
76#include "objectslist.h"
77
78#ifdef RT_OS_WINDOWS
79# include "win/svchlp.h"
80# include "win/VBoxComEvents.h"
81#endif
82
83////////////////////////////////////////////////////////////////////////////////
84//
85// Definitions
86//
87////////////////////////////////////////////////////////////////////////////////
88
89#define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
90
91////////////////////////////////////////////////////////////////////////////////
92//
93// Global variables
94//
95////////////////////////////////////////////////////////////////////////////////
96
97// static
98Bstr VirtualBox::sVersion;
99
100// static
101ULONG VirtualBox::sRevision;
102
103// static
104Bstr VirtualBox::sPackageType;
105
106////////////////////////////////////////////////////////////////////////////////
107//
108// VirtualBoxCallbackRegistration
109//
110////////////////////////////////////////////////////////////////////////////////
111
112/**
113 * Registered IVirtualBoxCallback, used by VirtualBox::CallbackList and
114 * VirtualBox::Data::llCallbacks.
115 *
116 * In addition to keeping the interface pointer this also keeps track of the
117 * methods that asked to not be called again. The latter is for reducing
118 * unnecessary IPC.
119 */
120class VirtualBoxCallbackRegistration
121{
122public:
123 /** Callback bit indexes (for bmDisabled). */
124 typedef enum
125 {
126 kOnMachineStateChanged = 0,
127 kOnMachineDataChanged,
128 kOnExtraDataCanChange,
129 kOnExtraDataChanged,
130 kOnMediumRegistered,
131 kOnMachineRegistered,
132 kOnSessionStateChanged,
133 kOnSnapshotTaken,
134 kOnSnapshotDeleted,
135 kOnSnapshotChanged,
136 kOnGuestPropertyChanged
137 } CallbackBit;
138
139 VirtualBoxCallbackRegistration()
140 {
141 /* nothing */
142 }
143
144 ~VirtualBoxCallbackRegistration()
145 {
146 /* nothing */
147 }
148};
149
150////////////////////////////////////////////////////////////////////////////////
151//
152// CallbackEvent class
153//
154////////////////////////////////////////////////////////////////////////////////
155
156/**
157 * Abstract callback event class to asynchronously call VirtualBox callbacks
158 * on a dedicated event thread. Subclasses reimplement #handleCallback()
159 * to call appropriate IVirtualBoxCallback methods depending on the event
160 * to be dispatched.
161 *
162 * @note The VirtualBox instance passed to the constructor is strongly
163 * referenced, so that the VirtualBox singleton won't be released until the
164 * event gets handled by the event thread.
165 */
166class VirtualBox::CallbackEvent : public Event
167{
168public:
169
170 CallbackEvent(VirtualBox *aVirtualBox, VirtualBoxCallbackRegistration::CallbackBit aWhat)
171 : mVirtualBox(aVirtualBox), mWhat(aWhat)
172 {
173 Assert(aVirtualBox);
174 }
175
176 void *handler();
177
178 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) = 0;
179
180private:
181
182 /**
183 * Note that this is a weak ref -- the CallbackEvent handler thread
184 * is bound to the lifetime of the VirtualBox instance, so it's safe.
185 */
186 VirtualBox *mVirtualBox;
187protected:
188 VirtualBoxCallbackRegistration::CallbackBit mWhat;
189};
190
191////////////////////////////////////////////////////////////////////////////////
192//
193// VirtualBox private member data definition
194//
195////////////////////////////////////////////////////////////////////////////////
196
197#if defined(RT_OS_WINDOWS)
198 #define UPDATEREQARG NULL
199 #define UPDATEREQTYPE HANDLE
200#elif defined(RT_OS_OS2)
201 #define UPDATEREQARG NIL_RTSEMEVENT
202 #define UPDATEREQTYPE RTSEMEVENT
203#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
204 #define UPDATEREQARG
205 #define UPDATEREQTYPE RTSEMEVENT
206#else
207# error "Port me!"
208#endif
209
210typedef ObjectsList<Machine> MachinesOList;
211typedef ObjectsList<Medium> MediaOList;
212typedef ObjectsList<GuestOSType> GuestOSTypesOList;
213typedef ObjectsList<SharedFolder> SharedFoldersOList;
214typedef ObjectsList<DHCPServer> DHCPServersOList;
215
216typedef std::map<Guid, ComPtr<IProgress> > ProgressMap;
217typedef std::map<Guid, ComObjPtr<Medium> > HardDiskMap;
218
219/**
220 * Main VirtualBox data structure.
221 * @note |const| members are persistent during lifetime so can be accessed
222 * without locking.
223 */
224struct VirtualBox::Data
225{
226 Data()
227 : pMainConfigFile(NULL),
228 uuidMediaRegistry("48024e5c-fdd9-470f-93af-ec29f7ea518c"),
229 lockMachines(LOCKCLASS_LISTOFMACHINES),
230 allMachines(lockMachines),
231 lockGuestOSTypes(LOCKCLASS_LISTOFOTHEROBJECTS),
232 allGuestOSTypes(lockGuestOSTypes),
233 lockMedia(LOCKCLASS_LISTOFMEDIA),
234 allHardDisks(lockMedia),
235 allDVDImages(lockMedia),
236 allFloppyImages(lockMedia),
237 lockSharedFolders(LOCKCLASS_LISTOFOTHEROBJECTS),
238 allSharedFolders(lockSharedFolders),
239 lockDHCPServers(LOCKCLASS_LISTOFOTHEROBJECTS),
240 allDHCPServers(lockDHCPServers),
241 mtxProgressOperations(LOCKCLASS_PROGRESSLIST),
242 updateReq(UPDATEREQARG),
243 threadClientWatcher(NIL_RTTHREAD),
244 threadAsyncEvent(NIL_RTTHREAD),
245 pAsyncEventQ(NULL)
246 {
247 }
248
249 ~Data()
250 {
251 if (pMainConfigFile)
252 {
253 delete pMainConfigFile;
254 pMainConfigFile = NULL;
255 }
256 };
257
258 // const data members not requiring locking
259 const Utf8Str strHomeDir;
260
261 // VirtualBox main settings file
262 const Utf8Str strSettingsFilePath;
263 settings::MainConfigFile *pMainConfigFile;
264
265 // constant pseudo-machine ID for global media registry
266 const Guid uuidMediaRegistry;
267
268 // const objects not requiring locking
269 const ComObjPtr<Host> pHost;
270 const ComObjPtr<SystemProperties> pSystemProperties;
271#ifdef VBOX_WITH_RESOURCE_USAGE_API
272 const ComObjPtr<PerformanceCollector> pPerformanceCollector;
273#endif /* VBOX_WITH_RESOURCE_USAGE_API */
274
275 // Each of the following lists use a particular lock handle that protects the
276 // list as a whole. As opposed to version 3.1 and earlier, these lists no
277 // longer need the main VirtualBox object lock, but only the respective list
278 // lock. In each case, the locking order is defined that the list must be
279 // requested before object locks of members of the lists (see the order definitions
280 // in AutoLock.h; e.g. LOCKCLASS_LISTOFMACHINES before LOCKCLASS_MACHINEOBJECT).
281 RWLockHandle lockMachines;
282 MachinesOList allMachines;
283
284 RWLockHandle lockGuestOSTypes;
285 GuestOSTypesOList allGuestOSTypes;
286
287 // All the media lists are protected by the following locking handle:
288 RWLockHandle lockMedia;
289 MediaOList allHardDisks, // base images only!
290 allDVDImages,
291 allFloppyImages;
292 // the hard disks map is an additional map sorted by UUID for quick lookup
293 // and contains ALL hard disks (base and differencing); it is protected by
294 // the same lock as the other media lists above
295 HardDiskMap mapHardDisks;
296
297 // list of pending machine renames (also protected by media tree lock;
298 // see VirtualBox::rememberMachineNameChangeForMedia())
299 struct PendingMachineRename
300 {
301 Utf8Str strConfigDirOld;
302 Utf8Str strConfigDirNew;
303 };
304 typedef std::list<PendingMachineRename> PendingMachineRenamesList;
305 PendingMachineRenamesList llPendingMachineRenames;
306
307 RWLockHandle lockSharedFolders;
308 SharedFoldersOList allSharedFolders;
309
310 RWLockHandle lockDHCPServers;
311 DHCPServersOList allDHCPServers;
312
313 RWLockHandle mtxProgressOperations;
314 ProgressMap mapProgressOperations;
315
316 // the following are data for the client watcher thread
317 const UPDATEREQTYPE updateReq;
318 const RTTHREAD threadClientWatcher;
319 typedef std::list<RTPROCESS> ProcessList;
320 ProcessList llProcesses;
321
322 // the following are data for the async event thread
323 const RTTHREAD threadAsyncEvent;
324 EventQueue * const pAsyncEventQ;
325 const ComObjPtr<EventSource> pEventSource;
326
327#ifdef VBOX_WITH_EXTPACK
328 /** The extension pack manager object lives here. */
329 const ComObjPtr<ExtPackManager> ptrExtPackManager;
330#endif
331};
332
333// constructor / destructor
334/////////////////////////////////////////////////////////////////////////////
335
336VirtualBox::VirtualBox()
337{}
338
339VirtualBox::~VirtualBox()
340{}
341
342HRESULT VirtualBox::FinalConstruct()
343{
344 LogFlowThisFunc(("\n"));
345
346 return init();
347}
348
349void VirtualBox::FinalRelease()
350{
351 LogFlowThisFunc(("\n"));
352
353 uninit();
354}
355
356// public initializer/uninitializer for internal purposes only
357/////////////////////////////////////////////////////////////////////////////
358
359/**
360 * Initializes the VirtualBox object.
361 *
362 * @return COM result code
363 */
364HRESULT VirtualBox::init()
365{
366 /* Enclose the state transition NotReady->InInit->Ready */
367 AutoInitSpan autoInitSpan(this);
368 AssertReturn(autoInitSpan.isOk(), E_FAIL);
369
370 /* Locking this object for writing during init sounds a bit paradoxical,
371 * but in the current locking mess this avoids that some code gets a
372 * read lock and later calls code which wants the same write lock. */
373 AutoWriteLock lock(this COMMA_LOCKVAL_SRC_POS);
374
375 // allocate our instance data
376 m = new Data;
377
378 LogFlow(("===========================================================\n"));
379 LogFlowThisFuncEnter();
380
381 if (sVersion.isEmpty())
382 sVersion = VBOX_VERSION_STRING;
383 sRevision = RTBldCfgRevision();
384 if (sPackageType.isEmpty())
385 sPackageType = VBOX_PACKAGE_STRING;
386 LogFlowThisFunc(("Version: %ls, Package: %ls\n", sVersion.raw(), sPackageType.raw()));
387
388 /* Get the VirtualBox home directory. */
389 {
390 char szHomeDir[RTPATH_MAX];
391 int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir));
392 if (RT_FAILURE(vrc))
393 return setError(E_FAIL,
394 tr("Could not create the VirtualBox home directory '%s' (%Rrc)"),
395 szHomeDir, vrc);
396
397 unconst(m->strHomeDir) = szHomeDir;
398 }
399
400 /* compose the VirtualBox.xml file name */
401 unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s",
402 m->strHomeDir.c_str(),
403 RTPATH_DELIMITER,
404 VBOX_GLOBAL_SETTINGS_FILE);
405 HRESULT rc = S_OK;
406 bool fCreate = false;
407 try
408 {
409 // load and parse VirtualBox.xml; this will throw on XML or logic errors
410 try
411 {
412 m->pMainConfigFile = new settings::MainConfigFile(&m->strSettingsFilePath);
413 }
414 catch (xml::EIPRTFailure &e)
415 {
416 // this is thrown by the XML backend if the RTOpen() call fails;
417 // only if the main settings file does not exist, create it,
418 // if there's something more serious, then do fail!
419 if (e.rc() == VERR_FILE_NOT_FOUND)
420 fCreate = true;
421 else
422 throw;
423 }
424
425 if (fCreate)
426 m->pMainConfigFile = new settings::MainConfigFile(NULL);
427
428#ifdef VBOX_WITH_RESOURCE_USAGE_API
429 /* create the performance collector object BEFORE host */
430 unconst(m->pPerformanceCollector).createObject();
431 rc = m->pPerformanceCollector->init();
432 ComAssertComRCThrowRC(rc);
433#endif /* VBOX_WITH_RESOURCE_USAGE_API */
434
435 /* create the host object early, machines will need it */
436 unconst(m->pHost).createObject();
437 rc = m->pHost->init(this);
438 ComAssertComRCThrowRC(rc);
439
440 rc = m->pHost->loadSettings(m->pMainConfigFile->host);
441 if (FAILED(rc)) throw rc;
442
443 /* create the system properties object, someone may need it too */
444 unconst(m->pSystemProperties).createObject();
445 rc = m->pSystemProperties->init(this);
446 ComAssertComRCThrowRC(rc);
447
448 rc = m->pSystemProperties->loadSettings(m->pMainConfigFile->systemProperties);
449 if (FAILED(rc)) throw rc;
450
451 /* guest OS type objects, needed by machines */
452 for (size_t i = 0; i < RT_ELEMENTS(Global::sOSTypes); ++i)
453 {
454 ComObjPtr<GuestOSType> guestOSTypeObj;
455 rc = guestOSTypeObj.createObject();
456 if (SUCCEEDED(rc))
457 {
458 rc = guestOSTypeObj->init(Global::sOSTypes[i]);
459 if (SUCCEEDED(rc))
460 m->allGuestOSTypes.addChild(guestOSTypeObj);
461 }
462 ComAssertComRCThrowRC(rc);
463 }
464
465 /* all registered media, needed by machines */
466 if (FAILED(rc = initMedia(m->uuidMediaRegistry,
467 m->pMainConfigFile->mediaRegistry,
468 Utf8Str::Empty))) // const Utf8Str &machineFolder
469 throw rc;
470
471 /* machines */
472 if (FAILED(rc = initMachines()))
473 throw rc;
474
475
476#ifdef DEBUG
477 LogFlowThisFunc(("Dumping media backreferences\n"));
478 dumpAllBackRefs();
479#endif
480
481 /* net services */
482 for (settings::DHCPServersList::const_iterator it = m->pMainConfigFile->llDhcpServers.begin();
483 it != m->pMainConfigFile->llDhcpServers.end();
484 ++it)
485 {
486 const settings::DHCPServer &data = *it;
487
488 ComObjPtr<DHCPServer> pDhcpServer;
489 if (SUCCEEDED(rc = pDhcpServer.createObject()))
490 rc = pDhcpServer->init(this, data);
491 if (FAILED(rc)) throw rc;
492
493 rc = registerDHCPServer(pDhcpServer, false /* aSaveRegistry */);
494 if (FAILED(rc)) throw rc;
495 }
496
497 /* events */
498 if (SUCCEEDED(rc = unconst(m->pEventSource).createObject()))
499 rc = m->pEventSource->init(static_cast<IVirtualBox*>(this));
500 if (FAILED(rc)) throw rc;
501
502#ifdef VBOX_WITH_EXTPACK
503 /* extension manager */
504 rc = unconst(m->ptrExtPackManager).createObject();
505 if (SUCCEEDED(rc))
506 rc = m->ptrExtPackManager->initExtPackManager(this, VBOXEXTPACKCTX_PER_USER_DAEMON);
507 if (FAILED(rc))
508 throw rc;
509#endif
510 }
511 catch (HRESULT err)
512 {
513 /* we assume that error info is set by the thrower */
514 rc = err;
515 }
516 catch (...)
517 {
518 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
519 }
520
521 if (SUCCEEDED(rc))
522 {
523 /* start the client watcher thread */
524#if defined(RT_OS_WINDOWS)
525 unconst(m->updateReq) = ::CreateEvent(NULL, FALSE, FALSE, NULL);
526#elif defined(RT_OS_OS2)
527 RTSemEventCreate(&unconst(m->updateReq));
528#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
529 RTSemEventCreate(&unconst(m->updateReq));
530#else
531# error "Port me!"
532#endif
533 int vrc = RTThreadCreate(&unconst(m->threadClientWatcher),
534 ClientWatcher,
535 (void *)this,
536 0,
537 RTTHREADTYPE_MAIN_WORKER,
538 RTTHREADFLAGS_WAITABLE,
539 "Watcher");
540 ComAssertRC(vrc);
541 if (RT_FAILURE(vrc))
542 rc = E_FAIL;
543 }
544
545 if (SUCCEEDED(rc))
546 {
547 try
548 {
549 /* start the async event handler thread */
550 int vrc = RTThreadCreate(&unconst(m->threadAsyncEvent),
551 AsyncEventHandler,
552 &unconst(m->pAsyncEventQ),
553 0,
554 RTTHREADTYPE_MAIN_WORKER,
555 RTTHREADFLAGS_WAITABLE,
556 "EventHandler");
557 ComAssertRCThrow(vrc, E_FAIL);
558
559 /* wait until the thread sets m->pAsyncEventQ */
560 RTThreadUserWait(m->threadAsyncEvent, RT_INDEFINITE_WAIT);
561 ComAssertThrow(m->pAsyncEventQ, E_FAIL);
562 }
563 catch (HRESULT aRC)
564 {
565 rc = aRC;
566 }
567 }
568
569 /* Confirm a successful initialization when it's the case */
570 if (SUCCEEDED(rc))
571 autoInitSpan.setSucceeded();
572
573#ifdef VBOX_WITH_EXTPACK
574 /* Let the extension packs have a go at things. */
575 if (SUCCEEDED(rc))
576 {
577 lock.release();
578 m->ptrExtPackManager->callAllVirtualBoxReadyHooks();
579 }
580#endif
581
582 LogFlowThisFunc(("rc=%08X\n", rc));
583 LogFlowThisFuncLeave();
584 LogFlow(("===========================================================\n"));
585 return rc;
586}
587
588HRESULT VirtualBox::initMachines()
589{
590 for (settings::MachinesRegistry::const_iterator it = m->pMainConfigFile->llMachines.begin();
591 it != m->pMainConfigFile->llMachines.end();
592 ++it)
593 {
594 HRESULT rc = S_OK;
595 const settings::MachineRegistryEntry &xmlMachine = *it;
596 Guid uuid = xmlMachine.uuid;
597
598 ComObjPtr<Machine> pMachine;
599 if (SUCCEEDED(rc = pMachine.createObject()))
600 {
601 rc = pMachine->init(this,
602 xmlMachine.strSettingsFile,
603 &uuid);
604 if (SUCCEEDED(rc))
605 rc = registerMachine(pMachine);
606 if (FAILED(rc))
607 return rc;
608 }
609 }
610
611 return S_OK;
612}
613
614/**
615 * Loads a media registry from XML and adds the media contained therein to
616 * the global lists of known media.
617 *
618 * This now (4.0) gets called from two locations:
619 *
620 * -- VirtualBox::init(), to load the global media registry from VirtualBox.xml;
621 *
622 * -- Machine::loadMachineDataFromSettings(), to load the per-machine registry
623 * from machine XML, for machines created with VirtualBox 4.0 or later.
624 *
625 * In both cases, the media found are added to the global lists so the
626 * global arrays of media (including the GUI's virtual media manager)
627 * continue to work as before.
628 *
629 * @param uuidMachineRegistry The UUID of the media registry. This is either the
630 * transient UUID created at VirtualBox startup for the global registry or
631 * a machine ID.
632 * @param mediaRegistry The XML settings structure to load, either from VirtualBox.xml
633 * or a machine XML.
634 * @return
635 */
636HRESULT VirtualBox::initMedia(const Guid &uuidRegistry,
637 const settings::MediaRegistry mediaRegistry,
638 const Utf8Str &strMachineFolder)
639{
640 LogFlow(("VirtualBox::initMedia ENTERING, uuidRegistry=%s, strMachineFolder=%s\n",
641 uuidRegistry.toString().c_str(),
642 strMachineFolder.c_str()));
643
644 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
645
646 HRESULT rc = S_OK;
647 settings::MediaList::const_iterator it;
648 for (it = mediaRegistry.llHardDisks.begin();
649 it != mediaRegistry.llHardDisks.end();
650 ++it)
651 {
652 const settings::Medium &xmlHD = *it;
653
654 ComObjPtr<Medium> pHardDisk;
655 if (SUCCEEDED(rc = pHardDisk.createObject()))
656 rc = pHardDisk->init(this,
657 NULL, // parent
658 DeviceType_HardDisk,
659 uuidRegistry,
660 xmlHD, // XML data; this recurses to processes the children
661 strMachineFolder);
662 if (FAILED(rc)) return rc;
663
664 rc = registerHardDisk(pHardDisk, NULL /* pllRegistriesThatNeedSaving */);
665 if (FAILED(rc)) return rc;
666 }
667
668 for (it = mediaRegistry.llDvdImages.begin();
669 it != mediaRegistry.llDvdImages.end();
670 ++it)
671 {
672 const settings::Medium &xmlDvd = *it;
673
674 ComObjPtr<Medium> pImage;
675 if (SUCCEEDED(pImage.createObject()))
676 rc = pImage->init(this,
677 NULL,
678 DeviceType_DVD,
679 uuidRegistry,
680 xmlDvd,
681 strMachineFolder);
682 if (FAILED(rc)) return rc;
683
684 rc = registerImage(pImage,
685 DeviceType_DVD,
686 NULL /* pllRegistriesThatNeedSaving */);
687 if (FAILED(rc)) return rc;
688 }
689
690 for (it = mediaRegistry.llFloppyImages.begin();
691 it != mediaRegistry.llFloppyImages.end();
692 ++it)
693 {
694 const settings::Medium &xmlFloppy = *it;
695
696 ComObjPtr<Medium> pImage;
697 if (SUCCEEDED(pImage.createObject()))
698 rc = pImage->init(this,
699 NULL,
700 DeviceType_Floppy,
701 uuidRegistry,
702 xmlFloppy,
703 strMachineFolder);
704 if (FAILED(rc)) return rc;
705
706 rc = registerImage(pImage,
707 DeviceType_Floppy,
708 NULL /* pllRegistriesThatNeedSaving */);
709 if (FAILED(rc)) return rc;
710 }
711
712 LogFlow(("VirtualBox::initMedia LEAVING\n"));
713
714 return S_OK;
715}
716
717void VirtualBox::uninit()
718{
719 /* Enclose the state transition Ready->InUninit->NotReady */
720 AutoUninitSpan autoUninitSpan(this);
721 if (autoUninitSpan.uninitDone())
722 return;
723
724 LogFlow(("===========================================================\n"));
725 LogFlowThisFuncEnter();
726 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
727
728 /* tell all our child objects we've been uninitialized */
729
730 LogFlowThisFunc(("Uninitializing machines (%d)...\n", m->allMachines.size()));
731 if (m->pHost)
732 {
733 /* It is necessary to hold the VirtualBox and Host locks here because
734 we may have to uninitialize SessionMachines. */
735 AutoMultiWriteLock2 multilock(this, m->pHost COMMA_LOCKVAL_SRC_POS);
736 m->allMachines.uninitAll();
737 }
738 else
739 m->allMachines.uninitAll();
740 m->allFloppyImages.uninitAll();
741 m->allDVDImages.uninitAll();
742 m->allHardDisks.uninitAll();
743 m->allDHCPServers.uninitAll();
744
745 m->mapProgressOperations.clear();
746
747 m->allGuestOSTypes.uninitAll();
748
749 /* Note that we release singleton children after we've all other children.
750 * In some cases this is important because these other children may use
751 * some resources of the singletons which would prevent them from
752 * uninitializing (as for example, mSystemProperties which owns
753 * MediumFormat objects which Medium objects refer to) */
754 if (m->pSystemProperties)
755 {
756 m->pSystemProperties->uninit();
757 unconst(m->pSystemProperties).setNull();
758 }
759
760 if (m->pHost)
761 {
762 m->pHost->uninit();
763 unconst(m->pHost).setNull();
764 }
765
766#ifdef VBOX_WITH_RESOURCE_USAGE_API
767 if (m->pPerformanceCollector)
768 {
769 m->pPerformanceCollector->uninit();
770 unconst(m->pPerformanceCollector).setNull();
771 }
772#endif /* VBOX_WITH_RESOURCE_USAGE_API */
773
774 LogFlowThisFunc(("Terminating the async event handler...\n"));
775 if (m->threadAsyncEvent != NIL_RTTHREAD)
776 {
777 /* signal to exit the event loop */
778 if (RT_SUCCESS(m->pAsyncEventQ->interruptEventQueueProcessing()))
779 {
780 /*
781 * Wait for thread termination (only after we've successfully
782 * interrupted the event queue processing!)
783 */
784 int vrc = RTThreadWait(m->threadAsyncEvent, 60000, NULL);
785 if (RT_FAILURE(vrc))
786 LogWarningFunc(("RTThreadWait(%RTthrd) -> %Rrc\n",
787 m->threadAsyncEvent, vrc));
788 }
789 else
790 {
791 AssertMsgFailed(("interruptEventQueueProcessing() failed\n"));
792 RTThreadWait(m->threadAsyncEvent, 0, NULL);
793 }
794
795 unconst(m->threadAsyncEvent) = NIL_RTTHREAD;
796 unconst(m->pAsyncEventQ) = NULL;
797 }
798
799 LogFlowThisFunc(("Releasing event source...\n"));
800 if (m->pEventSource)
801 {
802 // we don't perform uninit() as it's possible that some pending event refers to this source
803 unconst(m->pEventSource).setNull();
804 }
805
806 LogFlowThisFunc(("Terminating the client watcher...\n"));
807 if (m->threadClientWatcher != NIL_RTTHREAD)
808 {
809 /* signal the client watcher thread */
810 updateClientWatcher();
811 /* wait for the termination */
812 RTThreadWait(m->threadClientWatcher, RT_INDEFINITE_WAIT, NULL);
813 unconst(m->threadClientWatcher) = NIL_RTTHREAD;
814 }
815 m->llProcesses.clear();
816#if defined(RT_OS_WINDOWS)
817 if (m->updateReq != NULL)
818 {
819 ::CloseHandle(m->updateReq);
820 unconst(m->updateReq) = NULL;
821 }
822#elif defined(RT_OS_OS2)
823 if (m->updateReq != NIL_RTSEMEVENT)
824 {
825 RTSemEventDestroy(m->updateReq);
826 unconst(m->updateReq) = NIL_RTSEMEVENT;
827 }
828#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
829 if (m->updateReq != NIL_RTSEMEVENT)
830 {
831 RTSemEventDestroy(m->updateReq);
832 unconst(m->updateReq) = NIL_RTSEMEVENT;
833 }
834#else
835# error "Port me!"
836#endif
837
838 // clean up our instance data
839 delete m;
840
841 /* Unload hard disk plugin backends. */
842 VDShutdown();
843
844 LogFlowThisFuncLeave();
845 LogFlow(("===========================================================\n"));
846}
847
848// IVirtualBox properties
849/////////////////////////////////////////////////////////////////////////////
850
851STDMETHODIMP VirtualBox::COMGETTER(Version)(BSTR *aVersion)
852{
853 CheckComArgNotNull(aVersion);
854
855 AutoCaller autoCaller(this);
856 if (FAILED(autoCaller.rc())) return autoCaller.rc();
857
858 sVersion.cloneTo(aVersion);
859 return S_OK;
860}
861
862STDMETHODIMP VirtualBox::COMGETTER(Revision)(ULONG *aRevision)
863{
864 CheckComArgNotNull(aRevision);
865
866 AutoCaller autoCaller(this);
867 if (FAILED(autoCaller.rc())) return autoCaller.rc();
868
869 *aRevision = sRevision;
870 return S_OK;
871}
872
873STDMETHODIMP VirtualBox::COMGETTER(PackageType)(BSTR *aPackageType)
874{
875 CheckComArgNotNull(aPackageType);
876
877 AutoCaller autoCaller(this);
878 if (FAILED(autoCaller.rc())) return autoCaller.rc();
879
880 sPackageType.cloneTo(aPackageType);
881 return S_OK;
882}
883
884STDMETHODIMP VirtualBox::COMGETTER(HomeFolder)(BSTR *aHomeFolder)
885{
886 CheckComArgNotNull(aHomeFolder);
887
888 AutoCaller autoCaller(this);
889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
890
891 /* mHomeDir is const and doesn't need a lock */
892 m->strHomeDir.cloneTo(aHomeFolder);
893 return S_OK;
894}
895
896STDMETHODIMP VirtualBox::COMGETTER(SettingsFilePath)(BSTR *aSettingsFilePath)
897{
898 CheckComArgNotNull(aSettingsFilePath);
899
900 AutoCaller autoCaller(this);
901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
902
903 /* mCfgFile.mName is const and doesn't need a lock */
904 m->strSettingsFilePath.cloneTo(aSettingsFilePath);
905 return S_OK;
906}
907
908STDMETHODIMP VirtualBox::COMGETTER(Host)(IHost **aHost)
909{
910 CheckComArgOutSafeArrayPointerValid(aHost);
911
912 AutoCaller autoCaller(this);
913 if (FAILED(autoCaller.rc())) return autoCaller.rc();
914
915 /* mHost is const, no need to lock */
916 m->pHost.queryInterfaceTo(aHost);
917 return S_OK;
918}
919
920STDMETHODIMP
921VirtualBox::COMGETTER(SystemProperties)(ISystemProperties **aSystemProperties)
922{
923 CheckComArgOutSafeArrayPointerValid(aSystemProperties);
924
925 AutoCaller autoCaller(this);
926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
927
928 /* mSystemProperties is const, no need to lock */
929 m->pSystemProperties.queryInterfaceTo(aSystemProperties);
930 return S_OK;
931}
932
933STDMETHODIMP
934VirtualBox::COMGETTER(Machines)(ComSafeArrayOut(IMachine *, aMachines))
935{
936 if (ComSafeArrayOutIsNull(aMachines))
937 return E_POINTER;
938
939 AutoCaller autoCaller(this);
940 if (FAILED(autoCaller.rc())) return autoCaller.rc();
941
942 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
943 SafeIfaceArray<IMachine> machines(m->allMachines.getList());
944 machines.detachTo(ComSafeArrayOutArg(aMachines));
945
946 return S_OK;
947}
948
949STDMETHODIMP VirtualBox::COMGETTER(HardDisks)(ComSafeArrayOut(IMedium *, aHardDisks))
950{
951 if (ComSafeArrayOutIsNull(aHardDisks))
952 return E_POINTER;
953
954 AutoCaller autoCaller(this);
955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
956
957 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
958 SafeIfaceArray<IMedium> hardDisks(m->allHardDisks.getList());
959 hardDisks.detachTo(ComSafeArrayOutArg(aHardDisks));
960
961 return S_OK;
962}
963
964STDMETHODIMP VirtualBox::COMGETTER(DVDImages)(ComSafeArrayOut(IMedium *, aDVDImages))
965{
966 if (ComSafeArrayOutIsNull(aDVDImages))
967 return E_POINTER;
968
969 AutoCaller autoCaller(this);
970 if (FAILED(autoCaller.rc())) return autoCaller.rc();
971
972 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
973 SafeIfaceArray<IMedium> images(m->allDVDImages.getList());
974 images.detachTo(ComSafeArrayOutArg(aDVDImages));
975
976 return S_OK;
977}
978
979STDMETHODIMP VirtualBox::COMGETTER(FloppyImages)(ComSafeArrayOut(IMedium *, aFloppyImages))
980{
981 if (ComSafeArrayOutIsNull(aFloppyImages))
982 return E_POINTER;
983
984 AutoCaller autoCaller(this);
985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
986
987 AutoReadLock al(m->allFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
988 SafeIfaceArray<IMedium> images(m->allFloppyImages.getList());
989 images.detachTo(ComSafeArrayOutArg(aFloppyImages));
990
991 return S_OK;
992}
993
994STDMETHODIMP VirtualBox::COMGETTER(ProgressOperations)(ComSafeArrayOut(IProgress *, aOperations))
995{
996 CheckComArgOutSafeArrayPointerValid(aOperations);
997
998 AutoCaller autoCaller(this);
999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1000
1001 /* protect mProgressOperations */
1002 AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1003 SafeIfaceArray<IProgress> progress(m->mapProgressOperations);
1004 progress.detachTo(ComSafeArrayOutArg(aOperations));
1005
1006 return S_OK;
1007}
1008
1009STDMETHODIMP VirtualBox::COMGETTER(GuestOSTypes)(ComSafeArrayOut(IGuestOSType *, aGuestOSTypes))
1010{
1011 CheckComArgOutSafeArrayPointerValid(aGuestOSTypes);
1012
1013 AutoCaller autoCaller(this);
1014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1015
1016 AutoReadLock al(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1017 SafeIfaceArray<IGuestOSType> ostypes(m->allGuestOSTypes.getList());
1018 ostypes.detachTo(ComSafeArrayOutArg(aGuestOSTypes));
1019
1020 return S_OK;
1021}
1022
1023STDMETHODIMP VirtualBox::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
1024{
1025#ifndef RT_OS_WINDOWS
1026 NOREF(aSharedFoldersSize);
1027#endif /* RT_OS_WINDOWS */
1028
1029 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
1030
1031 AutoCaller autoCaller(this);
1032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1033
1034 return setError(E_NOTIMPL, "Not yet implemented");
1035}
1036
1037STDMETHODIMP
1038VirtualBox::COMGETTER(PerformanceCollector)(IPerformanceCollector **aPerformanceCollector)
1039{
1040#ifdef VBOX_WITH_RESOURCE_USAGE_API
1041 CheckComArgOutSafeArrayPointerValid(aPerformanceCollector);
1042
1043 AutoCaller autoCaller(this);
1044 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1045
1046 /* mPerformanceCollector is const, no need to lock */
1047 m->pPerformanceCollector.queryInterfaceTo(aPerformanceCollector);
1048
1049 return S_OK;
1050#else /* !VBOX_WITH_RESOURCE_USAGE_API */
1051 ReturnComNotImplemented();
1052#endif /* !VBOX_WITH_RESOURCE_USAGE_API */
1053}
1054
1055STDMETHODIMP
1056VirtualBox::COMGETTER(DHCPServers)(ComSafeArrayOut(IDHCPServer *, aDHCPServers))
1057{
1058 if (ComSafeArrayOutIsNull(aDHCPServers))
1059 return E_POINTER;
1060
1061 AutoCaller autoCaller(this);
1062 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1063
1064 AutoReadLock al(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1065 SafeIfaceArray<IDHCPServer> svrs(m->allDHCPServers.getList());
1066 svrs.detachTo(ComSafeArrayOutArg(aDHCPServers));
1067
1068 return S_OK;
1069}
1070
1071STDMETHODIMP
1072VirtualBox::COMGETTER(EventSource)(IEventSource ** aEventSource)
1073{
1074 CheckComArgOutPointerValid(aEventSource);
1075
1076 AutoCaller autoCaller(this);
1077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1078
1079 /* event source is const, no need to lock */
1080 m->pEventSource.queryInterfaceTo(aEventSource);
1081
1082 return S_OK;
1083}
1084
1085STDMETHODIMP
1086VirtualBox::COMGETTER(ExtensionPackManager)(IExtPackManager **aExtPackManager)
1087{
1088 CheckComArgOutPointerValid(aExtPackManager);
1089
1090 AutoCaller autoCaller(this);
1091 HRESULT hrc = autoCaller.rc();
1092 if (SUCCEEDED(hrc))
1093 {
1094#ifdef VBOX_WITH_EXTPACK
1095 /* The extension pack manager is const, no need to lock. */
1096 hrc = m->ptrExtPackManager.queryInterfaceTo(aExtPackManager);
1097#else
1098 hrc = E_NOTIMPL;
1099#endif
1100 }
1101
1102 return hrc;
1103}
1104
1105STDMETHODIMP
1106VirtualBox::CheckFirmwarePresent(FirmwareType_T aFirmwareType,
1107 IN_BSTR aVersion,
1108 BSTR *aUrl,
1109 BSTR *aFile,
1110 BOOL *aResult)
1111{
1112 CheckComArgNotNull(aResult);
1113
1114 AutoCaller autoCaller(this);
1115 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1116
1117 const char * url = NULL;
1118
1119 NOREF(aVersion);
1120
1121 static const struct {
1122 FirmwareType_T type;
1123 const char* fileName;
1124 const char* url;
1125 } firmwareDesc[] = {
1126 {
1127 /* compiled-in firmware */
1128 FirmwareType_BIOS, NULL, NULL
1129 },
1130 {
1131 FirmwareType_EFI32, "VBoxEFI32.fd", "http://virtualbox.org/firmware/VBoxEFI32.fd"
1132 },
1133 {
1134 FirmwareType_EFI64, "VBoxEFI64.fd", "http://virtualbox.org/firmware/VBoxEFI64.fd"
1135 },
1136 {
1137 FirmwareType_EFIDUAL, "VBoxEFIDual.fd", "http://virtualbox.org/firmware/VBoxEFIDual.fd"
1138 }
1139 };
1140
1141 for (size_t i = 0; i < sizeof(firmwareDesc) / sizeof(firmwareDesc[0]); i++)
1142 {
1143 if (aFirmwareType != firmwareDesc[i].type)
1144 continue;
1145
1146 /* compiled-in firmware */
1147 if (firmwareDesc[i].fileName == NULL)
1148 {
1149 *aResult = TRUE;
1150 break;
1151 }
1152
1153 Utf8Str shortName, fullName;
1154 int rc;
1155
1156 shortName = Utf8StrFmt("Firmware%c%s",
1157 RTPATH_DELIMITER,
1158 firmwareDesc[i].fileName);
1159 rc = calculateFullPath(shortName, fullName); AssertRCReturn(rc, rc);
1160 if (RTFileExists(fullName.c_str()))
1161 {
1162 *aResult = TRUE;
1163 if (aFile)
1164 Utf8Str(fullName).cloneTo(aFile);
1165 break;
1166 }
1167
1168 char pszVBoxPath[RTPATH_MAX];
1169 rc = RTPathExecDir(pszVBoxPath, RTPATH_MAX); AssertRCReturn(rc, rc);
1170 fullName = Utf8StrFmt("%s%c%s",
1171 pszVBoxPath,
1172 RTPATH_DELIMITER,
1173 firmwareDesc[i].fileName);
1174 if (RTFileExists(fullName.c_str()))
1175 {
1176 *aResult = TRUE;
1177 if (aFile)
1178 Utf8Str(fullName).cloneTo(aFile);
1179 break;
1180 }
1181
1182
1183 url = firmwareDesc[i].url;
1184 /** @todo: account for version in the URL */
1185 if (aUrl != NULL)
1186 {
1187 Utf8Str strUrl(firmwareDesc[i].url);
1188 strUrl.cloneTo(aUrl);
1189 }
1190 *aResult = FALSE;
1191
1192 /* Assume single record per firmware type */
1193 break;
1194 }
1195
1196 return S_OK;
1197}
1198// IVirtualBox methods
1199/////////////////////////////////////////////////////////////////////////////
1200
1201STDMETHODIMP VirtualBox::ComposeMachineFilename(IN_BSTR aName,
1202 IN_BSTR aBaseFolder,
1203 BSTR *aFilename)
1204{
1205 LogFlowThisFuncEnter();
1206 LogFlowThisFunc(("aName=\"%ls\",aBaseFolder=\"%ls\"\n", aName, aBaseFolder));
1207
1208 CheckComArgStrNotEmptyOrNull(aName);
1209 CheckComArgOutPointerValid(aFilename);
1210
1211 AutoCaller autoCaller(this);
1212 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1213
1214 /* Compose the settings file name using the following scheme:
1215 *
1216 * <base_folder>/<machine_name>/<machine_name>.xml
1217 *
1218 * If a non-null and non-empty base folder is specified, the default
1219 * machine folder will be used as a base folder.
1220 */
1221 Utf8Str strBase = aBaseFolder;
1222 if (strBase.isEmpty())
1223 /* we use the non-full folder value below to keep the path relative */
1224 getDefaultMachineFolder(strBase);
1225
1226 calculateFullPath(strBase, strBase);
1227
1228 Bstr bstrSettingsFile = BstrFmt("%s%c%ls%c%ls.vbox",
1229 strBase.c_str(),
1230 RTPATH_DELIMITER,
1231 aName,
1232 RTPATH_DELIMITER,
1233 aName);
1234
1235 bstrSettingsFile.detachTo(aFilename);
1236
1237 return S_OK;
1238}
1239
1240/** @note Locks mSystemProperties object for reading. */
1241STDMETHODIMP VirtualBox::CreateMachine(IN_BSTR aSettingsFile,
1242 IN_BSTR aName,
1243 IN_BSTR aOsTypeId,
1244 IN_BSTR aId,
1245 BOOL forceOverwrite,
1246 IMachine **aMachine)
1247{
1248 LogFlowThisFuncEnter();
1249 LogFlowThisFunc(("aSettingsFile=\"%ls\", aName=\"%ls\", aOsTypeId =\"%ls\"\n", aSettingsFile, aName, aOsTypeId));
1250
1251 CheckComArgStrNotEmptyOrNull(aName);
1252 /** @todo tighten checks on aId? */
1253 CheckComArgOutPointerValid(aMachine);
1254
1255 AutoCaller autoCaller(this);
1256 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1257
1258 /* NULL settings file means compose automatically */
1259 HRESULT rc;
1260 Bstr bstrSettingsFile(aSettingsFile);
1261 if (bstrSettingsFile.isEmpty())
1262 {
1263 rc = ComposeMachineFilename(aName,
1264 NULL,
1265 bstrSettingsFile.asOutParam());
1266 if (FAILED(rc)) return rc;
1267 }
1268
1269 /* create a new object */
1270 ComObjPtr<Machine> machine;
1271 rc = machine.createObject();
1272 if (FAILED(rc)) return rc;
1273
1274 /* Create UUID if an empty one was specified. */
1275 Guid id(aId);
1276 if (id.isEmpty())
1277 id.create();
1278
1279 GuestOSType *osType = NULL;
1280 rc = findGuestOSType(Bstr(aOsTypeId), osType);
1281 if (FAILED(rc)) return rc;
1282
1283 /* initialize the machine object */
1284 rc = machine->init(this,
1285 Utf8Str(bstrSettingsFile),
1286 Utf8Str(aName),
1287 osType,
1288 id,
1289 !!forceOverwrite);
1290 if (SUCCEEDED(rc))
1291 {
1292 /* set the return value */
1293 rc = machine.queryInterfaceTo(aMachine);
1294 AssertComRC(rc);
1295
1296#ifdef VBOX_WITH_EXTPACK
1297 /* call the extension pack hooks */
1298 m->ptrExtPackManager->callAllVmCreatedHooks(machine);
1299#endif
1300 }
1301
1302 LogFlowThisFuncLeave();
1303
1304 return rc;
1305}
1306
1307STDMETHODIMP VirtualBox::OpenMachine(IN_BSTR aSettingsFile,
1308 IMachine **aMachine)
1309{
1310 CheckComArgStrNotEmptyOrNull(aSettingsFile);
1311 CheckComArgOutSafeArrayPointerValid(aMachine);
1312
1313 AutoCaller autoCaller(this);
1314 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1315
1316 HRESULT rc = E_FAIL;
1317
1318 /* create a new object */
1319 ComObjPtr<Machine> machine;
1320 rc = machine.createObject();
1321 if (SUCCEEDED(rc))
1322 {
1323 /* initialize the machine object */
1324 rc = machine->init(this,
1325 aSettingsFile,
1326 NULL); /* const Guid *aId */
1327 if (SUCCEEDED(rc))
1328 {
1329 /* set the return value */
1330 rc = machine.queryInterfaceTo(aMachine);
1331 ComAssertComRC(rc);
1332 }
1333 }
1334
1335 return rc;
1336}
1337
1338/** @note Locks objects! */
1339STDMETHODIMP VirtualBox::RegisterMachine(IMachine *aMachine)
1340{
1341 CheckComArgNotNull(aMachine);
1342
1343 AutoCaller autoCaller(this);
1344 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1345
1346 HRESULT rc;
1347
1348 Bstr name;
1349 rc = aMachine->COMGETTER(Name)(name.asOutParam());
1350 if (FAILED(rc)) return rc;
1351
1352 /* We can safely cast child to Machine * here because only Machine
1353 * implementations of IMachine can be among our children. */
1354 Machine *pMachine = static_cast<Machine*>(aMachine);
1355
1356 AutoCaller machCaller(pMachine);
1357 ComAssertComRCRetRC(machCaller.rc());
1358
1359 rc = registerMachine(pMachine);
1360 /* fire an event */
1361 if (SUCCEEDED(rc))
1362 onMachineRegistered(pMachine->getId(), TRUE);
1363
1364 return rc;
1365}
1366
1367/** @note Locks this object for reading, then some machine objects for reading. */
1368STDMETHODIMP VirtualBox::FindMachine(IN_BSTR aNameOrId, IMachine **aMachine)
1369{
1370 LogFlowThisFuncEnter();
1371 LogFlowThisFunc(("aName=\"%ls\", aMachine={%p}\n", aNameOrId, aMachine));
1372
1373 CheckComArgStrNotEmptyOrNull(aNameOrId);
1374 CheckComArgOutSafeArrayPointerValid(aMachine);
1375
1376 AutoCaller autoCaller(this);
1377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1378
1379 /* start with not found */
1380 HRESULT rc = S_OK;
1381 ComObjPtr<Machine> pMachineFound;
1382
1383 Guid id(aNameOrId);
1384 if (!id.isEmpty())
1385 rc = findMachine(id,
1386 true /* fPermitInaccessible */,
1387 true /* setError */,
1388 &pMachineFound);
1389 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
1390 else
1391 {
1392 Utf8Str strName(aNameOrId);
1393 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1394 for (MachinesOList::iterator it = m->allMachines.begin();
1395 it != m->allMachines.end();
1396 ++it)
1397 {
1398 ComObjPtr<Machine> &pMachine2 = *it;
1399 AutoCaller machCaller(pMachine2);
1400 if (machCaller.rc())
1401 continue; // we can't ask inaccessible machines for their names
1402
1403 AutoReadLock machLock(pMachine2 COMMA_LOCKVAL_SRC_POS);
1404 if (pMachine2->getName() == strName)
1405 {
1406 pMachineFound = pMachine2;
1407 break;
1408 }
1409 if (!RTPathCompare(pMachine2->getSettingsFileFull().c_str(), strName.c_str()))
1410 {
1411 pMachineFound = pMachine2;
1412 break;
1413 }
1414 }
1415
1416 if (!pMachineFound)
1417 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1418 tr("Could not find a registered machine named '%ls'"), aNameOrId);
1419 }
1420
1421 /* this will set (*machine) to NULL if machineObj is null */
1422 pMachineFound.queryInterfaceTo(aMachine);
1423
1424 LogFlowThisFunc(("aName=\"%ls\", aMachine=%p, rc=%08X\n", aNameOrId, *aMachine, rc));
1425 LogFlowThisFuncLeave();
1426
1427 return rc;
1428}
1429
1430STDMETHODIMP VirtualBox::CreateHardDisk(IN_BSTR aFormat,
1431 IN_BSTR aLocation,
1432 IMedium **aHardDisk)
1433{
1434 CheckComArgOutPointerValid(aHardDisk);
1435
1436 AutoCaller autoCaller(this);
1437 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1438
1439 /* we don't access non-const data members so no need to lock */
1440
1441 Utf8Str format(aFormat);
1442 if (format.isEmpty())
1443 getDefaultHardDiskFormat(format);
1444
1445 ComObjPtr<Medium> hardDisk;
1446 hardDisk.createObject();
1447 HRESULT rc = hardDisk->init(this,
1448 format,
1449 aLocation,
1450 Guid::Empty, // media registry: none yet
1451 NULL /* pllRegistriesThatNeedSaving */);
1452
1453 if (SUCCEEDED(rc))
1454 hardDisk.queryInterfaceTo(aHardDisk);
1455
1456 return rc;
1457}
1458
1459STDMETHODIMP VirtualBox::OpenMedium(IN_BSTR aLocation,
1460 DeviceType_T deviceType,
1461 AccessMode_T accessMode,
1462 IMedium **aMedium)
1463{
1464 CheckComArgStrNotEmptyOrNull(aLocation);
1465 CheckComArgOutSafeArrayPointerValid(aMedium);
1466
1467 AutoCaller autoCaller(this);
1468 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1469
1470 ComObjPtr<Medium> pMedium;
1471
1472 /* we don't access non-const data members so no need to lock */
1473
1474 // check if the device type is correct, and see if a medium for the
1475 // given path has already initialized; if so, return that
1476 switch (deviceType)
1477 {
1478 case DeviceType_HardDisk:
1479 findHardDiskByLocation(aLocation,
1480 false, /* aSetError */
1481 &pMedium);
1482 break;
1483
1484 case DeviceType_Floppy:
1485 case DeviceType_DVD:
1486 findDVDOrFloppyImage(deviceType,
1487 NULL, /* guid */
1488 aLocation,
1489 false, /* aSetError */
1490 &pMedium);
1491
1492 // enforce read-only for DVDs even if caller specified ReadWrite
1493 if (deviceType == DeviceType_DVD)
1494 accessMode = AccessMode_ReadOnly;
1495 break;
1496
1497 default:
1498 return setError(E_INVALIDARG, "Device type must be HardDisk, DVD or Floppy");
1499 }
1500
1501 HRESULT rc = S_OK;
1502
1503 if (pMedium.isNull())
1504 {
1505 pMedium.createObject();
1506 rc = pMedium->init(this,
1507 aLocation,
1508 (accessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
1509 deviceType);
1510
1511 if (SUCCEEDED(rc))
1512 {
1513 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1514
1515 switch (deviceType)
1516 {
1517 case DeviceType_HardDisk:
1518 rc = registerHardDisk(pMedium, NULL /* pllRegistriesThatNeedSaving */);
1519 break;
1520
1521 case DeviceType_DVD:
1522 case DeviceType_Floppy:
1523 rc = registerImage(pMedium,
1524 deviceType,
1525 NULL /* pllRegistriesThatNeedSaving */);
1526 break;
1527 }
1528
1529 treeLock.release();
1530
1531 /* Note that it's important to call uninit() on failure to register
1532 * because the differencing hard disk would have been already associated
1533 * with the parent and this association needs to be broken. */
1534
1535 if (FAILED(rc))
1536 pMedium->uninit();
1537 }
1538 }
1539
1540 if (SUCCEEDED(rc))
1541 pMedium.queryInterfaceTo(aMedium);
1542
1543 return rc;
1544}
1545
1546STDMETHODIMP VirtualBox::FindMedium(IN_BSTR aLocation,
1547 DeviceType_T aDeviceType,
1548 IMedium **aMedium)
1549{
1550 CheckComArgStrNotEmptyOrNull(aLocation);
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 fireNATRedirectEvent(m->pEventSource, aMachineId.toUtf16().raw(), ulSlot, fRemove, aName, aProto, aHostIp,
2497 aHostPort, aGuestIp, aGuestPort);
2498}
2499
2500/**
2501 * @note Locks this object for reading.
2502 */
2503ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2504{
2505 ComObjPtr<GuestOSType> type;
2506 AutoCaller autoCaller(this);
2507 AssertComRCReturn(autoCaller.rc(), type);
2508
2509 /* unknown type must always be the first */
2510 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
2511
2512 return m->allGuestOSTypes.front();
2513}
2514
2515/**
2516 * Returns the list of opened machines (machines having direct sessions opened
2517 * by client processes) and optionally the list of direct session controls.
2518 *
2519 * @param aMachines Where to put opened machines (will be empty if none).
2520 * @param aControls Where to put direct session controls (optional).
2521 *
2522 * @note The returned lists contain smart pointers. So, clear it as soon as
2523 * it becomes no more necessary to release instances.
2524 *
2525 * @note It can be possible that a session machine from the list has been
2526 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2527 * when accessing unprotected data directly.
2528 *
2529 * @note Locks objects for reading.
2530 */
2531void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
2532 InternalControlList *aControls /*= NULL*/)
2533{
2534 AutoCaller autoCaller(this);
2535 AssertComRCReturnVoid(autoCaller.rc());
2536
2537 aMachines.clear();
2538 if (aControls)
2539 aControls->clear();
2540
2541 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2542
2543 for (MachinesOList::iterator it = m->allMachines.begin();
2544 it != m->allMachines.end();
2545 ++it)
2546 {
2547 ComObjPtr<SessionMachine> sm;
2548 ComPtr<IInternalSessionControl> ctl;
2549 if ((*it)->isSessionOpen(sm, &ctl))
2550 {
2551 aMachines.push_back(sm);
2552 if (aControls)
2553 aControls->push_back(ctl);
2554 }
2555 }
2556}
2557
2558/**
2559 * Searches for a machine object with the given ID in the collection
2560 * of registered machines.
2561 *
2562 * @param aId Machine UUID to look for.
2563 * @param aPermitInaccessible If true, inaccessible machines will be found;
2564 * if false, this will fail if the given machine is inaccessible.
2565 * @param aSetError If true, set errorinfo if the machine is not found.
2566 * @param aMachine Returned machine, if found.
2567 * @return
2568 */
2569HRESULT VirtualBox::findMachine(const Guid &aId,
2570 bool fPermitInaccessible,
2571 bool aSetError,
2572 ComObjPtr<Machine> *aMachine /* = NULL */)
2573{
2574 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
2575
2576 AutoCaller autoCaller(this);
2577 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2578
2579 {
2580 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2581
2582 for (MachinesOList::iterator it = m->allMachines.begin();
2583 it != m->allMachines.end();
2584 ++it)
2585 {
2586 ComObjPtr<Machine> pMachine2 = *it;
2587
2588 if (!fPermitInaccessible)
2589 {
2590 // skip inaccessible machines
2591 AutoCaller machCaller(pMachine2);
2592 if (FAILED(machCaller.rc()))
2593 continue;
2594 }
2595
2596 if (pMachine2->getId() == aId)
2597 {
2598 rc = S_OK;
2599 if (aMachine)
2600 *aMachine = pMachine2;
2601 break;
2602 }
2603 }
2604 }
2605
2606 if (aSetError && FAILED(rc))
2607 rc = setError(rc,
2608 tr("Could not find a registered machine with UUID {%RTuuid}"),
2609 aId.raw());
2610
2611 return rc;
2612}
2613
2614/**
2615 * Searches for a Medium object with the given ID in the list of registered
2616 * hard disks.
2617 *
2618 * @param aId ID of the hard disk. Must not be empty.
2619 * @param aSetError If @c true , the appropriate error info is set in case
2620 * when the hard disk is not found.
2621 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2622 *
2623 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2624 *
2625 * @note Locks the media tree for reading.
2626 */
2627HRESULT VirtualBox::findHardDiskById(const Guid &id,
2628 bool aSetError,
2629 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2630{
2631 AssertReturn(!id.isEmpty(), E_INVALIDARG);
2632
2633 // we use the hard disks map, but it is protected by the
2634 // hard disk _list_ lock handle
2635 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2636
2637 HardDiskMap::const_iterator it = m->mapHardDisks.find(id);
2638 if (it != m->mapHardDisks.end())
2639 {
2640 if (aHardDisk)
2641 *aHardDisk = (*it).second;
2642 return S_OK;
2643 }
2644
2645 if (aSetError)
2646 return setError(VBOX_E_OBJECT_NOT_FOUND,
2647 tr("Could not find an open hard disk with UUID {%RTuuid}"),
2648 id.raw());
2649
2650 return VBOX_E_OBJECT_NOT_FOUND;
2651}
2652
2653/**
2654 * Searches for a Medium object with the given ID or location in the list of
2655 * registered hard disks. If both ID and location are specified, the first
2656 * object that matches either of them (not necessarily both) is returned.
2657 *
2658 * @param aLocation Full location specification. Must not be empty.
2659 * @param aSetError If @c true , the appropriate error info is set in case
2660 * when the hard disk is not found.
2661 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2662 *
2663 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2664 *
2665 * @note Locks the media tree for reading.
2666 */
2667HRESULT VirtualBox::findHardDiskByLocation(const Utf8Str &strLocation,
2668 bool aSetError,
2669 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2670{
2671 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
2672
2673 // we use the hard disks map, but it is protected by the
2674 // hard disk _list_ lock handle
2675 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2676
2677 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
2678 it != m->mapHardDisks.end();
2679 ++it)
2680 {
2681 const ComObjPtr<Medium> &pHD = (*it).second;
2682
2683 AutoCaller autoCaller(pHD);
2684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2685 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
2686
2687 Utf8Str strLocationFull = pHD->getLocationFull();
2688
2689 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
2690 {
2691 if (aHardDisk)
2692 *aHardDisk = pHD;
2693 return S_OK;
2694 }
2695 }
2696
2697 if (aSetError)
2698 return setError(VBOX_E_OBJECT_NOT_FOUND,
2699 tr("Could not find an open hard disk with location '%s'"),
2700 strLocation.c_str());
2701
2702 return VBOX_E_OBJECT_NOT_FOUND;
2703}
2704
2705/**
2706 * Searches for a Medium object with the given ID or location in the list of
2707 * registered DVD or floppy images, depending on the @a mediumType argument.
2708 * If both ID and file path are specified, the first object that matches either
2709 * of them (not necessarily both) is returned.
2710 *
2711 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
2712 * @param aId ID of the image file (unused when NULL).
2713 * @param aLocation Full path to the image file (unused when NULL).
2714 * @param aSetError If @c true, the appropriate error info is set in case when
2715 * the image is not found.
2716 * @param aImage Where to store the found image object (can be NULL).
2717 *
2718 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2719 *
2720 * @note Locks the media tree for reading.
2721 */
2722HRESULT VirtualBox::findDVDOrFloppyImage(DeviceType_T mediumType,
2723 const Guid *aId,
2724 const Utf8Str &aLocation,
2725 bool aSetError,
2726 ComObjPtr<Medium> *aImage /* = NULL */)
2727{
2728 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
2729
2730 Utf8Str location;
2731 if (!aLocation.isEmpty())
2732 {
2733 int vrc = calculateFullPath(aLocation, location);
2734 if (RT_FAILURE(vrc))
2735 return setError(VBOX_E_FILE_ERROR,
2736 tr("Invalid image file location '%ls' (%Rrc)"),
2737 aLocation.c_str(),
2738 vrc);
2739 }
2740
2741 MediaOList *pMediaList;
2742
2743 switch (mediumType)
2744 {
2745 case DeviceType_DVD:
2746 pMediaList = &m->allDVDImages;
2747 break;
2748
2749 case DeviceType_Floppy:
2750 pMediaList = &m->allFloppyImages;
2751 break;
2752
2753 default:
2754 return E_INVALIDARG;
2755 }
2756
2757 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
2758
2759 bool found = false;
2760
2761 for (MediaList::const_iterator it = pMediaList->begin();
2762 it != pMediaList->end();
2763 ++it)
2764 {
2765 // no AutoCaller, registered image life time is bound to this
2766 Medium *pMedium = *it;
2767 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
2768 const Utf8Str &strLocationFull = pMedium->getLocationFull();
2769
2770 found = ( aId
2771 && pMedium->getId() == *aId)
2772 || ( !aLocation.isEmpty()
2773 && RTPathCompare(location.c_str(),
2774 strLocationFull.c_str()) == 0);
2775 if (found)
2776 {
2777 if (pMedium->getDeviceType() != mediumType)
2778 {
2779 if (mediumType == DeviceType_DVD)
2780 return setError(E_INVALIDARG,
2781 "Cannot mount DVD medium '%s' as floppy", strLocationFull.c_str());
2782 else
2783 return setError(E_INVALIDARG,
2784 "Cannot mount floppy medium '%s' as DVD", strLocationFull.c_str());
2785 }
2786
2787 if (aImage)
2788 *aImage = pMedium;
2789 break;
2790 }
2791 }
2792
2793 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2794
2795 if (aSetError && !found)
2796 {
2797 if (aId)
2798 setError(rc,
2799 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
2800 aId->raw(),
2801 m->strSettingsFilePath.c_str());
2802 else
2803 setError(rc,
2804 tr("Could not find an image file with location '%ls' in the media registry ('%s')"),
2805 aLocation.c_str(),
2806 m->strSettingsFilePath.c_str());
2807 }
2808
2809 return rc;
2810}
2811
2812/**
2813 * Searches for an IMedium object that represents the given UUID.
2814 *
2815 * If the UUID is empty (indicating an empty drive), this sets pMedium
2816 * to NULL and returns S_OK.
2817 *
2818 * If the UUID refers to a host drive of the given device type, this
2819 * sets pMedium to the object from the list in IHost and returns S_OK.
2820 *
2821 * If the UUID is an image file, this sets pMedium to the object that
2822 * findDVDOrFloppyImage() returned.
2823 *
2824 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
2825 *
2826 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
2827 * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
2828 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
2829 * @param pMedium out: IMedium object found.
2830 * @return
2831 */
2832HRESULT VirtualBox::findRemoveableMedium(DeviceType_T mediumType,
2833 const Guid &uuid,
2834 bool fRefresh,
2835 ComObjPtr<Medium> &pMedium)
2836{
2837 if (uuid.isEmpty())
2838 {
2839 // that's easy
2840 pMedium.setNull();
2841 return S_OK;
2842 }
2843
2844 // first search for host drive with that UUID
2845 HRESULT rc = m->pHost->findHostDrive(mediumType,
2846 uuid,
2847 fRefresh,
2848 pMedium);
2849 if (rc == VBOX_E_OBJECT_NOT_FOUND)
2850 // then search for an image with that UUID
2851 rc = findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, true /* aSetError */, &pMedium);
2852
2853 return rc;
2854}
2855
2856HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
2857 GuestOSType*& pGuestOSType)
2858{
2859 /* Look for a GuestOSType object */
2860 AssertMsg(m->allGuestOSTypes.size() != 0,
2861 ("Guest OS types array must be filled"));
2862
2863 if (bstrOSType.isEmpty())
2864 {
2865 pGuestOSType = NULL;
2866 return S_OK;
2867 }
2868
2869 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2870 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
2871 it != m->allGuestOSTypes.end();
2872 ++it)
2873 {
2874 if ((*it)->id() == bstrOSType)
2875 {
2876 pGuestOSType = *it;
2877 return S_OK;
2878 }
2879 }
2880
2881 return setError(VBOX_E_OBJECT_NOT_FOUND,
2882 tr("Guest OS type '%ls' is invalid"),
2883 bstrOSType.raw());
2884}
2885
2886/**
2887 * Returns the constant pseudo-machine UUID that is used to identify the
2888 * global media registry.
2889 *
2890 * Starting with VirtualBox 4.0 each medium remembers in its instance data
2891 * in which media registry it is saved (if any): this can either be a machine
2892 * UUID, if it's in a per-machine media registry, or this global ID.
2893 *
2894 * This UUID is only used to identify the VirtualBox object while VirtualBox
2895 * is running. It is a compile-time constant and not saved anywhere.
2896 *
2897 * @return
2898 */
2899const Guid& VirtualBox::getGlobalRegistryId() const
2900{
2901 return m->uuidMediaRegistry;
2902}
2903
2904const ComObjPtr<Host>& VirtualBox::host() const
2905{
2906 return m->pHost;
2907}
2908
2909SystemProperties* VirtualBox::getSystemProperties() const
2910{
2911 return m->pSystemProperties;
2912}
2913
2914#ifdef VBOX_WITH_EXTPACK
2915/**
2916 * Getter that SystemProperties and others can use to talk to the extension
2917 * pack manager.
2918 */
2919ExtPackManager* VirtualBox::getExtPackManager() const
2920{
2921 return m->ptrExtPackManager;
2922}
2923#endif
2924
2925#ifdef VBOX_WITH_RESOURCE_USAGE_API
2926const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
2927{
2928 return m->pPerformanceCollector;
2929}
2930#endif /* VBOX_WITH_RESOURCE_USAGE_API */
2931
2932/**
2933 * Returns the default machine folder from the system properties
2934 * with proper locking.
2935 * @return
2936 */
2937void VirtualBox::getDefaultMachineFolder(Utf8Str &str) const
2938{
2939 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2940 str = m->pSystemProperties->m->strDefaultMachineFolder;
2941}
2942
2943/**
2944 * Returns the default hard disk format from the system properties
2945 * with proper locking.
2946 * @return
2947 */
2948void VirtualBox::getDefaultHardDiskFormat(Utf8Str &str) const
2949{
2950 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2951 str = m->pSystemProperties->m->strDefaultHardDiskFormat;
2952}
2953
2954const Utf8Str& VirtualBox::homeDir() const
2955{
2956 return m->strHomeDir;
2957}
2958
2959/**
2960 * Calculates the absolute path of the given path taking the VirtualBox home
2961 * directory as the current directory.
2962 *
2963 * @param aPath Path to calculate the absolute path for.
2964 * @param aResult Where to put the result (used only on success, can be the
2965 * same Utf8Str instance as passed in @a aPath).
2966 * @return IPRT result.
2967 *
2968 * @note Doesn't lock any object.
2969 */
2970int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
2971{
2972 AutoCaller autoCaller(this);
2973 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
2974
2975 /* no need to lock since mHomeDir is const */
2976
2977 char folder[RTPATH_MAX];
2978 int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
2979 strPath.c_str(),
2980 folder,
2981 sizeof(folder));
2982 if (RT_SUCCESS(vrc))
2983 aResult = folder;
2984
2985 return vrc;
2986}
2987
2988/**
2989 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
2990 * if it is a subdirectory thereof, or simply copying it otherwise.
2991 *
2992 * @param strSource Path to evalue and copy.
2993 * @param strTarget Buffer to receive target path.
2994 */
2995void VirtualBox::copyPathRelativeToConfig(const Utf8Str &strSource,
2996 Utf8Str &strTarget)
2997{
2998 AutoCaller autoCaller(this);
2999 AssertComRCReturnVoid(autoCaller.rc());
3000
3001 // no need to lock since mHomeDir is const
3002
3003 // use strTarget as a temporary buffer to hold the machine settings dir
3004 strTarget = m->strHomeDir;
3005 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
3006 // is relative: then append what's left
3007 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
3008 else
3009 // is not relative: then overwrite
3010 strTarget = strSource;
3011}
3012
3013// private methods
3014/////////////////////////////////////////////////////////////////////////////
3015
3016/**
3017 * Checks if there is a hard disk, DVD or floppy image with the given ID or
3018 * location already registered.
3019 *
3020 * On return, sets @a aConflict to the string describing the conflicting medium,
3021 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3022 * either case. A failure is unexpected.
3023 *
3024 * @param aId UUID to check.
3025 * @param aLocation Location to check.
3026 * @param aConflict Where to return parameters of the conflicting medium.
3027 *
3028 * @note Locks the media tree and media objects for reading.
3029 */
3030HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId,
3031 const Utf8Str &aLocation,
3032 Utf8Str &aConflict,
3033 bool &fIdentical)
3034{
3035 aConflict.setNull();
3036
3037 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
3038
3039 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3040
3041 HRESULT rc = S_OK;
3042
3043 aConflict.setNull();
3044 fIdentical = false;
3045
3046 ComObjPtr<Medium> pMediumFound;
3047 const char *pcszType = NULL;
3048
3049 if (!aId.isEmpty())
3050 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
3051 if (FAILED(rc) && !aLocation.isEmpty())
3052 rc = findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
3053 if (SUCCEEDED(rc))
3054 pcszType = tr("hard disk");
3055
3056 if (!pcszType)
3057 {
3058 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
3059 if (SUCCEEDED(rc))
3060 pcszType = tr("CD/DVD image");
3061 }
3062
3063 if (!pcszType)
3064 {
3065 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
3066 if (SUCCEEDED(rc))
3067 pcszType = tr("floppy image");
3068 }
3069
3070 if (pcszType && pMediumFound)
3071 {
3072 /* Note: no AutoCaller since bound to this */
3073 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
3074
3075 Utf8Str strLocFound = pMediumFound->getLocationFull();
3076 Guid idFound = pMediumFound->getId();
3077
3078 if ( (strLocFound == aLocation)
3079 && (idFound == aId)
3080 )
3081 fIdentical = true;
3082
3083 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
3084 pcszType,
3085 strLocFound.c_str(),
3086 idFound.raw());
3087 }
3088
3089 return S_OK;
3090}
3091
3092/**
3093 * Called from Machine::prepareSaveSettings() when it has detected
3094 * that a machine has been renamed. Such renames will require
3095 * updating the global media registry during the
3096 * VirtualBox::saveSettings() that follows later.
3097*
3098 * When a machine is renamed, there may well be media (in particular,
3099 * diff images for snapshots) in the global registry that will need
3100 * to have their paths updated. Before 3.2, Machine::saveSettings
3101 * used to call VirtualBox::saveSettings implicitly, which was both
3102 * unintuitive and caused locking order problems. Now, we remember
3103 * such pending name changes with this method so that
3104 * VirtualBox::saveSettings() can process them properly.
3105 */
3106void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3107 const Utf8Str &strNewConfigDir)
3108{
3109 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3110
3111 Data::PendingMachineRename pmr;
3112 pmr.strConfigDirOld = strOldConfigDir;
3113 pmr.strConfigDirNew = strNewConfigDir;
3114 m->llPendingMachineRenames.push_back(pmr);
3115}
3116
3117/**
3118 * Goes through all known media (hard disks, floppies and DVDs) and saves
3119 * those into the given settings::MediaRegistry structures whose registry
3120 * ID match the given UUID.
3121 *
3122 * Before actually writing to the structures, all media paths (not just the
3123 * ones for the given registry) are updated if machines have been renamed
3124 * since the last call.
3125 *
3126 * This gets called from two contexts:
3127 *
3128 * -- VirtualBox::saveSettings() with the UUID of the global registry
3129 * (VirtualBox::Data.uuidRegistry); this will save those media
3130 * which had been loaded from the global registry or have been
3131 * attached to a "legacy" machine which can't save its own registry;
3132 *
3133 * -- Machine::saveSettings() with the UUID of a machine, if a medium
3134 * has been attached to a machine created with VirtualBox 4.0 or later.
3135 *
3136 * Media which have only been temporarily opened without having been
3137 * attached to a machine have a NULL registry UUID and therefore don't
3138 * get saved.
3139 *
3140 * This locks the media tree. Throws HRESULT on errors!
3141 *
3142 * @param mediaRegistry Settings structure to fill.
3143 * @param uuidRegistry The UUID of the media registry; either a machine UUID (if machine registry) or the UUID of the global registry.
3144 * @param hardDiskFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
3145 */
3146void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
3147 const Guid &uuidRegistry,
3148 const Utf8Str &strMachineFolder)
3149{
3150 // lock all media for the following; use a write lock because we're
3151 // modifying the PendingMachineRenamesList, which is protected by this
3152 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3153
3154 // if a machine was renamed, then we'll need to refresh media paths
3155 if (m->llPendingMachineRenames.size())
3156 {
3157 // make a single list from the three media lists so we don't need three loops
3158 MediaList llAllMedia;
3159 // with hard disks, we must use the map, not the list, because the list only has base images
3160 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3161 llAllMedia.push_back(it->second);
3162 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
3163 llAllMedia.push_back(*it);
3164 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
3165 llAllMedia.push_back(*it);
3166
3167 for (MediaList::iterator it = llAllMedia.begin();
3168 it != llAllMedia.end();
3169 ++it)
3170 {
3171 Medium *pMedium = *it;
3172 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3173 it2 != m->llPendingMachineRenames.end();
3174 ++it2)
3175 {
3176 const Data::PendingMachineRename &pmr = *it2;
3177 pMedium->updatePath(pmr.strConfigDirOld,
3178 pmr.strConfigDirNew);
3179 }
3180 }
3181 // done, don't do it again until we have more machine renames
3182 m->llPendingMachineRenames.clear();
3183 }
3184
3185 HRESULT rc;
3186 // hard disks
3187 mediaRegistry.llHardDisks.clear();
3188 for (MediaList::const_iterator it = m->allHardDisks.begin();
3189 it != m->allHardDisks.end();
3190 ++it)
3191 {
3192 Medium *pMedium = *it;
3193 if (pMedium->isInRegistry(uuidRegistry))
3194 {
3195 settings::Medium med;
3196 rc = pMedium->saveSettings(med, strMachineFolder); // this recurses into its children
3197 if (FAILED(rc)) throw rc;
3198 mediaRegistry.llHardDisks.push_back(med);
3199 }
3200 }
3201
3202 // CD/DVD images
3203 mediaRegistry.llDvdImages.clear();
3204 for (MediaList::const_iterator it = m->allDVDImages.begin();
3205 it != m->allDVDImages.end();
3206 ++it)
3207 {
3208 Medium *pMedium = *it;
3209 if (pMedium->isInRegistry(uuidRegistry))
3210 {
3211 settings::Medium med;
3212 rc = pMedium->saveSettings(med, strMachineFolder);
3213 if (FAILED(rc)) throw rc;
3214 mediaRegistry.llDvdImages.push_back(med);
3215 }
3216 }
3217
3218 // floppy images
3219 mediaRegistry.llFloppyImages.clear();
3220 for (MediaList::const_iterator it = m->allFloppyImages.begin();
3221 it != m->allFloppyImages.end();
3222 ++it)
3223 {
3224 Medium *pMedium = *it;
3225 if (pMedium->isInRegistry(uuidRegistry))
3226 {
3227 settings::Medium med;
3228 rc = pMedium->saveSettings(med, strMachineFolder);
3229 if (FAILED(rc)) throw rc;
3230 mediaRegistry.llFloppyImages.push_back(med);
3231 }
3232 }
3233}
3234
3235/**
3236 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
3237 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
3238 * places internally when settings need saving.
3239 *
3240 * @note Caller must have locked the VirtualBox object for writing and must not hold any
3241 * other locks since this locks all kinds of member objects and trees temporarily,
3242 * which could cause conflicts.
3243 */
3244HRESULT VirtualBox::saveSettings()
3245{
3246 AutoCaller autoCaller(this);
3247 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3248
3249 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3250 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3251
3252 HRESULT rc = S_OK;
3253
3254 try
3255 {
3256 // machines
3257 m->pMainConfigFile->llMachines.clear();
3258 {
3259 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3260 for (MachinesOList::iterator it = m->allMachines.begin();
3261 it != m->allMachines.end();
3262 ++it)
3263 {
3264 Machine *pMachine = *it;
3265 // save actual machine registry entry
3266 settings::MachineRegistryEntry mre;
3267 rc = pMachine->saveRegistryEntry(mre);
3268 m->pMainConfigFile->llMachines.push_back(mre);
3269 }
3270 }
3271
3272 saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
3273 m->uuidMediaRegistry, // global media registry ID
3274 Utf8Str::Empty); // strMachineFolder
3275
3276 m->pMainConfigFile->llDhcpServers.clear();
3277 {
3278 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3279 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
3280 it != m->allDHCPServers.end();
3281 ++it)
3282 {
3283 settings::DHCPServer d;
3284 rc = (*it)->saveSettings(d);
3285 if (FAILED(rc)) throw rc;
3286 m->pMainConfigFile->llDhcpServers.push_back(d);
3287 }
3288 }
3289
3290 // leave extra data alone, it's still in the config file
3291
3292 // host data (USB filters)
3293 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3294 if (FAILED(rc)) throw rc;
3295
3296 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3297 if (FAILED(rc)) throw rc;
3298
3299 // and write out the XML, still under the lock
3300 m->pMainConfigFile->write(m->strSettingsFilePath);
3301 }
3302 catch (HRESULT err)
3303 {
3304 /* we assume that error info is set by the thrower */
3305 rc = err;
3306 }
3307 catch (...)
3308 {
3309 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3310 }
3311
3312 return rc;
3313}
3314
3315/**
3316 * Helper to register the machine.
3317 *
3318 * When called during VirtualBox startup, adds the given machine to the
3319 * collection of registered machines. Otherwise tries to mark the machine
3320 * as registered, and, if succeeded, adds it to the collection and
3321 * saves global settings.
3322 *
3323 * @note The caller must have added itself as a caller of the @a aMachine
3324 * object if calls this method not on VirtualBox startup.
3325 *
3326 * @param aMachine machine to register
3327 *
3328 * @note Locks objects!
3329 */
3330HRESULT VirtualBox::registerMachine(Machine *aMachine)
3331{
3332 ComAssertRet(aMachine, E_INVALIDARG);
3333
3334 AutoCaller autoCaller(this);
3335 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3336
3337 HRESULT rc = S_OK;
3338
3339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3340
3341 {
3342 ComObjPtr<Machine> pMachine;
3343 rc = findMachine(aMachine->getId(),
3344 true /* fPermitInaccessible */,
3345 false /* aDoSetError */,
3346 &pMachine);
3347 if (SUCCEEDED(rc))
3348 {
3349 /* sanity */
3350 AutoLimitedCaller machCaller(pMachine);
3351 AssertComRC(machCaller.rc());
3352
3353 return setError(E_INVALIDARG,
3354 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
3355 aMachine->getId().raw(),
3356 pMachine->getSettingsFileFull().c_str());
3357 }
3358
3359 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3360 rc = S_OK;
3361 }
3362
3363 if (autoCaller.state() != InInit)
3364 {
3365 rc = aMachine->prepareRegister();
3366 if (FAILED(rc)) return rc;
3367 }
3368
3369 /* add to the collection of registered machines */
3370 m->allMachines.addChild(aMachine);
3371
3372 if (autoCaller.state() != InInit)
3373 rc = saveSettings();
3374
3375 return rc;
3376}
3377
3378/**
3379 * Remembers the given hard disk by storing it in either the global hard disk registry
3380 * or a machine one.
3381 *
3382 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3383 *
3384 * @param aHardDisk Hard disk object to remember.
3385 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3386 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs of media registries that need saving.
3387 * @return
3388 */
3389HRESULT VirtualBox::registerHardDisk(Medium *pMedium,
3390 GuidList *pllRegistriesThatNeedSaving)
3391{
3392 AssertReturn(pMedium != NULL, E_INVALIDARG);
3393
3394 AutoCaller autoCaller(this);
3395 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3396
3397 AutoCaller hardDiskCaller(pMedium);
3398 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3399
3400 // caller must hold the media tree write lock
3401 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3402
3403 Guid id;
3404 Utf8Str strLocationFull;
3405 ComObjPtr<Medium> pParent;
3406 {
3407 AutoReadLock hardDiskLock(pMedium COMMA_LOCKVAL_SRC_POS);
3408 id = pMedium->getId();
3409 strLocationFull = pMedium->getLocationFull();
3410 pParent = pMedium->getParent();
3411 }
3412
3413 HRESULT rc;
3414
3415 Utf8Str strConflict;
3416 bool fIdentical;
3417 rc = checkMediaForConflicts(id,
3418 strLocationFull,
3419 strConflict,
3420 fIdentical);
3421 if (FAILED(rc)) return rc;
3422
3423 if (!fIdentical)
3424 {
3425 if (strConflict.length())
3426 return setError(E_INVALIDARG,
3427 tr("Cannot register the hard disk '%s' {%RTuuid} because a %s already exists"),
3428 strLocationFull.c_str(),
3429 id.raw(),
3430 strConflict.c_str(),
3431 m->strSettingsFilePath.c_str());
3432
3433 // store base (root) hard disks in the list
3434 if (pParent.isNull())
3435 m->allHardDisks.getList().push_back(pMedium);
3436 // access the list directly because we already locked the list above
3437
3438 // store all hard disks (even differencing images) in the map
3439 m->mapHardDisks[id] = pMedium;
3440
3441 if (pllRegistriesThatNeedSaving)
3442 pMedium->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3443 }
3444
3445 return rc;
3446}
3447
3448/**
3449 * Removes the given hard disk from the hard disk registry.
3450 *
3451 * @param aHardDisk Hard disk object to remove.
3452 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3453 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3454 *
3455 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3456 */
3457HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3458 GuidList *pllRegistriesThatNeedSaving)
3459{
3460 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3461
3462 AutoCaller autoCaller(this);
3463 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3464
3465 AutoCaller hardDiskCaller(aHardDisk);
3466 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3467
3468 // caller must hold the media tree write lock
3469 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3470
3471 Guid id;
3472 ComObjPtr<Medium> pParent;
3473 {
3474 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3475 id = aHardDisk->getId();
3476 pParent = aHardDisk->getParent();
3477 }
3478
3479 // remove base (root) hard disks from the list
3480 if (pParent.isNull())
3481 m->allHardDisks.getList().remove(aHardDisk);
3482 // access the list directly because caller must have locked the list
3483
3484 // remove all hard disks (even differencing images) from map
3485 size_t cnt = m->mapHardDisks.erase(id);
3486 Assert(cnt == 1);
3487 NOREF(cnt);
3488
3489 if (pllRegistriesThatNeedSaving)
3490 aHardDisk->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3491
3492 return S_OK;
3493}
3494
3495/**
3496 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3497 *
3498 * @param argImage Image object to remember.
3499 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3500 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3501 *
3502 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3503 */
3504HRESULT VirtualBox::registerImage(Medium *pMedium,
3505 DeviceType_T argType,
3506 GuidList *pllRegistriesThatNeedSaving)
3507{
3508 AssertReturn(pMedium != NULL, E_INVALIDARG);
3509
3510 AutoCaller autoCaller(this);
3511 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3512
3513 AutoCaller imageCaller(pMedium);
3514 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3515
3516 // caller must hold the media tree write lock
3517 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3518
3519 Guid id;
3520 Utf8Str strLocationFull;
3521 ComObjPtr<Medium> pParent;
3522 {
3523 AutoReadLock al(pMedium COMMA_LOCKVAL_SRC_POS);
3524 id = pMedium->getId();
3525 strLocationFull = pMedium->getLocationFull();
3526 pParent = pMedium->getParent();
3527 }
3528
3529 // work on DVDs or floppies list?
3530 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3531
3532 HRESULT rc;
3533 // lock the images lists (list + map) while checking for conflicts
3534 AutoWriteLock al(all.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3535
3536 Utf8Str strConflict;
3537 bool fIdentical;
3538 rc = checkMediaForConflicts(id,
3539 strLocationFull,
3540 strConflict,
3541 fIdentical);
3542 if (FAILED(rc)) return rc;
3543
3544 if (!fIdentical)
3545 {
3546 if (strConflict.length())
3547 return setError(VBOX_E_INVALID_OBJECT_STATE,
3548 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists"),
3549 strLocationFull.c_str(),
3550 id.raw(),
3551 strConflict.c_str());
3552
3553 // add to the collection
3554 all.getList().push_back(pMedium);
3555 // access the list directly because we already locked the list above
3556
3557 if (pllRegistriesThatNeedSaving)
3558 pMedium->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3559 }
3560
3561 return rc;
3562}
3563
3564/**
3565 * Removes the given image from the CD/DVD or floppy image registry.
3566 *
3567 * @param argImage Image object to remove.
3568 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3569 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3570 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3571 *
3572 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3573 */
3574HRESULT VirtualBox::unregisterImage(Medium *argImage,
3575 DeviceType_T argType,
3576 GuidList *pllRegistriesThatNeedSaving)
3577{
3578 AssertReturn(argImage != NULL, E_INVALIDARG);
3579
3580 AutoCaller autoCaller(this);
3581 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3582
3583 AutoCaller imageCaller(argImage);
3584 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3585
3586 // caller must hold the media tree write lock
3587 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3588
3589 Guid id;
3590 ComObjPtr<Medium> pParent;
3591 {
3592 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3593 id = argImage->getId();
3594 pParent = argImage->getParent();
3595 }
3596
3597 // work on DVDs or floppies list?
3598 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3599
3600 // access the list directly because the caller must have requested the lock
3601 all.getList().remove(argImage);
3602
3603 HRESULT rc = S_OK;
3604
3605 if (pllRegistriesThatNeedSaving)
3606 argImage->addToRegistryIDList(*pllRegistriesThatNeedSaving);
3607
3608 return rc;
3609}
3610
3611/**
3612 * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
3613 * with children appearing before their parents.
3614 * @param llMedia
3615 * @param pMedium
3616 */
3617void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
3618{
3619 // recurse first, then add ourselves; this way children end up on the
3620 // list before their parents
3621
3622 const MediaList &llChildren = pMedium->getChildren();
3623 for (MediaList::const_iterator it = llChildren.begin();
3624 it != llChildren.end();
3625 ++it)
3626 {
3627 Medium *pChild = *it;
3628 pushMediumToListWithChildren(llMedia, pChild);
3629 }
3630
3631 Log(("Pushing medium %RTuuid\n", pMedium->getId().raw()));
3632 llMedia.push_back(pMedium);
3633}
3634
3635/**
3636 * Unregisters all Medium objects which belong to the given machine registry.
3637 * Gets called from Machine::uninit() just before the machine object dies
3638 * and must only be called with a machine UUID as the registry ID.
3639 *
3640 * Locks the media tree.
3641 *
3642 * @param uuidMachine Medium registry ID (always a machine UUID)
3643 * @return
3644 */
3645HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine)
3646{
3647 Assert(!uuidMachine.isEmpty());
3648
3649 LogFlowFuncEnter();
3650
3651 AutoCaller autoCaller(this);
3652 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3653
3654 MediaList llMedia2Close;
3655
3656 {
3657 AutoWriteLock mlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3658 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
3659 it != m->allHardDisks.getList().end();
3660 ++it)
3661 {
3662 ComObjPtr<Medium> pMedium = *it;
3663
3664 if (pMedium->isInRegistry(uuidMachine))
3665 // recursively with children first
3666 pushMediumToListWithChildren(llMedia2Close, pMedium);
3667 }
3668 }
3669
3670 for (MediaList::iterator it = llMedia2Close.begin();
3671 it != llMedia2Close.end();
3672 ++it)
3673 {
3674 ComObjPtr<Medium> pMedium = *it;
3675 Log(("Closing medium %RTuuid\n", pMedium->getId().raw()));
3676 AutoCaller mac(pMedium);
3677 pMedium->close(NULL /* pfNeedsGlobalSaveSettings*/, mac);
3678 }
3679
3680 LogFlowFuncLeave();
3681
3682 return S_OK;
3683}
3684
3685/**
3686 * Removes the given machine object from the internal list of registered machines.
3687 * Called from Machine::Unregister().
3688 * @param pMachine
3689 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
3690 * @return
3691 */
3692HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
3693 const Guid &id)
3694{
3695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3696
3697 // remove from the collection of registered machines
3698 m->allMachines.removeChild(pMachine);
3699
3700 // save the global registry
3701 HRESULT rc = saveSettings();
3702
3703 alock.release();
3704
3705 /* fire an event */
3706 onMachineRegistered(id, FALSE);
3707
3708 return rc;
3709}
3710
3711/**
3712 * Adds uuid to llRegistriesThatNeedSaving unless it's already on the list.
3713 *
3714 * @todo maybe there's something in libstdc++ for this
3715 *
3716 * @param llRegistriesThatNeedSaving
3717 * @param uuid
3718 */
3719void VirtualBox::addGuidToListUniquely(GuidList &llRegistriesThatNeedSaving,
3720 Guid uuid)
3721{
3722 for (GuidList::const_iterator it = llRegistriesThatNeedSaving.begin();
3723 it != llRegistriesThatNeedSaving.end();
3724 ++it)
3725 {
3726 if (*it == uuid)
3727 // uuid is already in list:
3728 return;
3729 }
3730
3731 llRegistriesThatNeedSaving.push_back(uuid);
3732}
3733
3734/**
3735 * Saves all settings files according to the given list of UUIDs, which are
3736 * either machine IDs (in which case Machine::saveSettings is invoked) or
3737 * the global registry UUID (in which case VirtualBox::saveSettings is invoked).
3738 *
3739 * This locks machines and the VirtualBox object as necessary, so better not
3740 * hold any locks before calling this.
3741 *
3742 * @param llRegistriesThatNeedSaving
3743 * @return
3744 */
3745HRESULT VirtualBox::saveRegistries(const GuidList &llRegistriesThatNeedSaving)
3746{
3747 bool fNeedsGlobalSettings = false;
3748 HRESULT rc = S_OK;
3749
3750 for (GuidList::const_iterator it = llRegistriesThatNeedSaving.begin();
3751 it != llRegistriesThatNeedSaving.end();
3752 ++it)
3753 {
3754 const Guid &uuid = *it;
3755
3756 if (uuid == getGlobalRegistryId())
3757 fNeedsGlobalSettings = true;
3758 else
3759 {
3760 // should be machine ID then:
3761 ComObjPtr<Machine> pMachine;
3762 rc = findMachine(uuid,
3763 false /* fPermitInaccessible */,
3764 false /* aSetError */,
3765 &pMachine);
3766 if (SUCCEEDED(rc))
3767 {
3768 AutoCaller autoCaller(pMachine);
3769 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3770 AutoWriteLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);
3771 rc = pMachine->saveSettings(&fNeedsGlobalSettings,
3772 Machine::SaveS_Force); // caller said save, so stop arguing
3773 }
3774
3775 if (FAILED(rc))
3776 return rc;
3777 }
3778 }
3779
3780 if (fNeedsGlobalSettings)
3781 {
3782 AutoWriteLock vlock(this COMMA_LOCKVAL_SRC_POS);
3783 rc = saveSettings();
3784 }
3785
3786 return S_OK;
3787}
3788
3789/**
3790 * Creates the path to the specified file according to the path information
3791 * present in the file name.
3792 *
3793 * Note that the given file name must contain the full path otherwise the
3794 * extracted relative path will be created based on the current working
3795 * directory which is normally unknown.
3796 *
3797 * @param aFileName Full file name which path needs to be created.
3798 *
3799 * @return Extended error information on failure to create the path.
3800 */
3801/* static */
3802HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3803{
3804 Utf8Str strDir(strFileName);
3805 strDir.stripFilename();
3806 if (!RTDirExists(strDir.c_str()))
3807 {
3808 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3809 if (RT_FAILURE(vrc))
3810 return setErrorStatic(E_FAIL,
3811 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
3812 strDir.c_str(),
3813 vrc));
3814 }
3815
3816 return S_OK;
3817}
3818
3819/**
3820 * Handles unexpected exceptions by turning them into COM errors in release
3821 * builds or by hitting a breakpoint in the release builds.
3822 *
3823 * Usage pattern:
3824 * @code
3825 try
3826 {
3827 // ...
3828 }
3829 catch (LaLalA)
3830 {
3831 // ...
3832 }
3833 catch (...)
3834 {
3835 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3836 }
3837 * @endcode
3838 *
3839 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3840 */
3841/* static */
3842HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3843{
3844 try
3845 {
3846 /* re-throw the current exception */
3847 throw;
3848 }
3849 catch (const iprt::Error &err) // includes all XML exceptions
3850 {
3851 return setErrorStatic(E_FAIL,
3852 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
3853 err.what(),
3854 pszFile, iLine, pszFunction).c_str());
3855 }
3856 catch (const std::exception &err)
3857 {
3858 return setErrorStatic(E_FAIL,
3859 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3860 err.what(), typeid(err).name(),
3861 pszFile, iLine, pszFunction).c_str());
3862 }
3863 catch (...)
3864 {
3865 return setErrorStatic(E_FAIL,
3866 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
3867 pszFile, iLine, pszFunction).c_str());
3868 }
3869
3870 /* should not get here */
3871 AssertFailed();
3872 return E_FAIL;
3873}
3874
3875const Utf8Str& VirtualBox::settingsFilePath()
3876{
3877 return m->strSettingsFilePath;
3878}
3879
3880/**
3881 * Returns the lock handle which protects the media trees (hard disks,
3882 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3883 * are no longer protected by the VirtualBox lock, but by this more
3884 * specialized lock. Mind the locking order: always request this lock
3885 * after the VirtualBox object lock but before the locks of the media
3886 * objects contained in these lists. See AutoLock.h.
3887 */
3888RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3889{
3890 return m->lockMedia;
3891}
3892
3893/**
3894 * Thread function that watches the termination of all client processes
3895 * that have opened sessions using IMachine::LockMachine()
3896 */
3897// static
3898DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3899{
3900 LogFlowFuncEnter();
3901
3902 VirtualBox *that = (VirtualBox*)pvUser;
3903 Assert(that);
3904
3905 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3906 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3907
3908 SessionMachineVector machines;
3909 MachineVector spawnedMachines;
3910
3911 size_t cnt = 0;
3912 size_t cntSpawned = 0;
3913
3914#if defined(RT_OS_WINDOWS)
3915
3916 HRESULT hrc = CoInitializeEx(NULL,
3917 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3918 COINIT_SPEED_OVER_MEMORY);
3919 AssertComRC(hrc);
3920
3921 /// @todo (dmik) processes reaping!
3922
3923 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3924 handles[0] = that->m->updateReq;
3925
3926 do
3927 {
3928 AutoCaller autoCaller(that);
3929 /* VirtualBox has been early uninitialized, terminate */
3930 if (!autoCaller.isOk())
3931 break;
3932
3933 do
3934 {
3935 /* release the caller to let uninit() ever proceed */
3936 autoCaller.release();
3937
3938 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3939 handles,
3940 FALSE,
3941 INFINITE);
3942
3943 /* Restore the caller before using VirtualBox. If it fails, this
3944 * means VirtualBox is being uninitialized and we must terminate. */
3945 autoCaller.add();
3946 if (!autoCaller.isOk())
3947 break;
3948
3949 bool update = false;
3950
3951 if (rc == WAIT_OBJECT_0)
3952 {
3953 /* update event is signaled */
3954 update = true;
3955 }
3956 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3957 {
3958 /* machine mutex is released */
3959 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3960 update = true;
3961 }
3962 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3963 {
3964 /* machine mutex is abandoned due to client process termination */
3965 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3966 update = true;
3967 }
3968 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3969 {
3970 /* spawned VM process has terminated (normally or abnormally) */
3971 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
3972 checkForSpawnFailure();
3973 update = true;
3974 }
3975
3976 if (update)
3977 {
3978 /* close old process handles */
3979 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
3980 CloseHandle(handles[i]);
3981
3982 // lock the machines list for reading
3983 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3984
3985 /* obtain a new set of opened machines */
3986 cnt = 0;
3987 machines.clear();
3988
3989 for (MachinesOList::iterator it = that->m->allMachines.begin();
3990 it != that->m->allMachines.end();
3991 ++it)
3992 {
3993 /// @todo handle situations with more than 64 objects
3994 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3995 ("MAXIMUM_WAIT_OBJECTS reached"));
3996
3997 ComObjPtr<SessionMachine> sm;
3998 HANDLE ipcSem;
3999 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4000 {
4001 machines.push_back(sm);
4002 handles[1 + cnt] = ipcSem;
4003 ++cnt;
4004 }
4005 }
4006
4007 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4008
4009 /* obtain a new set of spawned machines */
4010 cntSpawned = 0;
4011 spawnedMachines.clear();
4012
4013 for (MachinesOList::iterator it = that->m->allMachines.begin();
4014 it != that->m->allMachines.end();
4015 ++it)
4016 {
4017 /// @todo handle situations with more than 64 objects
4018 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
4019 ("MAXIMUM_WAIT_OBJECTS reached"));
4020
4021 RTPROCESS pid;
4022 if ((*it)->isSessionSpawning(&pid))
4023 {
4024 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
4025 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
4026 pid, GetLastError()));
4027 if (rc == 0)
4028 {
4029 spawnedMachines.push_back(*it);
4030 handles[1 + cnt + cntSpawned] = ph;
4031 ++cntSpawned;
4032 }
4033 }
4034 }
4035
4036 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4037
4038 // machines lock unwinds here
4039 }
4040 }
4041 while (true);
4042 }
4043 while (0);
4044
4045 /* close old process handles */
4046 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4047 CloseHandle(handles[i]);
4048
4049 /* release sets of machines if any */
4050 machines.clear();
4051 spawnedMachines.clear();
4052
4053 ::CoUninitialize();
4054
4055#elif defined(RT_OS_OS2)
4056
4057 /// @todo (dmik) processes reaping!
4058
4059 /* according to PMREF, 64 is the maximum for the muxwait list */
4060 SEMRECORD handles[64];
4061
4062 HMUX muxSem = NULLHANDLE;
4063
4064 do
4065 {
4066 AutoCaller autoCaller(that);
4067 /* VirtualBox has been early uninitialized, terminate */
4068 if (!autoCaller.isOk())
4069 break;
4070
4071 do
4072 {
4073 /* release the caller to let uninit() ever proceed */
4074 autoCaller.release();
4075
4076 int vrc = RTSemEventWait(that->m->updateReq, 500);
4077
4078 /* Restore the caller before using VirtualBox. If it fails, this
4079 * means VirtualBox is being uninitialized and we must terminate. */
4080 autoCaller.add();
4081 if (!autoCaller.isOk())
4082 break;
4083
4084 bool update = false;
4085 bool updateSpawned = false;
4086
4087 if (RT_SUCCESS(vrc))
4088 {
4089 /* update event is signaled */
4090 update = true;
4091 updateSpawned = true;
4092 }
4093 else
4094 {
4095 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4096 ("RTSemEventWait returned %Rrc\n", vrc));
4097
4098 /* are there any mutexes? */
4099 if (cnt > 0)
4100 {
4101 /* figure out what's going on with machines */
4102
4103 unsigned long semId = 0;
4104 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
4105 SEM_IMMEDIATE_RETURN, &semId);
4106
4107 if (arc == NO_ERROR)
4108 {
4109 /* machine mutex is normally released */
4110 Assert(semId >= 0 && semId < cnt);
4111 if (semId >= 0 && semId < cnt)
4112 {
4113#if 0//def DEBUG
4114 {
4115 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4116 LogFlowFunc(("released mutex: machine='%ls'\n",
4117 machines[semId]->name().raw()));
4118 }
4119#endif
4120 machines[semId]->checkForDeath();
4121 }
4122 update = true;
4123 }
4124 else if (arc == ERROR_SEM_OWNER_DIED)
4125 {
4126 /* machine mutex is abandoned due to client process
4127 * termination; find which mutex is in the Owner Died
4128 * state */
4129 for (size_t i = 0; i < cnt; ++ i)
4130 {
4131 PID pid; TID tid;
4132 unsigned long reqCnt;
4133 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
4134 if (arc == ERROR_SEM_OWNER_DIED)
4135 {
4136 /* close the dead mutex as asked by PMREF */
4137 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
4138
4139 Assert(i >= 0 && i < cnt);
4140 if (i >= 0 && i < cnt)
4141 {
4142#if 0//def DEBUG
4143 {
4144 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4145 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4146 machines[i]->name().raw()));
4147 }
4148#endif
4149 machines[i]->checkForDeath();
4150 }
4151 }
4152 }
4153 update = true;
4154 }
4155 else
4156 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4157 ("DosWaitMuxWaitSem returned %d\n", arc));
4158 }
4159
4160 /* are there any spawning sessions? */
4161 if (cntSpawned > 0)
4162 {
4163 for (size_t i = 0; i < cntSpawned; ++ i)
4164 updateSpawned |= (spawnedMachines[i])->
4165 checkForSpawnFailure();
4166 }
4167 }
4168
4169 if (update || updateSpawned)
4170 {
4171 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4172
4173 if (update)
4174 {
4175 /* close the old muxsem */
4176 if (muxSem != NULLHANDLE)
4177 ::DosCloseMuxWaitSem(muxSem);
4178
4179 /* obtain a new set of opened machines */
4180 cnt = 0;
4181 machines.clear();
4182
4183 for (MachinesOList::iterator it = that->m->allMachines.begin();
4184 it != that->m->allMachines.end(); ++ it)
4185 {
4186 /// @todo handle situations with more than 64 objects
4187 AssertMsg(cnt <= 64 /* according to PMREF */,
4188 ("maximum of 64 mutex semaphores reached (%d)",
4189 cnt));
4190
4191 ComObjPtr<SessionMachine> sm;
4192 HMTX ipcSem;
4193 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4194 {
4195 machines.push_back(sm);
4196 handles[cnt].hsemCur = (HSEM)ipcSem;
4197 handles[cnt].ulUser = cnt;
4198 ++ cnt;
4199 }
4200 }
4201
4202 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4203
4204 if (cnt > 0)
4205 {
4206 /* create a new muxsem */
4207 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4208 handles,
4209 DCMW_WAIT_ANY);
4210 AssertMsg(arc == NO_ERROR,
4211 ("DosCreateMuxWaitSem returned %d\n", arc));
4212 NOREF(arc);
4213 }
4214 }
4215
4216 if (updateSpawned)
4217 {
4218 /* obtain a new set of spawned machines */
4219 spawnedMachines.clear();
4220
4221 for (MachinesOList::iterator it = that->m->allMachines.begin();
4222 it != that->m->allMachines.end(); ++ it)
4223 {
4224 if ((*it)->isSessionSpawning())
4225 spawnedMachines.push_back(*it);
4226 }
4227
4228 cntSpawned = spawnedMachines.size();
4229 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4230 }
4231 }
4232 }
4233 while (true);
4234 }
4235 while (0);
4236
4237 /* close the muxsem */
4238 if (muxSem != NULLHANDLE)
4239 ::DosCloseMuxWaitSem(muxSem);
4240
4241 /* release sets of machines if any */
4242 machines.clear();
4243 spawnedMachines.clear();
4244
4245#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4246
4247 bool update = false;
4248 bool updateSpawned = false;
4249
4250 do
4251 {
4252 AutoCaller autoCaller(that);
4253 if (!autoCaller.isOk())
4254 break;
4255
4256 do
4257 {
4258 /* release the caller to let uninit() ever proceed */
4259 autoCaller.release();
4260
4261 int rc = RTSemEventWait(that->m->updateReq, 500);
4262
4263 /*
4264 * Restore the caller before using VirtualBox. If it fails, this
4265 * means VirtualBox is being uninitialized and we must terminate.
4266 */
4267 autoCaller.add();
4268 if (!autoCaller.isOk())
4269 break;
4270
4271 if (RT_SUCCESS(rc) || update || updateSpawned)
4272 {
4273 /* RT_SUCCESS(rc) means an update event is signaled */
4274
4275 // lock the machines list for reading
4276 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4277
4278 if (RT_SUCCESS(rc) || update)
4279 {
4280 /* obtain a new set of opened machines */
4281 machines.clear();
4282
4283 for (MachinesOList::iterator it = that->m->allMachines.begin();
4284 it != that->m->allMachines.end();
4285 ++it)
4286 {
4287 ComObjPtr<SessionMachine> sm;
4288 if ((*it)->isSessionOpenOrClosing(sm))
4289 machines.push_back(sm);
4290 }
4291
4292 cnt = machines.size();
4293 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4294 }
4295
4296 if (RT_SUCCESS(rc) || updateSpawned)
4297 {
4298 /* obtain a new set of spawned machines */
4299 spawnedMachines.clear();
4300
4301 for (MachinesOList::iterator it = that->m->allMachines.begin();
4302 it != that->m->allMachines.end();
4303 ++it)
4304 {
4305 if ((*it)->isSessionSpawning())
4306 spawnedMachines.push_back(*it);
4307 }
4308
4309 cntSpawned = spawnedMachines.size();
4310 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4311 }
4312
4313 // machines lock unwinds here
4314 }
4315
4316 update = false;
4317 for (size_t i = 0; i < cnt; ++ i)
4318 update |= (machines[i])->checkForDeath();
4319
4320 updateSpawned = false;
4321 for (size_t i = 0; i < cntSpawned; ++ i)
4322 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4323
4324 /* reap child processes */
4325 {
4326 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4327 if (that->m->llProcesses.size())
4328 {
4329 LogFlowFunc(("UPDATE: child process count = %d\n",
4330 that->m->llProcesses.size()));
4331 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4332 while (it != that->m->llProcesses.end())
4333 {
4334 RTPROCESS pid = *it;
4335 RTPROCSTATUS status;
4336 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4337 if (vrc == VINF_SUCCESS)
4338 {
4339 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4340 pid, pid, status.iStatus,
4341 status.enmReason));
4342 it = that->m->llProcesses.erase(it);
4343 }
4344 else
4345 {
4346 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4347 pid, pid, vrc));
4348 if (vrc != VERR_PROCESS_RUNNING)
4349 {
4350 /* remove the process if it is not already running */
4351 it = that->m->llProcesses.erase(it);
4352 }
4353 else
4354 ++ it;
4355 }
4356 }
4357 }
4358 }
4359 }
4360 while (true);
4361 }
4362 while (0);
4363
4364 /* release sets of machines if any */
4365 machines.clear();
4366 spawnedMachines.clear();
4367
4368#else
4369# error "Port me!"
4370#endif
4371
4372 LogFlowFuncLeave();
4373 return 0;
4374}
4375
4376/**
4377 * Thread function that handles custom events posted using #postEvent().
4378 */
4379// static
4380DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4381{
4382 LogFlowFuncEnter();
4383
4384 AssertReturn(pvUser, VERR_INVALID_POINTER);
4385
4386 // create an event queue for the current thread
4387 EventQueue *eventQ = new EventQueue();
4388 AssertReturn(eventQ, VERR_NO_MEMORY);
4389
4390 // return the queue to the one who created this thread
4391 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4392 // signal that we're ready
4393 RTThreadUserSignal(thread);
4394
4395 while (RT_SUCCESS(eventQ->processEventQueue(RT_INDEFINITE_WAIT)))
4396 /* nothing */ ;
4397
4398 delete eventQ;
4399
4400 LogFlowFuncLeave();
4401
4402 return 0;
4403}
4404
4405
4406////////////////////////////////////////////////////////////////////////////////
4407
4408/**
4409 * Takes the current list of registered callbacks of the managed VirtualBox
4410 * instance, and calls #handleCallback() for every callback item from the
4411 * list, passing the item as an argument.
4412 *
4413 * @note Locks the managed VirtualBox object for reading but leaves the lock
4414 * before iterating over callbacks and calling their methods.
4415 */
4416void *VirtualBox::CallbackEvent::handler()
4417{
4418 if (!mVirtualBox)
4419 return NULL;
4420
4421 AutoCaller autoCaller(mVirtualBox);
4422 if (!autoCaller.isOk())
4423 {
4424 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4425 autoCaller.state()));
4426 /* We don't need mVirtualBox any more, so release it */
4427 mVirtualBox = NULL;
4428 return NULL;
4429 }
4430
4431 {
4432 VBoxEventDesc evDesc;
4433 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4434
4435 evDesc.fire(/* don't wait for delivery */0);
4436 }
4437
4438 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4439 return NULL;
4440}
4441
4442//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4443//{
4444// return E_NOTIMPL;
4445//}
4446
4447STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4448{
4449 CheckComArgStrNotEmptyOrNull(aName);
4450 CheckComArgNotNull(aServer);
4451
4452 AutoCaller autoCaller(this);
4453 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4454
4455 ComObjPtr<DHCPServer> dhcpServer;
4456 dhcpServer.createObject();
4457 HRESULT rc = dhcpServer->init(this, aName);
4458 if (FAILED(rc)) return rc;
4459
4460 rc = registerDHCPServer(dhcpServer, true);
4461 if (FAILED(rc)) return rc;
4462
4463 dhcpServer.queryInterfaceTo(aServer);
4464
4465 return rc;
4466}
4467
4468STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4469{
4470 CheckComArgStrNotEmptyOrNull(aName);
4471 CheckComArgNotNull(aServer);
4472
4473 AutoCaller autoCaller(this);
4474 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4475
4476 HRESULT rc;
4477 Bstr bstr;
4478 ComPtr<DHCPServer> found;
4479
4480 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4481
4482 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4483 it != m->allDHCPServers.end();
4484 ++it)
4485 {
4486 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4487 if (FAILED(rc)) throw rc;
4488
4489 if (bstr == aName)
4490 {
4491 found = *it;
4492 break;
4493 }
4494 }
4495
4496 if (!found)
4497 return E_INVALIDARG;
4498
4499 return found.queryInterfaceTo(aServer);
4500}
4501
4502STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4503{
4504 CheckComArgNotNull(aServer);
4505
4506 AutoCaller autoCaller(this);
4507 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4508
4509 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4510
4511 return rc;
4512}
4513
4514/**
4515 * Remembers the given dhcp server by storing it in the hard disk registry.
4516 *
4517 * @param aDHCPServer Dhcp Server object to remember.
4518 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4519 *
4520 * When @a aSaveRegistry is @c true, this operation may fail because of the
4521 * failed #saveSettings() method it calls. In this case, the dhcp server object
4522 * will not be remembered. It is therefore the responsibility of the caller to
4523 * call this method as the last step of some action that requires registration
4524 * in order to make sure that only fully functional dhcp server objects get
4525 * registered.
4526 *
4527 * @note Locks this object for writing and @a aDHCPServer for reading.
4528 */
4529HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4530 bool aSaveRegistry /*= true*/)
4531{
4532 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4533
4534 AutoCaller autoCaller(this);
4535 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4536
4537 AutoCaller dhcpServerCaller(aDHCPServer);
4538 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4539
4540 Bstr name;
4541 HRESULT rc;
4542 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4543 if (FAILED(rc)) return rc;
4544
4545 ComPtr<IDHCPServer> existing;
4546 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
4547 if (SUCCEEDED(rc))
4548 return E_INVALIDARG;
4549
4550 rc = S_OK;
4551
4552 m->allDHCPServers.addChild(aDHCPServer);
4553
4554 if (aSaveRegistry)
4555 {
4556 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4557 rc = saveSettings();
4558 vboxLock.release();
4559
4560 if (FAILED(rc))
4561 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4562 }
4563
4564 return rc;
4565}
4566
4567/**
4568 * Removes the given hard disk from the hard disk registry.
4569 *
4570 * @param aHardDisk Hard disk object to remove.
4571 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4572 *
4573 * When @a aSaveRegistry is @c true, this operation may fail because of the
4574 * failed #saveSettings() method it calls. In this case, the hard disk object
4575 * will NOT be removed from the registry when this method returns. It is
4576 * therefore the responsibility of the caller to call this method as the first
4577 * step of some action that requires unregistration, before calling uninit() on
4578 * @a aHardDisk.
4579 *
4580 * @note Locks this object for writing and @a aHardDisk for reading.
4581 */
4582HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4583 bool aSaveRegistry /*= true*/)
4584{
4585 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4586
4587 AutoCaller autoCaller(this);
4588 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4589
4590 AutoCaller dhcpServerCaller(aDHCPServer);
4591 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4592
4593 m->allDHCPServers.removeChild(aDHCPServer);
4594
4595 HRESULT rc = S_OK;
4596
4597 if (aSaveRegistry)
4598 {
4599 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4600 rc = saveSettings();
4601 vboxLock.release();
4602
4603 if (FAILED(rc))
4604 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4605 }
4606
4607 return rc;
4608}
4609
4610/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use