VirtualBox

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

Last change on this file since 31825 was 31814, checked in by vboxsync, 14 years ago

Main: cleanup medium objects from media registry when machine object is uninitialized (fixes totally misleading error reports when machine XML cannot be loaded because the actually relevant error gets overwritten)

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

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