VirtualBox

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

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

Main: partial revert or r67042, bring back the overwrite parameter

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