VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp@ 35757

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

Main: reworked listener objects creation, fixes Win problems with events, few cleanups

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

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