VirtualBox

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

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

Main: fix regression that media in 4.0 machine folders became inaccessible after a machine rename (since 4.0 directory changes)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 147.1 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 33905 2010-11-09 15:10:12Z vboxsync $ */
2
3/** @file
4 * Implementation of IVirtualBox in VBoxSVC.
5 */
6
7/*
8 * Copyright (C) 2006-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/asm.h>
20#include <iprt/buildconfig.h>
21#include <iprt/cpp/utils.h>
22#include <iprt/dir.h>
23#include <iprt/env.h>
24#include <iprt/file.h>
25#include <iprt/path.h>
26#include <iprt/process.h>
27#include <iprt/string.h>
28#include <iprt/stream.h>
29#include <iprt/thread.h>
30#include <iprt/uuid.h>
31#include <iprt/cpp/xml.h>
32
33#include <VBox/com/com.h>
34#include <VBox/com/array.h>
35#include "VBox/com/EventQueue.h"
36
37#include <VBox/err.h>
38#include <VBox/param.h>
39#include <VBox/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 * Before actually writing to the structures, all media paths (not just the
3103 * ones for the given registry) are updated if machines have been renamed
3104 * since the last call.
3105 *
3106 * This gets called from two contexts:
3107 *
3108 * -- VirtualBox::saveSettings() with the UUID of the global registry
3109 * (VirtualBox::Data.uuidRegistry); this will save those media
3110 * which had been loaded from the global registry or have been
3111 * attached to a "legacy" machine which can't save its own registry;
3112 *
3113 * -- Machine::saveSettings() with the UUID of a machine, if a medium
3114 * has been attached to a machine created with VirtualBox 4.0 or later.
3115 *
3116 * Media which have only been temporarily opened without having been
3117 * attached to a machine have a NULL registry UUID and therefore don't
3118 * get saved.
3119 *
3120 * This locks the media tree. Throws HRESULT on errors!
3121 *
3122 * @param mediaRegistry Settings structure to fill.
3123 * @param uuidRegistry The UUID of the media registry; either a machine UUID (if machine registry) or the UUID of the global registry.
3124 * @param hardDiskFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
3125 */
3126void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
3127 const Guid &uuidRegistry,
3128 const Utf8Str &strMachineFolder)
3129{
3130 // lock all media for the following; use a write lock because we're
3131 // modifying the PendingMachineRenamesList, which is protected by this
3132 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3133
3134 // if a machine was renamed, then we'll need to refresh media paths
3135 if (m->llPendingMachineRenames.size())
3136 {
3137 // make a single list from the three media lists so we don't need three loops
3138 MediaList llAllMedia;
3139 // with hard disks, we must use the map, not the list, because the list only has base images
3140 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3141 llAllMedia.push_back(it->second);
3142 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
3143 llAllMedia.push_back(*it);
3144 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
3145 llAllMedia.push_back(*it);
3146
3147 for (MediaList::iterator it = llAllMedia.begin();
3148 it != llAllMedia.end();
3149 ++it)
3150 {
3151 Medium *pMedium = *it;
3152 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3153 it2 != m->llPendingMachineRenames.end();
3154 ++it2)
3155 {
3156 const Data::PendingMachineRename &pmr = *it2;
3157 pMedium->updatePath(pmr.strConfigDirOld,
3158 pmr.strConfigDirNew);
3159 }
3160 }
3161 // done, don't do it again until we have more machine renames
3162 m->llPendingMachineRenames.clear();
3163 }
3164
3165 HRESULT rc;
3166 // hard disks
3167 mediaRegistry.llHardDisks.clear();
3168 for (MediaList::const_iterator it = m->allHardDisks.begin();
3169 it != m->allHardDisks.end();
3170 ++it)
3171 {
3172 Medium *pMedium = *it;
3173 if (pMedium->isInRegistry(uuidRegistry))
3174 {
3175 settings::Medium med;
3176 rc = pMedium->saveSettings(med, strMachineFolder); // this recurses into its children
3177 if (FAILED(rc)) throw rc;
3178 mediaRegistry.llHardDisks.push_back(med);
3179 }
3180 }
3181
3182 // CD/DVD images
3183 mediaRegistry.llDvdImages.clear();
3184 for (MediaList::const_iterator it = m->allDVDImages.begin();
3185 it != m->allDVDImages.end();
3186 ++it)
3187 {
3188 Medium *pMedium = *it;
3189 if (pMedium->isInRegistry(uuidRegistry))
3190 {
3191 settings::Medium med;
3192 rc = pMedium->saveSettings(med, strMachineFolder);
3193 if (FAILED(rc)) throw rc;
3194 mediaRegistry.llDvdImages.push_back(med);
3195 }
3196 }
3197
3198 // floppy images
3199 mediaRegistry.llFloppyImages.clear();
3200 for (MediaList::const_iterator it = m->allFloppyImages.begin();
3201 it != m->allFloppyImages.end();
3202 ++it)
3203 {
3204 Medium *pMedium = *it;
3205 if (pMedium->isInRegistry(uuidRegistry))
3206 {
3207 settings::Medium med;
3208 rc = pMedium->saveSettings(med, strMachineFolder);
3209 if (FAILED(rc)) throw rc;
3210 mediaRegistry.llFloppyImages.push_back(med);
3211 }
3212 }
3213}
3214
3215/**
3216 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
3217 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
3218 * places internally when settings need saving.
3219 *
3220 * @note Caller must have locked the VirtualBox object for writing and must not hold any
3221 * other locks since this locks all kinds of member objects and trees temporarily,
3222 * which could cause conflicts.
3223 */
3224HRESULT VirtualBox::saveSettings()
3225{
3226 AutoCaller autoCaller(this);
3227 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3228
3229 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3230 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3231
3232 HRESULT rc = S_OK;
3233
3234 try
3235 {
3236 // machines
3237 m->pMainConfigFile->llMachines.clear();
3238 {
3239 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3240 for (MachinesOList::iterator it = m->allMachines.begin();
3241 it != m->allMachines.end();
3242 ++it)
3243 {
3244 Machine *pMachine = *it;
3245 // save actual machine registry entry
3246 settings::MachineRegistryEntry mre;
3247 rc = pMachine->saveRegistryEntry(mre);
3248 m->pMainConfigFile->llMachines.push_back(mre);
3249 }
3250 }
3251
3252 saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
3253 m->uuidMediaRegistry, // global media registry ID
3254 Utf8Str::Empty); // strMachineFolder
3255
3256 m->pMainConfigFile->llDhcpServers.clear();
3257 {
3258 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3259 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
3260 it != m->allDHCPServers.end();
3261 ++it)
3262 {
3263 settings::DHCPServer d;
3264 rc = (*it)->saveSettings(d);
3265 if (FAILED(rc)) throw rc;
3266 m->pMainConfigFile->llDhcpServers.push_back(d);
3267 }
3268 }
3269
3270 // leave extra data alone, it's still in the config file
3271
3272 // host data (USB filters)
3273 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3274 if (FAILED(rc)) throw rc;
3275
3276 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3277 if (FAILED(rc)) throw rc;
3278
3279 // and write out the XML, still under the lock
3280 m->pMainConfigFile->write(m->strSettingsFilePath);
3281 }
3282 catch (HRESULT err)
3283 {
3284 /* we assume that error info is set by the thrower */
3285 rc = err;
3286 }
3287 catch (...)
3288 {
3289 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3290 }
3291
3292 return rc;
3293}
3294
3295/**
3296 * Helper to register the machine.
3297 *
3298 * When called during VirtualBox startup, adds the given machine to the
3299 * collection of registered machines. Otherwise tries to mark the machine
3300 * as registered, and, if succeeded, adds it to the collection and
3301 * saves global settings.
3302 *
3303 * @note The caller must have added itself as a caller of the @a aMachine
3304 * object if calls this method not on VirtualBox startup.
3305 *
3306 * @param aMachine machine to register
3307 *
3308 * @note Locks objects!
3309 */
3310HRESULT VirtualBox::registerMachine(Machine *aMachine)
3311{
3312 ComAssertRet(aMachine, E_INVALIDARG);
3313
3314 AutoCaller autoCaller(this);
3315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3316
3317 HRESULT rc = S_OK;
3318
3319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3320
3321 {
3322 ComObjPtr<Machine> pMachine;
3323 rc = findMachine(aMachine->getId(),
3324 true /* fPermitInaccessible */,
3325 false /* aDoSetError */,
3326 &pMachine);
3327 if (SUCCEEDED(rc))
3328 {
3329 /* sanity */
3330 AutoLimitedCaller machCaller(pMachine);
3331 AssertComRC(machCaller.rc());
3332
3333 return setError(E_INVALIDARG,
3334 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
3335 aMachine->getId().raw(),
3336 pMachine->getSettingsFileFull().c_str());
3337 }
3338
3339 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3340 rc = S_OK;
3341 }
3342
3343 if (autoCaller.state() != InInit)
3344 {
3345 rc = aMachine->prepareRegister();
3346 if (FAILED(rc)) return rc;
3347 }
3348
3349 /* add to the collection of registered machines */
3350 m->allMachines.addChild(aMachine);
3351
3352 if (autoCaller.state() != InInit)
3353 rc = saveSettings();
3354
3355 return rc;
3356}
3357
3358/**
3359 * Remembers the given hard disk by storing it in either the global hard disk registry
3360 * or a machine one.
3361 *
3362 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3363 *
3364 * @param aHardDisk Hard disk object to remember.
3365 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3366 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3367 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3368 * @return
3369 */
3370HRESULT VirtualBox::registerHardDisk(Medium *pMedium,
3371 bool *pfNeedsGlobalSaveSettings)
3372{
3373 AssertReturn(pMedium != NULL, E_INVALIDARG);
3374
3375 AutoCaller autoCaller(this);
3376 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3377
3378 AutoCaller hardDiskCaller(pMedium);
3379 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3380
3381 // caller must hold the media tree write lock
3382 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3383
3384 Guid id;
3385 Utf8Str strLocationFull;
3386 ComObjPtr<Medium> pParent;
3387 {
3388 AutoReadLock hardDiskLock(pMedium COMMA_LOCKVAL_SRC_POS);
3389 id = pMedium->getId();
3390 strLocationFull = pMedium->getLocationFull();
3391 pParent = pMedium->getParent();
3392 }
3393
3394 HRESULT rc;
3395
3396 Utf8Str strConflict;
3397 bool fIdentical;
3398 rc = checkMediaForConflicts(id,
3399 strLocationFull,
3400 strConflict,
3401 fIdentical);
3402 if (FAILED(rc)) return rc;
3403
3404 if (!fIdentical)
3405 {
3406 if (strConflict.length())
3407 return setError(E_INVALIDARG,
3408 tr("Cannot register the hard disk '%s' {%RTuuid} because a %s already exists"),
3409 strLocationFull.c_str(),
3410 id.raw(),
3411 strConflict.c_str(),
3412 m->strSettingsFilePath.c_str());
3413
3414 // store base (root) hard disks in the list
3415 if (pParent.isNull())
3416 m->allHardDisks.getList().push_back(pMedium);
3417 // access the list directly because we already locked the list above
3418
3419 // store all hard disks (even differencing images) in the map
3420 m->mapHardDisks[id] = pMedium;
3421
3422 if (pfNeedsGlobalSaveSettings)
3423 // global settings need saving only if the medium is to be saved in the global registry
3424 if (pMedium->isInRegistry(m->uuidMediaRegistry))
3425 *pfNeedsGlobalSaveSettings = true;
3426 }
3427
3428 return rc;
3429}
3430
3431/**
3432 * Removes the given hard disk from the hard disk registry.
3433 *
3434 * @param aHardDisk Hard disk object to remove.
3435 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3436 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3437 *
3438 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3439 */
3440HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3441 bool *pfNeedsGlobalSaveSettings)
3442{
3443 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3444
3445 AutoCaller autoCaller(this);
3446 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3447
3448 AutoCaller hardDiskCaller(aHardDisk);
3449 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3450
3451 // caller must hold the media tree write lock
3452 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3453
3454 Guid id;
3455 ComObjPtr<Medium> pParent;
3456 {
3457 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3458 id = aHardDisk->getId();
3459 pParent = aHardDisk->getParent();
3460 }
3461
3462 // remove base (root) hard disks from the list
3463 if (pParent.isNull())
3464 m->allHardDisks.getList().remove(aHardDisk);
3465 // access the list directly because caller must have locked the list
3466
3467 // remove all hard disks (even differencing images) from map
3468 size_t cnt = m->mapHardDisks.erase(id);
3469 Assert(cnt == 1);
3470 NOREF(cnt);
3471
3472 if (pfNeedsGlobalSaveSettings)
3473 // global settings need saving only if the medium is to be saved in the global registry
3474 if (aHardDisk->isInRegistry(m->uuidMediaRegistry))
3475 *pfNeedsGlobalSaveSettings = true;
3476
3477 return S_OK;
3478}
3479
3480/**
3481 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3482 *
3483 * @param argImage Image object to remember.
3484 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3485 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3486 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3487 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3488 *
3489 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3490 */
3491HRESULT VirtualBox::registerImage(Medium *pMedium,
3492 DeviceType_T argType,
3493 bool *pfNeedsGlobalSaveSettings)
3494{
3495 AssertReturn(pMedium != NULL, E_INVALIDARG);
3496
3497 AutoCaller autoCaller(this);
3498 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3499
3500 AutoCaller imageCaller(pMedium);
3501 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3502
3503 // caller must hold the media tree write lock
3504 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3505
3506 Guid id;
3507 Utf8Str strLocationFull;
3508 ComObjPtr<Medium> pParent;
3509 {
3510 AutoReadLock al(pMedium COMMA_LOCKVAL_SRC_POS);
3511 id = pMedium->getId();
3512 strLocationFull = pMedium->getLocationFull();
3513 pParent = pMedium->getParent();
3514 }
3515
3516 // work on DVDs or floppies list?
3517 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3518
3519 HRESULT rc;
3520 // lock the images lists (list + map) while checking for conflicts
3521 AutoWriteLock al(all.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3522
3523 Utf8Str strConflict;
3524 bool fIdentical;
3525 rc = checkMediaForConflicts(id,
3526 strLocationFull,
3527 strConflict,
3528 fIdentical);
3529 if (FAILED(rc)) return rc;
3530
3531 if (!fIdentical)
3532 {
3533 if (strConflict.length())
3534 return setError(VBOX_E_INVALID_OBJECT_STATE,
3535 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists"),
3536 strLocationFull.c_str(),
3537 id.raw(),
3538 strConflict.c_str());
3539
3540 // add to the collection
3541 all.getList().push_back(pMedium);
3542 // access the list directly because we already locked the list above
3543
3544 if (pfNeedsGlobalSaveSettings)
3545 // global settings need saving only if the medium is to be saved in the global registry
3546 if (pMedium->isInRegistry(m->uuidMediaRegistry))
3547 *pfNeedsGlobalSaveSettings = true;
3548 }
3549
3550 return rc;
3551}
3552
3553/**
3554 * Removes the given image from the CD/DVD or floppy image registry.
3555 *
3556 * @param argImage Image object to remove.
3557 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3558 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3559 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3560 *
3561 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3562 */
3563HRESULT VirtualBox::unregisterImage(Medium *argImage,
3564 DeviceType_T argType,
3565 bool *pfNeedsGlobalSaveSettings)
3566{
3567 AssertReturn(argImage != NULL, E_INVALIDARG);
3568
3569 AutoCaller autoCaller(this);
3570 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3571
3572 AutoCaller imageCaller(argImage);
3573 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3574
3575 // caller must hold the media tree write lock
3576 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3577
3578 Guid id;
3579 ComObjPtr<Medium> pParent;
3580 {
3581 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3582 id = argImage->getId();
3583 pParent = argImage->getParent();
3584 }
3585
3586 // work on DVDs or floppies list?
3587 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3588
3589 // access the list directly because the caller must have requested the lock
3590 all.getList().remove(argImage);
3591
3592 HRESULT rc = S_OK;
3593
3594 if (pfNeedsGlobalSaveSettings)
3595 // global settings need saving only if the medium is to be saved in the global registry
3596 if (argImage->isInRegistry(m->uuidMediaRegistry))
3597 *pfNeedsGlobalSaveSettings = true;
3598
3599 return rc;
3600}
3601
3602/**
3603 * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
3604 * with children appearing before their parents.
3605 * @param llMedia
3606 * @param pMedium
3607 */
3608void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
3609{
3610 // recurse first, then add ourselves; this way children end up on the
3611 // list before their parents
3612
3613 const MediaList &llChildren = pMedium->getChildren();
3614 for (MediaList::const_iterator it = llChildren.begin();
3615 it != llChildren.end();
3616 ++it)
3617 {
3618 Medium *pChild = *it;
3619 pushMediumToListWithChildren(llMedia, pChild);
3620 }
3621
3622 Log(("Pushing medium %RTuuid\n", pMedium->getId().raw()));
3623 llMedia.push_back(pMedium);
3624}
3625
3626/**
3627 * Unregisters all Medium objects which belong to the given machine registry.
3628 * Gets called from Machine::uninit() just before the machine object dies
3629 * and must only be called with a machine UUID as the registry ID.
3630 *
3631 * Locks the media tree.
3632 *
3633 * @param uuidMachine Medium registry ID (always a machine UUID)
3634 * @return
3635 */
3636HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine)
3637{
3638 Assert(!uuidMachine.isEmpty());
3639
3640 LogFlowFuncEnter();
3641
3642 AutoCaller autoCaller(this);
3643 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3644
3645 MediaList llMedia2Close;
3646
3647 {
3648 AutoWriteLock mlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3649 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
3650 it != m->allHardDisks.getList().end();
3651 ++it)
3652 {
3653 ComObjPtr<Medium> pMedium = *it;
3654
3655 if (pMedium->isInRegistry(uuidMachine))
3656 // recursively with children first
3657 pushMediumToListWithChildren(llMedia2Close, pMedium);
3658 }
3659 }
3660
3661 for (MediaList::iterator it = llMedia2Close.begin();
3662 it != llMedia2Close.end();
3663 ++it)
3664 {
3665 ComObjPtr<Medium> pMedium = *it;
3666 Log(("Closing medium %RTuuid\n", pMedium->getId().raw()));
3667 AutoCaller mac(pMedium);
3668 pMedium->close(NULL /* pfNeedsGlobalSaveSettings*/, mac);
3669 }
3670
3671 LogFlowFuncLeave();
3672
3673 return S_OK;
3674}
3675
3676/**
3677 * Removes the given machine object from the internal list of registered machines.
3678 * Called from Machine::Unregister().
3679 * @param pMachine
3680 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
3681 * @return
3682 */
3683HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
3684 const Guid &id)
3685{
3686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3687
3688 // remove from the collection of registered machines
3689 m->allMachines.removeChild(pMachine);
3690
3691 // save the global registry
3692 HRESULT rc = saveSettings();
3693
3694 alock.release();
3695
3696 /* fire an event */
3697 onMachineRegistered(id, FALSE);
3698
3699 return rc;
3700}
3701
3702/**
3703 * Creates the path to the specified file according to the path information
3704 * present in the file name.
3705 *
3706 * Note that the given file name must contain the full path otherwise the
3707 * extracted relative path will be created based on the current working
3708 * directory which is normally unknown.
3709 *
3710 * @param aFileName Full file name which path needs to be created.
3711 *
3712 * @return Extended error information on failure to create the path.
3713 */
3714/* static */
3715HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3716{
3717 Utf8Str strDir(strFileName);
3718 strDir.stripFilename();
3719 if (!RTDirExists(strDir.c_str()))
3720 {
3721 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3722 if (RT_FAILURE(vrc))
3723 return setErrorStatic(E_FAIL,
3724 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
3725 strDir.c_str(),
3726 vrc));
3727 }
3728
3729 return S_OK;
3730}
3731
3732/**
3733 * Handles unexpected exceptions by turning them into COM errors in release
3734 * builds or by hitting a breakpoint in the release builds.
3735 *
3736 * Usage pattern:
3737 * @code
3738 try
3739 {
3740 // ...
3741 }
3742 catch (LaLalA)
3743 {
3744 // ...
3745 }
3746 catch (...)
3747 {
3748 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3749 }
3750 * @endcode
3751 *
3752 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3753 */
3754/* static */
3755HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3756{
3757 try
3758 {
3759 /* re-throw the current exception */
3760 throw;
3761 }
3762 catch (const iprt::Error &err) // includes all XML exceptions
3763 {
3764 return setErrorStatic(E_FAIL,
3765 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
3766 err.what(),
3767 pszFile, iLine, pszFunction).c_str());
3768 }
3769 catch (const std::exception &err)
3770 {
3771 return setErrorStatic(E_FAIL,
3772 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3773 err.what(), typeid(err).name(),
3774 pszFile, iLine, pszFunction).c_str());
3775 }
3776 catch (...)
3777 {
3778 return setErrorStatic(E_FAIL,
3779 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
3780 pszFile, iLine, pszFunction).c_str());
3781 }
3782
3783 /* should not get here */
3784 AssertFailed();
3785 return E_FAIL;
3786}
3787
3788const Utf8Str& VirtualBox::settingsFilePath()
3789{
3790 return m->strSettingsFilePath;
3791}
3792
3793/**
3794 * Returns the lock handle which protects the media trees (hard disks,
3795 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3796 * are no longer protected by the VirtualBox lock, but by this more
3797 * specialized lock. Mind the locking order: always request this lock
3798 * after the VirtualBox object lock but before the locks of the media
3799 * objects contained in these lists. See AutoLock.h.
3800 */
3801RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3802{
3803 return m->lockMedia;
3804}
3805
3806/**
3807 * Thread function that watches the termination of all client processes
3808 * that have opened sessions using IMachine::LockMachine()
3809 */
3810// static
3811DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3812{
3813 LogFlowFuncEnter();
3814
3815 VirtualBox *that = (VirtualBox*)pvUser;
3816 Assert(that);
3817
3818 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3819 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3820
3821 SessionMachineVector machines;
3822 MachineVector spawnedMachines;
3823
3824 size_t cnt = 0;
3825 size_t cntSpawned = 0;
3826
3827#if defined(RT_OS_WINDOWS)
3828
3829 HRESULT hrc = CoInitializeEx(NULL,
3830 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3831 COINIT_SPEED_OVER_MEMORY);
3832 AssertComRC(hrc);
3833
3834 /// @todo (dmik) processes reaping!
3835
3836 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3837 handles[0] = that->m->updateReq;
3838
3839 do
3840 {
3841 AutoCaller autoCaller(that);
3842 /* VirtualBox has been early uninitialized, terminate */
3843 if (!autoCaller.isOk())
3844 break;
3845
3846 do
3847 {
3848 /* release the caller to let uninit() ever proceed */
3849 autoCaller.release();
3850
3851 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3852 handles,
3853 FALSE,
3854 INFINITE);
3855
3856 /* Restore the caller before using VirtualBox. If it fails, this
3857 * means VirtualBox is being uninitialized and we must terminate. */
3858 autoCaller.add();
3859 if (!autoCaller.isOk())
3860 break;
3861
3862 bool update = false;
3863
3864 if (rc == WAIT_OBJECT_0)
3865 {
3866 /* update event is signaled */
3867 update = true;
3868 }
3869 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3870 {
3871 /* machine mutex is released */
3872 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3873 update = true;
3874 }
3875 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3876 {
3877 /* machine mutex is abandoned due to client process termination */
3878 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3879 update = true;
3880 }
3881 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3882 {
3883 /* spawned VM process has terminated (normally or abnormally) */
3884 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
3885 checkForSpawnFailure();
3886 update = true;
3887 }
3888
3889 if (update)
3890 {
3891 /* close old process handles */
3892 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
3893 CloseHandle(handles[i]);
3894
3895 // lock the machines list for reading
3896 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3897
3898 /* obtain a new set of opened machines */
3899 cnt = 0;
3900 machines.clear();
3901
3902 for (MachinesOList::iterator it = that->m->allMachines.begin();
3903 it != that->m->allMachines.end();
3904 ++it)
3905 {
3906 /// @todo handle situations with more than 64 objects
3907 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3908 ("MAXIMUM_WAIT_OBJECTS reached"));
3909
3910 ComObjPtr<SessionMachine> sm;
3911 HANDLE ipcSem;
3912 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
3913 {
3914 machines.push_back(sm);
3915 handles[1 + cnt] = ipcSem;
3916 ++cnt;
3917 }
3918 }
3919
3920 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
3921
3922 /* obtain a new set of spawned machines */
3923 cntSpawned = 0;
3924 spawnedMachines.clear();
3925
3926 for (MachinesOList::iterator it = that->m->allMachines.begin();
3927 it != that->m->allMachines.end();
3928 ++it)
3929 {
3930 /// @todo handle situations with more than 64 objects
3931 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
3932 ("MAXIMUM_WAIT_OBJECTS reached"));
3933
3934 RTPROCESS pid;
3935 if ((*it)->isSessionSpawning(&pid))
3936 {
3937 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
3938 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
3939 pid, GetLastError()));
3940 if (rc == 0)
3941 {
3942 spawnedMachines.push_back(*it);
3943 handles[1 + cnt + cntSpawned] = ph;
3944 ++cntSpawned;
3945 }
3946 }
3947 }
3948
3949 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
3950
3951 // machines lock unwinds here
3952 }
3953 }
3954 while (true);
3955 }
3956 while (0);
3957
3958 /* close old process handles */
3959 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3960 CloseHandle(handles[i]);
3961
3962 /* release sets of machines if any */
3963 machines.clear();
3964 spawnedMachines.clear();
3965
3966 ::CoUninitialize();
3967
3968#elif defined(RT_OS_OS2)
3969
3970 /// @todo (dmik) processes reaping!
3971
3972 /* according to PMREF, 64 is the maximum for the muxwait list */
3973 SEMRECORD handles[64];
3974
3975 HMUX muxSem = NULLHANDLE;
3976
3977 do
3978 {
3979 AutoCaller autoCaller(that);
3980 /* VirtualBox has been early uninitialized, terminate */
3981 if (!autoCaller.isOk())
3982 break;
3983
3984 do
3985 {
3986 /* release the caller to let uninit() ever proceed */
3987 autoCaller.release();
3988
3989 int vrc = RTSemEventWait(that->m->updateReq, 500);
3990
3991 /* Restore the caller before using VirtualBox. If it fails, this
3992 * means VirtualBox is being uninitialized and we must terminate. */
3993 autoCaller.add();
3994 if (!autoCaller.isOk())
3995 break;
3996
3997 bool update = false;
3998 bool updateSpawned = false;
3999
4000 if (RT_SUCCESS(vrc))
4001 {
4002 /* update event is signaled */
4003 update = true;
4004 updateSpawned = true;
4005 }
4006 else
4007 {
4008 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4009 ("RTSemEventWait returned %Rrc\n", vrc));
4010
4011 /* are there any mutexes? */
4012 if (cnt > 0)
4013 {
4014 /* figure out what's going on with machines */
4015
4016 unsigned long semId = 0;
4017 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
4018 SEM_IMMEDIATE_RETURN, &semId);
4019
4020 if (arc == NO_ERROR)
4021 {
4022 /* machine mutex is normally released */
4023 Assert(semId >= 0 && semId < cnt);
4024 if (semId >= 0 && semId < cnt)
4025 {
4026#if 0//def DEBUG
4027 {
4028 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4029 LogFlowFunc(("released mutex: machine='%ls'\n",
4030 machines[semId]->name().raw()));
4031 }
4032#endif
4033 machines[semId]->checkForDeath();
4034 }
4035 update = true;
4036 }
4037 else if (arc == ERROR_SEM_OWNER_DIED)
4038 {
4039 /* machine mutex is abandoned due to client process
4040 * termination; find which mutex is in the Owner Died
4041 * state */
4042 for (size_t i = 0; i < cnt; ++ i)
4043 {
4044 PID pid; TID tid;
4045 unsigned long reqCnt;
4046 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
4047 if (arc == ERROR_SEM_OWNER_DIED)
4048 {
4049 /* close the dead mutex as asked by PMREF */
4050 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
4051
4052 Assert(i >= 0 && i < cnt);
4053 if (i >= 0 && i < cnt)
4054 {
4055#if 0//def DEBUG
4056 {
4057 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4058 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4059 machines[i]->name().raw()));
4060 }
4061#endif
4062 machines[i]->checkForDeath();
4063 }
4064 }
4065 }
4066 update = true;
4067 }
4068 else
4069 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4070 ("DosWaitMuxWaitSem returned %d\n", arc));
4071 }
4072
4073 /* are there any spawning sessions? */
4074 if (cntSpawned > 0)
4075 {
4076 for (size_t i = 0; i < cntSpawned; ++ i)
4077 updateSpawned |= (spawnedMachines[i])->
4078 checkForSpawnFailure();
4079 }
4080 }
4081
4082 if (update || updateSpawned)
4083 {
4084 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4085
4086 if (update)
4087 {
4088 /* close the old muxsem */
4089 if (muxSem != NULLHANDLE)
4090 ::DosCloseMuxWaitSem(muxSem);
4091
4092 /* obtain a new set of opened machines */
4093 cnt = 0;
4094 machines.clear();
4095
4096 for (MachinesOList::iterator it = that->m->allMachines.begin();
4097 it != that->m->allMachines.end(); ++ it)
4098 {
4099 /// @todo handle situations with more than 64 objects
4100 AssertMsg(cnt <= 64 /* according to PMREF */,
4101 ("maximum of 64 mutex semaphores reached (%d)",
4102 cnt));
4103
4104 ComObjPtr<SessionMachine> sm;
4105 HMTX ipcSem;
4106 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4107 {
4108 machines.push_back(sm);
4109 handles[cnt].hsemCur = (HSEM)ipcSem;
4110 handles[cnt].ulUser = cnt;
4111 ++ cnt;
4112 }
4113 }
4114
4115 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4116
4117 if (cnt > 0)
4118 {
4119 /* create a new muxsem */
4120 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4121 handles,
4122 DCMW_WAIT_ANY);
4123 AssertMsg(arc == NO_ERROR,
4124 ("DosCreateMuxWaitSem returned %d\n", arc));
4125 NOREF(arc);
4126 }
4127 }
4128
4129 if (updateSpawned)
4130 {
4131 /* obtain a new set of spawned machines */
4132 spawnedMachines.clear();
4133
4134 for (MachinesOList::iterator it = that->m->allMachines.begin();
4135 it != that->m->allMachines.end(); ++ it)
4136 {
4137 if ((*it)->isSessionSpawning())
4138 spawnedMachines.push_back(*it);
4139 }
4140
4141 cntSpawned = spawnedMachines.size();
4142 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4143 }
4144 }
4145 }
4146 while (true);
4147 }
4148 while (0);
4149
4150 /* close the muxsem */
4151 if (muxSem != NULLHANDLE)
4152 ::DosCloseMuxWaitSem(muxSem);
4153
4154 /* release sets of machines if any */
4155 machines.clear();
4156 spawnedMachines.clear();
4157
4158#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4159
4160 bool update = false;
4161 bool updateSpawned = false;
4162
4163 do
4164 {
4165 AutoCaller autoCaller(that);
4166 if (!autoCaller.isOk())
4167 break;
4168
4169 do
4170 {
4171 /* release the caller to let uninit() ever proceed */
4172 autoCaller.release();
4173
4174 int rc = RTSemEventWait(that->m->updateReq, 500);
4175
4176 /*
4177 * Restore the caller before using VirtualBox. If it fails, this
4178 * means VirtualBox is being uninitialized and we must terminate.
4179 */
4180 autoCaller.add();
4181 if (!autoCaller.isOk())
4182 break;
4183
4184 if (RT_SUCCESS(rc) || update || updateSpawned)
4185 {
4186 /* RT_SUCCESS(rc) means an update event is signaled */
4187
4188 // lock the machines list for reading
4189 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4190
4191 if (RT_SUCCESS(rc) || update)
4192 {
4193 /* obtain a new set of opened machines */
4194 machines.clear();
4195
4196 for (MachinesOList::iterator it = that->m->allMachines.begin();
4197 it != that->m->allMachines.end();
4198 ++it)
4199 {
4200 ComObjPtr<SessionMachine> sm;
4201 if ((*it)->isSessionOpenOrClosing(sm))
4202 machines.push_back(sm);
4203 }
4204
4205 cnt = machines.size();
4206 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4207 }
4208
4209 if (RT_SUCCESS(rc) || updateSpawned)
4210 {
4211 /* obtain a new set of spawned machines */
4212 spawnedMachines.clear();
4213
4214 for (MachinesOList::iterator it = that->m->allMachines.begin();
4215 it != that->m->allMachines.end();
4216 ++it)
4217 {
4218 if ((*it)->isSessionSpawning())
4219 spawnedMachines.push_back(*it);
4220 }
4221
4222 cntSpawned = spawnedMachines.size();
4223 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4224 }
4225
4226 // machines lock unwinds here
4227 }
4228
4229 update = false;
4230 for (size_t i = 0; i < cnt; ++ i)
4231 update |= (machines[i])->checkForDeath();
4232
4233 updateSpawned = false;
4234 for (size_t i = 0; i < cntSpawned; ++ i)
4235 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4236
4237 /* reap child processes */
4238 {
4239 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4240 if (that->m->llProcesses.size())
4241 {
4242 LogFlowFunc(("UPDATE: child process count = %d\n",
4243 that->m->llProcesses.size()));
4244 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4245 while (it != that->m->llProcesses.end())
4246 {
4247 RTPROCESS pid = *it;
4248 RTPROCSTATUS status;
4249 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4250 if (vrc == VINF_SUCCESS)
4251 {
4252 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4253 pid, pid, status.iStatus,
4254 status.enmReason));
4255 it = that->m->llProcesses.erase(it);
4256 }
4257 else
4258 {
4259 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4260 pid, pid, vrc));
4261 if (vrc != VERR_PROCESS_RUNNING)
4262 {
4263 /* remove the process if it is not already running */
4264 it = that->m->llProcesses.erase(it);
4265 }
4266 else
4267 ++ it;
4268 }
4269 }
4270 }
4271 }
4272 }
4273 while (true);
4274 }
4275 while (0);
4276
4277 /* release sets of machines if any */
4278 machines.clear();
4279 spawnedMachines.clear();
4280
4281#else
4282# error "Port me!"
4283#endif
4284
4285 LogFlowFuncLeave();
4286 return 0;
4287}
4288
4289/**
4290 * Thread function that handles custom events posted using #postEvent().
4291 */
4292// static
4293DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4294{
4295 LogFlowFuncEnter();
4296
4297 AssertReturn(pvUser, VERR_INVALID_POINTER);
4298
4299 // create an event queue for the current thread
4300 EventQueue *eventQ = new EventQueue();
4301 AssertReturn(eventQ, VERR_NO_MEMORY);
4302
4303 // return the queue to the one who created this thread
4304 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4305 // signal that we're ready
4306 RTThreadUserSignal(thread);
4307
4308 while (RT_SUCCESS(eventQ->processEventQueue(RT_INDEFINITE_WAIT)))
4309 /* nothing */ ;
4310
4311 delete eventQ;
4312
4313 LogFlowFuncLeave();
4314
4315 return 0;
4316}
4317
4318
4319////////////////////////////////////////////////////////////////////////////////
4320
4321/**
4322 * Takes the current list of registered callbacks of the managed VirtualBox
4323 * instance, and calls #handleCallback() for every callback item from the
4324 * list, passing the item as an argument.
4325 *
4326 * @note Locks the managed VirtualBox object for reading but leaves the lock
4327 * before iterating over callbacks and calling their methods.
4328 */
4329void *VirtualBox::CallbackEvent::handler()
4330{
4331 if (!mVirtualBox)
4332 return NULL;
4333
4334 AutoCaller autoCaller(mVirtualBox);
4335 if (!autoCaller.isOk())
4336 {
4337 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4338 autoCaller.state()));
4339 /* We don't need mVirtualBox any more, so release it */
4340 mVirtualBox = NULL;
4341 return NULL;
4342 }
4343
4344
4345 {
4346 VBoxEventDesc evDesc;
4347 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4348
4349 evDesc.fire(/* don't wait for delivery */0);
4350 }
4351
4352 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4353 return NULL;
4354}
4355
4356//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4357//{
4358// return E_NOTIMPL;
4359//}
4360
4361STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4362{
4363 CheckComArgStrNotEmptyOrNull(aName);
4364 CheckComArgNotNull(aServer);
4365
4366 AutoCaller autoCaller(this);
4367 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4368
4369 ComObjPtr<DHCPServer> dhcpServer;
4370 dhcpServer.createObject();
4371 HRESULT rc = dhcpServer->init(this, aName);
4372 if (FAILED(rc)) return rc;
4373
4374 rc = registerDHCPServer(dhcpServer, true);
4375 if (FAILED(rc)) return rc;
4376
4377 dhcpServer.queryInterfaceTo(aServer);
4378
4379 return rc;
4380}
4381
4382STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4383{
4384 CheckComArgStrNotEmptyOrNull(aName);
4385 CheckComArgNotNull(aServer);
4386
4387 AutoCaller autoCaller(this);
4388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4389
4390 HRESULT rc;
4391 Bstr bstr;
4392 ComPtr<DHCPServer> found;
4393
4394 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4395
4396 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4397 it != m->allDHCPServers.end();
4398 ++it)
4399 {
4400 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4401 if (FAILED(rc)) throw rc;
4402
4403 if (bstr == aName)
4404 {
4405 found = *it;
4406 break;
4407 }
4408 }
4409
4410 if (!found)
4411 return E_INVALIDARG;
4412
4413 return found.queryInterfaceTo(aServer);
4414}
4415
4416STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4417{
4418 CheckComArgNotNull(aServer);
4419
4420 AutoCaller autoCaller(this);
4421 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4422
4423 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4424
4425 return rc;
4426}
4427
4428/**
4429 * Remembers the given dhcp server by storing it in the hard disk registry.
4430 *
4431 * @param aDHCPServer Dhcp Server object to remember.
4432 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4433 *
4434 * When @a aSaveRegistry is @c true, this operation may fail because of the
4435 * failed #saveSettings() method it calls. In this case, the dhcp server object
4436 * will not be remembered. It is therefore the responsibility of the caller to
4437 * call this method as the last step of some action that requires registration
4438 * in order to make sure that only fully functional dhcp server objects get
4439 * registered.
4440 *
4441 * @note Locks this object for writing and @a aDHCPServer for reading.
4442 */
4443HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4444 bool aSaveRegistry /*= true*/)
4445{
4446 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4447
4448 AutoCaller autoCaller(this);
4449 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4450
4451 AutoCaller dhcpServerCaller(aDHCPServer);
4452 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4453
4454 Bstr name;
4455 HRESULT rc;
4456 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4457 if (FAILED(rc)) return rc;
4458
4459 ComPtr<IDHCPServer> existing;
4460 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
4461 if (SUCCEEDED(rc))
4462 return E_INVALIDARG;
4463
4464 rc = S_OK;
4465
4466 m->allDHCPServers.addChild(aDHCPServer);
4467
4468 if (aSaveRegistry)
4469 {
4470 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4471 rc = saveSettings();
4472 vboxLock.release();
4473
4474 if (FAILED(rc))
4475 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4476 }
4477
4478 return rc;
4479}
4480
4481/**
4482 * Removes the given hard disk from the hard disk registry.
4483 *
4484 * @param aHardDisk Hard disk object to remove.
4485 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4486 *
4487 * When @a aSaveRegistry is @c true, this operation may fail because of the
4488 * failed #saveSettings() method it calls. In this case, the hard disk object
4489 * will NOT be removed from the registry when this method returns. It is
4490 * therefore the responsibility of the caller to call this method as the first
4491 * step of some action that requires unregistration, before calling uninit() on
4492 * @a aHardDisk.
4493 *
4494 * @note Locks this object for writing and @a aHardDisk for reading.
4495 */
4496HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4497 bool aSaveRegistry /*= true*/)
4498{
4499 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4500
4501 AutoCaller autoCaller(this);
4502 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4503
4504 AutoCaller dhcpServerCaller(aDHCPServer);
4505 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4506
4507 m->allDHCPServers.removeChild(aDHCPServer);
4508
4509 HRESULT rc = S_OK;
4510
4511 if (aSaveRegistry)
4512 {
4513 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4514 rc = saveSettings();
4515 vboxLock.release();
4516
4517 if (FAILED(rc))
4518 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4519 }
4520
4521 return rc;
4522}
4523
4524STDMETHODIMP VirtualBox::VRDERegisterLibrary(IN_BSTR aName)
4525{
4526 CheckComArgStrNotEmptyOrNull(aName);
4527
4528 AutoCaller autoCaller(this);
4529 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4530
4531 Utf8Str strName(aName);
4532
4533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4534
4535 /* If the name already exists, there is nothing to do. */
4536 settings::VRDELibrariesList::const_iterator it;
4537
4538 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4539 it != m->pMainConfigFile->llVRDELibraries.end();
4540 ++it)
4541 {
4542 const Utf8Str &str = *it;
4543 if (strName == str)
4544 {
4545 break;
4546 }
4547 }
4548
4549 if (it == m->pMainConfigFile->llVRDELibraries.end())
4550 {
4551 /* Not found, add it. */
4552 m->pMainConfigFile->llVRDELibraries.push_back(strName);
4553
4554 /* Save settings on success. */
4555 HRESULT rc = saveSettings();
4556 if (FAILED(rc)) return rc;
4557 }
4558
4559 return S_OK;
4560}
4561
4562STDMETHODIMP VirtualBox::VRDEUnregisterLibrary(IN_BSTR aName)
4563{
4564 CheckComArgStrNotEmptyOrNull(aName);
4565
4566 AutoCaller autoCaller(this);
4567 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4568
4569 Utf8Str strName(aName);
4570
4571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4572
4573 settings::VRDELibrariesList::const_iterator it;
4574
4575 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4576 it != m->pMainConfigFile->llVRDELibraries.end();
4577 ++it)
4578 {
4579 const Utf8Str &str = *it;
4580 if (strName == str)
4581 {
4582 break;
4583 }
4584 }
4585
4586 if (it != m->pMainConfigFile->llVRDELibraries.end())
4587 {
4588 /* Found, remove it. */
4589 m->pMainConfigFile->llVRDELibraries.remove(strName);
4590
4591 /* Save settings on success. */
4592 HRESULT rc = saveSettings();
4593 if (FAILED(rc)) return rc;
4594 }
4595
4596 return S_OK;
4597}
4598
4599STDMETHODIMP VirtualBox::VRDEListLibraries(ComSafeArrayOut(BSTR, aNames))
4600{
4601 using namespace settings;
4602
4603 if (ComSafeArrayOutIsNull(aNames))
4604 return E_POINTER;
4605
4606 AutoCaller autoCaller(this);
4607 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4608
4609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4610
4611 com::SafeArray<BSTR> saNames(m->pMainConfigFile->llVRDELibraries.size());
4612 int i = 0;
4613 for (VRDELibrariesList::const_iterator it = m->pMainConfigFile->llVRDELibraries.begin();
4614 it != m->pMainConfigFile->llVRDELibraries.end();
4615 ++it, ++i)
4616 {
4617 const Utf8Str &strName = *it;
4618 strName.cloneTo(&saNames[i]);
4619 }
4620 saNames.detachTo(ComSafeArrayOutArg(aNames));
4621
4622 return S_OK;
4623}
4624
4625STDMETHODIMP VirtualBox::VRDEIsLibraryRegistered(IN_BSTR aName, BOOL *aRegistered)
4626{
4627 CheckComArgStrNotEmptyOrNull(aName);
4628 CheckComArgOutPointerValid(aRegistered);
4629
4630 AutoCaller autoCaller(this);
4631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4632
4633 Utf8Str strName(aName);
4634
4635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4636
4637 settings::VRDELibrariesList::const_iterator it;
4638
4639 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4640 it != m->pMainConfigFile->llVRDELibraries.end();
4641 ++it)
4642 {
4643 const Utf8Str &str = *it;
4644 if (strName == str)
4645 {
4646 break;
4647 }
4648 }
4649
4650 *aRegistered = (it != m->pMainConfigFile->llVRDELibraries.end());
4651
4652 return S_OK;
4653}
4654
4655/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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