VirtualBox

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

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

Main: fix memory leak in VirtualBox::postEvent() when posting the event fails

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