VirtualBox

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

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

Main: Some OS/2 build fixes.

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