VirtualBox

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

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

com/string: Windows build fixes

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