VirtualBox

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

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

Main: clean up GuestOSType constructor

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 141.1 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 32894 2010-10-05 09:03:39Z 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.xml",
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
2914Utf8Str VirtualBox::getRegistryPath(Medium *pMedium)
2915{
2916 const Guid &rid = pMedium->getRegistryId();
2917
2918 if (!rid.isEmpty())
2919 {
2920 if (rid == getGlobalRegistryId())
2921 return settingsFilePath();
2922
2923 return rid.toString();
2924 }
2925 return "[null]";
2926}
2927
2928/**
2929 * Checks if there is a hard disk, DVD or floppy image with the given ID or
2930 * location already registered.
2931 *
2932 * On return, sets @a aConflict to the string describing the conflicting medium,
2933 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
2934 * either case. A failure is unexpected.
2935 *
2936 * @param aId UUID to check.
2937 * @param aLocation Location to check.
2938 * @param aConflict Where to return parameters of the conflicting medium.
2939 *
2940 * @note Locks the media tree and media objects for reading.
2941 */
2942HRESULT VirtualBox::checkMediaForConflicts2(const Guid &aId,
2943 const Utf8Str &aLocation,
2944 Utf8Str &aConflict)
2945{
2946 aConflict.setNull();
2947
2948 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
2949
2950 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2951
2952 HRESULT rc = S_OK;
2953
2954 Bstr bstrLocation(aLocation);
2955
2956 {
2957 ComObjPtr<Medium> hardDisk;
2958 rc = findHardDisk(&aId, bstrLocation, false /* aSetError */, &hardDisk);
2959 if (SUCCEEDED(rc))
2960 {
2961 /* Note: no AutoCaller since bound to this */
2962 AutoReadLock mediaLock(hardDisk COMMA_LOCKVAL_SRC_POS);
2963 aConflict = Utf8StrFmt(tr("hard disk '%s' with UUID {%RTuuid}"),
2964 hardDisk->getLocationFull().c_str(),
2965 hardDisk->getId().raw());
2966 return S_OK;
2967 }
2968 }
2969
2970 {
2971 ComObjPtr<Medium> image;
2972 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, bstrLocation, false /* aSetError */, &image);
2973 if (SUCCEEDED(rc))
2974 {
2975 /* Note: no AutoCaller since bound to this */
2976 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
2977 aConflict = Utf8StrFmt(tr("CD/DVD image '%s' with UUID {%RTuuid}"),
2978 image->getLocationFull().c_str(),
2979 image->getId().raw());
2980 return S_OK;
2981 }
2982 }
2983
2984 {
2985 ComObjPtr<Medium> image;
2986 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, bstrLocation, false /* aSetError */, &image);
2987 if (SUCCEEDED(rc))
2988 {
2989 /* Note: no AutoCaller since bound to this */
2990 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
2991 aConflict = Utf8StrFmt(tr("floppy image '%s' with UUID {%RTuuid}"),
2992 image->getLocationFull().c_str(),
2993 image->getId().raw());
2994 return S_OK;
2995 }
2996 }
2997
2998 return S_OK;
2999}
3000
3001/**
3002 * Called from Machine::prepareSaveSettings() when it has detected
3003 * that a machine has been renamed. Such renames will require
3004 * updating the global media registry during the
3005 * VirtualBox::saveSettings() that follows later.
3006*
3007 * When a machine is renamed, there may well be media (in particular,
3008 * diff images for snapshots) in the global registry that will need
3009 * to have their paths updated. Before 3.2, Machine::saveSettings
3010 * used to call VirtualBox::saveSettings implicitly, which was both
3011 * unintuitive and caused locking order problems. Now, we remeber
3012 * such pending name changes with this method so that
3013 * VirtualBox::saveSettings() can process them properly.
3014 */
3015void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3016 const Utf8Str &strNewConfigDir)
3017{
3018 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3019
3020 Data::PendingMachineRename pmr;
3021 pmr.strConfigDirOld = strOldConfigDir;
3022 pmr.strConfigDirNew = strNewConfigDir;
3023 m->llPendingMachineRenames.push_back(pmr);
3024}
3025
3026/**
3027 * Goes through all known media (hard disks, floppies and DVDs) and saves
3028 * those into the given settings::MediaRegistry structures whose registry
3029 * ID match the given UUID.
3030 *
3031 * This gets called from two contexts:
3032 *
3033 * -- VirtualBox::saveSettings() with the UUID of the global registry
3034 * (VirtualBox::Data.uuidRegistry); this will save those media
3035 * which had been loaded from the global registry or have been
3036 * attached to a "legacy" machine which can't save its own registry;
3037 *
3038 * -- Machine::saveSettings() with the UUID of a machine, if a medium
3039 * has been attached to a machine created with VirtualBox 4.0 or later.
3040 *
3041 * Media which have only been temporarily opened without having been
3042 * attached to a machine have a NULL registry UUID and therefore don't
3043 * get saved.
3044 *
3045 * This throws HRESULT on errors!
3046 *
3047 * @param mediaRegistry
3048 * @param uuidRegistry
3049 * @return
3050 */
3051void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
3052 const Guid &uuidRegistry)
3053{
3054 HRESULT rc;
3055 // hard disks
3056 mediaRegistry.llHardDisks.clear();
3057 for (MediaList::const_iterator it = m->allHardDisks.begin();
3058 it != m->allHardDisks.end();
3059 ++it)
3060 {
3061 Medium *pMedium = *it;
3062 // is medium in global registry:?
3063 if (pMedium->getRegistryId() == uuidRegistry)
3064 {
3065 settings::Medium med;
3066 rc = pMedium->saveSettings(med); // this recurses into its children
3067 if (FAILED(rc)) throw rc;
3068 mediaRegistry.llHardDisks.push_back(med);
3069 }
3070 }
3071
3072 // CD/DVD images
3073 mediaRegistry.llDvdImages.clear();
3074 for (MediaList::const_iterator it = m->allDVDImages.begin();
3075 it != m->allDVDImages.end();
3076 ++it)
3077 {
3078 Medium *pMedium = *it;
3079 // is medium in global registry:?
3080 if (pMedium->getRegistryId() == uuidRegistry)
3081 {
3082 settings::Medium med;
3083 rc = pMedium->saveSettings(med);
3084 if (FAILED(rc)) throw rc;
3085 mediaRegistry.llDvdImages.push_back(med);
3086 }
3087 }
3088
3089 // floppy images
3090 mediaRegistry.llFloppyImages.clear();
3091 for (MediaList::const_iterator it = m->allFloppyImages.begin();
3092 it != m->allFloppyImages.end();
3093 ++it)
3094 {
3095 Medium *pMedium = *it;
3096 // is medium in global registry:?
3097 if (pMedium->getRegistryId() == uuidRegistry)
3098 {
3099 settings::Medium med;
3100 rc = pMedium->saveSettings(med);
3101 if (FAILED(rc)) throw rc;
3102 mediaRegistry.llFloppyImages.push_back(med);
3103 }
3104 }
3105}
3106
3107/**
3108 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
3109 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
3110 * places internally when settings need saving.
3111 *
3112 * @note Caller must have locked the VirtualBox object for writing and must not hold any
3113 * other locks since this locks all kinds of member objects and trees temporarily,
3114 * which could cause conflicts.
3115 */
3116HRESULT VirtualBox::saveSettings()
3117{
3118 AutoCaller autoCaller(this);
3119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3120
3121 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3122 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3123
3124 HRESULT rc = S_OK;
3125
3126 try
3127 {
3128 // machines
3129 m->pMainConfigFile->llMachines.clear();
3130 {
3131 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3132 for (MachinesOList::iterator it = m->allMachines.begin();
3133 it != m->allMachines.end();
3134 ++it)
3135 {
3136 Machine *pMachine = *it;
3137 // save actual machine registry entry
3138 settings::MachineRegistryEntry mre;
3139 rc = pMachine->saveRegistryEntry(mre);
3140 m->pMainConfigFile->llMachines.push_back(mre);
3141 }
3142 }
3143
3144 // lock all media for the following; use a write lock because we're
3145 // modifying the PendingMachineRenamesList, which is protected by this
3146 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3147
3148 // if a machine was renamed, then we'll need to refresh media paths
3149 if (m->llPendingMachineRenames.size())
3150 {
3151 // make a single list from the three media lists so we don't need three loops
3152 MediaList llAllMedia;
3153 // with hard disks, we must use the map, not the list, because the list only has base images
3154 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3155 llAllMedia.push_back(it->second);
3156 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
3157 llAllMedia.push_back(*it);
3158 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
3159 llAllMedia.push_back(*it);
3160
3161 for (MediaList::iterator it = llAllMedia.begin();
3162 it != llAllMedia.end();
3163 ++it)
3164 {
3165 Medium *pMedium = *it;
3166 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3167 it2 != m->llPendingMachineRenames.end();
3168 ++it2)
3169 {
3170 // is medium in global registry:?
3171 if (pMedium->getRegistryId() == m->uuidMediaRegistry)
3172 {
3173 const Data::PendingMachineRename &pmr = *it2;
3174 pMedium->updatePath(pmr.strConfigDirOld,
3175 pmr.strConfigDirNew);
3176 }
3177 }
3178 }
3179 // done, don't do it again until we have more machine renames
3180 m->llPendingMachineRenames.clear();
3181 }
3182
3183 saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
3184 m->uuidMediaRegistry);
3185
3186 mediaLock.release();
3187
3188 m->pMainConfigFile->llDhcpServers.clear();
3189 {
3190 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3191 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
3192 it != m->allDHCPServers.end();
3193 ++it)
3194 {
3195 settings::DHCPServer d;
3196 rc = (*it)->saveSettings(d);
3197 if (FAILED(rc)) throw rc;
3198 m->pMainConfigFile->llDhcpServers.push_back(d);
3199 }
3200 }
3201
3202 // leave extra data alone, it's still in the config file
3203
3204 // host data (USB filters)
3205 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3206 if (FAILED(rc)) throw rc;
3207
3208 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3209 if (FAILED(rc)) throw rc;
3210
3211 // and write out the XML, still under the lock
3212 m->pMainConfigFile->write(m->strSettingsFilePath);
3213 }
3214 catch (HRESULT err)
3215 {
3216 /* we assume that error info is set by the thrower */
3217 rc = err;
3218 }
3219 catch (...)
3220 {
3221 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3222 }
3223
3224 return rc;
3225}
3226
3227/**
3228 * Helper to register the machine.
3229 *
3230 * When called during VirtualBox startup, adds the given machine to the
3231 * collection of registered machines. Otherwise tries to mark the machine
3232 * as registered, and, if succeeded, adds it to the collection and
3233 * saves global settings.
3234 *
3235 * @note The caller must have added itself as a caller of the @a aMachine
3236 * object if calls this method not on VirtualBox startup.
3237 *
3238 * @param aMachine machine to register
3239 *
3240 * @note Locks objects!
3241 */
3242HRESULT VirtualBox::registerMachine(Machine *aMachine)
3243{
3244 ComAssertRet(aMachine, E_INVALIDARG);
3245
3246 AutoCaller autoCaller(this);
3247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3248
3249 HRESULT rc = S_OK;
3250
3251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3252
3253 {
3254 ComObjPtr<Machine> pMachine;
3255 rc = findMachine(aMachine->getId(),
3256 true /* fPermitInaccessible */,
3257 false /* aDoSetError */,
3258 &pMachine);
3259 if (SUCCEEDED(rc))
3260 {
3261 /* sanity */
3262 AutoLimitedCaller machCaller(pMachine);
3263 AssertComRC(machCaller.rc());
3264
3265 return setError(E_INVALIDARG,
3266 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
3267 aMachine->getId().raw(),
3268 pMachine->getSettingsFileFull().c_str());
3269 }
3270
3271 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3272 rc = S_OK;
3273 }
3274
3275 if (autoCaller.state() != InInit)
3276 {
3277 rc = aMachine->prepareRegister();
3278 if (FAILED(rc)) return rc;
3279 }
3280
3281 /* add to the collection of registered machines */
3282 m->allMachines.addChild(aMachine);
3283
3284 if (autoCaller.state() != InInit)
3285 rc = saveSettings();
3286
3287 return rc;
3288}
3289
3290/**
3291 * Remembers the given hard disk by storing it in either the global hard disk registry
3292 * or a machine one.
3293 *
3294 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3295 *
3296 * @param aHardDisk Hard disk object to remember.
3297 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3298 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3299 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3300 * @return
3301 */
3302HRESULT VirtualBox::registerHardDisk(Medium *pMedium,
3303 bool *pfNeedsGlobalSaveSettings)
3304{
3305 AssertReturn(pMedium != NULL, E_INVALIDARG);
3306
3307 AutoCaller autoCaller(this);
3308 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3309
3310 AutoCaller hardDiskCaller(pMedium);
3311 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3312
3313 // caller must hold the media tree write lock
3314 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3315
3316 Guid id;
3317 Utf8Str strLocationFull;
3318 ComObjPtr<Medium> pParent;
3319 {
3320 AutoReadLock hardDiskLock(pMedium COMMA_LOCKVAL_SRC_POS);
3321 id = pMedium->getId();
3322 strLocationFull = pMedium->getLocationFull();
3323 pParent = pMedium->getParent();
3324 }
3325
3326 HRESULT rc;
3327
3328 Utf8Str strConflict;
3329 rc = checkMediaForConflicts2(id,
3330 strLocationFull,
3331 strConflict);
3332 if (FAILED(rc)) return rc;
3333
3334 if (strConflict.length())
3335 return setError(E_INVALIDARG,
3336 tr("Cannot register the hard disk '%s' {%RTuuid} because a %s already exists"),
3337 strLocationFull.c_str(),
3338 id.raw(),
3339 strConflict.c_str(),
3340 m->strSettingsFilePath.c_str());
3341
3342 // store base (root) hard disks in the list
3343 if (pParent.isNull())
3344 m->allHardDisks.getList().push_back(pMedium);
3345 // access the list directly because we already locked the list above
3346
3347 // store all hard disks (even differencing images) in the map
3348 m->mapHardDisks[id] = pMedium;
3349
3350 if (pfNeedsGlobalSaveSettings)
3351 // global settings need saving only if the medium is to be saved in the global registry
3352 if (pMedium->getRegistryId() == m->uuidMediaRegistry)
3353 *pfNeedsGlobalSaveSettings = true;
3354
3355 return rc;
3356}
3357
3358/**
3359 * Removes the given hard disk from the hard disk registry.
3360 *
3361 * @param aHardDisk Hard disk object to remove.
3362 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3363 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3364 *
3365 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3366 */
3367HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3368 bool *pfNeedsGlobalSaveSettings)
3369{
3370 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3371
3372 AutoCaller autoCaller(this);
3373 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3374
3375 AutoCaller hardDiskCaller(aHardDisk);
3376 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3377
3378 // caller must hold the media tree write lock
3379 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3380
3381 Guid id;
3382 ComObjPtr<Medium> pParent;
3383 {
3384 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3385 id = aHardDisk->getId();
3386 pParent = aHardDisk->getParent();
3387 }
3388
3389 // remove base (root) hard disks from the list
3390 if (pParent.isNull())
3391 m->allHardDisks.getList().remove(aHardDisk);
3392 // access the list directly because caller must have locked the list
3393
3394 // remove all hard disks (even differencing images) from map
3395 size_t cnt = m->mapHardDisks.erase(id);
3396 Assert(cnt == 1);
3397 NOREF(cnt);
3398
3399 if (pfNeedsGlobalSaveSettings)
3400 // global settings need saving only if the medium is to be saved in the global registry
3401 if (aHardDisk->getRegistryId() == m->uuidMediaRegistry)
3402 *pfNeedsGlobalSaveSettings = true;
3403
3404 return S_OK;
3405}
3406
3407/**
3408 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3409 *
3410 * @param argImage Image object to remember.
3411 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3412 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3413 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3414 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3415 *
3416 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3417 */
3418HRESULT VirtualBox::registerImage(Medium *pMedium,
3419 DeviceType_T argType,
3420 bool *pfNeedsGlobalSaveSettings)
3421{
3422 AssertReturn(pMedium != NULL, E_INVALIDARG);
3423
3424 AutoCaller autoCaller(this);
3425 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3426
3427 AutoCaller imageCaller(pMedium);
3428 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3429
3430 // caller must hold the media tree write lock
3431 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3432
3433 Guid id;
3434 Utf8Str strLocationFull;
3435 ComObjPtr<Medium> pParent;
3436 {
3437 AutoReadLock al(pMedium COMMA_LOCKVAL_SRC_POS);
3438 id = pMedium->getId();
3439 strLocationFull = pMedium->getLocationFull();
3440 pParent = pMedium->getParent();
3441 }
3442
3443 // work on DVDs or floppies list?
3444 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3445
3446 HRESULT rc;
3447 // lock the images lists (list + map) while checking for conflicts
3448 AutoWriteLock al(all.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3449
3450 Utf8Str strConflict;
3451 rc = checkMediaForConflicts2(id,
3452 strLocationFull,
3453 strConflict);
3454 if (FAILED(rc)) return rc;
3455
3456 if (strConflict.length())
3457 return setError(VBOX_E_INVALID_OBJECT_STATE,
3458 tr("Cannot register the image '%s' with UUID {%RTuuid} from '%s' because a %s already exists"),
3459 strLocationFull.c_str(),
3460 id.raw(),
3461 getRegistryPath(pMedium).c_str(),
3462 strConflict.c_str());
3463
3464 // add to the collection
3465 all.getList().push_back(pMedium);
3466 // access the list directly because we already locked the list above
3467
3468 if (pfNeedsGlobalSaveSettings)
3469 // global settings need saving only if the medium is to be saved in the global registry
3470 if (pMedium->getRegistryId() == m->uuidMediaRegistry)
3471 *pfNeedsGlobalSaveSettings = true;
3472
3473 return rc;
3474}
3475
3476/**
3477 * Removes the given image from the CD/DVD or floppy image registry.
3478 *
3479 * @param argImage Image object to remove.
3480 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3481 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3482 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3483 *
3484 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3485 */
3486HRESULT VirtualBox::unregisterImage(Medium *argImage,
3487 DeviceType_T argType,
3488 bool *pfNeedsGlobalSaveSettings)
3489{
3490 AssertReturn(argImage != NULL, E_INVALIDARG);
3491
3492 AutoCaller autoCaller(this);
3493 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3494
3495 AutoCaller imageCaller(argImage);
3496 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3497
3498 // caller must hold the media tree write lock
3499 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3500
3501 Guid id;
3502 ComObjPtr<Medium> pParent;
3503 {
3504 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3505 id = argImage->getId();
3506 pParent = argImage->getParent();
3507 }
3508
3509 // work on DVDs or floppies list?
3510 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3511
3512 // access the list directly because the caller must have requested the lock
3513 all.getList().remove(argImage);
3514
3515 HRESULT rc = S_OK;
3516
3517 if (pfNeedsGlobalSaveSettings)
3518 // global settings need saving only if the medium is to be saved in the global registry
3519 if (argImage->getRegistryId() == m->uuidMediaRegistry)
3520 *pfNeedsGlobalSaveSettings = true;
3521
3522 return rc;
3523}
3524
3525/**
3526 * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
3527 * with children appearing before their parents.
3528 * @param llMedia
3529 * @param pMedium
3530 */
3531void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
3532{
3533 // recurse first, then add ourselves; this way children end up on the
3534 // list before their parents
3535
3536 const MediaList &llChildren = pMedium->getChildren();
3537 for (MediaList::const_iterator it = llChildren.begin();
3538 it != llChildren.end();
3539 ++it)
3540 {
3541 Medium *pChild = *it;
3542 pushMediumToListWithChildren(llMedia, pChild);
3543 }
3544
3545 Log(("Pushing medium %RTuuid\n", pMedium->getId().raw()));
3546 llMedia.push_back(pMedium);
3547}
3548
3549/**
3550 * Unregisters all Medium objects which belong to the given machine registry.
3551 * Gets called from Machine::uninit() just before the machine object dies
3552 * and must only be called with a machine UUID as the registry ID.
3553 *
3554 * Locks the media tree.
3555 *
3556 * @param uuidMachine Medium registry ID (always a machine UUID)
3557 * @return
3558 */
3559HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine)
3560{
3561 Assert(!uuidMachine.isEmpty());
3562
3563 LogFlowFuncEnter();
3564
3565 AutoCaller autoCaller(this);
3566 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3567
3568 MediaList llMedia2Close;
3569
3570 {
3571 AutoWriteLock mlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3572 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
3573 it != m->allHardDisks.getList().end();
3574 ++it)
3575 {
3576 ComObjPtr<Medium> pMedium = *it;
3577
3578 const Guid &uuidRegistryThis = pMedium->getRegistryId();
3579 if ( !uuidRegistryThis.isEmpty()
3580 && (uuidRegistryThis == uuidMachine)
3581 )
3582 {
3583 // recursively with children first
3584 pushMediumToListWithChildren(llMedia2Close, pMedium);
3585 }
3586 }
3587 }
3588
3589 for (MediaList::iterator it = llMedia2Close.begin();
3590 it != llMedia2Close.end();
3591 ++it)
3592 {
3593 ComObjPtr<Medium> pMedium = *it;
3594 Log(("Closing medium %RTuuid\n", pMedium->getId().raw()));
3595 AutoCaller mac(pMedium);
3596 pMedium->close(NULL /* pfNeedsGlobalSaveSettings*/, mac);
3597 }
3598
3599 LogFlowFuncLeave();
3600
3601 return S_OK;
3602}
3603
3604/**
3605 * Removes the given machine object from the internal list of registered machines.
3606 * Called from Machine::Unregister().
3607 * @param pMachine
3608 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
3609 * @return
3610 */
3611HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
3612 const Guid &id)
3613{
3614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3615
3616 // remove from the collection of registered machines
3617 m->allMachines.removeChild(pMachine);
3618
3619 // save the global registry
3620 HRESULT rc = saveSettings();
3621
3622 alock.release();
3623
3624 /* fire an event */
3625 onMachineRegistered(id, FALSE);
3626
3627 return rc;
3628}
3629
3630/**
3631 * Creates the path to the specified file according to the path information
3632 * present in the file name.
3633 *
3634 * Note that the given file name must contain the full path otherwise the
3635 * extracted relative path will be created based on the current working
3636 * directory which is normally unknown.
3637 *
3638 * @param aFileName Full file name which path needs to be created.
3639 *
3640 * @return Extended error information on failure to create the path.
3641 */
3642/* static */
3643HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3644{
3645 Utf8Str strDir(strFileName);
3646 strDir.stripFilename();
3647 if (!RTDirExists(strDir.c_str()))
3648 {
3649 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3650 if (RT_FAILURE(vrc))
3651 return setErrorStatic(E_FAIL,
3652 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
3653 strDir.c_str(),
3654 vrc));
3655 }
3656
3657 return S_OK;
3658}
3659
3660/**
3661 * Handles unexpected exceptions by turning them into COM errors in release
3662 * builds or by hitting a breakpoint in the release builds.
3663 *
3664 * Usage pattern:
3665 * @code
3666 try
3667 {
3668 // ...
3669 }
3670 catch (LaLalA)
3671 {
3672 // ...
3673 }
3674 catch (...)
3675 {
3676 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3677 }
3678 * @endcode
3679 *
3680 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3681 */
3682/* static */
3683HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3684{
3685 try
3686 {
3687 /* re-throw the current exception */
3688 throw;
3689 }
3690 catch (const iprt::Error &err) // includes all XML exceptions
3691 {
3692 return setErrorStatic(E_FAIL,
3693 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
3694 err.what(),
3695 pszFile, iLine, pszFunction).c_str());
3696 }
3697 catch (const std::exception &err)
3698 {
3699 return setErrorStatic(E_FAIL,
3700 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3701 err.what(), typeid(err).name(),
3702 pszFile, iLine, pszFunction).c_str());
3703 }
3704 catch (...)
3705 {
3706 return setErrorStatic(E_FAIL,
3707 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
3708 pszFile, iLine, pszFunction).c_str());
3709 }
3710
3711 /* should not get here */
3712 AssertFailed();
3713 return E_FAIL;
3714}
3715
3716const Utf8Str& VirtualBox::settingsFilePath()
3717{
3718 return m->strSettingsFilePath;
3719}
3720
3721/**
3722 * Returns the lock handle which protects the media trees (hard disks,
3723 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3724 * are no longer protected by the VirtualBox lock, but by this more
3725 * specialized lock. Mind the locking order: always request this lock
3726 * after the VirtualBox object lock but before the locks of the media
3727 * objects contained in these lists. See AutoLock.h.
3728 */
3729RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3730{
3731 return m->lockMedia;
3732}
3733
3734/**
3735 * Thread function that watches the termination of all client processes
3736 * that have opened sessions using IMachine::LockMachine()
3737 */
3738// static
3739DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3740{
3741 LogFlowFuncEnter();
3742
3743 VirtualBox *that = (VirtualBox*)pvUser;
3744 Assert(that);
3745
3746 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3747 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3748
3749 SessionMachineVector machines;
3750 MachineVector spawnedMachines;
3751
3752 size_t cnt = 0;
3753 size_t cntSpawned = 0;
3754
3755#if defined(RT_OS_WINDOWS)
3756
3757 HRESULT hrc = CoInitializeEx(NULL,
3758 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3759 COINIT_SPEED_OVER_MEMORY);
3760 AssertComRC(hrc);
3761
3762 /// @todo (dmik) processes reaping!
3763
3764 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3765 handles[0] = that->m->updateReq;
3766
3767 do
3768 {
3769 AutoCaller autoCaller(that);
3770 /* VirtualBox has been early uninitialized, terminate */
3771 if (!autoCaller.isOk())
3772 break;
3773
3774 do
3775 {
3776 /* release the caller to let uninit() ever proceed */
3777 autoCaller.release();
3778
3779 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3780 handles,
3781 FALSE,
3782 INFINITE);
3783
3784 /* Restore the caller before using VirtualBox. If it fails, this
3785 * means VirtualBox is being uninitialized and we must terminate. */
3786 autoCaller.add();
3787 if (!autoCaller.isOk())
3788 break;
3789
3790 bool update = false;
3791
3792 if (rc == WAIT_OBJECT_0)
3793 {
3794 /* update event is signaled */
3795 update = true;
3796 }
3797 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3798 {
3799 /* machine mutex is released */
3800 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3801 update = true;
3802 }
3803 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3804 {
3805 /* machine mutex is abandoned due to client process termination */
3806 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3807 update = true;
3808 }
3809 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3810 {
3811 /* spawned VM process has terminated (normally or abnormally) */
3812 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
3813 checkForSpawnFailure();
3814 update = true;
3815 }
3816
3817 if (update)
3818 {
3819 /* close old process handles */
3820 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
3821 CloseHandle(handles[i]);
3822
3823 // lock the machines list for reading
3824 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3825
3826 /* obtain a new set of opened machines */
3827 cnt = 0;
3828 machines.clear();
3829
3830 for (MachinesOList::iterator it = that->m->allMachines.begin();
3831 it != that->m->allMachines.end();
3832 ++it)
3833 {
3834 /// @todo handle situations with more than 64 objects
3835 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3836 ("MAXIMUM_WAIT_OBJECTS reached"));
3837
3838 ComObjPtr<SessionMachine> sm;
3839 HANDLE ipcSem;
3840 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
3841 {
3842 machines.push_back(sm);
3843 handles[1 + cnt] = ipcSem;
3844 ++cnt;
3845 }
3846 }
3847
3848 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
3849
3850 /* obtain a new set of spawned machines */
3851 cntSpawned = 0;
3852 spawnedMachines.clear();
3853
3854 for (MachinesOList::iterator it = that->m->allMachines.begin();
3855 it != that->m->allMachines.end();
3856 ++it)
3857 {
3858 /// @todo handle situations with more than 64 objects
3859 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
3860 ("MAXIMUM_WAIT_OBJECTS reached"));
3861
3862 RTPROCESS pid;
3863 if ((*it)->isSessionSpawning(&pid))
3864 {
3865 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
3866 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
3867 pid, GetLastError()));
3868 if (rc == 0)
3869 {
3870 spawnedMachines.push_back(*it);
3871 handles[1 + cnt + cntSpawned] = ph;
3872 ++cntSpawned;
3873 }
3874 }
3875 }
3876
3877 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
3878
3879 // machines lock unwinds here
3880 }
3881 }
3882 while (true);
3883 }
3884 while (0);
3885
3886 /* close old process handles */
3887 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3888 CloseHandle(handles[i]);
3889
3890 /* release sets of machines if any */
3891 machines.clear();
3892 spawnedMachines.clear();
3893
3894 ::CoUninitialize();
3895
3896#elif defined(RT_OS_OS2)
3897
3898 /// @todo (dmik) processes reaping!
3899
3900 /* according to PMREF, 64 is the maximum for the muxwait list */
3901 SEMRECORD handles[64];
3902
3903 HMUX muxSem = NULLHANDLE;
3904
3905 do
3906 {
3907 AutoCaller autoCaller(that);
3908 /* VirtualBox has been early uninitialized, terminate */
3909 if (!autoCaller.isOk())
3910 break;
3911
3912 do
3913 {
3914 /* release the caller to let uninit() ever proceed */
3915 autoCaller.release();
3916
3917 int vrc = RTSemEventWait(that->m->updateReq, 500);
3918
3919 /* Restore the caller before using VirtualBox. If it fails, this
3920 * means VirtualBox is being uninitialized and we must terminate. */
3921 autoCaller.add();
3922 if (!autoCaller.isOk())
3923 break;
3924
3925 bool update = false;
3926 bool updateSpawned = false;
3927
3928 if (RT_SUCCESS(vrc))
3929 {
3930 /* update event is signaled */
3931 update = true;
3932 updateSpawned = true;
3933 }
3934 else
3935 {
3936 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
3937 ("RTSemEventWait returned %Rrc\n", vrc));
3938
3939 /* are there any mutexes? */
3940 if (cnt > 0)
3941 {
3942 /* figure out what's going on with machines */
3943
3944 unsigned long semId = 0;
3945 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
3946 SEM_IMMEDIATE_RETURN, &semId);
3947
3948 if (arc == NO_ERROR)
3949 {
3950 /* machine mutex is normally released */
3951 Assert(semId >= 0 && semId < cnt);
3952 if (semId >= 0 && semId < cnt)
3953 {
3954#ifdef DEBUG
3955 {
3956 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
3957 LogFlowFunc(("released mutex: machine='%ls'\n",
3958 machines[semId]->name().raw()));
3959 }
3960#endif
3961 machines[semId]->checkForDeath();
3962 }
3963 update = true;
3964 }
3965 else if (arc == ERROR_SEM_OWNER_DIED)
3966 {
3967 /* machine mutex is abandoned due to client process
3968 * termination; find which mutex is in the Owner Died
3969 * state */
3970 for (size_t i = 0; i < cnt; ++ i)
3971 {
3972 PID pid; TID tid;
3973 unsigned long reqCnt;
3974 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
3975 if (arc == ERROR_SEM_OWNER_DIED)
3976 {
3977 /* close the dead mutex as asked by PMREF */
3978 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
3979
3980 Assert(i >= 0 && i < cnt);
3981 if (i >= 0 && i < cnt)
3982 {
3983#ifdef DEBUG
3984 {
3985 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
3986 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
3987 machines[i]->name().raw()));
3988 }
3989#endif
3990 machines[i]->checkForDeath();
3991 }
3992 }
3993 }
3994 update = true;
3995 }
3996 else
3997 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
3998 ("DosWaitMuxWaitSem returned %d\n", arc));
3999 }
4000
4001 /* are there any spawning sessions? */
4002 if (cntSpawned > 0)
4003 {
4004 for (size_t i = 0; i < cntSpawned; ++ i)
4005 updateSpawned |= (spawnedMachines[i])->
4006 checkForSpawnFailure();
4007 }
4008 }
4009
4010 if (update || updateSpawned)
4011 {
4012 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4013
4014 if (update)
4015 {
4016 /* close the old muxsem */
4017 if (muxSem != NULLHANDLE)
4018 ::DosCloseMuxWaitSem(muxSem);
4019
4020 /* obtain a new set of opened machines */
4021 cnt = 0;
4022 machines.clear();
4023
4024 for (MachinesOList::iterator it = that->m->llMachines.begin();
4025 it != that->m->llMachines.end(); ++ it)
4026 {
4027 /// @todo handle situations with more than 64 objects
4028 AssertMsg(cnt <= 64 /* according to PMREF */,
4029 ("maximum of 64 mutex semaphores reached (%d)",
4030 cnt));
4031
4032 ComObjPtr<SessionMachine> sm;
4033 HMTX ipcSem;
4034 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4035 {
4036 machines.push_back(sm);
4037 handles[cnt].hsemCur = (HSEM)ipcSem;
4038 handles[cnt].ulUser = cnt;
4039 ++ cnt;
4040 }
4041 }
4042
4043 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4044
4045 if (cnt > 0)
4046 {
4047 /* create a new muxsem */
4048 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4049 handles,
4050 DCMW_WAIT_ANY);
4051 AssertMsg(arc == NO_ERROR,
4052 ("DosCreateMuxWaitSem returned %d\n", arc));
4053 NOREF(arc);
4054 }
4055 }
4056
4057 if (updateSpawned)
4058 {
4059 /* obtain a new set of spawned machines */
4060 spawnedMachines.clear();
4061
4062 for (MachinesOList::iterator it = that->m->llMachines.begin();
4063 it != that->m->llMachines.end(); ++ it)
4064 {
4065 if ((*it)->isSessionSpawning())
4066 spawnedMachines.push_back(*it);
4067 }
4068
4069 cntSpawned = spawnedMachines.size();
4070 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4071 }
4072 }
4073 }
4074 while (true);
4075 }
4076 while (0);
4077
4078 /* close the muxsem */
4079 if (muxSem != NULLHANDLE)
4080 ::DosCloseMuxWaitSem(muxSem);
4081
4082 /* release sets of machines if any */
4083 machines.clear();
4084 spawnedMachines.clear();
4085
4086#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4087
4088 bool update = false;
4089 bool updateSpawned = false;
4090
4091 do
4092 {
4093 AutoCaller autoCaller(that);
4094 if (!autoCaller.isOk())
4095 break;
4096
4097 do
4098 {
4099 /* release the caller to let uninit() ever proceed */
4100 autoCaller.release();
4101
4102 int rc = RTSemEventWait(that->m->updateReq, 500);
4103
4104 /*
4105 * Restore the caller before using VirtualBox. If it fails, this
4106 * means VirtualBox is being uninitialized and we must terminate.
4107 */
4108 autoCaller.add();
4109 if (!autoCaller.isOk())
4110 break;
4111
4112 if (RT_SUCCESS(rc) || update || updateSpawned)
4113 {
4114 /* RT_SUCCESS(rc) means an update event is signaled */
4115
4116 // lock the machines list for reading
4117 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4118
4119 if (RT_SUCCESS(rc) || update)
4120 {
4121 /* obtain a new set of opened machines */
4122 machines.clear();
4123
4124 for (MachinesOList::iterator it = that->m->allMachines.begin();
4125 it != that->m->allMachines.end();
4126 ++it)
4127 {
4128 ComObjPtr<SessionMachine> sm;
4129 if ((*it)->isSessionOpenOrClosing(sm))
4130 machines.push_back(sm);
4131 }
4132
4133 cnt = machines.size();
4134 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4135 }
4136
4137 if (RT_SUCCESS(rc) || updateSpawned)
4138 {
4139 /* obtain a new set of spawned machines */
4140 spawnedMachines.clear();
4141
4142 for (MachinesOList::iterator it = that->m->allMachines.begin();
4143 it != that->m->allMachines.end();
4144 ++it)
4145 {
4146 if ((*it)->isSessionSpawning())
4147 spawnedMachines.push_back(*it);
4148 }
4149
4150 cntSpawned = spawnedMachines.size();
4151 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4152 }
4153
4154 // machines lock unwinds here
4155 }
4156
4157 update = false;
4158 for (size_t i = 0; i < cnt; ++ i)
4159 update |= (machines[i])->checkForDeath();
4160
4161 updateSpawned = false;
4162 for (size_t i = 0; i < cntSpawned; ++ i)
4163 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4164
4165 /* reap child processes */
4166 {
4167 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4168 if (that->m->llProcesses.size())
4169 {
4170 LogFlowFunc(("UPDATE: child process count = %d\n",
4171 that->m->llProcesses.size()));
4172 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4173 while (it != that->m->llProcesses.end())
4174 {
4175 RTPROCESS pid = *it;
4176 RTPROCSTATUS status;
4177 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4178 if (vrc == VINF_SUCCESS)
4179 {
4180 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4181 pid, pid, status.iStatus,
4182 status.enmReason));
4183 it = that->m->llProcesses.erase(it);
4184 }
4185 else
4186 {
4187 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4188 pid, pid, vrc));
4189 if (vrc != VERR_PROCESS_RUNNING)
4190 {
4191 /* remove the process if it is not already running */
4192 it = that->m->llProcesses.erase(it);
4193 }
4194 else
4195 ++ it;
4196 }
4197 }
4198 }
4199 }
4200 }
4201 while (true);
4202 }
4203 while (0);
4204
4205 /* release sets of machines if any */
4206 machines.clear();
4207 spawnedMachines.clear();
4208
4209#else
4210# error "Port me!"
4211#endif
4212
4213 LogFlowFuncLeave();
4214 return 0;
4215}
4216
4217/**
4218 * Thread function that handles custom events posted using #postEvent().
4219 */
4220// static
4221DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4222{
4223 LogFlowFuncEnter();
4224
4225 AssertReturn(pvUser, VERR_INVALID_POINTER);
4226
4227 // create an event queue for the current thread
4228 EventQueue *eventQ = new EventQueue();
4229 AssertReturn(eventQ, VERR_NO_MEMORY);
4230
4231 // return the queue to the one who created this thread
4232 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4233 // signal that we're ready
4234 RTThreadUserSignal(thread);
4235
4236 while (RT_SUCCESS(eventQ->processEventQueue(RT_INDEFINITE_WAIT)))
4237 /* nothing */ ;
4238
4239 delete eventQ;
4240
4241 LogFlowFuncLeave();
4242
4243 return 0;
4244}
4245
4246
4247////////////////////////////////////////////////////////////////////////////////
4248
4249/**
4250 * Takes the current list of registered callbacks of the managed VirtualBox
4251 * instance, and calls #handleCallback() for every callback item from the
4252 * list, passing the item as an argument.
4253 *
4254 * @note Locks the managed VirtualBox object for reading but leaves the lock
4255 * before iterating over callbacks and calling their methods.
4256 */
4257void *VirtualBox::CallbackEvent::handler()
4258{
4259 if (!mVirtualBox)
4260 return NULL;
4261
4262 AutoCaller autoCaller(mVirtualBox);
4263 if (!autoCaller.isOk())
4264 {
4265 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4266 autoCaller.state()));
4267 /* We don't need mVirtualBox any more, so release it */
4268 mVirtualBox = NULL;
4269 return NULL;
4270 }
4271
4272
4273 {
4274 VBoxEventDesc evDesc;
4275 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4276
4277 evDesc.fire(/* don't wait for delivery */0);
4278 }
4279
4280 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4281 return NULL;
4282}
4283
4284//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4285//{
4286// return E_NOTIMPL;
4287//}
4288
4289STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4290{
4291 CheckComArgStrNotEmptyOrNull(aName);
4292 CheckComArgNotNull(aServer);
4293
4294 AutoCaller autoCaller(this);
4295 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4296
4297 ComObjPtr<DHCPServer> dhcpServer;
4298 dhcpServer.createObject();
4299 HRESULT rc = dhcpServer->init(this, aName);
4300 if (FAILED(rc)) return rc;
4301
4302 rc = registerDHCPServer(dhcpServer, true);
4303 if (FAILED(rc)) return rc;
4304
4305 dhcpServer.queryInterfaceTo(aServer);
4306
4307 return rc;
4308}
4309
4310STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4311{
4312 CheckComArgStrNotEmptyOrNull(aName);
4313 CheckComArgNotNull(aServer);
4314
4315 AutoCaller autoCaller(this);
4316 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4317
4318 HRESULT rc;
4319 Bstr bstr;
4320 ComPtr<DHCPServer> found;
4321
4322 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4323
4324 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4325 it != m->allDHCPServers.end();
4326 ++it)
4327 {
4328 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4329 if (FAILED(rc)) throw rc;
4330
4331 if (bstr == aName)
4332 {
4333 found = *it;
4334 break;
4335 }
4336 }
4337
4338 if (!found)
4339 return E_INVALIDARG;
4340
4341 return found.queryInterfaceTo(aServer);
4342}
4343
4344STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4345{
4346 CheckComArgNotNull(aServer);
4347
4348 AutoCaller autoCaller(this);
4349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4350
4351 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4352
4353 return rc;
4354}
4355
4356/**
4357 * Remembers the given dhcp server by storing it in the hard disk registry.
4358 *
4359 * @param aDHCPServer Dhcp Server object to remember.
4360 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4361 *
4362 * When @a aSaveRegistry is @c true, this operation may fail because of the
4363 * failed #saveSettings() method it calls. In this case, the dhcp server object
4364 * will not be remembered. It is therefore the responsibility of the caller to
4365 * call this method as the last step of some action that requires registration
4366 * in order to make sure that only fully functional dhcp server objects get
4367 * registered.
4368 *
4369 * @note Locks this object for writing and @a aDHCPServer for reading.
4370 */
4371HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4372 bool aSaveRegistry /*= true*/)
4373{
4374 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4375
4376 AutoCaller autoCaller(this);
4377 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4378
4379 AutoCaller dhcpServerCaller(aDHCPServer);
4380 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4381
4382 Bstr name;
4383 HRESULT rc;
4384 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4385 if (FAILED(rc)) return rc;
4386
4387 ComPtr<IDHCPServer> existing;
4388 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
4389 if (SUCCEEDED(rc))
4390 return E_INVALIDARG;
4391
4392 rc = S_OK;
4393
4394 m->allDHCPServers.addChild(aDHCPServer);
4395
4396 if (aSaveRegistry)
4397 {
4398 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4399 rc = saveSettings();
4400 vboxLock.release();
4401
4402 if (FAILED(rc))
4403 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4404 }
4405
4406 return rc;
4407}
4408
4409/**
4410 * Removes the given hard disk from the hard disk registry.
4411 *
4412 * @param aHardDisk Hard disk object to remove.
4413 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4414 *
4415 * When @a aSaveRegistry is @c true, this operation may fail because of the
4416 * failed #saveSettings() method it calls. In this case, the hard disk object
4417 * will NOT be removed from the registry when this method returns. It is
4418 * therefore the responsibility of the caller to call this method as the first
4419 * step of some action that requires unregistration, before calling uninit() on
4420 * @a aHardDisk.
4421 *
4422 * @note Locks this object for writing and @a aHardDisk for reading.
4423 */
4424HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4425 bool aSaveRegistry /*= true*/)
4426{
4427 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4428
4429 AutoCaller autoCaller(this);
4430 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4431
4432 AutoCaller dhcpServerCaller(aDHCPServer);
4433 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4434
4435 m->allDHCPServers.removeChild(aDHCPServer);
4436
4437 HRESULT rc = S_OK;
4438
4439 if (aSaveRegistry)
4440 {
4441 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4442 rc = saveSettings();
4443 vboxLock.release();
4444
4445 if (FAILED(rc))
4446 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4447 }
4448
4449 return rc;
4450}
4451
4452/* 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