VirtualBox

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

Last change on this file since 34286 was 34277, checked in by vboxsync, 14 years ago

Main: important addition to r68023; allow searching of machines by .vbox file

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

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