VirtualBox

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

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

Main: gcc warning

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

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