VirtualBox

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

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

build fix

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