VirtualBox

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

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

Main/Machine+Performance+VirtualBox: move deleting the performance metrics registration of a VM out of SessionMachine::uninit, where it was a risk of hangs due to callbacks. Doing it a little later won't hurt anyone. Additionally also make sure the total VMM memory stats are zeroed when the last VM has disabled its collector.

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