VirtualBox

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

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

Main: change per-machine media registries so that removeable media files (ISO, RAW) can appear in more than one registry; fix saving registry with removeable media (mount at runtime), hopefully

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

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