VirtualBox

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

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

Main: silently ignore removable media that cannot be found when loading machine settings XML

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