VirtualBox

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

Last change on this file since 31685 was 31615, checked in by vboxsync, 14 years ago

Main: Implemenation of per-machine media registries; VirtualBox::openMedium() no longer adds media to the global registry, instead a media are stored in a machine XML registry after Machine::AttachDevice() has been called; Machine::AttachDevice() now takes an IMedium object instead of a UUID; also make Machine::Unregister() work again for inaccessible machines

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 139.9 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 31615 2010-08-12 18:12:39Z 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 {
3350 return setError(E_INVALIDARG,
3351 tr("Cannot register the hard disk '%s' {%RTuuid} because a %s already exists"),
3352 strLocationFull.c_str(),
3353 id.raw(),
3354 strConflict.c_str(),
3355 m->strSettingsFilePath.c_str());
3356 }
3357
3358 // store base (root) hard disks in the list
3359 if (pParent.isNull())
3360 m->allHardDisks.getList().push_back(pMedium);
3361 // access the list directly because we already locked the list above
3362
3363 // store all hard disks (even differencing images) in the map
3364 m->mapHardDisks[id] = pMedium;
3365
3366 if (pfNeedsGlobalSaveSettings)
3367 // global settings need saving only if the medium is to be saved in the global registry
3368 if (pMedium->getRegistryId() == m->uuidMediaRegistry)
3369 *pfNeedsGlobalSaveSettings = true;
3370
3371 return rc;
3372}
3373
3374/**
3375 * Removes the given hard disk from the hard disk registry.
3376 *
3377 * @param aHardDisk Hard disk object to remove.
3378 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3379 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3380 *
3381 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3382 */
3383HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3384 bool *pfNeedsGlobalSaveSettings)
3385{
3386 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3387
3388 AutoCaller autoCaller(this);
3389 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3390
3391 AutoCaller hardDiskCaller(aHardDisk);
3392 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3393
3394 // caller must hold the media tree write lock
3395 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3396
3397 Guid id;
3398 ComObjPtr<Medium> pParent;
3399 {
3400 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3401 id = aHardDisk->getId();
3402 pParent = aHardDisk->getParent();
3403 }
3404
3405 // remove base (root) hard disks from the list
3406 if (pParent.isNull())
3407 m->allHardDisks.getList().remove(aHardDisk);
3408 // access the list directly because caller must have locked the list
3409
3410 // remove all hard disks (even differencing images) from map
3411 size_t cnt = m->mapHardDisks.erase(id);
3412 Assert(cnt == 1);
3413 NOREF(cnt);
3414
3415 if (pfNeedsGlobalSaveSettings)
3416 // global settings need saving only if the medium is to be saved in the global registry
3417 if (aHardDisk->getRegistryId() == m->uuidMediaRegistry)
3418 *pfNeedsGlobalSaveSettings = true;
3419
3420 return S_OK;
3421}
3422
3423/**
3424 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3425 *
3426 * @param argImage Image object to remember.
3427 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3428 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3429 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3430 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3431 *
3432 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3433 */
3434HRESULT VirtualBox::registerImage(Medium *pMedium,
3435 DeviceType_T argType,
3436 bool *pfNeedsGlobalSaveSettings)
3437{
3438 AssertReturn(pMedium != NULL, E_INVALIDARG);
3439
3440 AutoCaller autoCaller(this);
3441 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3442
3443 AutoCaller imageCaller(pMedium);
3444 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3445
3446 // caller must hold the media tree write lock
3447 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3448
3449 Guid id;
3450 Utf8Str strLocationFull;
3451 ComObjPtr<Medium> pParent;
3452 {
3453 AutoReadLock al(pMedium COMMA_LOCKVAL_SRC_POS);
3454 id = pMedium->getId();
3455 strLocationFull = pMedium->getLocationFull();
3456 pParent = pMedium->getParent();
3457 }
3458
3459 // work on DVDs or floppies list?
3460 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3461
3462 HRESULT rc;
3463 // lock the images lists (list + map) while checking for conflicts
3464 AutoWriteLock al(all.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3465
3466 Utf8Str strConflict;
3467 rc = checkMediaForConflicts2(id,
3468 strLocationFull,
3469 strConflict);
3470 if (FAILED(rc)) return rc;
3471
3472 if (strConflict.length())
3473 return setError(VBOX_E_INVALID_OBJECT_STATE,
3474 tr("Cannot register the image '%s' with UUID {%RTuuid} from '%s' because a %s already exists"),
3475 strLocationFull.c_str(),
3476 id.raw(),
3477 getRegistryPath(pMedium).c_str(),
3478 strConflict.c_str());
3479
3480 // add to the collection
3481 all.getList().push_back(pMedium);
3482 // access the list directly because we already locked the list above
3483
3484 if (pfNeedsGlobalSaveSettings)
3485 // global settings need saving only if the medium is to be saved in the global registry
3486 if (pMedium->getRegistryId() == m->uuidMediaRegistry)
3487 *pfNeedsGlobalSaveSettings = true;
3488
3489 return rc;
3490}
3491
3492/**
3493 * Removes the given image from the CD/DVD or floppy image registry.
3494 *
3495 * @param argImage Image object to remove.
3496 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3497 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3498 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3499 *
3500 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3501 */
3502HRESULT VirtualBox::unregisterImage(Medium *argImage,
3503 DeviceType_T argType,
3504 bool *pfNeedsGlobalSaveSettings)
3505{
3506 AssertReturn(argImage != NULL, E_INVALIDARG);
3507
3508 AutoCaller autoCaller(this);
3509 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3510
3511 AutoCaller imageCaller(argImage);
3512 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3513
3514 // caller must hold the media tree write lock
3515 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3516
3517 Guid id;
3518 ComObjPtr<Medium> pParent;
3519 {
3520 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3521 id = argImage->getId();
3522 pParent = argImage->getParent();
3523 }
3524
3525 // work on DVDs or floppies list?
3526 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3527
3528 // access the list directly because the caller must have requested the lock
3529 all.getList().remove(argImage);
3530
3531 HRESULT rc = S_OK;
3532
3533 if (pfNeedsGlobalSaveSettings)
3534 // global settings need saving only if the medium is to be saved in the global registry
3535 if (argImage->getRegistryId() == m->uuidMediaRegistry)
3536 *pfNeedsGlobalSaveSettings = true;
3537
3538 return rc;
3539}
3540
3541/**
3542 * Removes the given machine object from the internal list of registered machines.
3543 * Called from Machine::Unregister().
3544 * @param pMachine
3545 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
3546 * @return
3547 */
3548HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
3549 const Guid &id)
3550{
3551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3552
3553 // remove from the collection of registered machines
3554 m->allMachines.removeChild(pMachine);
3555
3556 // save the global registry
3557 HRESULT rc = saveSettings();
3558
3559 alock.release();
3560
3561 /* fire an event */
3562 onMachineRegistered(id, FALSE);
3563
3564 return rc;
3565}
3566
3567/**
3568 * Creates the path to the specified file according to the path information
3569 * present in the file name.
3570 *
3571 * Note that the given file name must contain the full path otherwise the
3572 * extracted relative path will be created based on the current working
3573 * directory which is normally unknown.
3574 *
3575 * @param aFileName Full file name which path needs to be created.
3576 *
3577 * @return Extended error information on failure to create the path.
3578 */
3579/* static */
3580HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3581{
3582 Utf8Str strDir(strFileName);
3583 strDir.stripFilename();
3584 if (!RTDirExists(strDir.c_str()))
3585 {
3586 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3587 if (RT_FAILURE(vrc))
3588 return setErrorStatic(E_FAIL,
3589 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
3590 strDir.c_str(),
3591 vrc));
3592 }
3593
3594 return S_OK;
3595}
3596
3597/**
3598 * Handles unexpected exceptions by turning them into COM errors in release
3599 * builds or by hitting a breakpoint in the release builds.
3600 *
3601 * Usage pattern:
3602 * @code
3603 try
3604 {
3605 // ...
3606 }
3607 catch (LaLalA)
3608 {
3609 // ...
3610 }
3611 catch (...)
3612 {
3613 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3614 }
3615 * @endcode
3616 *
3617 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3618 */
3619/* static */
3620HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3621{
3622 try
3623 {
3624 /* re-throw the current exception */
3625 throw;
3626 }
3627 catch (const iprt::Error &err) // includes all XML exceptions
3628 {
3629 return setErrorStatic(E_FAIL,
3630 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
3631 err.what(),
3632 pszFile, iLine, pszFunction).c_str());
3633 }
3634 catch (const std::exception &err)
3635 {
3636 return setErrorStatic(E_FAIL,
3637 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3638 err.what(), typeid(err).name(),
3639 pszFile, iLine, pszFunction).c_str());
3640 }
3641 catch (...)
3642 {
3643 return setErrorStatic(E_FAIL,
3644 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
3645 pszFile, iLine, pszFunction).c_str());
3646 }
3647
3648 /* should not get here */
3649 AssertFailed();
3650 return E_FAIL;
3651}
3652
3653const Utf8Str& VirtualBox::settingsFilePath()
3654{
3655 return m->strSettingsFilePath;
3656}
3657
3658/**
3659 * Returns the lock handle which protects the media trees (hard disks,
3660 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3661 * are no longer protected by the VirtualBox lock, but by this more
3662 * specialized lock. Mind the locking order: always request this lock
3663 * after the VirtualBox object lock but before the locks of the media
3664 * objects contained in these lists. See AutoLock.h.
3665 */
3666RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3667{
3668 return m->lockMedia;
3669}
3670
3671/**
3672 * Thread function that watches the termination of all client processes
3673 * that have opened sessions using IMachine::LockMachine()
3674 */
3675// static
3676DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3677{
3678 LogFlowFuncEnter();
3679
3680 VirtualBox *that = (VirtualBox*)pvUser;
3681 Assert(that);
3682
3683 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3684 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3685
3686 SessionMachineVector machines;
3687 MachineVector spawnedMachines;
3688
3689 size_t cnt = 0;
3690 size_t cntSpawned = 0;
3691
3692#if defined(RT_OS_WINDOWS)
3693
3694 HRESULT hrc = CoInitializeEx(NULL,
3695 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3696 COINIT_SPEED_OVER_MEMORY);
3697 AssertComRC(hrc);
3698
3699 /// @todo (dmik) processes reaping!
3700
3701 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3702 handles[0] = that->m->updateReq;
3703
3704 do
3705 {
3706 AutoCaller autoCaller(that);
3707 /* VirtualBox has been early uninitialized, terminate */
3708 if (!autoCaller.isOk())
3709 break;
3710
3711 do
3712 {
3713 /* release the caller to let uninit() ever proceed */
3714 autoCaller.release();
3715
3716 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3717 handles,
3718 FALSE,
3719 INFINITE);
3720
3721 /* Restore the caller before using VirtualBox. If it fails, this
3722 * means VirtualBox is being uninitialized and we must terminate. */
3723 autoCaller.add();
3724 if (!autoCaller.isOk())
3725 break;
3726
3727 bool update = false;
3728
3729 if (rc == WAIT_OBJECT_0)
3730 {
3731 /* update event is signaled */
3732 update = true;
3733 }
3734 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3735 {
3736 /* machine mutex is released */
3737 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3738 update = true;
3739 }
3740 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3741 {
3742 /* machine mutex is abandoned due to client process termination */
3743 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3744 update = true;
3745 }
3746 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3747 {
3748 /* spawned VM process has terminated (normally or abnormally) */
3749 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
3750 checkForSpawnFailure();
3751 update = true;
3752 }
3753
3754 if (update)
3755 {
3756 /* close old process handles */
3757 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
3758 CloseHandle(handles[i]);
3759
3760 // lock the machines list for reading
3761 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3762
3763 /* obtain a new set of opened machines */
3764 cnt = 0;
3765 machines.clear();
3766
3767 for (MachinesOList::iterator it = that->m->allMachines.begin();
3768 it != that->m->allMachines.end();
3769 ++it)
3770 {
3771 /// @todo handle situations with more than 64 objects
3772 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3773 ("MAXIMUM_WAIT_OBJECTS reached"));
3774
3775 ComObjPtr<SessionMachine> sm;
3776 HANDLE ipcSem;
3777 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
3778 {
3779 machines.push_back(sm);
3780 handles[1 + cnt] = ipcSem;
3781 ++cnt;
3782 }
3783 }
3784
3785 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
3786
3787 /* obtain a new set of spawned machines */
3788 cntSpawned = 0;
3789 spawnedMachines.clear();
3790
3791 for (MachinesOList::iterator it = that->m->allMachines.begin();
3792 it != that->m->allMachines.end();
3793 ++it)
3794 {
3795 /// @todo handle situations with more than 64 objects
3796 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
3797 ("MAXIMUM_WAIT_OBJECTS reached"));
3798
3799 RTPROCESS pid;
3800 if ((*it)->isSessionSpawning(&pid))
3801 {
3802 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
3803 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
3804 pid, GetLastError()));
3805 if (rc == 0)
3806 {
3807 spawnedMachines.push_back(*it);
3808 handles[1 + cnt + cntSpawned] = ph;
3809 ++cntSpawned;
3810 }
3811 }
3812 }
3813
3814 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
3815
3816 // machines lock unwinds here
3817 }
3818 }
3819 while (true);
3820 }
3821 while (0);
3822
3823 /* close old process handles */
3824 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3825 CloseHandle(handles[i]);
3826
3827 /* release sets of machines if any */
3828 machines.clear();
3829 spawnedMachines.clear();
3830
3831 ::CoUninitialize();
3832
3833#elif defined(RT_OS_OS2)
3834
3835 /// @todo (dmik) processes reaping!
3836
3837 /* according to PMREF, 64 is the maximum for the muxwait list */
3838 SEMRECORD handles[64];
3839
3840 HMUX muxSem = NULLHANDLE;
3841
3842 do
3843 {
3844 AutoCaller autoCaller(that);
3845 /* VirtualBox has been early uninitialized, terminate */
3846 if (!autoCaller.isOk())
3847 break;
3848
3849 do
3850 {
3851 /* release the caller to let uninit() ever proceed */
3852 autoCaller.release();
3853
3854 int vrc = RTSemEventWait(that->m->updateReq, 500);
3855
3856 /* Restore the caller before using VirtualBox. If it fails, this
3857 * means VirtualBox is being uninitialized and we must terminate. */
3858 autoCaller.add();
3859 if (!autoCaller.isOk())
3860 break;
3861
3862 bool update = false;
3863 bool updateSpawned = false;
3864
3865 if (RT_SUCCESS(vrc))
3866 {
3867 /* update event is signaled */
3868 update = true;
3869 updateSpawned = true;
3870 }
3871 else
3872 {
3873 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
3874 ("RTSemEventWait returned %Rrc\n", vrc));
3875
3876 /* are there any mutexes? */
3877 if (cnt > 0)
3878 {
3879 /* figure out what's going on with machines */
3880
3881 unsigned long semId = 0;
3882 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
3883 SEM_IMMEDIATE_RETURN, &semId);
3884
3885 if (arc == NO_ERROR)
3886 {
3887 /* machine mutex is normally released */
3888 Assert(semId >= 0 && semId < cnt);
3889 if (semId >= 0 && semId < cnt)
3890 {
3891#ifdef DEBUG
3892 {
3893 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
3894 LogFlowFunc(("released mutex: machine='%ls'\n",
3895 machines[semId]->name().raw()));
3896 }
3897#endif
3898 machines[semId]->checkForDeath();
3899 }
3900 update = true;
3901 }
3902 else if (arc == ERROR_SEM_OWNER_DIED)
3903 {
3904 /* machine mutex is abandoned due to client process
3905 * termination; find which mutex is in the Owner Died
3906 * state */
3907 for (size_t i = 0; i < cnt; ++ i)
3908 {
3909 PID pid; TID tid;
3910 unsigned long reqCnt;
3911 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
3912 if (arc == ERROR_SEM_OWNER_DIED)
3913 {
3914 /* close the dead mutex as asked by PMREF */
3915 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
3916
3917 Assert(i >= 0 && i < cnt);
3918 if (i >= 0 && i < cnt)
3919 {
3920#ifdef DEBUG
3921 {
3922 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
3923 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
3924 machines[i]->name().raw()));
3925 }
3926#endif
3927 machines[i]->checkForDeath();
3928 }
3929 }
3930 }
3931 update = true;
3932 }
3933 else
3934 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
3935 ("DosWaitMuxWaitSem returned %d\n", arc));
3936 }
3937
3938 /* are there any spawning sessions? */
3939 if (cntSpawned > 0)
3940 {
3941 for (size_t i = 0; i < cntSpawned; ++ i)
3942 updateSpawned |= (spawnedMachines[i])->
3943 checkForSpawnFailure();
3944 }
3945 }
3946
3947 if (update || updateSpawned)
3948 {
3949 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
3950
3951 if (update)
3952 {
3953 /* close the old muxsem */
3954 if (muxSem != NULLHANDLE)
3955 ::DosCloseMuxWaitSem(muxSem);
3956
3957 /* obtain a new set of opened machines */
3958 cnt = 0;
3959 machines.clear();
3960
3961 for (MachinesOList::iterator it = that->m->llMachines.begin();
3962 it != that->m->llMachines.end(); ++ it)
3963 {
3964 /// @todo handle situations with more than 64 objects
3965 AssertMsg(cnt <= 64 /* according to PMREF */,
3966 ("maximum of 64 mutex semaphores reached (%d)",
3967 cnt));
3968
3969 ComObjPtr<SessionMachine> sm;
3970 HMTX ipcSem;
3971 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
3972 {
3973 machines.push_back(sm);
3974 handles[cnt].hsemCur = (HSEM)ipcSem;
3975 handles[cnt].ulUser = cnt;
3976 ++ cnt;
3977 }
3978 }
3979
3980 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
3981
3982 if (cnt > 0)
3983 {
3984 /* create a new muxsem */
3985 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
3986 handles,
3987 DCMW_WAIT_ANY);
3988 AssertMsg(arc == NO_ERROR,
3989 ("DosCreateMuxWaitSem returned %d\n", arc));
3990 NOREF(arc);
3991 }
3992 }
3993
3994 if (updateSpawned)
3995 {
3996 /* obtain a new set of spawned machines */
3997 spawnedMachines.clear();
3998
3999 for (MachinesOList::iterator it = that->m->llMachines.begin();
4000 it != that->m->llMachines.end(); ++ it)
4001 {
4002 if ((*it)->isSessionSpawning())
4003 spawnedMachines.push_back(*it);
4004 }
4005
4006 cntSpawned = spawnedMachines.size();
4007 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4008 }
4009 }
4010 }
4011 while (true);
4012 }
4013 while (0);
4014
4015 /* close the muxsem */
4016 if (muxSem != NULLHANDLE)
4017 ::DosCloseMuxWaitSem(muxSem);
4018
4019 /* release sets of machines if any */
4020 machines.clear();
4021 spawnedMachines.clear();
4022
4023#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4024
4025 bool update = false;
4026 bool updateSpawned = false;
4027
4028 do
4029 {
4030 AutoCaller autoCaller(that);
4031 if (!autoCaller.isOk())
4032 break;
4033
4034 do
4035 {
4036 /* release the caller to let uninit() ever proceed */
4037 autoCaller.release();
4038
4039 int rc = RTSemEventWait(that->m->updateReq, 500);
4040
4041 /*
4042 * Restore the caller before using VirtualBox. If it fails, this
4043 * means VirtualBox is being uninitialized and we must terminate.
4044 */
4045 autoCaller.add();
4046 if (!autoCaller.isOk())
4047 break;
4048
4049 if (RT_SUCCESS(rc) || update || updateSpawned)
4050 {
4051 /* RT_SUCCESS(rc) means an update event is signaled */
4052
4053 // lock the machines list for reading
4054 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4055
4056 if (RT_SUCCESS(rc) || update)
4057 {
4058 /* obtain a new set of opened machines */
4059 machines.clear();
4060
4061 for (MachinesOList::iterator it = that->m->allMachines.begin();
4062 it != that->m->allMachines.end();
4063 ++it)
4064 {
4065 ComObjPtr<SessionMachine> sm;
4066 if ((*it)->isSessionOpenOrClosing(sm))
4067 machines.push_back(sm);
4068 }
4069
4070 cnt = machines.size();
4071 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4072 }
4073
4074 if (RT_SUCCESS(rc) || updateSpawned)
4075 {
4076 /* obtain a new set of spawned machines */
4077 spawnedMachines.clear();
4078
4079 for (MachinesOList::iterator it = that->m->allMachines.begin();
4080 it != that->m->allMachines.end();
4081 ++it)
4082 {
4083 if ((*it)->isSessionSpawning())
4084 spawnedMachines.push_back(*it);
4085 }
4086
4087 cntSpawned = spawnedMachines.size();
4088 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4089 }
4090
4091 // machines lock unwinds here
4092 }
4093
4094 update = false;
4095 for (size_t i = 0; i < cnt; ++ i)
4096 update |= (machines[i])->checkForDeath();
4097
4098 updateSpawned = false;
4099 for (size_t i = 0; i < cntSpawned; ++ i)
4100 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4101
4102 /* reap child processes */
4103 {
4104 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4105 if (that->m->llProcesses.size())
4106 {
4107 LogFlowFunc(("UPDATE: child process count = %d\n",
4108 that->m->llProcesses.size()));
4109 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4110 while (it != that->m->llProcesses.end())
4111 {
4112 RTPROCESS pid = *it;
4113 RTPROCSTATUS status;
4114 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4115 if (vrc == VINF_SUCCESS)
4116 {
4117 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4118 pid, pid, status.iStatus,
4119 status.enmReason));
4120 it = that->m->llProcesses.erase(it);
4121 }
4122 else
4123 {
4124 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4125 pid, pid, vrc));
4126 if (vrc != VERR_PROCESS_RUNNING)
4127 {
4128 /* remove the process if it is not already running */
4129 it = that->m->llProcesses.erase(it);
4130 }
4131 else
4132 ++ it;
4133 }
4134 }
4135 }
4136 }
4137 }
4138 while (true);
4139 }
4140 while (0);
4141
4142 /* release sets of machines if any */
4143 machines.clear();
4144 spawnedMachines.clear();
4145
4146#else
4147# error "Port me!"
4148#endif
4149
4150 LogFlowFuncLeave();
4151 return 0;
4152}
4153
4154/**
4155 * Thread function that handles custom events posted using #postEvent().
4156 */
4157// static
4158DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4159{
4160 LogFlowFuncEnter();
4161
4162 AssertReturn(pvUser, VERR_INVALID_POINTER);
4163
4164 // create an event queue for the current thread
4165 EventQueue *eventQ = new EventQueue();
4166 AssertReturn(eventQ, VERR_NO_MEMORY);
4167
4168 // return the queue to the one who created this thread
4169 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4170 // signal that we're ready
4171 RTThreadUserSignal(thread);
4172
4173 while (RT_SUCCESS(eventQ->processEventQueue(RT_INDEFINITE_WAIT)))
4174 /* nothing */ ;
4175
4176 delete eventQ;
4177
4178 LogFlowFuncLeave();
4179
4180 return 0;
4181}
4182
4183
4184////////////////////////////////////////////////////////////////////////////////
4185
4186/**
4187 * Takes the current list of registered callbacks of the managed VirtualBox
4188 * instance, and calls #handleCallback() for every callback item from the
4189 * list, passing the item as an argument.
4190 *
4191 * @note Locks the managed VirtualBox object for reading but leaves the lock
4192 * before iterating over callbacks and calling their methods.
4193 */
4194void *VirtualBox::CallbackEvent::handler()
4195{
4196 if (!mVirtualBox)
4197 return NULL;
4198
4199 AutoCaller autoCaller(mVirtualBox);
4200 if (!autoCaller.isOk())
4201 {
4202 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4203 autoCaller.state()));
4204 /* We don't need mVirtualBox any more, so release it */
4205 mVirtualBox = NULL;
4206 return NULL;
4207 }
4208
4209
4210 {
4211 VBoxEventDesc evDesc;
4212 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4213
4214 evDesc.fire(/* don't wait for delivery */0);
4215 }
4216
4217 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4218 return NULL;
4219}
4220
4221//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4222//{
4223// return E_NOTIMPL;
4224//}
4225
4226STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4227{
4228 CheckComArgStrNotEmptyOrNull(aName);
4229 CheckComArgNotNull(aServer);
4230
4231 AutoCaller autoCaller(this);
4232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4233
4234 ComObjPtr<DHCPServer> dhcpServer;
4235 dhcpServer.createObject();
4236 HRESULT rc = dhcpServer->init(this, aName);
4237 if (FAILED(rc)) return rc;
4238
4239 rc = registerDHCPServer(dhcpServer, true);
4240 if (FAILED(rc)) return rc;
4241
4242 dhcpServer.queryInterfaceTo(aServer);
4243
4244 return rc;
4245}
4246
4247STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4248{
4249 CheckComArgStrNotEmptyOrNull(aName);
4250 CheckComArgNotNull(aServer);
4251
4252 AutoCaller autoCaller(this);
4253 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4254
4255 HRESULT rc;
4256 Bstr bstr;
4257 ComPtr<DHCPServer> found;
4258
4259 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4260
4261 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4262 it != m->allDHCPServers.end();
4263 ++it)
4264 {
4265 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4266 if (FAILED(rc)) throw rc;
4267
4268 if (bstr == aName)
4269 {
4270 found = *it;
4271 break;
4272 }
4273 }
4274
4275 if (!found)
4276 return E_INVALIDARG;
4277
4278 return found.queryInterfaceTo(aServer);
4279}
4280
4281STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4282{
4283 CheckComArgNotNull(aServer);
4284
4285 AutoCaller autoCaller(this);
4286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4287
4288 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4289
4290 return rc;
4291}
4292
4293/**
4294 * Remembers the given dhcp server by storing it in the hard disk registry.
4295 *
4296 * @param aDHCPServer Dhcp Server object to remember.
4297 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4298 *
4299 * When @a aSaveRegistry is @c true, this operation may fail because of the
4300 * failed #saveSettings() method it calls. In this case, the dhcp server object
4301 * will not be remembered. It is therefore the responsibility of the caller to
4302 * call this method as the last step of some action that requires registration
4303 * in order to make sure that only fully functional dhcp server objects get
4304 * registered.
4305 *
4306 * @note Locks this object for writing and @a aDHCPServer for reading.
4307 */
4308HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4309 bool aSaveRegistry /*= true*/)
4310{
4311 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4312
4313 AutoCaller autoCaller(this);
4314 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4315
4316 AutoCaller dhcpServerCaller(aDHCPServer);
4317 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4318
4319 Bstr name;
4320 HRESULT rc;
4321 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4322 if (FAILED(rc)) return rc;
4323
4324 ComPtr<IDHCPServer> existing;
4325 rc = FindDHCPServerByNetworkName(name, existing.asOutParam());
4326 if (SUCCEEDED(rc))
4327 return E_INVALIDARG;
4328
4329 rc = S_OK;
4330
4331 m->allDHCPServers.addChild(aDHCPServer);
4332
4333 if (aSaveRegistry)
4334 {
4335 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4336 rc = saveSettings();
4337 vboxLock.release();
4338
4339 if (FAILED(rc))
4340 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4341 }
4342
4343 return rc;
4344}
4345
4346/**
4347 * Removes the given hard disk from the hard disk registry.
4348 *
4349 * @param aHardDisk Hard disk object to remove.
4350 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4351 *
4352 * When @a aSaveRegistry is @c true, this operation may fail because of the
4353 * failed #saveSettings() method it calls. In this case, the hard disk object
4354 * will NOT be removed from the registry when this method returns. It is
4355 * therefore the responsibility of the caller to call this method as the first
4356 * step of some action that requires unregistration, before calling uninit() on
4357 * @a aHardDisk.
4358 *
4359 * @note Locks this object for writing and @a aHardDisk for reading.
4360 */
4361HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4362 bool aSaveRegistry /*= true*/)
4363{
4364 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4365
4366 AutoCaller autoCaller(this);
4367 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4368
4369 AutoCaller dhcpServerCaller(aDHCPServer);
4370 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4371
4372 m->allDHCPServers.removeChild(aDHCPServer);
4373
4374 HRESULT rc = S_OK;
4375
4376 if (aSaveRegistry)
4377 {
4378 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4379 rc = saveSettings();
4380 vboxLock.release();
4381
4382 if (FAILED(rc))
4383 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4384 }
4385
4386 return rc;
4387}
4388
4389/* 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