VirtualBox

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

Last change on this file since 33769 was 33769, checked in by vboxsync, 15 years ago

Main: use a UUID constant for the global media registry

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

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