VirtualBox

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

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

ExtPack changes, related IPRT changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 147.3 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 33806 2010-11-05 17:20:15Z 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 // constant pseudo-machine ID for global media registry
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#ifdef VBOX_WITH_EXTPACK
1285 /* call the extension pack hooks */
1286 m->ptrExtPackManager->callAllVmCreatedHooks(machine);
1287#endif
1288
1289 /* set the return value */
1290 rc = machine.queryInterfaceTo(aMachine);
1291 AssertComRC(rc);
1292 }
1293
1294 LogFlowThisFuncLeave();
1295
1296 return rc;
1297}
1298
1299STDMETHODIMP VirtualBox::OpenMachine(IN_BSTR aSettingsFile,
1300 IMachine **aMachine)
1301{
1302 CheckComArgStrNotEmptyOrNull(aSettingsFile);
1303 CheckComArgOutSafeArrayPointerValid(aMachine);
1304
1305 AutoCaller autoCaller(this);
1306 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1307
1308 HRESULT rc = E_FAIL;
1309
1310 /* create a new object */
1311 ComObjPtr<Machine> machine;
1312 rc = machine.createObject();
1313 if (SUCCEEDED(rc))
1314 {
1315 /* initialize the machine object */
1316 rc = machine->init(this,
1317 aSettingsFile,
1318 NULL); /* const Guid *aId */
1319 if (SUCCEEDED(rc))
1320 {
1321 /* set the return value */
1322 rc = machine.queryInterfaceTo(aMachine);
1323 ComAssertComRC(rc);
1324 }
1325 }
1326
1327 return rc;
1328}
1329
1330/** @note Locks objects! */
1331STDMETHODIMP VirtualBox::RegisterMachine(IMachine *aMachine)
1332{
1333 CheckComArgNotNull(aMachine);
1334
1335 AutoCaller autoCaller(this);
1336 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1337
1338 HRESULT rc;
1339
1340 Bstr name;
1341 rc = aMachine->COMGETTER(Name)(name.asOutParam());
1342 if (FAILED(rc)) return rc;
1343
1344 /* We can safely cast child to Machine * here because only Machine
1345 * implementations of IMachine can be among our children. */
1346 Machine *pMachine = static_cast<Machine*>(aMachine);
1347
1348 AutoCaller machCaller(pMachine);
1349 ComAssertComRCRetRC(machCaller.rc());
1350
1351 rc = registerMachine(pMachine);
1352 /* fire an event */
1353 if (SUCCEEDED(rc))
1354 onMachineRegistered(pMachine->getId(), TRUE);
1355
1356 return rc;
1357}
1358
1359/** @note Locks this object for reading, then some machine objects for reading. */
1360STDMETHODIMP VirtualBox::FindMachine(IN_BSTR aNameOrId, IMachine **aMachine)
1361{
1362 LogFlowThisFuncEnter();
1363 LogFlowThisFunc(("aName=\"%ls\", aMachine={%p}\n", aNameOrId, aMachine));
1364
1365 CheckComArgStrNotEmptyOrNull(aNameOrId);
1366 CheckComArgOutSafeArrayPointerValid(aMachine);
1367
1368 AutoCaller autoCaller(this);
1369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1370
1371 /* start with not found */
1372 HRESULT rc = S_OK;
1373 ComObjPtr<Machine> pMachineFound;
1374
1375 Guid id(aNameOrId);
1376 if (!id.isEmpty())
1377 rc = findMachine(id,
1378 true /* fPermitInaccessible */,
1379 true /* setError */,
1380 &pMachineFound);
1381 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error
1382 else
1383 {
1384 Utf8Str strName(aNameOrId);
1385 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1386 for (MachinesOList::iterator it = m->allMachines.begin();
1387 it != m->allMachines.end();
1388 ++it)
1389 {
1390 ComObjPtr<Machine> &pMachine2 = *it;
1391 AutoCaller machCaller(pMachine2);
1392 if (machCaller.rc())
1393 continue; // we can't ask inaccessible machines for their names
1394
1395 AutoReadLock machLock(pMachine2 COMMA_LOCKVAL_SRC_POS);
1396 if (pMachine2->getName() == strName)
1397 {
1398 pMachineFound = pMachine2;
1399 break;
1400 }
1401 }
1402
1403 if (!pMachineFound)
1404 rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1405 tr("Could not find a registered machine named '%ls'"), aNameOrId);
1406 }
1407
1408 /* this will set (*machine) to NULL if machineObj is null */
1409 pMachineFound.queryInterfaceTo(aMachine);
1410
1411 LogFlowThisFunc(("aName=\"%ls\", aMachine=%p, rc=%08X\n", aNameOrId, *aMachine, rc));
1412 LogFlowThisFuncLeave();
1413
1414 return rc;
1415}
1416
1417STDMETHODIMP VirtualBox::CreateHardDisk(IN_BSTR aFormat,
1418 IN_BSTR aLocation,
1419 IMedium **aHardDisk)
1420{
1421 CheckComArgOutPointerValid(aHardDisk);
1422
1423 AutoCaller autoCaller(this);
1424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1425
1426 /* we don't access non-const data members so no need to lock */
1427
1428 Utf8Str format(aFormat);
1429 if (format.isEmpty())
1430 getDefaultHardDiskFormat(format);
1431
1432 bool fNeedsGlobalSaveSettings = false;
1433
1434 ComObjPtr<Medium> hardDisk;
1435 hardDisk.createObject();
1436 HRESULT rc = hardDisk->init(this,
1437 format,
1438 aLocation,
1439 Guid::Empty, // media registry
1440 &fNeedsGlobalSaveSettings);
1441
1442 if (SUCCEEDED(rc))
1443 hardDisk.queryInterfaceTo(aHardDisk);
1444
1445 if (fNeedsGlobalSaveSettings)
1446 {
1447 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
1448 saveSettings();
1449 }
1450
1451 return rc;
1452}
1453
1454STDMETHODIMP VirtualBox::OpenMedium(IN_BSTR aLocation,
1455 DeviceType_T deviceType,
1456 AccessMode_T accessMode,
1457 IMedium **aMedium)
1458{
1459 CheckComArgStrNotEmptyOrNull(aLocation);
1460 CheckComArgOutSafeArrayPointerValid(aMedium);
1461
1462 AutoCaller autoCaller(this);
1463 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1464
1465 bool fNeedsGlobalSaveSettings = false;
1466 ComObjPtr<Medium> pMedium;
1467
1468 /* we don't access non-const data members so no need to lock */
1469
1470 // check if the device type is correct, and see if a medium for the
1471 // given path has already initialized; if so, return that
1472 switch (deviceType)
1473 {
1474 case DeviceType_HardDisk:
1475 findHardDiskByLocation(aLocation,
1476 false, /* aSetError */
1477 &pMedium);
1478 break;
1479
1480 case DeviceType_Floppy:
1481 case DeviceType_DVD:
1482 findDVDOrFloppyImage(deviceType,
1483 NULL, /* guid */
1484 aLocation,
1485 false, /* aSetError */
1486 &pMedium);
1487
1488 // enforce read-only for DVDs even if caller specified ReadWrite
1489 if (deviceType == DeviceType_DVD)
1490 accessMode = AccessMode_ReadOnly;
1491 break;
1492
1493 default:
1494 return setError(E_INVALIDARG, "Device type must be HardDisk, DVD or Floppy");
1495 }
1496
1497 HRESULT rc = S_OK;
1498
1499 if (pMedium.isNull())
1500 {
1501 pMedium.createObject();
1502 rc = pMedium->init(this,
1503 aLocation,
1504 (accessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
1505 deviceType);
1506
1507 if (SUCCEEDED(rc))
1508 {
1509 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1510
1511 switch (deviceType)
1512 {
1513 case DeviceType_HardDisk:
1514 rc = registerHardDisk(pMedium,
1515 &fNeedsGlobalSaveSettings);
1516 break;
1517
1518 case DeviceType_DVD:
1519 case DeviceType_Floppy:
1520 rc = registerImage(pMedium,
1521 deviceType,
1522 &fNeedsGlobalSaveSettings);
1523 break;
1524 }
1525
1526 treeLock.release();
1527
1528 /* Note that it's important to call uninit() on failure to register
1529 * because the differencing hard disk would have been already associated
1530 * with the parent and this association needs to be broken. */
1531
1532 if (FAILED(rc))
1533 pMedium->uninit();
1534 }
1535 }
1536
1537 if (SUCCEEDED(rc))
1538 pMedium.queryInterfaceTo(aMedium);
1539
1540 if (fNeedsGlobalSaveSettings)
1541 {
1542 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
1543 saveSettings();
1544 }
1545
1546 return rc;
1547}
1548
1549STDMETHODIMP VirtualBox::FindMedium(IN_BSTR aLocation,
1550 DeviceType_T aDeviceType,
1551 IMedium **aMedium)
1552{
1553 CheckComArgOutSafeArrayPointerValid(aMedium);
1554
1555 AutoCaller autoCaller(this);
1556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1557
1558 Guid id(aLocation);
1559 Utf8Str strLocation(aLocation);
1560
1561 HRESULT rc;
1562 ComObjPtr<Medium> pMedium;
1563
1564 switch (aDeviceType)
1565 {
1566 case DeviceType_HardDisk:
1567 if (!id.isEmpty())
1568 rc = findHardDiskById(id, true /* setError */, &pMedium);
1569 else
1570 rc = findHardDiskByLocation(strLocation, true /* setError */, &pMedium);
1571 break;
1572
1573 case DeviceType_Floppy:
1574 case DeviceType_DVD:
1575 if (!id.isEmpty())
1576 rc = findDVDOrFloppyImage(aDeviceType, &id, Utf8Str::Empty, true /* setError */, &pMedium);
1577 else
1578 rc = findDVDOrFloppyImage(aDeviceType, NULL, strLocation, true /* setError */, &pMedium);
1579 break;
1580
1581 default:
1582 return setError(E_INVALIDARG,
1583 tr("Invalid device type %d"), aDeviceType);
1584 }
1585
1586 /* the below will set *aHardDisk to NULL if hardDisk is null */
1587 pMedium.queryInterfaceTo(aMedium);
1588
1589 return rc;
1590}
1591
1592/** @note Locks this object for reading. */
1593STDMETHODIMP VirtualBox::GetGuestOSType(IN_BSTR aId, IGuestOSType **aType)
1594{
1595 /* Old ID to new ID conversion table. See r39691 for a source */
1596 static const wchar_t *kOldNewIDs[] =
1597 {
1598 L"unknown", L"Other",
1599 L"win31", L"Windows31",
1600 L"win95", L"Windows95",
1601 L"win98", L"Windows98",
1602 L"winme", L"WindowsMe",
1603 L"winnt4", L"WindowsNT4",
1604 L"win2k", L"Windows2000",
1605 L"winxp", L"WindowsXP",
1606 L"win2k3", L"Windows2003",
1607 L"winvista", L"WindowsVista",
1608 L"win2k8", L"Windows2008",
1609 L"ecs", L"OS2eCS",
1610 L"fedoracore", L"Fedora",
1611 /* the rest is covered by the case-insensitive comparison */
1612 };
1613
1614 CheckComArgNotNull(aType);
1615
1616 AutoCaller autoCaller(this);
1617 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1618
1619 /* first, look for a substitution */
1620 Bstr id = aId;
1621 for (size_t i = 0; i < RT_ELEMENTS(kOldNewIDs) / 2; i += 2)
1622 {
1623 if (id == kOldNewIDs[i])
1624 {
1625 id = kOldNewIDs[i + 1];
1626 break;
1627 }
1628 }
1629
1630 *aType = NULL;
1631
1632 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1633 for (GuestOSTypesOList::iterator it = m->allGuestOSTypes.begin();
1634 it != m->allGuestOSTypes.end();
1635 ++it)
1636 {
1637 const Bstr &typeId = (*it)->id();
1638 AssertMsg(!typeId.isEmpty(), ("ID must not be NULL"));
1639 if (typeId.compare(id, Bstr::CaseInsensitive) == 0)
1640 {
1641 (*it).queryInterfaceTo(aType);
1642 break;
1643 }
1644 }
1645
1646 return (*aType) ? S_OK :
1647 setError(E_INVALIDARG,
1648 tr("'%ls' is not a valid Guest OS type"),
1649 aId);
1650}
1651
1652STDMETHODIMP VirtualBox::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath,
1653 BOOL /* aWritable */, BOOL /* aAutoMount */)
1654{
1655 CheckComArgStrNotEmptyOrNull(aName);
1656 CheckComArgStrNotEmptyOrNull(aHostPath);
1657
1658 AutoCaller autoCaller(this);
1659 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1660
1661 return setError(E_NOTIMPL, "Not yet implemented");
1662}
1663
1664STDMETHODIMP VirtualBox::RemoveSharedFolder(IN_BSTR aName)
1665{
1666 CheckComArgStrNotEmptyOrNull(aName);
1667
1668 AutoCaller autoCaller(this);
1669 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1670
1671 return setError(E_NOTIMPL, "Not yet implemented");
1672}
1673
1674/**
1675 * @note Locks this object for reading.
1676 */
1677STDMETHODIMP VirtualBox::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
1678{
1679 using namespace settings;
1680
1681 if (ComSafeArrayOutIsNull(aKeys))
1682 return E_POINTER;
1683
1684 AutoCaller autoCaller(this);
1685 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1686
1687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1688
1689 com::SafeArray<BSTR> saKeys(m->pMainConfigFile->mapExtraDataItems.size());
1690 int i = 0;
1691 for (StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin();
1692 it != m->pMainConfigFile->mapExtraDataItems.end();
1693 ++it, ++i)
1694 {
1695 const Utf8Str &strName = it->first; // the key
1696 strName.cloneTo(&saKeys[i]);
1697 }
1698 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
1699
1700 return S_OK;
1701}
1702
1703/**
1704 * @note Locks this object for reading.
1705 */
1706STDMETHODIMP VirtualBox::GetExtraData(IN_BSTR aKey,
1707 BSTR *aValue)
1708{
1709 CheckComArgStrNotEmptyOrNull(aKey);
1710 CheckComArgNotNull(aValue);
1711
1712 AutoCaller autoCaller(this);
1713 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1714
1715 /* start with nothing found */
1716 Utf8Str strKey(aKey);
1717 Bstr bstrResult;
1718
1719 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
1720 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1721 // found:
1722 bstrResult = it->second; // source is a Utf8Str
1723
1724 /* return the result to caller (may be empty) */
1725 bstrResult.cloneTo(aValue);
1726
1727 return S_OK;
1728}
1729
1730/**
1731 * @note Locks this object for writing.
1732 */
1733STDMETHODIMP VirtualBox::SetExtraData(IN_BSTR aKey,
1734 IN_BSTR aValue)
1735{
1736 CheckComArgStrNotEmptyOrNull(aKey);
1737
1738 AutoCaller autoCaller(this);
1739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1740
1741 Utf8Str strKey(aKey);
1742 Utf8Str strValue(aValue);
1743 Utf8Str strOldValue; // empty
1744
1745 // locking note: we only hold the read lock briefly to look up the old value,
1746 // then release it and call the onExtraCanChange callbacks. There is a small
1747 // chance of a race insofar as the callback might be called twice if two callers
1748 // change the same key at the same time, but that's a much better solution
1749 // than the deadlock we had here before. The actual changing of the extradata
1750 // is then performed under the write lock and race-free.
1751
1752 // look up the old value first; if nothing has changed then we need not do anything
1753 {
1754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
1755 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
1756 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1757 strOldValue = it->second;
1758 }
1759
1760 bool fChanged;
1761 if ((fChanged = (strOldValue != strValue)))
1762 {
1763 // ask for permission from all listeners outside the locks;
1764 // onExtraDataCanChange() only briefly requests the VirtualBox
1765 // lock to copy the list of callbacks to invoke
1766 Bstr error;
1767 Bstr bstrValue(aValue);
1768
1769 if (!onExtraDataCanChange(Guid::Empty, aKey, bstrValue.raw(), error))
1770 {
1771 const char *sep = error.isEmpty() ? "" : ": ";
1772 CBSTR err = error.raw();
1773 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
1774 sep, err));
1775 return setError(E_ACCESSDENIED,
1776 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
1777 aKey,
1778 bstrValue.raw(),
1779 sep,
1780 err);
1781 }
1782
1783 // data is changing and change not vetoed: then write it out under the lock
1784
1785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1786
1787 if (strValue.isEmpty())
1788 m->pMainConfigFile->mapExtraDataItems.erase(strKey);
1789 else
1790 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
1791 // creates a new key if needed
1792
1793 /* save settings on success */
1794 HRESULT rc = saveSettings();
1795 if (FAILED(rc)) return rc;
1796 }
1797
1798 // fire notification outside the lock
1799 if (fChanged)
1800 onExtraDataChange(Guid::Empty, aKey, aValue);
1801
1802 return S_OK;
1803}
1804
1805// public methods only for internal purposes
1806/////////////////////////////////////////////////////////////////////////////
1807
1808#ifdef DEBUG
1809void VirtualBox::dumpAllBackRefs()
1810{
1811 {
1812 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1813 for (MediaList::const_iterator mt = m->allHardDisks.begin();
1814 mt != m->allHardDisks.end();
1815 ++mt)
1816 {
1817 ComObjPtr<Medium> pMedium = *mt;
1818 pMedium->dumpBackRefs();
1819 }
1820 }
1821 {
1822 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1823 for (MediaList::const_iterator mt = m->allDVDImages.begin();
1824 mt != m->allDVDImages.end();
1825 ++mt)
1826 {
1827 ComObjPtr<Medium> pMedium = *mt;
1828 pMedium->dumpBackRefs();
1829 }
1830 }
1831}
1832#endif
1833
1834/**
1835 * Posts an event to the event queue that is processed asynchronously
1836 * on a dedicated thread.
1837 *
1838 * Posting events to the dedicated event queue is useful to perform secondary
1839 * actions outside any object locks -- for example, to iterate over a list
1840 * of callbacks and inform them about some change caused by some object's
1841 * method call.
1842 *
1843 * @param event event to post; must have been allocated using |new|, will
1844 * be deleted automatically by the event thread after processing
1845 *
1846 * @note Doesn't lock any object.
1847 */
1848HRESULT VirtualBox::postEvent(Event *event)
1849{
1850 AssertReturn(event, E_FAIL);
1851
1852 HRESULT rc;
1853 AutoCaller autoCaller(this);
1854 if (SUCCEEDED((rc = autoCaller.rc())))
1855 {
1856 if (autoCaller.state() != Ready)
1857 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
1858 autoCaller.state()));
1859 // return S_OK
1860 else if ( (m->pAsyncEventQ)
1861 && (m->pAsyncEventQ->postEvent(event))
1862 )
1863 return S_OK;
1864 else
1865 rc = E_FAIL;
1866 }
1867
1868 // in any event of failure, we must clean up here, or we'll leak;
1869 // the caller has allocated the object using new()
1870 delete event;
1871 return rc;
1872}
1873
1874/**
1875 * Adds a progress to the global collection of pending operations.
1876 * Usually gets called upon progress object initialization.
1877 *
1878 * @param aProgress Operation to add to the collection.
1879 *
1880 * @note Doesn't lock objects.
1881 */
1882HRESULT VirtualBox::addProgress(IProgress *aProgress)
1883{
1884 CheckComArgNotNull(aProgress);
1885
1886 AutoCaller autoCaller(this);
1887 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1888
1889 Bstr id;
1890 HRESULT rc = aProgress->COMGETTER(Id)(id.asOutParam());
1891 AssertComRCReturnRC(rc);
1892
1893 /* protect mProgressOperations */
1894 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1895
1896 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
1897 return S_OK;
1898}
1899
1900/**
1901 * Removes the progress from the global collection of pending operations.
1902 * Usually gets called upon progress completion.
1903 *
1904 * @param aId UUID of the progress operation to remove
1905 *
1906 * @note Doesn't lock objects.
1907 */
1908HRESULT VirtualBox::removeProgress(IN_GUID aId)
1909{
1910 AutoCaller autoCaller(this);
1911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1912
1913 ComPtr<IProgress> progress;
1914
1915 /* protect mProgressOperations */
1916 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1917
1918 size_t cnt = m->mapProgressOperations.erase(aId);
1919 Assert(cnt == 1);
1920 NOREF(cnt);
1921
1922 return S_OK;
1923}
1924
1925#ifdef RT_OS_WINDOWS
1926
1927struct StartSVCHelperClientData
1928{
1929 ComObjPtr<VirtualBox> that;
1930 ComObjPtr<Progress> progress;
1931 bool privileged;
1932 VirtualBox::SVCHelperClientFunc func;
1933 void *user;
1934};
1935
1936/**
1937 * Helper method that starts a worker thread that:
1938 * - creates a pipe communication channel using SVCHlpClient;
1939 * - starts an SVC Helper process that will inherit this channel;
1940 * - executes the supplied function by passing it the created SVCHlpClient
1941 * and opened instance to communicate to the Helper process and the given
1942 * Progress object.
1943 *
1944 * The user function is supposed to communicate to the helper process
1945 * using the \a aClient argument to do the requested job and optionally expose
1946 * the progress through the \a aProgress object. The user function should never
1947 * call notifyComplete() on it: this will be done automatically using the
1948 * result code returned by the function.
1949 *
1950 * Before the user function is started, the communication channel passed to
1951 * the \a aClient argument is fully set up, the function should start using
1952 * its write() and read() methods directly.
1953 *
1954 * The \a aVrc parameter of the user function may be used to return an error
1955 * code if it is related to communication errors (for example, returned by
1956 * the SVCHlpClient members when they fail). In this case, the correct error
1957 * message using this value will be reported to the caller. Note that the
1958 * value of \a aVrc is inspected only if the user function itself returns
1959 * success.
1960 *
1961 * If a failure happens anywhere before the user function would be normally
1962 * called, it will be called anyway in special "cleanup only" mode indicated
1963 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
1964 * all the function is supposed to do is to cleanup its aUser argument if
1965 * necessary (it's assumed that the ownership of this argument is passed to
1966 * the user function once #startSVCHelperClient() returns a success, thus
1967 * making it responsible for the cleanup).
1968 *
1969 * After the user function returns, the thread will send the SVCHlpMsg::Null
1970 * message to indicate a process termination.
1971 *
1972 * @param aPrivileged |true| to start the SVC Helper process as a privileged
1973 * user that can perform administrative tasks
1974 * @param aFunc user function to run
1975 * @param aUser argument to the user function
1976 * @param aProgress progress object that will track operation completion
1977 *
1978 * @note aPrivileged is currently ignored (due to some unsolved problems in
1979 * Vista) and the process will be started as a normal (unprivileged)
1980 * process.
1981 *
1982 * @note Doesn't lock anything.
1983 */
1984HRESULT VirtualBox::startSVCHelperClient(bool aPrivileged,
1985 SVCHelperClientFunc aFunc,
1986 void *aUser, Progress *aProgress)
1987{
1988 AssertReturn(aFunc, E_POINTER);
1989 AssertReturn(aProgress, E_POINTER);
1990
1991 AutoCaller autoCaller(this);
1992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1993
1994 /* create the SVCHelperClientThread() argument */
1995 std::auto_ptr <StartSVCHelperClientData>
1996 d(new StartSVCHelperClientData());
1997 AssertReturn(d.get(), E_OUTOFMEMORY);
1998
1999 d->that = this;
2000 d->progress = aProgress;
2001 d->privileged = aPrivileged;
2002 d->func = aFunc;
2003 d->user = aUser;
2004
2005 RTTHREAD tid = NIL_RTTHREAD;
2006 int vrc = RTThreadCreate(&tid, SVCHelperClientThread,
2007 static_cast <void *>(d.get()),
2008 0, RTTHREADTYPE_MAIN_WORKER,
2009 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2010 if (RT_FAILURE(vrc))
2011 return setError(E_FAIL, "Could not create SVCHelper thread (%Rrc)", vrc);
2012
2013 /* d is now owned by SVCHelperClientThread(), so release it */
2014 d.release();
2015
2016 return S_OK;
2017}
2018
2019/**
2020 * Worker thread for startSVCHelperClient().
2021 */
2022/* static */
2023DECLCALLBACK(int)
2024VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser)
2025{
2026 LogFlowFuncEnter();
2027
2028 std::auto_ptr<StartSVCHelperClientData>
2029 d(static_cast<StartSVCHelperClientData*>(aUser));
2030
2031 HRESULT rc = S_OK;
2032 bool userFuncCalled = false;
2033
2034 do
2035 {
2036 AssertBreakStmt(d.get(), rc = E_POINTER);
2037 AssertReturn(!d->progress.isNull(), E_POINTER);
2038
2039 /* protect VirtualBox from uninitialization */
2040 AutoCaller autoCaller(d->that);
2041 if (!autoCaller.isOk())
2042 {
2043 /* it's too late */
2044 rc = autoCaller.rc();
2045 break;
2046 }
2047
2048 int vrc = VINF_SUCCESS;
2049
2050 Guid id;
2051 id.create();
2052 SVCHlpClient client;
2053 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
2054 id.raw()).c_str());
2055 if (RT_FAILURE(vrc))
2056 {
2057 rc = d->that->setError(E_FAIL,
2058 tr("Could not create the communication channel (%Rrc)"), vrc);
2059 break;
2060 }
2061
2062 /* get the path to the executable */
2063 char exePathBuf[RTPATH_MAX];
2064 char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX);
2065 if (!exePath)
2066 {
2067 rc = d->that->setError(E_FAIL, tr("Cannot get executable name"));
2068 break;
2069 }
2070
2071 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().c_str());
2072
2073 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.c_str()));
2074
2075 RTPROCESS pid = NIL_RTPROCESS;
2076
2077 if (d->privileged)
2078 {
2079 /* Attempt to start a privileged process using the Run As dialog */
2080
2081 Bstr file = exePath;
2082 Bstr parameters = argsStr;
2083
2084 SHELLEXECUTEINFO shExecInfo;
2085
2086 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
2087
2088 shExecInfo.fMask = NULL;
2089 shExecInfo.hwnd = NULL;
2090 shExecInfo.lpVerb = L"runas";
2091 shExecInfo.lpFile = file.raw();
2092 shExecInfo.lpParameters = parameters.raw();
2093 shExecInfo.lpDirectory = NULL;
2094 shExecInfo.nShow = SW_NORMAL;
2095 shExecInfo.hInstApp = NULL;
2096
2097 if (!ShellExecuteEx(&shExecInfo))
2098 {
2099 int vrc2 = RTErrConvertFromWin32(GetLastError());
2100 /* hide excessive details in case of a frequent error
2101 * (pressing the Cancel button to close the Run As dialog) */
2102 if (vrc2 == VERR_CANCELLED)
2103 rc = d->that->setError(E_FAIL,
2104 tr("Operation canceled by the user"));
2105 else
2106 rc = d->that->setError(E_FAIL,
2107 tr("Could not launch a privileged process '%s' (%Rrc)"),
2108 exePath, vrc2);
2109 break;
2110 }
2111 }
2112 else
2113 {
2114 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2115 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
2116 if (RT_FAILURE(vrc))
2117 {
2118 rc = d->that->setError(E_FAIL,
2119 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2120 break;
2121 }
2122 }
2123
2124 /* wait for the client to connect */
2125 vrc = client.connect();
2126 if (RT_SUCCESS(vrc))
2127 {
2128 /* start the user supplied function */
2129 rc = d->func(&client, d->progress, d->user, &vrc);
2130 userFuncCalled = true;
2131 }
2132
2133 /* send the termination signal to the process anyway */
2134 {
2135 int vrc2 = client.write(SVCHlpMsg::Null);
2136 if (RT_SUCCESS(vrc))
2137 vrc = vrc2;
2138 }
2139
2140 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2141 {
2142 rc = d->that->setError(E_FAIL,
2143 tr("Could not operate the communication channel (%Rrc)"), vrc);
2144 break;
2145 }
2146 }
2147 while (0);
2148
2149 if (FAILED(rc) && !userFuncCalled)
2150 {
2151 /* call the user function in the "cleanup only" mode
2152 * to let it free resources passed to in aUser */
2153 d->func(NULL, NULL, d->user, NULL);
2154 }
2155
2156 d->progress->notifyComplete(rc);
2157
2158 LogFlowFuncLeave();
2159 return 0;
2160}
2161
2162#endif /* RT_OS_WINDOWS */
2163
2164/**
2165 * Sends a signal to the client watcher thread to rescan the set of machines
2166 * that have open sessions.
2167 *
2168 * @note Doesn't lock anything.
2169 */
2170void VirtualBox::updateClientWatcher()
2171{
2172 AutoCaller autoCaller(this);
2173 AssertComRCReturnVoid(autoCaller.rc());
2174
2175 AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD);
2176
2177 /* sent an update request */
2178#if defined(RT_OS_WINDOWS)
2179 ::SetEvent(m->updateReq);
2180#elif defined(RT_OS_OS2)
2181 RTSemEventSignal(m->updateReq);
2182#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2183 RTSemEventSignal(m->updateReq);
2184#else
2185# error "Port me!"
2186#endif
2187}
2188
2189/**
2190 * Adds the given child process ID to the list of processes to be reaped.
2191 * This call should be followed by #updateClientWatcher() to take the effect.
2192 */
2193void VirtualBox::addProcessToReap(RTPROCESS pid)
2194{
2195 AutoCaller autoCaller(this);
2196 AssertComRCReturnVoid(autoCaller.rc());
2197
2198 /// @todo (dmik) Win32?
2199#ifndef RT_OS_WINDOWS
2200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2201 m->llProcesses.push_back(pid);
2202#endif
2203}
2204
2205/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2206struct MachineEvent : public VirtualBox::CallbackEvent
2207{
2208 MachineEvent(VirtualBox *aVB, const Guid &aId)
2209 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineDataChanged), id(aId.toUtf16())
2210 {}
2211
2212 MachineEvent(VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2213 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineStateChanged), id(aId.toUtf16())
2214 , state(aState)
2215 {}
2216
2217 MachineEvent(VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2218 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineRegistered), id(aId.toUtf16())
2219 , registered(aRegistered)
2220 {}
2221
2222 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2223 {
2224 switch (mWhat)
2225 {
2226 case VirtualBoxCallbackRegistration::kOnMachineDataChanged:
2227 aEvDesc.init(aSource, VBoxEventType_OnMachineDataChanged, id.raw());
2228 break;
2229
2230 case VirtualBoxCallbackRegistration::kOnMachineStateChanged:
2231 aEvDesc.init(aSource, VBoxEventType_OnMachineStateChanged, id.raw(), state);
2232 break;
2233
2234 case VirtualBoxCallbackRegistration::kOnMachineRegistered:
2235 aEvDesc.init(aSource, VBoxEventType_OnMachineRegistered, id.raw(), registered);
2236 break;
2237
2238 default:
2239 AssertFailedReturn(S_OK);
2240 }
2241 return S_OK;
2242 }
2243
2244 Bstr id;
2245 MachineState_T state;
2246 BOOL registered;
2247};
2248
2249/**
2250 * @note Doesn't lock any object.
2251 */
2252void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2253{
2254 postEvent(new MachineEvent(this, aId, aState));
2255}
2256
2257/**
2258 * @note Doesn't lock any object.
2259 */
2260void VirtualBox::onMachineDataChange(const Guid &aId)
2261{
2262 postEvent(new MachineEvent(this, aId));
2263}
2264
2265/**
2266 * @note Locks this object for reading.
2267 */
2268BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2269 Bstr &aError)
2270{
2271 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2272 aId.toString().c_str(), aKey, aValue));
2273
2274 AutoCaller autoCaller(this);
2275 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2276
2277 BOOL allowChange = TRUE;
2278 Bstr id = aId.toUtf16();
2279
2280 VBoxEventDesc evDesc;
2281 evDesc.init(m->pEventSource, VBoxEventType_OnExtraDataCanChange, id.raw(), aKey, aValue);
2282 BOOL fDelivered = evDesc.fire(3000); /* Wait up to 3 secs for delivery */
2283 //Assert(fDelivered);
2284 if (fDelivered)
2285 {
2286 ComPtr<IEvent> aEvent;
2287 evDesc.getEvent(aEvent.asOutParam());
2288 ComPtr<IExtraDataCanChangeEvent> aCanChangeEvent = aEvent;
2289 Assert(aCanChangeEvent);
2290 BOOL fVetoed = FALSE;
2291 aCanChangeEvent->IsVetoed(&fVetoed);
2292 allowChange = !fVetoed;
2293
2294 if (!allowChange)
2295 {
2296 SafeArray<BSTR> aVetos;
2297 aCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos));
2298 if (aVetos.size() > 0)
2299 aError = aVetos[0];
2300 }
2301 }
2302 else
2303 allowChange = TRUE;
2304
2305 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2306 return allowChange;
2307}
2308
2309/** Event for onExtraDataChange() */
2310struct ExtraDataEvent : public VirtualBox::CallbackEvent
2311{
2312 ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId,
2313 IN_BSTR aKey, IN_BSTR aVal)
2314 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnExtraDataChanged)
2315 , machineId(aMachineId.toUtf16()), key(aKey), val(aVal)
2316 {}
2317
2318 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2319 {
2320 return aEvDesc.init(aSource, VBoxEventType_OnExtraDataChanged, machineId.raw(), key.raw(), val.raw());
2321 }
2322
2323 Bstr machineId, key, val;
2324};
2325
2326/**
2327 * @note Doesn't lock any object.
2328 */
2329void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2330{
2331 postEvent(new ExtraDataEvent(this, aId, aKey, aValue));
2332}
2333
2334/**
2335 * @note Doesn't lock any object.
2336 */
2337void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered)
2338{
2339 postEvent(new MachineEvent(this, aId, aRegistered));
2340}
2341
2342/** Event for onSessionStateChange() */
2343struct SessionEvent : public VirtualBox::CallbackEvent
2344{
2345 SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2346 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnSessionStateChanged)
2347 , machineId(aMachineId.toUtf16()), sessionState(aState)
2348 {}
2349
2350 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2351 {
2352 return aEvDesc.init(aSource, VBoxEventType_OnSessionStateChanged, machineId.raw(), sessionState);
2353 }
2354 Bstr machineId;
2355 SessionState_T sessionState;
2356};
2357
2358/**
2359 * @note Doesn't lock any object.
2360 */
2361void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState)
2362{
2363 postEvent(new SessionEvent(this, aId, aState));
2364}
2365
2366/** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */
2367struct SnapshotEvent : public VirtualBox::CallbackEvent
2368{
2369 SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2370 VirtualBoxCallbackRegistration::CallbackBit aWhat)
2371 : CallbackEvent(aVB, aWhat)
2372 , machineId(aMachineId), snapshotId(aSnapshotId)
2373 {}
2374
2375 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2376 {
2377 return aEvDesc.init(aSource, VBoxEventType_OnSnapshotTaken,
2378 machineId.toUtf16().raw(), snapshotId.toUtf16().raw());
2379 }
2380
2381 Guid machineId;
2382 Guid snapshotId;
2383};
2384
2385/**
2386 * @note Doesn't lock any object.
2387 */
2388void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
2389{
2390 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2391 VirtualBoxCallbackRegistration::kOnSnapshotTaken));
2392}
2393
2394/**
2395 * @note Doesn't lock any object.
2396 */
2397void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
2398{
2399 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2400 VirtualBoxCallbackRegistration::kOnSnapshotDeleted));
2401}
2402
2403/**
2404 * @note Doesn't lock any object.
2405 */
2406void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
2407{
2408 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2409 VirtualBoxCallbackRegistration::kOnSnapshotChanged));
2410}
2411
2412/** Event for onGuestPropertyChange() */
2413struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2414{
2415 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
2416 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2417 : CallbackEvent(aVBox, VirtualBoxCallbackRegistration::kOnGuestPropertyChanged),
2418 machineId(aMachineId),
2419 name(aName),
2420 value(aValue),
2421 flags(aFlags)
2422 {}
2423
2424 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2425 {
2426 return aEvDesc.init(aSource, VBoxEventType_OnGuestPropertyChanged,
2427 machineId.toUtf16().raw(), name.raw(), value.raw(), flags.raw());
2428 }
2429
2430 Guid machineId;
2431 Bstr name, value, flags;
2432};
2433
2434/**
2435 * @note Doesn't lock any object.
2436 */
2437void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
2438 IN_BSTR aValue, IN_BSTR aFlags)
2439{
2440 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
2441}
2442
2443/** Event for onMachineUninit(), this is not a CallbackEvent */
2444class MachineUninitEvent : public Event
2445{
2446public:
2447
2448 MachineUninitEvent(VirtualBox *aVirtualBox, Machine *aMachine)
2449 : mVirtualBox(aVirtualBox), mMachine(aMachine)
2450 {
2451 Assert(aVirtualBox);
2452 Assert(aMachine);
2453 }
2454
2455 void *handler()
2456 {
2457#ifdef VBOX_WITH_RESOURCE_USAGE_API
2458 /* Handle unregistering metrics here, as it is not vital to get
2459 * it done immediately. It reduces the number of locks needed and
2460 * the lock contention in SessionMachine::uninit. */
2461 {
2462 AutoWriteLock mLock(mMachine COMMA_LOCKVAL_SRC_POS);
2463 mMachine->unregisterMetrics(mVirtualBox->performanceCollector(), mMachine);
2464 }
2465#endif /* VBOX_WITH_RESOURCE_USAGE_API */
2466
2467 return NULL;
2468 }
2469
2470private:
2471
2472 /**
2473 * Note that this is a weak ref -- the CallbackEvent handler thread
2474 * is bound to the lifetime of the VirtualBox instance, so it's safe.
2475 */
2476 VirtualBox *mVirtualBox;
2477
2478 /** Reference to the machine object. */
2479 ComObjPtr<Machine> mMachine;
2480};
2481
2482/**
2483 * Trigger internal event. This isn't meant to be signalled to clients.
2484 * @note Doesn't lock any object.
2485 */
2486void VirtualBox::onMachineUninit(Machine *aMachine)
2487{
2488 postEvent(new MachineUninitEvent(this, aMachine));
2489}
2490
2491/**
2492 * @note Locks this object for reading.
2493 */
2494ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2495{
2496 ComObjPtr<GuestOSType> type;
2497 AutoCaller autoCaller(this);
2498 AssertComRCReturn(autoCaller.rc(), type);
2499
2500 /* unknown type must always be the first */
2501 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
2502
2503 return m->allGuestOSTypes.front();
2504}
2505
2506/**
2507 * Returns the list of opened machines (machines having direct sessions opened
2508 * by client processes) and optionally the list of direct session controls.
2509 *
2510 * @param aMachines Where to put opened machines (will be empty if none).
2511 * @param aControls Where to put direct session controls (optional).
2512 *
2513 * @note The returned lists contain smart pointers. So, clear it as soon as
2514 * it becomes no more necessary to release instances.
2515 *
2516 * @note It can be possible that a session machine from the list has been
2517 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2518 * when accessing unprotected data directly.
2519 *
2520 * @note Locks objects for reading.
2521 */
2522void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
2523 InternalControlList *aControls /*= NULL*/)
2524{
2525 AutoCaller autoCaller(this);
2526 AssertComRCReturnVoid(autoCaller.rc());
2527
2528 aMachines.clear();
2529 if (aControls)
2530 aControls->clear();
2531
2532 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2533
2534 for (MachinesOList::iterator it = m->allMachines.begin();
2535 it != m->allMachines.end();
2536 ++it)
2537 {
2538 ComObjPtr<SessionMachine> sm;
2539 ComPtr<IInternalSessionControl> ctl;
2540 if ((*it)->isSessionOpen(sm, &ctl))
2541 {
2542 aMachines.push_back(sm);
2543 if (aControls)
2544 aControls->push_back(ctl);
2545 }
2546 }
2547}
2548
2549/**
2550 * Searches for a machine object with the given ID in the collection
2551 * of registered machines.
2552 *
2553 * @param aId Machine UUID to look for.
2554 * @param aPermitInaccessible If true, inaccessible machines will be found;
2555 * if false, this will fail if the given machine is inaccessible.
2556 * @param aSetError If true, set errorinfo if the machine is not found.
2557 * @param aMachine Returned machine, if found.
2558 * @return
2559 */
2560HRESULT VirtualBox::findMachine(const Guid &aId,
2561 bool fPermitInaccessible,
2562 bool aSetError,
2563 ComObjPtr<Machine> *aMachine /* = NULL */)
2564{
2565 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
2566
2567 AutoCaller autoCaller(this);
2568 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2569
2570 {
2571 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2572
2573 for (MachinesOList::iterator it = m->allMachines.begin();
2574 it != m->allMachines.end();
2575 ++it)
2576 {
2577 ComObjPtr<Machine> pMachine2 = *it;
2578
2579 if (!fPermitInaccessible)
2580 {
2581 // skip inaccessible machines
2582 AutoCaller machCaller(pMachine2);
2583 if (FAILED(machCaller.rc()))
2584 continue;
2585 }
2586
2587 if (pMachine2->getId() == aId)
2588 {
2589 rc = S_OK;
2590 if (aMachine)
2591 *aMachine = pMachine2;
2592 break;
2593 }
2594 }
2595 }
2596
2597 if (aSetError && FAILED(rc))
2598 rc = setError(rc,
2599 tr("Could not find a registered machine with UUID {%RTuuid}"),
2600 aId.raw());
2601
2602 return rc;
2603}
2604
2605/**
2606 * Searches for a Medium object with the given ID in the list of registered
2607 * hard disks.
2608 *
2609 * @param aId ID of the hard disk. Must not be empty.
2610 * @param aSetError If @c true , the appropriate error info is set in case
2611 * when the hard disk is not found.
2612 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2613 *
2614 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2615 *
2616 * @note Locks the media tree for reading.
2617 */
2618HRESULT VirtualBox::findHardDiskById(const Guid &id,
2619 bool aSetError,
2620 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2621{
2622 AssertReturn(!id.isEmpty(), E_INVALIDARG);
2623
2624 // we use the hard disks map, but it is protected by the
2625 // hard disk _list_ lock handle
2626 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2627
2628 HardDiskMap::const_iterator it = m->mapHardDisks.find(id);
2629 if (it != m->mapHardDisks.end())
2630 {
2631 if (aHardDisk)
2632 *aHardDisk = (*it).second;
2633 return S_OK;
2634 }
2635
2636 if (aSetError)
2637 return setError(VBOX_E_OBJECT_NOT_FOUND,
2638 tr("Could not find an open hard disk with UUID {%RTuuid}"),
2639 id.raw());
2640
2641 return VBOX_E_OBJECT_NOT_FOUND;
2642}
2643
2644/**
2645 * Searches for a Medium object with the given ID or location in the list of
2646 * registered hard disks. If both ID and location are specified, the first
2647 * object that matches either of them (not necessarily both) is returned.
2648 *
2649 * @param aLocation Full location specification. Must not be empty.
2650 * @param aSetError If @c true , the appropriate error info is set in case
2651 * when the hard disk is not found.
2652 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2653 *
2654 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2655 *
2656 * @note Locks the media tree for reading.
2657 */
2658HRESULT VirtualBox::findHardDiskByLocation(const Utf8Str &strLocation,
2659 bool aSetError,
2660 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2661{
2662 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
2663
2664 // we use the hard disks map, but it is protected by the
2665 // hard disk _list_ lock handle
2666 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2667
2668 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
2669 it != m->mapHardDisks.end();
2670 ++it)
2671 {
2672 const ComObjPtr<Medium> &pHD = (*it).second;
2673
2674 AutoCaller autoCaller(pHD);
2675 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2676 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
2677
2678 Utf8Str strLocationFull = pHD->getLocationFull();
2679
2680 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
2681 {
2682 if (aHardDisk)
2683 *aHardDisk = pHD;
2684 return S_OK;
2685 }
2686 }
2687
2688 if (aSetError)
2689 return setError(VBOX_E_OBJECT_NOT_FOUND,
2690 tr("Could not find an open hard disk with location '%s'"),
2691 strLocation.c_str());
2692
2693 return VBOX_E_OBJECT_NOT_FOUND;
2694}
2695
2696/**
2697 * Searches for a Medium object with the given ID or location in the list of
2698 * registered DVD or floppy images, depending on the @a mediumType argument.
2699 * If both ID and file path are specified, the first object that matches either
2700 * of them (not necessarily both) is returned.
2701 *
2702 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
2703 * @param aId ID of the image file (unused when NULL).
2704 * @param aLocation Full path to the image file (unused when NULL).
2705 * @param aSetError If @c true, the appropriate error info is set in case when
2706 * the image is not found.
2707 * @param aImage Where to store the found image object (can be NULL).
2708 *
2709 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2710 *
2711 * @note Locks the media tree for reading.
2712 */
2713HRESULT VirtualBox::findDVDOrFloppyImage(DeviceType_T mediumType,
2714 const Guid *aId,
2715 const Utf8Str &aLocation,
2716 bool aSetError,
2717 ComObjPtr<Medium> *aImage /* = NULL */)
2718{
2719 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
2720
2721 Utf8Str location;
2722 if (!aLocation.isEmpty())
2723 {
2724 int vrc = calculateFullPath(aLocation, location);
2725 if (RT_FAILURE(vrc))
2726 return setError(VBOX_E_FILE_ERROR,
2727 tr("Invalid image file location '%ls' (%Rrc)"),
2728 aLocation.c_str(),
2729 vrc);
2730 }
2731
2732 MediaOList *pMediaList;
2733
2734 switch (mediumType)
2735 {
2736 case DeviceType_DVD:
2737 pMediaList = &m->allDVDImages;
2738 break;
2739
2740 case DeviceType_Floppy:
2741 pMediaList = &m->allFloppyImages;
2742 break;
2743
2744 default:
2745 return E_INVALIDARG;
2746 }
2747
2748 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
2749
2750 bool found = false;
2751
2752 for (MediaList::const_iterator it = pMediaList->begin();
2753 it != pMediaList->end();
2754 ++it)
2755 {
2756 // no AutoCaller, registered image life time is bound to this
2757 Medium *pMedium = *it;
2758 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
2759 const Utf8Str &strLocationFull = pMedium->getLocationFull();
2760
2761 found = ( aId
2762 && pMedium->getId() == *aId)
2763 || ( !aLocation.isEmpty()
2764 && RTPathCompare(location.c_str(),
2765 strLocationFull.c_str()) == 0);
2766 if (found)
2767 {
2768 if (pMedium->getDeviceType() != mediumType)
2769 {
2770 if (mediumType == DeviceType_DVD)
2771 return setError(E_INVALIDARG,
2772 "Cannot mount DVD medium '%s' as floppy", strLocationFull.c_str());
2773 else
2774 return setError(E_INVALIDARG,
2775 "Cannot mount floppy medium '%s' as DVD", strLocationFull.c_str());
2776 }
2777
2778 if (aImage)
2779 *aImage = pMedium;
2780 break;
2781 }
2782 }
2783
2784 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2785
2786 if (aSetError && !found)
2787 {
2788 if (aId)
2789 setError(rc,
2790 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
2791 aId->raw(),
2792 m->strSettingsFilePath.c_str());
2793 else
2794 setError(rc,
2795 tr("Could not find an image file with location '%ls' in the media registry ('%s')"),
2796 aLocation.c_str(),
2797 m->strSettingsFilePath.c_str());
2798 }
2799
2800 return rc;
2801}
2802
2803/**
2804 * Searches for an IMedium object that represents the given UUID.
2805 *
2806 * If the UUID is empty (indicating an empty drive), this sets pMedium
2807 * to NULL and returns S_OK.
2808 *
2809 * If the UUID refers to a host drive of the given device type, this
2810 * sets pMedium to the object from the list in IHost and returns S_OK.
2811 *
2812 * If the UUID is an image file, this sets pMedium to the object that
2813 * findDVDOrFloppyImage() returned.
2814 *
2815 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
2816 *
2817 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
2818 * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
2819 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
2820 * @param pMedium out: IMedium object found.
2821 * @return
2822 */
2823HRESULT VirtualBox::findRemoveableMedium(DeviceType_T mediumType,
2824 const Guid &uuid,
2825 bool fRefresh,
2826 ComObjPtr<Medium> &pMedium)
2827{
2828 if (uuid.isEmpty())
2829 {
2830 // that's easy
2831 pMedium.setNull();
2832 return S_OK;
2833 }
2834
2835 // first search for host drive with that UUID
2836 HRESULT rc = m->pHost->findHostDrive(mediumType,
2837 uuid,
2838 fRefresh,
2839 pMedium);
2840 if (rc == VBOX_E_OBJECT_NOT_FOUND)
2841 // then search for an image with that UUID
2842 rc = findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, true /* aSetError */, &pMedium);
2843
2844 return rc;
2845}
2846
2847HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
2848 GuestOSType*& pGuestOSType)
2849{
2850 /* Look for a GuestOSType object */
2851 AssertMsg(m->allGuestOSTypes.size() != 0,
2852 ("Guest OS types array must be filled"));
2853
2854 if (bstrOSType.isEmpty())
2855 {
2856 pGuestOSType = NULL;
2857 return S_OK;
2858 }
2859
2860 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2861 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
2862 it != m->allGuestOSTypes.end();
2863 ++it)
2864 {
2865 if ((*it)->id() == bstrOSType)
2866 {
2867 pGuestOSType = *it;
2868 return S_OK;
2869 }
2870 }
2871
2872 return setError(VBOX_E_OBJECT_NOT_FOUND,
2873 tr("Guest OS type '%ls' is invalid"),
2874 bstrOSType.raw());
2875}
2876
2877/**
2878 * Returns the constant pseudo-machine UUID that is used to identify the
2879 * global media registry.
2880 *
2881 * Starting with VirtualBox 4.0 each medium remembers in its instance data
2882 * in which media registry it is saved (if any): this can either be a machine
2883 * UUID, if it's in a per-machine media registry, or this global ID.
2884 *
2885 * This UUID is only used to identify the VirtualBox object while VirtualBox
2886 * is running. It is a compile-time constant and not saved anywhere.
2887 *
2888 * @return
2889 */
2890const Guid& VirtualBox::getGlobalRegistryId() const
2891{
2892 return m->uuidMediaRegistry;
2893}
2894
2895const ComObjPtr<Host>& VirtualBox::host() const
2896{
2897 return m->pHost;
2898}
2899
2900SystemProperties* VirtualBox::getSystemProperties() const
2901{
2902 return m->pSystemProperties;
2903}
2904
2905#ifdef VBOX_WITH_RESOURCE_USAGE_API
2906const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
2907{
2908 return m->pPerformanceCollector;
2909}
2910#endif /* VBOX_WITH_RESOURCE_USAGE_API */
2911
2912/**
2913 * Returns the default machine folder from the system properties
2914 * with proper locking.
2915 * @return
2916 */
2917void VirtualBox::getDefaultMachineFolder(Utf8Str &str) const
2918{
2919 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2920 str = m->pSystemProperties->m->strDefaultMachineFolder;
2921}
2922
2923/**
2924 * Returns the default hard disk format from the system properties
2925 * with proper locking.
2926 * @return
2927 */
2928void VirtualBox::getDefaultHardDiskFormat(Utf8Str &str) const
2929{
2930 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2931 str = m->pSystemProperties->m->strDefaultHardDiskFormat;
2932}
2933
2934const Utf8Str& VirtualBox::homeDir() const
2935{
2936 return m->strHomeDir;
2937}
2938
2939/**
2940 * Calculates the absolute path of the given path taking the VirtualBox home
2941 * directory as the current directory.
2942 *
2943 * @param aPath Path to calculate the absolute path for.
2944 * @param aResult Where to put the result (used only on success, can be the
2945 * same Utf8Str instance as passed in @a aPath).
2946 * @return IPRT result.
2947 *
2948 * @note Doesn't lock any object.
2949 */
2950int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
2951{
2952 AutoCaller autoCaller(this);
2953 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
2954
2955 /* no need to lock since mHomeDir is const */
2956
2957 char folder[RTPATH_MAX];
2958 int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
2959 strPath.c_str(),
2960 folder,
2961 sizeof(folder));
2962 if (RT_SUCCESS(vrc))
2963 aResult = folder;
2964
2965 return vrc;
2966}
2967
2968/**
2969 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
2970 * if it is a subdirectory thereof, or simply copying it otherwise.
2971 *
2972 * @param strSource Path to evalue and copy.
2973 * @param strTarget Buffer to receive target path.
2974 */
2975void VirtualBox::copyPathRelativeToConfig(const Utf8Str &strSource,
2976 Utf8Str &strTarget)
2977{
2978 AutoCaller autoCaller(this);
2979 AssertComRCReturnVoid(autoCaller.rc());
2980
2981 // no need to lock since mHomeDir is const
2982
2983 // use strTarget as a temporary buffer to hold the machine settings dir
2984 strTarget = m->strHomeDir;
2985 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
2986 // is relative: then append what's left
2987 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
2988 else
2989 // is not relative: then overwrite
2990 strTarget = strSource;
2991}
2992
2993// private methods
2994/////////////////////////////////////////////////////////////////////////////
2995
2996/**
2997 * Checks if there is a hard disk, DVD or floppy image with the given ID or
2998 * location already registered.
2999 *
3000 * On return, sets @a aConflict to the string describing the conflicting medium,
3001 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3002 * either case. A failure is unexpected.
3003 *
3004 * @param aId UUID to check.
3005 * @param aLocation Location to check.
3006 * @param aConflict Where to return parameters of the conflicting medium.
3007 *
3008 * @note Locks the media tree and media objects for reading.
3009 */
3010HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId,
3011 const Utf8Str &aLocation,
3012 Utf8Str &aConflict,
3013 bool &fIdentical)
3014{
3015 aConflict.setNull();
3016
3017 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
3018
3019 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3020
3021 HRESULT rc = S_OK;
3022
3023 aConflict.setNull();
3024 fIdentical = false;
3025
3026 ComObjPtr<Medium> pMediumFound;
3027 const char *pcszType = NULL;
3028
3029 if (!aId.isEmpty())
3030 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
3031 if (FAILED(rc) && !aLocation.isEmpty())
3032 rc = findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
3033 if (SUCCEEDED(rc))
3034 pcszType = tr("hard disk");
3035
3036 if (!pcszType)
3037 {
3038 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
3039 if (SUCCEEDED(rc))
3040 pcszType = tr("CD/DVD image");
3041 }
3042
3043 if (!pcszType)
3044 {
3045 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
3046 if (SUCCEEDED(rc))
3047 pcszType = tr("floppy image");
3048 }
3049
3050 if (pcszType && pMediumFound)
3051 {
3052 /* Note: no AutoCaller since bound to this */
3053 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
3054
3055 Utf8Str strLocFound = pMediumFound->getLocationFull();
3056 Guid idFound = pMediumFound->getId();
3057
3058 if ( (strLocFound == aLocation)
3059 && (idFound == aId)
3060 )
3061 fIdentical = true;
3062
3063 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
3064 pcszType,
3065 strLocFound.c_str(),
3066 idFound.raw());
3067 }
3068
3069 return S_OK;
3070}
3071
3072/**
3073 * Called from Machine::prepareSaveSettings() when it has detected
3074 * that a machine has been renamed. Such renames will require
3075 * updating the global media registry during the
3076 * VirtualBox::saveSettings() that follows later.
3077*
3078 * When a machine is renamed, there may well be media (in particular,
3079 * diff images for snapshots) in the global registry that will need
3080 * to have their paths updated. Before 3.2, Machine::saveSettings
3081 * used to call VirtualBox::saveSettings implicitly, which was both
3082 * unintuitive and caused locking order problems. Now, we remember
3083 * such pending name changes with this method so that
3084 * VirtualBox::saveSettings() can process them properly.
3085 */
3086void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3087 const Utf8Str &strNewConfigDir)
3088{
3089 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3090
3091 Data::PendingMachineRename pmr;
3092 pmr.strConfigDirOld = strOldConfigDir;
3093 pmr.strConfigDirNew = strNewConfigDir;
3094 m->llPendingMachineRenames.push_back(pmr);
3095}
3096
3097/**
3098 * Goes through all known media (hard disks, floppies and DVDs) and saves
3099 * those into the given settings::MediaRegistry structures whose registry
3100 * ID match the given UUID.
3101 *
3102 * This gets called from two contexts:
3103 *
3104 * -- VirtualBox::saveSettings() with the UUID of the global registry
3105 * (VirtualBox::Data.uuidRegistry); this will save those media
3106 * which had been loaded from the global registry or have been
3107 * attached to a "legacy" machine which can't save its own registry;
3108 *
3109 * -- Machine::saveSettings() with the UUID of a machine, if a medium
3110 * has been attached to a machine created with VirtualBox 4.0 or later.
3111 *
3112 * Media which have only been temporarily opened without having been
3113 * attached to a machine have a NULL registry UUID and therefore don't
3114 * get saved.
3115 *
3116 * This throws HRESULT on errors!
3117 *
3118 * @param mediaRegistry Settings structure to fill.
3119 * @param uuidRegistry The UUID of the media registry; either a machine UUID (if machine registry) or the UUID of the global registry.
3120 * @param hardDiskFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
3121 */
3122void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
3123 const Guid &uuidRegistry,
3124 const Utf8Str &strMachineFolder)
3125{
3126 HRESULT rc;
3127 // hard disks
3128 mediaRegistry.llHardDisks.clear();
3129 for (MediaList::const_iterator it = m->allHardDisks.begin();
3130 it != m->allHardDisks.end();
3131 ++it)
3132 {
3133 Medium *pMedium = *it;
3134 if (pMedium->isInRegistry(uuidRegistry))
3135 {
3136 settings::Medium med;
3137 rc = pMedium->saveSettings(med, strMachineFolder); // this recurses into its children
3138 if (FAILED(rc)) throw rc;
3139 mediaRegistry.llHardDisks.push_back(med);
3140 }
3141 }
3142
3143 // CD/DVD images
3144 mediaRegistry.llDvdImages.clear();
3145 for (MediaList::const_iterator it = m->allDVDImages.begin();
3146 it != m->allDVDImages.end();
3147 ++it)
3148 {
3149 Medium *pMedium = *it;
3150 if (pMedium->isInRegistry(uuidRegistry))
3151 {
3152 settings::Medium med;
3153 rc = pMedium->saveSettings(med, strMachineFolder);
3154 if (FAILED(rc)) throw rc;
3155 mediaRegistry.llDvdImages.push_back(med);
3156 }
3157 }
3158
3159 // floppy images
3160 mediaRegistry.llFloppyImages.clear();
3161 for (MediaList::const_iterator it = m->allFloppyImages.begin();
3162 it != m->allFloppyImages.end();
3163 ++it)
3164 {
3165 Medium *pMedium = *it;
3166 if (pMedium->isInRegistry(uuidRegistry))
3167 {
3168 settings::Medium med;
3169 rc = pMedium->saveSettings(med, strMachineFolder);
3170 if (FAILED(rc)) throw rc;
3171 mediaRegistry.llFloppyImages.push_back(med);
3172 }
3173 }
3174}
3175
3176/**
3177 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
3178 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
3179 * places internally when settings need saving.
3180 *
3181 * @note Caller must have locked the VirtualBox object for writing and must not hold any
3182 * other locks since this locks all kinds of member objects and trees temporarily,
3183 * which could cause conflicts.
3184 */
3185HRESULT VirtualBox::saveSettings()
3186{
3187 AutoCaller autoCaller(this);
3188 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3189
3190 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3191 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3192
3193 HRESULT rc = S_OK;
3194
3195 try
3196 {
3197 // machines
3198 m->pMainConfigFile->llMachines.clear();
3199 {
3200 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3201 for (MachinesOList::iterator it = m->allMachines.begin();
3202 it != m->allMachines.end();
3203 ++it)
3204 {
3205 Machine *pMachine = *it;
3206 // save actual machine registry entry
3207 settings::MachineRegistryEntry mre;
3208 rc = pMachine->saveRegistryEntry(mre);
3209 m->pMainConfigFile->llMachines.push_back(mre);
3210 }
3211 }
3212
3213 // lock all media for the following; use a write lock because we're
3214 // modifying the PendingMachineRenamesList, which is protected by this
3215 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3216
3217 // if a machine was renamed, then we'll need to refresh media paths
3218 if (m->llPendingMachineRenames.size())
3219 {
3220 // make a single list from the three media lists so we don't need three loops
3221 MediaList llAllMedia;
3222 // with hard disks, we must use the map, not the list, because the list only has base images
3223 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3224 llAllMedia.push_back(it->second);
3225 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
3226 llAllMedia.push_back(*it);
3227 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
3228 llAllMedia.push_back(*it);
3229
3230 for (MediaList::iterator it = llAllMedia.begin();
3231 it != llAllMedia.end();
3232 ++it)
3233 {
3234 Medium *pMedium = *it;
3235 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3236 it2 != m->llPendingMachineRenames.end();
3237 ++it2)
3238 {
3239 // is medium in global registry:?
3240 if (pMedium->isInRegistry(m->uuidMediaRegistry))
3241 {
3242 const Data::PendingMachineRename &pmr = *it2;
3243 pMedium->updatePath(pmr.strConfigDirOld,
3244 pmr.strConfigDirNew);
3245 }
3246 }
3247 }
3248 // done, don't do it again until we have more machine renames
3249 m->llPendingMachineRenames.clear();
3250 }
3251
3252 saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
3253 m->uuidMediaRegistry, // global media registry ID
3254 Utf8Str::Empty); // strMachineFolder
3255
3256 mediaLock.release();
3257
3258 m->pMainConfigFile->llDhcpServers.clear();
3259 {
3260 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3261 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
3262 it != m->allDHCPServers.end();
3263 ++it)
3264 {
3265 settings::DHCPServer d;
3266 rc = (*it)->saveSettings(d);
3267 if (FAILED(rc)) throw rc;
3268 m->pMainConfigFile->llDhcpServers.push_back(d);
3269 }
3270 }
3271
3272 // leave extra data alone, it's still in the config file
3273
3274 // host data (USB filters)
3275 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3276 if (FAILED(rc)) throw rc;
3277
3278 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3279 if (FAILED(rc)) throw rc;
3280
3281 // and write out the XML, still under the lock
3282 m->pMainConfigFile->write(m->strSettingsFilePath);
3283 }
3284 catch (HRESULT err)
3285 {
3286 /* we assume that error info is set by the thrower */
3287 rc = err;
3288 }
3289 catch (...)
3290 {
3291 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3292 }
3293
3294 return rc;
3295}
3296
3297/**
3298 * Helper to register the machine.
3299 *
3300 * When called during VirtualBox startup, adds the given machine to the
3301 * collection of registered machines. Otherwise tries to mark the machine
3302 * as registered, and, if succeeded, adds it to the collection and
3303 * saves global settings.
3304 *
3305 * @note The caller must have added itself as a caller of the @a aMachine
3306 * object if calls this method not on VirtualBox startup.
3307 *
3308 * @param aMachine machine to register
3309 *
3310 * @note Locks objects!
3311 */
3312HRESULT VirtualBox::registerMachine(Machine *aMachine)
3313{
3314 ComAssertRet(aMachine, E_INVALIDARG);
3315
3316 AutoCaller autoCaller(this);
3317 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3318
3319 HRESULT rc = S_OK;
3320
3321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3322
3323 {
3324 ComObjPtr<Machine> pMachine;
3325 rc = findMachine(aMachine->getId(),
3326 true /* fPermitInaccessible */,
3327 false /* aDoSetError */,
3328 &pMachine);
3329 if (SUCCEEDED(rc))
3330 {
3331 /* sanity */
3332 AutoLimitedCaller machCaller(pMachine);
3333 AssertComRC(machCaller.rc());
3334
3335 return setError(E_INVALIDARG,
3336 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
3337 aMachine->getId().raw(),
3338 pMachine->getSettingsFileFull().c_str());
3339 }
3340
3341 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3342 rc = S_OK;
3343 }
3344
3345 if (autoCaller.state() != InInit)
3346 {
3347 rc = aMachine->prepareRegister();
3348 if (FAILED(rc)) return rc;
3349 }
3350
3351 /* add to the collection of registered machines */
3352 m->allMachines.addChild(aMachine);
3353
3354 if (autoCaller.state() != InInit)
3355 rc = saveSettings();
3356
3357 return rc;
3358}
3359
3360/**
3361 * Remembers the given hard disk by storing it in either the global hard disk registry
3362 * or a machine one.
3363 *
3364 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3365 *
3366 * @param aHardDisk Hard disk object to remember.
3367 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3368 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3369 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3370 * @return
3371 */
3372HRESULT VirtualBox::registerHardDisk(Medium *pMedium,
3373 bool *pfNeedsGlobalSaveSettings)
3374{
3375 AssertReturn(pMedium != NULL, E_INVALIDARG);
3376
3377 AutoCaller autoCaller(this);
3378 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3379
3380 AutoCaller hardDiskCaller(pMedium);
3381 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3382
3383 // caller must hold the media tree write lock
3384 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3385
3386 Guid id;
3387 Utf8Str strLocationFull;
3388 ComObjPtr<Medium> pParent;
3389 {
3390 AutoReadLock hardDiskLock(pMedium COMMA_LOCKVAL_SRC_POS);
3391 id = pMedium->getId();
3392 strLocationFull = pMedium->getLocationFull();
3393 pParent = pMedium->getParent();
3394 }
3395
3396 HRESULT rc;
3397
3398 Utf8Str strConflict;
3399 bool fIdentical;
3400 rc = checkMediaForConflicts(id,
3401 strLocationFull,
3402 strConflict,
3403 fIdentical);
3404 if (FAILED(rc)) return rc;
3405
3406 if (!fIdentical)
3407 {
3408 if (strConflict.length())
3409 return setError(E_INVALIDARG,
3410 tr("Cannot register the hard disk '%s' {%RTuuid} because a %s already exists"),
3411 strLocationFull.c_str(),
3412 id.raw(),
3413 strConflict.c_str(),
3414 m->strSettingsFilePath.c_str());
3415
3416 // store base (root) hard disks in the list
3417 if (pParent.isNull())
3418 m->allHardDisks.getList().push_back(pMedium);
3419 // access the list directly because we already locked the list above
3420
3421 // store all hard disks (even differencing images) in the map
3422 m->mapHardDisks[id] = pMedium;
3423
3424 if (pfNeedsGlobalSaveSettings)
3425 // global settings need saving only if the medium is to be saved in the global registry
3426 if (pMedium->isInRegistry(m->uuidMediaRegistry))
3427 *pfNeedsGlobalSaveSettings = true;
3428 }
3429
3430 return rc;
3431}
3432
3433/**
3434 * Removes the given hard disk from the hard disk registry.
3435 *
3436 * @param aHardDisk Hard disk object to remove.
3437 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3438 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3439 *
3440 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3441 */
3442HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3443 bool *pfNeedsGlobalSaveSettings)
3444{
3445 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3446
3447 AutoCaller autoCaller(this);
3448 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3449
3450 AutoCaller hardDiskCaller(aHardDisk);
3451 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3452
3453 // caller must hold the media tree write lock
3454 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3455
3456 Guid id;
3457 ComObjPtr<Medium> pParent;
3458 {
3459 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3460 id = aHardDisk->getId();
3461 pParent = aHardDisk->getParent();
3462 }
3463
3464 // remove base (root) hard disks from the list
3465 if (pParent.isNull())
3466 m->allHardDisks.getList().remove(aHardDisk);
3467 // access the list directly because caller must have locked the list
3468
3469 // remove all hard disks (even differencing images) from map
3470 size_t cnt = m->mapHardDisks.erase(id);
3471 Assert(cnt == 1);
3472 NOREF(cnt);
3473
3474 if (pfNeedsGlobalSaveSettings)
3475 // global settings need saving only if the medium is to be saved in the global registry
3476 if (aHardDisk->isInRegistry(m->uuidMediaRegistry))
3477 *pfNeedsGlobalSaveSettings = true;
3478
3479 return S_OK;
3480}
3481
3482/**
3483 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3484 *
3485 * @param argImage Image object to remember.
3486 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3487 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3488 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3489 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3490 *
3491 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3492 */
3493HRESULT VirtualBox::registerImage(Medium *pMedium,
3494 DeviceType_T argType,
3495 bool *pfNeedsGlobalSaveSettings)
3496{
3497 AssertReturn(pMedium != NULL, E_INVALIDARG);
3498
3499 AutoCaller autoCaller(this);
3500 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3501
3502 AutoCaller imageCaller(pMedium);
3503 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3504
3505 // caller must hold the media tree write lock
3506 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3507
3508 Guid id;
3509 Utf8Str strLocationFull;
3510 ComObjPtr<Medium> pParent;
3511 {
3512 AutoReadLock al(pMedium COMMA_LOCKVAL_SRC_POS);
3513 id = pMedium->getId();
3514 strLocationFull = pMedium->getLocationFull();
3515 pParent = pMedium->getParent();
3516 }
3517
3518 // work on DVDs or floppies list?
3519 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3520
3521 HRESULT rc;
3522 // lock the images lists (list + map) while checking for conflicts
3523 AutoWriteLock al(all.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3524
3525 Utf8Str strConflict;
3526 bool fIdentical;
3527 rc = checkMediaForConflicts(id,
3528 strLocationFull,
3529 strConflict,
3530 fIdentical);
3531 if (FAILED(rc)) return rc;
3532
3533 if (!fIdentical)
3534 {
3535 if (strConflict.length())
3536 return setError(VBOX_E_INVALID_OBJECT_STATE,
3537 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists"),
3538 strLocationFull.c_str(),
3539 id.raw(),
3540 strConflict.c_str());
3541
3542 // add to the collection
3543 all.getList().push_back(pMedium);
3544 // access the list directly because we already locked the list above
3545
3546 if (pfNeedsGlobalSaveSettings)
3547 // global settings need saving only if the medium is to be saved in the global registry
3548 if (pMedium->isInRegistry(m->uuidMediaRegistry))
3549 *pfNeedsGlobalSaveSettings = true;
3550 }
3551
3552 return rc;
3553}
3554
3555/**
3556 * Removes the given image from the CD/DVD or floppy image registry.
3557 *
3558 * @param argImage Image object to remove.
3559 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3560 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3561 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3562 *
3563 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3564 */
3565HRESULT VirtualBox::unregisterImage(Medium *argImage,
3566 DeviceType_T argType,
3567 bool *pfNeedsGlobalSaveSettings)
3568{
3569 AssertReturn(argImage != NULL, E_INVALIDARG);
3570
3571 AutoCaller autoCaller(this);
3572 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3573
3574 AutoCaller imageCaller(argImage);
3575 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3576
3577 // caller must hold the media tree write lock
3578 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3579
3580 Guid id;
3581 ComObjPtr<Medium> pParent;
3582 {
3583 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3584 id = argImage->getId();
3585 pParent = argImage->getParent();
3586 }
3587
3588 // work on DVDs or floppies list?
3589 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3590
3591 // access the list directly because the caller must have requested the lock
3592 all.getList().remove(argImage);
3593
3594 HRESULT rc = S_OK;
3595
3596 if (pfNeedsGlobalSaveSettings)
3597 // global settings need saving only if the medium is to be saved in the global registry
3598 if (argImage->isInRegistry(m->uuidMediaRegistry))
3599 *pfNeedsGlobalSaveSettings = true;
3600
3601 return rc;
3602}
3603
3604/**
3605 * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
3606 * with children appearing before their parents.
3607 * @param llMedia
3608 * @param pMedium
3609 */
3610void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
3611{
3612 // recurse first, then add ourselves; this way children end up on the
3613 // list before their parents
3614
3615 const MediaList &llChildren = pMedium->getChildren();
3616 for (MediaList::const_iterator it = llChildren.begin();
3617 it != llChildren.end();
3618 ++it)
3619 {
3620 Medium *pChild = *it;
3621 pushMediumToListWithChildren(llMedia, pChild);
3622 }
3623
3624 Log(("Pushing medium %RTuuid\n", pMedium->getId().raw()));
3625 llMedia.push_back(pMedium);
3626}
3627
3628/**
3629 * Unregisters all Medium objects which belong to the given machine registry.
3630 * Gets called from Machine::uninit() just before the machine object dies
3631 * and must only be called with a machine UUID as the registry ID.
3632 *
3633 * Locks the media tree.
3634 *
3635 * @param uuidMachine Medium registry ID (always a machine UUID)
3636 * @return
3637 */
3638HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine)
3639{
3640 Assert(!uuidMachine.isEmpty());
3641
3642 LogFlowFuncEnter();
3643
3644 AutoCaller autoCaller(this);
3645 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3646
3647 MediaList llMedia2Close;
3648
3649 {
3650 AutoWriteLock mlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3651 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
3652 it != m->allHardDisks.getList().end();
3653 ++it)
3654 {
3655 ComObjPtr<Medium> pMedium = *it;
3656
3657 if (pMedium->isInRegistry(uuidMachine))
3658 // recursively with children first
3659 pushMediumToListWithChildren(llMedia2Close, pMedium);
3660 }
3661 }
3662
3663 for (MediaList::iterator it = llMedia2Close.begin();
3664 it != llMedia2Close.end();
3665 ++it)
3666 {
3667 ComObjPtr<Medium> pMedium = *it;
3668 Log(("Closing medium %RTuuid\n", pMedium->getId().raw()));
3669 AutoCaller mac(pMedium);
3670 pMedium->close(NULL /* pfNeedsGlobalSaveSettings*/, mac);
3671 }
3672
3673 LogFlowFuncLeave();
3674
3675 return S_OK;
3676}
3677
3678/**
3679 * Removes the given machine object from the internal list of registered machines.
3680 * Called from Machine::Unregister().
3681 * @param pMachine
3682 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
3683 * @return
3684 */
3685HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
3686 const Guid &id)
3687{
3688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3689
3690 // remove from the collection of registered machines
3691 m->allMachines.removeChild(pMachine);
3692
3693 // save the global registry
3694 HRESULT rc = saveSettings();
3695
3696 alock.release();
3697
3698 /* fire an event */
3699 onMachineRegistered(id, FALSE);
3700
3701 return rc;
3702}
3703
3704/**
3705 * Creates the path to the specified file according to the path information
3706 * present in the file name.
3707 *
3708 * Note that the given file name must contain the full path otherwise the
3709 * extracted relative path will be created based on the current working
3710 * directory which is normally unknown.
3711 *
3712 * @param aFileName Full file name which path needs to be created.
3713 *
3714 * @return Extended error information on failure to create the path.
3715 */
3716/* static */
3717HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3718{
3719 Utf8Str strDir(strFileName);
3720 strDir.stripFilename();
3721 if (!RTDirExists(strDir.c_str()))
3722 {
3723 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3724 if (RT_FAILURE(vrc))
3725 return setErrorStatic(E_FAIL,
3726 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
3727 strDir.c_str(),
3728 vrc));
3729 }
3730
3731 return S_OK;
3732}
3733
3734/**
3735 * Handles unexpected exceptions by turning them into COM errors in release
3736 * builds or by hitting a breakpoint in the release builds.
3737 *
3738 * Usage pattern:
3739 * @code
3740 try
3741 {
3742 // ...
3743 }
3744 catch (LaLalA)
3745 {
3746 // ...
3747 }
3748 catch (...)
3749 {
3750 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3751 }
3752 * @endcode
3753 *
3754 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3755 */
3756/* static */
3757HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3758{
3759 try
3760 {
3761 /* re-throw the current exception */
3762 throw;
3763 }
3764 catch (const iprt::Error &err) // includes all XML exceptions
3765 {
3766 return setErrorStatic(E_FAIL,
3767 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
3768 err.what(),
3769 pszFile, iLine, pszFunction).c_str());
3770 }
3771 catch (const std::exception &err)
3772 {
3773 return setErrorStatic(E_FAIL,
3774 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3775 err.what(), typeid(err).name(),
3776 pszFile, iLine, pszFunction).c_str());
3777 }
3778 catch (...)
3779 {
3780 return setErrorStatic(E_FAIL,
3781 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
3782 pszFile, iLine, pszFunction).c_str());
3783 }
3784
3785 /* should not get here */
3786 AssertFailed();
3787 return E_FAIL;
3788}
3789
3790const Utf8Str& VirtualBox::settingsFilePath()
3791{
3792 return m->strSettingsFilePath;
3793}
3794
3795/**
3796 * Returns the lock handle which protects the media trees (hard disks,
3797 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3798 * are no longer protected by the VirtualBox lock, but by this more
3799 * specialized lock. Mind the locking order: always request this lock
3800 * after the VirtualBox object lock but before the locks of the media
3801 * objects contained in these lists. See AutoLock.h.
3802 */
3803RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3804{
3805 return m->lockMedia;
3806}
3807
3808/**
3809 * Thread function that watches the termination of all client processes
3810 * that have opened sessions using IMachine::LockMachine()
3811 */
3812// static
3813DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3814{
3815 LogFlowFuncEnter();
3816
3817 VirtualBox *that = (VirtualBox*)pvUser;
3818 Assert(that);
3819
3820 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3821 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3822
3823 SessionMachineVector machines;
3824 MachineVector spawnedMachines;
3825
3826 size_t cnt = 0;
3827 size_t cntSpawned = 0;
3828
3829#if defined(RT_OS_WINDOWS)
3830
3831 HRESULT hrc = CoInitializeEx(NULL,
3832 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3833 COINIT_SPEED_OVER_MEMORY);
3834 AssertComRC(hrc);
3835
3836 /// @todo (dmik) processes reaping!
3837
3838 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3839 handles[0] = that->m->updateReq;
3840
3841 do
3842 {
3843 AutoCaller autoCaller(that);
3844 /* VirtualBox has been early uninitialized, terminate */
3845 if (!autoCaller.isOk())
3846 break;
3847
3848 do
3849 {
3850 /* release the caller to let uninit() ever proceed */
3851 autoCaller.release();
3852
3853 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3854 handles,
3855 FALSE,
3856 INFINITE);
3857
3858 /* Restore the caller before using VirtualBox. If it fails, this
3859 * means VirtualBox is being uninitialized and we must terminate. */
3860 autoCaller.add();
3861 if (!autoCaller.isOk())
3862 break;
3863
3864 bool update = false;
3865
3866 if (rc == WAIT_OBJECT_0)
3867 {
3868 /* update event is signaled */
3869 update = true;
3870 }
3871 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3872 {
3873 /* machine mutex is released */
3874 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3875 update = true;
3876 }
3877 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3878 {
3879 /* machine mutex is abandoned due to client process termination */
3880 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3881 update = true;
3882 }
3883 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3884 {
3885 /* spawned VM process has terminated (normally or abnormally) */
3886 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
3887 checkForSpawnFailure();
3888 update = true;
3889 }
3890
3891 if (update)
3892 {
3893 /* close old process handles */
3894 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
3895 CloseHandle(handles[i]);
3896
3897 // lock the machines list for reading
3898 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3899
3900 /* obtain a new set of opened machines */
3901 cnt = 0;
3902 machines.clear();
3903
3904 for (MachinesOList::iterator it = that->m->allMachines.begin();
3905 it != that->m->allMachines.end();
3906 ++it)
3907 {
3908 /// @todo handle situations with more than 64 objects
3909 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3910 ("MAXIMUM_WAIT_OBJECTS reached"));
3911
3912 ComObjPtr<SessionMachine> sm;
3913 HANDLE ipcSem;
3914 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
3915 {
3916 machines.push_back(sm);
3917 handles[1 + cnt] = ipcSem;
3918 ++cnt;
3919 }
3920 }
3921
3922 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
3923
3924 /* obtain a new set of spawned machines */
3925 cntSpawned = 0;
3926 spawnedMachines.clear();
3927
3928 for (MachinesOList::iterator it = that->m->allMachines.begin();
3929 it != that->m->allMachines.end();
3930 ++it)
3931 {
3932 /// @todo handle situations with more than 64 objects
3933 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
3934 ("MAXIMUM_WAIT_OBJECTS reached"));
3935
3936 RTPROCESS pid;
3937 if ((*it)->isSessionSpawning(&pid))
3938 {
3939 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
3940 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
3941 pid, GetLastError()));
3942 if (rc == 0)
3943 {
3944 spawnedMachines.push_back(*it);
3945 handles[1 + cnt + cntSpawned] = ph;
3946 ++cntSpawned;
3947 }
3948 }
3949 }
3950
3951 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
3952
3953 // machines lock unwinds here
3954 }
3955 }
3956 while (true);
3957 }
3958 while (0);
3959
3960 /* close old process handles */
3961 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3962 CloseHandle(handles[i]);
3963
3964 /* release sets of machines if any */
3965 machines.clear();
3966 spawnedMachines.clear();
3967
3968 ::CoUninitialize();
3969
3970#elif defined(RT_OS_OS2)
3971
3972 /// @todo (dmik) processes reaping!
3973
3974 /* according to PMREF, 64 is the maximum for the muxwait list */
3975 SEMRECORD handles[64];
3976
3977 HMUX muxSem = NULLHANDLE;
3978
3979 do
3980 {
3981 AutoCaller autoCaller(that);
3982 /* VirtualBox has been early uninitialized, terminate */
3983 if (!autoCaller.isOk())
3984 break;
3985
3986 do
3987 {
3988 /* release the caller to let uninit() ever proceed */
3989 autoCaller.release();
3990
3991 int vrc = RTSemEventWait(that->m->updateReq, 500);
3992
3993 /* Restore the caller before using VirtualBox. If it fails, this
3994 * means VirtualBox is being uninitialized and we must terminate. */
3995 autoCaller.add();
3996 if (!autoCaller.isOk())
3997 break;
3998
3999 bool update = false;
4000 bool updateSpawned = false;
4001
4002 if (RT_SUCCESS(vrc))
4003 {
4004 /* update event is signaled */
4005 update = true;
4006 updateSpawned = true;
4007 }
4008 else
4009 {
4010 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4011 ("RTSemEventWait returned %Rrc\n", vrc));
4012
4013 /* are there any mutexes? */
4014 if (cnt > 0)
4015 {
4016 /* figure out what's going on with machines */
4017
4018 unsigned long semId = 0;
4019 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
4020 SEM_IMMEDIATE_RETURN, &semId);
4021
4022 if (arc == NO_ERROR)
4023 {
4024 /* machine mutex is normally released */
4025 Assert(semId >= 0 && semId < cnt);
4026 if (semId >= 0 && semId < cnt)
4027 {
4028#if 0//def DEBUG
4029 {
4030 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4031 LogFlowFunc(("released mutex: machine='%ls'\n",
4032 machines[semId]->name().raw()));
4033 }
4034#endif
4035 machines[semId]->checkForDeath();
4036 }
4037 update = true;
4038 }
4039 else if (arc == ERROR_SEM_OWNER_DIED)
4040 {
4041 /* machine mutex is abandoned due to client process
4042 * termination; find which mutex is in the Owner Died
4043 * state */
4044 for (size_t i = 0; i < cnt; ++ i)
4045 {
4046 PID pid; TID tid;
4047 unsigned long reqCnt;
4048 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
4049 if (arc == ERROR_SEM_OWNER_DIED)
4050 {
4051 /* close the dead mutex as asked by PMREF */
4052 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
4053
4054 Assert(i >= 0 && i < cnt);
4055 if (i >= 0 && i < cnt)
4056 {
4057#if 0//def DEBUG
4058 {
4059 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4060 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4061 machines[i]->name().raw()));
4062 }
4063#endif
4064 machines[i]->checkForDeath();
4065 }
4066 }
4067 }
4068 update = true;
4069 }
4070 else
4071 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4072 ("DosWaitMuxWaitSem returned %d\n", arc));
4073 }
4074
4075 /* are there any spawning sessions? */
4076 if (cntSpawned > 0)
4077 {
4078 for (size_t i = 0; i < cntSpawned; ++ i)
4079 updateSpawned |= (spawnedMachines[i])->
4080 checkForSpawnFailure();
4081 }
4082 }
4083
4084 if (update || updateSpawned)
4085 {
4086 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4087
4088 if (update)
4089 {
4090 /* close the old muxsem */
4091 if (muxSem != NULLHANDLE)
4092 ::DosCloseMuxWaitSem(muxSem);
4093
4094 /* obtain a new set of opened machines */
4095 cnt = 0;
4096 machines.clear();
4097
4098 for (MachinesOList::iterator it = that->m->allMachines.begin();
4099 it != that->m->allMachines.end(); ++ it)
4100 {
4101 /// @todo handle situations with more than 64 objects
4102 AssertMsg(cnt <= 64 /* according to PMREF */,
4103 ("maximum of 64 mutex semaphores reached (%d)",
4104 cnt));
4105
4106 ComObjPtr<SessionMachine> sm;
4107 HMTX ipcSem;
4108 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4109 {
4110 machines.push_back(sm);
4111 handles[cnt].hsemCur = (HSEM)ipcSem;
4112 handles[cnt].ulUser = cnt;
4113 ++ cnt;
4114 }
4115 }
4116
4117 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4118
4119 if (cnt > 0)
4120 {
4121 /* create a new muxsem */
4122 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4123 handles,
4124 DCMW_WAIT_ANY);
4125 AssertMsg(arc == NO_ERROR,
4126 ("DosCreateMuxWaitSem returned %d\n", arc));
4127 NOREF(arc);
4128 }
4129 }
4130
4131 if (updateSpawned)
4132 {
4133 /* obtain a new set of spawned machines */
4134 spawnedMachines.clear();
4135
4136 for (MachinesOList::iterator it = that->m->allMachines.begin();
4137 it != that->m->allMachines.end(); ++ it)
4138 {
4139 if ((*it)->isSessionSpawning())
4140 spawnedMachines.push_back(*it);
4141 }
4142
4143 cntSpawned = spawnedMachines.size();
4144 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4145 }
4146 }
4147 }
4148 while (true);
4149 }
4150 while (0);
4151
4152 /* close the muxsem */
4153 if (muxSem != NULLHANDLE)
4154 ::DosCloseMuxWaitSem(muxSem);
4155
4156 /* release sets of machines if any */
4157 machines.clear();
4158 spawnedMachines.clear();
4159
4160#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4161
4162 bool update = false;
4163 bool updateSpawned = false;
4164
4165 do
4166 {
4167 AutoCaller autoCaller(that);
4168 if (!autoCaller.isOk())
4169 break;
4170
4171 do
4172 {
4173 /* release the caller to let uninit() ever proceed */
4174 autoCaller.release();
4175
4176 int rc = RTSemEventWait(that->m->updateReq, 500);
4177
4178 /*
4179 * Restore the caller before using VirtualBox. If it fails, this
4180 * means VirtualBox is being uninitialized and we must terminate.
4181 */
4182 autoCaller.add();
4183 if (!autoCaller.isOk())
4184 break;
4185
4186 if (RT_SUCCESS(rc) || update || updateSpawned)
4187 {
4188 /* RT_SUCCESS(rc) means an update event is signaled */
4189
4190 // lock the machines list for reading
4191 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4192
4193 if (RT_SUCCESS(rc) || update)
4194 {
4195 /* obtain a new set of opened machines */
4196 machines.clear();
4197
4198 for (MachinesOList::iterator it = that->m->allMachines.begin();
4199 it != that->m->allMachines.end();
4200 ++it)
4201 {
4202 ComObjPtr<SessionMachine> sm;
4203 if ((*it)->isSessionOpenOrClosing(sm))
4204 machines.push_back(sm);
4205 }
4206
4207 cnt = machines.size();
4208 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4209 }
4210
4211 if (RT_SUCCESS(rc) || updateSpawned)
4212 {
4213 /* obtain a new set of spawned machines */
4214 spawnedMachines.clear();
4215
4216 for (MachinesOList::iterator it = that->m->allMachines.begin();
4217 it != that->m->allMachines.end();
4218 ++it)
4219 {
4220 if ((*it)->isSessionSpawning())
4221 spawnedMachines.push_back(*it);
4222 }
4223
4224 cntSpawned = spawnedMachines.size();
4225 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4226 }
4227
4228 // machines lock unwinds here
4229 }
4230
4231 update = false;
4232 for (size_t i = 0; i < cnt; ++ i)
4233 update |= (machines[i])->checkForDeath();
4234
4235 updateSpawned = false;
4236 for (size_t i = 0; i < cntSpawned; ++ i)
4237 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4238
4239 /* reap child processes */
4240 {
4241 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4242 if (that->m->llProcesses.size())
4243 {
4244 LogFlowFunc(("UPDATE: child process count = %d\n",
4245 that->m->llProcesses.size()));
4246 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4247 while (it != that->m->llProcesses.end())
4248 {
4249 RTPROCESS pid = *it;
4250 RTPROCSTATUS status;
4251 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4252 if (vrc == VINF_SUCCESS)
4253 {
4254 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4255 pid, pid, status.iStatus,
4256 status.enmReason));
4257 it = that->m->llProcesses.erase(it);
4258 }
4259 else
4260 {
4261 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4262 pid, pid, vrc));
4263 if (vrc != VERR_PROCESS_RUNNING)
4264 {
4265 /* remove the process if it is not already running */
4266 it = that->m->llProcesses.erase(it);
4267 }
4268 else
4269 ++ it;
4270 }
4271 }
4272 }
4273 }
4274 }
4275 while (true);
4276 }
4277 while (0);
4278
4279 /* release sets of machines if any */
4280 machines.clear();
4281 spawnedMachines.clear();
4282
4283#else
4284# error "Port me!"
4285#endif
4286
4287 LogFlowFuncLeave();
4288 return 0;
4289}
4290
4291/**
4292 * Thread function that handles custom events posted using #postEvent().
4293 */
4294// static
4295DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4296{
4297 LogFlowFuncEnter();
4298
4299 AssertReturn(pvUser, VERR_INVALID_POINTER);
4300
4301 // create an event queue for the current thread
4302 EventQueue *eventQ = new EventQueue();
4303 AssertReturn(eventQ, VERR_NO_MEMORY);
4304
4305 // return the queue to the one who created this thread
4306 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4307 // signal that we're ready
4308 RTThreadUserSignal(thread);
4309
4310 while (RT_SUCCESS(eventQ->processEventQueue(RT_INDEFINITE_WAIT)))
4311 /* nothing */ ;
4312
4313 delete eventQ;
4314
4315 LogFlowFuncLeave();
4316
4317 return 0;
4318}
4319
4320
4321////////////////////////////////////////////////////////////////////////////////
4322
4323/**
4324 * Takes the current list of registered callbacks of the managed VirtualBox
4325 * instance, and calls #handleCallback() for every callback item from the
4326 * list, passing the item as an argument.
4327 *
4328 * @note Locks the managed VirtualBox object for reading but leaves the lock
4329 * before iterating over callbacks and calling their methods.
4330 */
4331void *VirtualBox::CallbackEvent::handler()
4332{
4333 if (!mVirtualBox)
4334 return NULL;
4335
4336 AutoCaller autoCaller(mVirtualBox);
4337 if (!autoCaller.isOk())
4338 {
4339 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4340 autoCaller.state()));
4341 /* We don't need mVirtualBox any more, so release it */
4342 mVirtualBox = NULL;
4343 return NULL;
4344 }
4345
4346
4347 {
4348 VBoxEventDesc evDesc;
4349 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4350
4351 evDesc.fire(/* don't wait for delivery */0);
4352 }
4353
4354 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4355 return NULL;
4356}
4357
4358//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4359//{
4360// return E_NOTIMPL;
4361//}
4362
4363STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4364{
4365 CheckComArgStrNotEmptyOrNull(aName);
4366 CheckComArgNotNull(aServer);
4367
4368 AutoCaller autoCaller(this);
4369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4370
4371 ComObjPtr<DHCPServer> dhcpServer;
4372 dhcpServer.createObject();
4373 HRESULT rc = dhcpServer->init(this, aName);
4374 if (FAILED(rc)) return rc;
4375
4376 rc = registerDHCPServer(dhcpServer, true);
4377 if (FAILED(rc)) return rc;
4378
4379 dhcpServer.queryInterfaceTo(aServer);
4380
4381 return rc;
4382}
4383
4384STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4385{
4386 CheckComArgStrNotEmptyOrNull(aName);
4387 CheckComArgNotNull(aServer);
4388
4389 AutoCaller autoCaller(this);
4390 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4391
4392 HRESULT rc;
4393 Bstr bstr;
4394 ComPtr<DHCPServer> found;
4395
4396 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4397
4398 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4399 it != m->allDHCPServers.end();
4400 ++it)
4401 {
4402 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4403 if (FAILED(rc)) throw rc;
4404
4405 if (bstr == aName)
4406 {
4407 found = *it;
4408 break;
4409 }
4410 }
4411
4412 if (!found)
4413 return E_INVALIDARG;
4414
4415 return found.queryInterfaceTo(aServer);
4416}
4417
4418STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4419{
4420 CheckComArgNotNull(aServer);
4421
4422 AutoCaller autoCaller(this);
4423 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4424
4425 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4426
4427 return rc;
4428}
4429
4430/**
4431 * Remembers the given dhcp server by storing it in the hard disk registry.
4432 *
4433 * @param aDHCPServer Dhcp Server object to remember.
4434 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4435 *
4436 * When @a aSaveRegistry is @c true, this operation may fail because of the
4437 * failed #saveSettings() method it calls. In this case, the dhcp server object
4438 * will not be remembered. It is therefore the responsibility of the caller to
4439 * call this method as the last step of some action that requires registration
4440 * in order to make sure that only fully functional dhcp server objects get
4441 * registered.
4442 *
4443 * @note Locks this object for writing and @a aDHCPServer for reading.
4444 */
4445HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4446 bool aSaveRegistry /*= true*/)
4447{
4448 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4449
4450 AutoCaller autoCaller(this);
4451 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4452
4453 AutoCaller dhcpServerCaller(aDHCPServer);
4454 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4455
4456 Bstr name;
4457 HRESULT rc;
4458 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4459 if (FAILED(rc)) return rc;
4460
4461 ComPtr<IDHCPServer> existing;
4462 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
4463 if (SUCCEEDED(rc))
4464 return E_INVALIDARG;
4465
4466 rc = S_OK;
4467
4468 m->allDHCPServers.addChild(aDHCPServer);
4469
4470 if (aSaveRegistry)
4471 {
4472 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4473 rc = saveSettings();
4474 vboxLock.release();
4475
4476 if (FAILED(rc))
4477 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4478 }
4479
4480 return rc;
4481}
4482
4483/**
4484 * Removes the given hard disk from the hard disk registry.
4485 *
4486 * @param aHardDisk Hard disk object to remove.
4487 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4488 *
4489 * When @a aSaveRegistry is @c true, this operation may fail because of the
4490 * failed #saveSettings() method it calls. In this case, the hard disk object
4491 * will NOT be removed from the registry when this method returns. It is
4492 * therefore the responsibility of the caller to call this method as the first
4493 * step of some action that requires unregistration, before calling uninit() on
4494 * @a aHardDisk.
4495 *
4496 * @note Locks this object for writing and @a aHardDisk for reading.
4497 */
4498HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4499 bool aSaveRegistry /*= true*/)
4500{
4501 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4502
4503 AutoCaller autoCaller(this);
4504 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4505
4506 AutoCaller dhcpServerCaller(aDHCPServer);
4507 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4508
4509 m->allDHCPServers.removeChild(aDHCPServer);
4510
4511 HRESULT rc = S_OK;
4512
4513 if (aSaveRegistry)
4514 {
4515 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4516 rc = saveSettings();
4517 vboxLock.release();
4518
4519 if (FAILED(rc))
4520 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4521 }
4522
4523 return rc;
4524}
4525
4526STDMETHODIMP VirtualBox::VRDERegisterLibrary(IN_BSTR aName)
4527{
4528 CheckComArgStrNotEmptyOrNull(aName);
4529
4530 AutoCaller autoCaller(this);
4531 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4532
4533 Utf8Str strName(aName);
4534
4535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4536
4537 /* If the name already exists, there is nothing to do. */
4538 settings::VRDELibrariesList::const_iterator it;
4539
4540 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4541 it != m->pMainConfigFile->llVRDELibraries.end();
4542 ++it)
4543 {
4544 const Utf8Str &str = *it;
4545 if (strName == str)
4546 {
4547 break;
4548 }
4549 }
4550
4551 if (it == m->pMainConfigFile->llVRDELibraries.end())
4552 {
4553 /* Not found, add it. */
4554 m->pMainConfigFile->llVRDELibraries.push_back(strName);
4555
4556 /* Save settings on success. */
4557 HRESULT rc = saveSettings();
4558 if (FAILED(rc)) return rc;
4559 }
4560
4561 return S_OK;
4562}
4563
4564STDMETHODIMP VirtualBox::VRDEUnregisterLibrary(IN_BSTR aName)
4565{
4566 CheckComArgStrNotEmptyOrNull(aName);
4567
4568 AutoCaller autoCaller(this);
4569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4570
4571 Utf8Str strName(aName);
4572
4573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4574
4575 settings::VRDELibrariesList::const_iterator it;
4576
4577 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4578 it != m->pMainConfigFile->llVRDELibraries.end();
4579 ++it)
4580 {
4581 const Utf8Str &str = *it;
4582 if (strName == str)
4583 {
4584 break;
4585 }
4586 }
4587
4588 if (it != m->pMainConfigFile->llVRDELibraries.end())
4589 {
4590 /* Found, remove it. */
4591 m->pMainConfigFile->llVRDELibraries.remove(strName);
4592
4593 /* Save settings on success. */
4594 HRESULT rc = saveSettings();
4595 if (FAILED(rc)) return rc;
4596 }
4597
4598 return S_OK;
4599}
4600
4601STDMETHODIMP VirtualBox::VRDEListLibraries(ComSafeArrayOut(BSTR, aNames))
4602{
4603 using namespace settings;
4604
4605 if (ComSafeArrayOutIsNull(aNames))
4606 return E_POINTER;
4607
4608 AutoCaller autoCaller(this);
4609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4610
4611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4612
4613 com::SafeArray<BSTR> saNames(m->pMainConfigFile->llVRDELibraries.size());
4614 int i = 0;
4615 for (VRDELibrariesList::const_iterator it = m->pMainConfigFile->llVRDELibraries.begin();
4616 it != m->pMainConfigFile->llVRDELibraries.end();
4617 ++it, ++i)
4618 {
4619 const Utf8Str &strName = *it;
4620 strName.cloneTo(&saNames[i]);
4621 }
4622 saNames.detachTo(ComSafeArrayOutArg(aNames));
4623
4624 return S_OK;
4625}
4626
4627STDMETHODIMP VirtualBox::VRDEIsLibraryRegistered(IN_BSTR aName, BOOL *aRegistered)
4628{
4629 CheckComArgStrNotEmptyOrNull(aName);
4630 CheckComArgOutPointerValid(aRegistered);
4631
4632 AutoCaller autoCaller(this);
4633 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4634
4635 Utf8Str strName(aName);
4636
4637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4638
4639 settings::VRDELibrariesList::const_iterator it;
4640
4641 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4642 it != m->pMainConfigFile->llVRDELibraries.end();
4643 ++it)
4644 {
4645 const Utf8Str &str = *it;
4646 if (strName == str)
4647 {
4648 break;
4649 }
4650 }
4651
4652 *aRegistered = (it != m->pMainConfigFile->llVRDELibraries.end());
4653
4654 return S_OK;
4655}
4656
4657/* 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