VirtualBox

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

Last change on this file since 31387 was 31387, checked in by vboxsync, 15 years ago

Main: Lock order when saving host usb filters.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 140.8 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 31387 2010-08-05 10:13:58Z 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), will take host lock. */
3300 {
3301 mediaLock.release();
3302 machinesLock.release();
3303 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3304 if (FAILED(rc)) throw rc;
3305 }
3306
3307 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3308 if (FAILED(rc)) throw rc;
3309
3310 // and write out the XML, still under the lock
3311 m->pMainConfigFile->write(m->strSettingsFilePath);
3312 }
3313 catch (HRESULT err)
3314 {
3315 /* we assume that error info is set by the thrower */
3316 rc = err;
3317 }
3318 catch (...)
3319 {
3320 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3321 }
3322
3323 return rc;
3324}
3325
3326/**
3327 * Helper to register the machine.
3328 *
3329 * When called during VirtualBox startup, adds the given machine to the
3330 * collection of registered machines. Otherwise tries to mark the machine
3331 * as registered, and, if succeeded, adds it to the collection and
3332 * saves global settings.
3333 *
3334 * @note The caller must have added itself as a caller of the @a aMachine
3335 * object if calls this method not on VirtualBox startup.
3336 *
3337 * @param aMachine machine to register
3338 *
3339 * @note Locks objects!
3340 */
3341HRESULT VirtualBox::registerMachine(Machine *aMachine)
3342{
3343 ComAssertRet(aMachine, E_INVALIDARG);
3344
3345 AutoCaller autoCaller(this);
3346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3347
3348 HRESULT rc = S_OK;
3349
3350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3351
3352 {
3353 ComObjPtr<Machine> pMachine;
3354 rc = findMachine(aMachine->getId(),
3355 true /* fPermitInaccessible */,
3356 false /* aDoSetError */,
3357 &pMachine);
3358 if (SUCCEEDED(rc))
3359 {
3360 /* sanity */
3361 AutoLimitedCaller machCaller(pMachine);
3362 AssertComRC(machCaller.rc());
3363
3364 return setError(E_INVALIDARG,
3365 tr("Registered machine with UUID {%RTuuid} ('%ls') already exists"),
3366 aMachine->getId().raw(),
3367 pMachine->getSettingsFileFull().raw());
3368 }
3369
3370 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3371 rc = S_OK;
3372 }
3373
3374 if (autoCaller.state() != InInit)
3375 {
3376 rc = aMachine->prepareRegister();
3377 if (FAILED(rc)) return rc;
3378 }
3379
3380 /* add to the collection of registered machines */
3381 m->allMachines.addChild(aMachine);
3382
3383 if (autoCaller.state() != InInit)
3384 rc = saveSettings();
3385
3386 return rc;
3387}
3388
3389/**
3390 * Remembers the given hard disk by storing it in the hard disk registry.
3391 *
3392 * @param aHardDisk Hard disk object to remember.
3393 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3394 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3395 *
3396 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3397 */
3398HRESULT VirtualBox::registerHardDisk(Medium *aHardDisk,
3399 bool *pfNeedsSaveSettings)
3400{
3401 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3402
3403 AutoCaller autoCaller(this);
3404 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3405
3406 AutoCaller hardDiskCaller(aHardDisk);
3407 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3408
3409 // caller must hold the media tree write lock
3410 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3411
3412 Guid id;
3413 Utf8Str strLocationFull;
3414 ComObjPtr<Medium> pParent;
3415 {
3416 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3417 id = aHardDisk->getId();
3418 strLocationFull = aHardDisk->getLocationFull();
3419 pParent = aHardDisk->getParent();
3420 }
3421
3422 HRESULT rc;
3423
3424 Utf8Str strConflict;
3425 rc = checkMediaForConflicts2(id,
3426 strLocationFull,
3427 strConflict);
3428 if (FAILED(rc)) return rc;
3429
3430 if (strConflict.length())
3431 return setError(E_INVALIDARG,
3432 tr("Cannot register the hard disk '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3433 strLocationFull.raw(),
3434 id.raw(),
3435 strConflict.raw(),
3436 m->strSettingsFilePath.raw());
3437
3438 // store base (root) hard disks in the list
3439 if (pParent.isNull())
3440 m->allHardDisks.getList().push_back(aHardDisk);
3441 // access the list directly because we already locked the list above
3442
3443 // store all hard disks (even differencing images) in the map
3444 m->mapHardDisks[id] = aHardDisk;
3445
3446 if (pfNeedsSaveSettings)
3447 *pfNeedsSaveSettings = true;
3448
3449 return rc;
3450}
3451
3452/**
3453 * Removes the given hard disk from the hard disk registry.
3454 *
3455 * @param aHardDisk Hard disk object to remove.
3456 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3457 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3458 *
3459 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3460 */
3461HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3462 bool *pfNeedsSaveSettings)
3463{
3464 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3465
3466 AutoCaller autoCaller(this);
3467 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3468
3469 AutoCaller hardDiskCaller(aHardDisk);
3470 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3471
3472 // caller must hold the media tree write lock
3473 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3474
3475 Guid id;
3476 ComObjPtr<Medium> pParent;
3477 {
3478 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3479 id = aHardDisk->getId();
3480 pParent = aHardDisk->getParent();
3481 }
3482
3483 // remove base (root) hard disks from the list
3484 if (pParent.isNull())
3485 m->allHardDisks.getList().remove(aHardDisk);
3486 // access the list directly because caller must have locked the list
3487
3488 // remove all hard disks (even differencing images) from map
3489 size_t cnt = m->mapHardDisks.erase(id);
3490 Assert(cnt == 1);
3491 NOREF(cnt);
3492
3493 if (pfNeedsSaveSettings)
3494 *pfNeedsSaveSettings = true;
3495
3496 return S_OK;
3497}
3498
3499/**
3500 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3501 *
3502 * @param argImage Image object to remember.
3503 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3504 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3505 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3506 *
3507 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3508 */
3509HRESULT VirtualBox::registerImage(Medium *argImage,
3510 DeviceType_T argType,
3511 bool *pfNeedsSaveSettings)
3512{
3513 AssertReturn(argImage != NULL, E_INVALIDARG);
3514
3515 AutoCaller autoCaller(this);
3516 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3517
3518 AutoCaller imageCaller(argImage);
3519 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3520
3521 // caller must hold the media tree write lock
3522 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3523
3524 Guid id;
3525 Utf8Str strLocationFull;
3526 ComObjPtr<Medium> pParent;
3527 {
3528 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3529 id = argImage->getId();
3530 strLocationFull = argImage->getLocationFull();
3531 pParent = argImage->getParent();
3532 }
3533
3534 // work on DVDs or floppies list?
3535 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3536
3537 HRESULT rc;
3538 // lock the images lists (list + map) while checking for conflicts
3539 AutoWriteLock al(all.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3540
3541 Utf8Str strConflict;
3542 rc = checkMediaForConflicts2(id,
3543 strLocationFull,
3544 strConflict);
3545 if (FAILED(rc)) return rc;
3546
3547 if (strConflict.length())
3548 return setError(VBOX_E_INVALID_OBJECT_STATE,
3549 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3550 strLocationFull.raw(),
3551 id.raw(),
3552 strConflict.raw(),
3553 m->strSettingsFilePath.raw());
3554
3555 // add to the collection
3556 all.getList().push_back(argImage);
3557 // access the list directly because we already locked the list above
3558
3559 if (pfNeedsSaveSettings)
3560 *pfNeedsSaveSettings = true;
3561
3562 return rc;
3563}
3564
3565/**
3566 * Removes the given image from the CD/DVD or floppy image registry.
3567 *
3568 * @param argImage Image object to remove.
3569 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3570 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3571 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3572 *
3573 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3574 */
3575HRESULT VirtualBox::unregisterImage(Medium *argImage,
3576 DeviceType_T argType,
3577 bool *pfNeedsSaveSettings)
3578{
3579 AssertReturn(argImage != NULL, E_INVALIDARG);
3580
3581 AutoCaller autoCaller(this);
3582 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3583
3584 AutoCaller imageCaller(argImage);
3585 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3586
3587 // caller must hold the media tree write lock
3588 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3589
3590 Guid id;
3591 ComObjPtr<Medium> pParent;
3592 {
3593 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3594 id = argImage->getId();
3595 pParent = argImage->getParent();
3596 }
3597
3598 // work on DVDs or floppies list?
3599 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3600
3601 // access the list directly because the caller must have requested the lock
3602 all.getList().remove(argImage);
3603
3604 HRESULT rc = S_OK;
3605
3606 if (pfNeedsSaveSettings)
3607 *pfNeedsSaveSettings = true;
3608
3609 return rc;
3610}
3611
3612/**
3613 * Removes the given machine object from the internal list of registered machines.
3614 * Called from Machine::Unregister().
3615 * @param pMachine
3616 * @return
3617 */
3618HRESULT VirtualBox::unregisterMachine(Machine *pMachine)
3619{
3620 const Guid &id = pMachine->getId();
3621
3622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3623
3624 // remove from the collection of registered machines
3625 m->allMachines.removeChild(pMachine);
3626
3627 // save the global registry
3628 HRESULT rc = saveSettings();
3629
3630 alock.release();
3631
3632 /* fire an event */
3633 onMachineRegistered(id, FALSE);
3634
3635 return rc;
3636}
3637
3638/**
3639 * Creates the path to the specified file according to the path information
3640 * present in the file name.
3641 *
3642 * Note that the given file name must contain the full path otherwise the
3643 * extracted relative path will be created based on the current working
3644 * directory which is normally unknown.
3645 *
3646 * @param aFileName Full file name which path needs to be created.
3647 *
3648 * @return Extended error information on failure to create the path.
3649 */
3650/* static */
3651HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3652{
3653 Utf8Str strDir(strFileName);
3654 strDir.stripFilename();
3655 if (!RTDirExists(strDir.c_str()))
3656 {
3657 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3658 if (RT_FAILURE(vrc))
3659 return setErrorStatic(E_FAIL,
3660 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
3661 strDir.c_str(),
3662 vrc));
3663 }
3664
3665 return S_OK;
3666}
3667
3668/**
3669 * Handles unexpected exceptions by turning them into COM errors in release
3670 * builds or by hitting a breakpoint in the release builds.
3671 *
3672 * Usage pattern:
3673 * @code
3674 try
3675 {
3676 // ...
3677 }
3678 catch (LaLalA)
3679 {
3680 // ...
3681 }
3682 catch (...)
3683 {
3684 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3685 }
3686 * @endcode
3687 *
3688 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3689 */
3690/* static */
3691HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3692{
3693 try
3694 {
3695 /* re-throw the current exception */
3696 throw;
3697 }
3698 catch (const iprt::Error &err) // includes all XML exceptions
3699 {
3700 return setErrorStatic(E_FAIL,
3701 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
3702 err.what(),
3703 pszFile, iLine, pszFunction).c_str());
3704 }
3705 catch (const std::exception &err)
3706 {
3707 return setErrorStatic(E_FAIL,
3708 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3709 err.what(), typeid(err).name(),
3710 pszFile, iLine, pszFunction).c_str());
3711 }
3712 catch (...)
3713 {
3714 return setErrorStatic(E_FAIL,
3715 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
3716 pszFile, iLine, pszFunction).c_str());
3717 }
3718
3719 /* should not get here */
3720 AssertFailed();
3721 return E_FAIL;
3722}
3723
3724const Utf8Str& VirtualBox::settingsFilePath()
3725{
3726 return m->strSettingsFilePath;
3727}
3728
3729/**
3730 * Returns the lock handle which protects the media trees (hard disks,
3731 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3732 * are no longer protected by the VirtualBox lock, but by this more
3733 * specialized lock. Mind the locking order: always request this lock
3734 * after the VirtualBox object lock but before the locks of the media
3735 * objects contained in these lists. See AutoLock.h.
3736 */
3737RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3738{
3739 return m->lockMedia;
3740}
3741
3742/**
3743 * Thread function that watches the termination of all client processes
3744 * that have opened sessions using IMachine::LockMachine()
3745 */
3746// static
3747DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3748{
3749 LogFlowFuncEnter();
3750
3751 VirtualBox *that = (VirtualBox*)pvUser;
3752 Assert(that);
3753
3754 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3755 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3756
3757 SessionMachineVector machines;
3758 MachineVector spawnedMachines;
3759
3760 size_t cnt = 0;
3761 size_t cntSpawned = 0;
3762
3763#if defined(RT_OS_WINDOWS)
3764
3765 HRESULT hrc = CoInitializeEx(NULL,
3766 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3767 COINIT_SPEED_OVER_MEMORY);
3768 AssertComRC(hrc);
3769
3770 /// @todo (dmik) processes reaping!
3771
3772 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3773 handles[0] = that->m->updateReq;
3774
3775 do
3776 {
3777 AutoCaller autoCaller(that);
3778 /* VirtualBox has been early uninitialized, terminate */
3779 if (!autoCaller.isOk())
3780 break;
3781
3782 do
3783 {
3784 /* release the caller to let uninit() ever proceed */
3785 autoCaller.release();
3786
3787 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3788 handles,
3789 FALSE,
3790 INFINITE);
3791
3792 /* Restore the caller before using VirtualBox. If it fails, this
3793 * means VirtualBox is being uninitialized and we must terminate. */
3794 autoCaller.add();
3795 if (!autoCaller.isOk())
3796 break;
3797
3798 bool update = false;
3799
3800 if (rc == WAIT_OBJECT_0)
3801 {
3802 /* update event is signaled */
3803 update = true;
3804 }
3805 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3806 {
3807 /* machine mutex is released */
3808 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3809 update = true;
3810 }
3811 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3812 {
3813 /* machine mutex is abandoned due to client process termination */
3814 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3815 update = true;
3816 }
3817 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3818 {
3819 /* spawned VM process has terminated (normally or abnormally) */
3820 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
3821 checkForSpawnFailure();
3822 update = true;
3823 }
3824
3825 if (update)
3826 {
3827 /* close old process handles */
3828 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
3829 CloseHandle(handles[i]);
3830
3831 // lock the machines list for reading
3832 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3833
3834 /* obtain a new set of opened machines */
3835 cnt = 0;
3836 machines.clear();
3837
3838 for (MachinesOList::iterator it = that->m->allMachines.begin();
3839 it != that->m->allMachines.end();
3840 ++it)
3841 {
3842 /// @todo handle situations with more than 64 objects
3843 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3844 ("MAXIMUM_WAIT_OBJECTS reached"));
3845
3846 ComObjPtr<SessionMachine> sm;
3847 HANDLE ipcSem;
3848 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
3849 {
3850 machines.push_back(sm);
3851 handles[1 + cnt] = ipcSem;
3852 ++cnt;
3853 }
3854 }
3855
3856 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
3857
3858 /* obtain a new set of spawned machines */
3859 cntSpawned = 0;
3860 spawnedMachines.clear();
3861
3862 for (MachinesOList::iterator it = that->m->allMachines.begin();
3863 it != that->m->allMachines.end();
3864 ++it)
3865 {
3866 /// @todo handle situations with more than 64 objects
3867 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
3868 ("MAXIMUM_WAIT_OBJECTS reached"));
3869
3870 RTPROCESS pid;
3871 if ((*it)->isSessionSpawning(&pid))
3872 {
3873 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
3874 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
3875 pid, GetLastError()));
3876 if (rc == 0)
3877 {
3878 spawnedMachines.push_back(*it);
3879 handles[1 + cnt + cntSpawned] = ph;
3880 ++cntSpawned;
3881 }
3882 }
3883 }
3884
3885 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
3886
3887 // machines lock unwinds here
3888 }
3889 }
3890 while (true);
3891 }
3892 while (0);
3893
3894 /* close old process handles */
3895 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3896 CloseHandle(handles[i]);
3897
3898 /* release sets of machines if any */
3899 machines.clear();
3900 spawnedMachines.clear();
3901
3902 ::CoUninitialize();
3903
3904#elif defined(RT_OS_OS2)
3905
3906 /// @todo (dmik) processes reaping!
3907
3908 /* according to PMREF, 64 is the maximum for the muxwait list */
3909 SEMRECORD handles[64];
3910
3911 HMUX muxSem = NULLHANDLE;
3912
3913 do
3914 {
3915 AutoCaller autoCaller(that);
3916 /* VirtualBox has been early uninitialized, terminate */
3917 if (!autoCaller.isOk())
3918 break;
3919
3920 do
3921 {
3922 /* release the caller to let uninit() ever proceed */
3923 autoCaller.release();
3924
3925 int vrc = RTSemEventWait(that->m->updateReq, 500);
3926
3927 /* Restore the caller before using VirtualBox. If it fails, this
3928 * means VirtualBox is being uninitialized and we must terminate. */
3929 autoCaller.add();
3930 if (!autoCaller.isOk())
3931 break;
3932
3933 bool update = false;
3934 bool updateSpawned = false;
3935
3936 if (RT_SUCCESS(vrc))
3937 {
3938 /* update event is signaled */
3939 update = true;
3940 updateSpawned = true;
3941 }
3942 else
3943 {
3944 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
3945 ("RTSemEventWait returned %Rrc\n", vrc));
3946
3947 /* are there any mutexes? */
3948 if (cnt > 0)
3949 {
3950 /* figure out what's going on with machines */
3951
3952 unsigned long semId = 0;
3953 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
3954 SEM_IMMEDIATE_RETURN, &semId);
3955
3956 if (arc == NO_ERROR)
3957 {
3958 /* machine mutex is normally released */
3959 Assert(semId >= 0 && semId < cnt);
3960 if (semId >= 0 && semId < cnt)
3961 {
3962#ifdef DEBUG
3963 {
3964 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
3965 LogFlowFunc(("released mutex: machine='%ls'\n",
3966 machines[semId]->name().raw()));
3967 }
3968#endif
3969 machines[semId]->checkForDeath();
3970 }
3971 update = true;
3972 }
3973 else if (arc == ERROR_SEM_OWNER_DIED)
3974 {
3975 /* machine mutex is abandoned due to client process
3976 * termination; find which mutex is in the Owner Died
3977 * state */
3978 for (size_t i = 0; i < cnt; ++ i)
3979 {
3980 PID pid; TID tid;
3981 unsigned long reqCnt;
3982 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
3983 if (arc == ERROR_SEM_OWNER_DIED)
3984 {
3985 /* close the dead mutex as asked by PMREF */
3986 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
3987
3988 Assert(i >= 0 && i < cnt);
3989 if (i >= 0 && i < cnt)
3990 {
3991#ifdef DEBUG
3992 {
3993 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
3994 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
3995 machines[i]->name().raw()));
3996 }
3997#endif
3998 machines[i]->checkForDeath();
3999 }
4000 }
4001 }
4002 update = true;
4003 }
4004 else
4005 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4006 ("DosWaitMuxWaitSem returned %d\n", arc));
4007 }
4008
4009 /* are there any spawning sessions? */
4010 if (cntSpawned > 0)
4011 {
4012 for (size_t i = 0; i < cntSpawned; ++ i)
4013 updateSpawned |= (spawnedMachines[i])->
4014 checkForSpawnFailure();
4015 }
4016 }
4017
4018 if (update || updateSpawned)
4019 {
4020 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4021
4022 if (update)
4023 {
4024 /* close the old muxsem */
4025 if (muxSem != NULLHANDLE)
4026 ::DosCloseMuxWaitSem(muxSem);
4027
4028 /* obtain a new set of opened machines */
4029 cnt = 0;
4030 machines.clear();
4031
4032 for (MachinesOList::iterator it = that->m->llMachines.begin();
4033 it != that->m->llMachines.end(); ++ it)
4034 {
4035 /// @todo handle situations with more than 64 objects
4036 AssertMsg(cnt <= 64 /* according to PMREF */,
4037 ("maximum of 64 mutex semaphores reached (%d)",
4038 cnt));
4039
4040 ComObjPtr<SessionMachine> sm;
4041 HMTX ipcSem;
4042 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4043 {
4044 machines.push_back(sm);
4045 handles[cnt].hsemCur = (HSEM)ipcSem;
4046 handles[cnt].ulUser = cnt;
4047 ++ cnt;
4048 }
4049 }
4050
4051 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4052
4053 if (cnt > 0)
4054 {
4055 /* create a new muxsem */
4056 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4057 handles,
4058 DCMW_WAIT_ANY);
4059 AssertMsg(arc == NO_ERROR,
4060 ("DosCreateMuxWaitSem returned %d\n", arc));
4061 NOREF(arc);
4062 }
4063 }
4064
4065 if (updateSpawned)
4066 {
4067 /* obtain a new set of spawned machines */
4068 spawnedMachines.clear();
4069
4070 for (MachinesOList::iterator it = that->m->llMachines.begin();
4071 it != that->m->llMachines.end(); ++ it)
4072 {
4073 if ((*it)->isSessionSpawning())
4074 spawnedMachines.push_back(*it);
4075 }
4076
4077 cntSpawned = spawnedMachines.size();
4078 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4079 }
4080 }
4081 }
4082 while (true);
4083 }
4084 while (0);
4085
4086 /* close the muxsem */
4087 if (muxSem != NULLHANDLE)
4088 ::DosCloseMuxWaitSem(muxSem);
4089
4090 /* release sets of machines if any */
4091 machines.clear();
4092 spawnedMachines.clear();
4093
4094#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4095
4096 bool update = false;
4097 bool updateSpawned = false;
4098
4099 do
4100 {
4101 AutoCaller autoCaller(that);
4102 if (!autoCaller.isOk())
4103 break;
4104
4105 do
4106 {
4107 /* release the caller to let uninit() ever proceed */
4108 autoCaller.release();
4109
4110 int rc = RTSemEventWait(that->m->updateReq, 500);
4111
4112 /*
4113 * Restore the caller before using VirtualBox. If it fails, this
4114 * means VirtualBox is being uninitialized and we must terminate.
4115 */
4116 autoCaller.add();
4117 if (!autoCaller.isOk())
4118 break;
4119
4120 if (RT_SUCCESS(rc) || update || updateSpawned)
4121 {
4122 /* RT_SUCCESS(rc) means an update event is signaled */
4123
4124 // lock the machines list for reading
4125 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4126
4127 if (RT_SUCCESS(rc) || update)
4128 {
4129 /* obtain a new set of opened machines */
4130 machines.clear();
4131
4132 for (MachinesOList::iterator it = that->m->allMachines.begin();
4133 it != that->m->allMachines.end();
4134 ++it)
4135 {
4136 ComObjPtr<SessionMachine> sm;
4137 if ((*it)->isSessionOpenOrClosing(sm))
4138 machines.push_back(sm);
4139 }
4140
4141 cnt = machines.size();
4142 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4143 }
4144
4145 if (RT_SUCCESS(rc) || updateSpawned)
4146 {
4147 /* obtain a new set of spawned machines */
4148 spawnedMachines.clear();
4149
4150 for (MachinesOList::iterator it = that->m->allMachines.begin();
4151 it != that->m->allMachines.end();
4152 ++it)
4153 {
4154 if ((*it)->isSessionSpawning())
4155 spawnedMachines.push_back(*it);
4156 }
4157
4158 cntSpawned = spawnedMachines.size();
4159 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4160 }
4161
4162 // machines lock unwinds here
4163 }
4164
4165 update = false;
4166 for (size_t i = 0; i < cnt; ++ i)
4167 update |= (machines[i])->checkForDeath();
4168
4169 updateSpawned = false;
4170 for (size_t i = 0; i < cntSpawned; ++ i)
4171 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4172
4173 /* reap child processes */
4174 {
4175 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4176 if (that->m->llProcesses.size())
4177 {
4178 LogFlowFunc(("UPDATE: child process count = %d\n",
4179 that->m->llProcesses.size()));
4180 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4181 while (it != that->m->llProcesses.end())
4182 {
4183 RTPROCESS pid = *it;
4184 RTPROCSTATUS status;
4185 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4186 if (vrc == VINF_SUCCESS)
4187 {
4188 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4189 pid, pid, status.iStatus,
4190 status.enmReason));
4191 it = that->m->llProcesses.erase(it);
4192 }
4193 else
4194 {
4195 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4196 pid, pid, vrc));
4197 if (vrc != VERR_PROCESS_RUNNING)
4198 {
4199 /* remove the process if it is not already running */
4200 it = that->m->llProcesses.erase(it);
4201 }
4202 else
4203 ++ it;
4204 }
4205 }
4206 }
4207 }
4208 }
4209 while (true);
4210 }
4211 while (0);
4212
4213 /* release sets of machines if any */
4214 machines.clear();
4215 spawnedMachines.clear();
4216
4217#else
4218# error "Port me!"
4219#endif
4220
4221 LogFlowFuncLeave();
4222 return 0;
4223}
4224
4225/**
4226 * Thread function that handles custom events posted using #postEvent().
4227 */
4228// static
4229DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4230{
4231 LogFlowFuncEnter();
4232
4233 AssertReturn(pvUser, VERR_INVALID_POINTER);
4234
4235 // create an event queue for the current thread
4236 EventQueue *eventQ = new EventQueue();
4237 AssertReturn(eventQ, VERR_NO_MEMORY);
4238
4239 // return the queue to the one who created this thread
4240 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4241 // signal that we're ready
4242 RTThreadUserSignal(thread);
4243
4244 BOOL ok = TRUE;
4245 Event *event = NULL;
4246
4247 while ((ok = eventQ->waitForEvent(&event)) && event)
4248 eventQ->handleEvent(event);
4249
4250 AssertReturn(ok, VERR_GENERAL_FAILURE);
4251
4252 delete eventQ;
4253
4254 LogFlowFuncLeave();
4255
4256 return 0;
4257}
4258
4259
4260////////////////////////////////////////////////////////////////////////////////
4261
4262/**
4263 * Takes the current list of registered callbacks of the managed VirtualBox
4264 * instance, and calls #handleCallback() for every callback item from the
4265 * list, passing the item as an argument.
4266 *
4267 * @note Locks the managed VirtualBox object for reading but leaves the lock
4268 * before iterating over callbacks and calling their methods.
4269 */
4270void *VirtualBox::CallbackEvent::handler()
4271{
4272 if (!mVirtualBox)
4273 return NULL;
4274
4275 AutoCaller autoCaller(mVirtualBox);
4276 if (!autoCaller.isOk())
4277 {
4278 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4279 autoCaller.state()));
4280 /* We don't need mVirtualBox any more, so release it */
4281 mVirtualBox = NULL;
4282 return NULL;
4283 }
4284
4285
4286 {
4287 VBoxEventDesc evDesc;
4288 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4289
4290 evDesc.fire(/* don't wait for delivery */0);
4291 }
4292
4293 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4294 return NULL;
4295}
4296
4297//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4298//{
4299// return E_NOTIMPL;
4300//}
4301
4302STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4303{
4304 CheckComArgStrNotEmptyOrNull(aName);
4305 CheckComArgNotNull(aServer);
4306
4307 AutoCaller autoCaller(this);
4308 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4309
4310 ComObjPtr<DHCPServer> dhcpServer;
4311 dhcpServer.createObject();
4312 HRESULT rc = dhcpServer->init(this, aName);
4313 if (FAILED(rc)) return rc;
4314
4315 rc = registerDHCPServer(dhcpServer, true);
4316 if (FAILED(rc)) return rc;
4317
4318 dhcpServer.queryInterfaceTo(aServer);
4319
4320 return rc;
4321}
4322
4323STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4324{
4325 CheckComArgStrNotEmptyOrNull(aName);
4326 CheckComArgNotNull(aServer);
4327
4328 AutoCaller autoCaller(this);
4329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4330
4331 HRESULT rc;
4332 Bstr bstr;
4333 ComPtr<DHCPServer> found;
4334
4335 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4336
4337 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4338 it != m->allDHCPServers.end();
4339 ++it)
4340 {
4341 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4342 if (FAILED(rc)) throw rc;
4343
4344 if (bstr == aName)
4345 {
4346 found = *it;
4347 break;
4348 }
4349 }
4350
4351 if (!found)
4352 return E_INVALIDARG;
4353
4354 return found.queryInterfaceTo(aServer);
4355}
4356
4357STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4358{
4359 CheckComArgNotNull(aServer);
4360
4361 AutoCaller autoCaller(this);
4362 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4363
4364 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4365
4366 return rc;
4367}
4368
4369/**
4370 * Remembers the given dhcp server by storing it in the hard disk registry.
4371 *
4372 * @param aDHCPServer Dhcp Server object to remember.
4373 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4374 *
4375 * When @a aSaveRegistry is @c true, this operation may fail because of the
4376 * failed #saveSettings() method it calls. In this case, the dhcp server object
4377 * will not be remembered. It is therefore the responsibility of the caller to
4378 * call this method as the last step of some action that requires registration
4379 * in order to make sure that only fully functional dhcp server objects get
4380 * registered.
4381 *
4382 * @note Locks this object for writing and @a aDHCPServer for reading.
4383 */
4384HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4385 bool aSaveRegistry /*= true*/)
4386{
4387 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4388
4389 AutoCaller autoCaller(this);
4390 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4391
4392 AutoCaller dhcpServerCaller(aDHCPServer);
4393 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4394
4395 Bstr name;
4396 HRESULT rc;
4397 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4398 if (FAILED(rc)) return rc;
4399
4400 ComPtr<IDHCPServer> existing;
4401 rc = FindDHCPServerByNetworkName(name, existing.asOutParam());
4402 if (SUCCEEDED(rc))
4403 return E_INVALIDARG;
4404
4405 rc = S_OK;
4406
4407 m->allDHCPServers.addChild(aDHCPServer);
4408
4409 if (aSaveRegistry)
4410 {
4411 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4412 rc = saveSettings();
4413 vboxLock.release();
4414
4415 if (FAILED(rc))
4416 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4417 }
4418
4419 return rc;
4420}
4421
4422/**
4423 * Removes the given hard disk from the hard disk registry.
4424 *
4425 * @param aHardDisk Hard disk object to remove.
4426 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4427 *
4428 * When @a aSaveRegistry is @c true, this operation may fail because of the
4429 * failed #saveSettings() method it calls. In this case, the hard disk object
4430 * will NOT be removed from the registry when this method returns. It is
4431 * therefore the responsibility of the caller to call this method as the first
4432 * step of some action that requires unregistration, before calling uninit() on
4433 * @a aHardDisk.
4434 *
4435 * @note Locks this object for writing and @a aHardDisk for reading.
4436 */
4437HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4438 bool aSaveRegistry /*= true*/)
4439{
4440 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4441
4442 AutoCaller autoCaller(this);
4443 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4444
4445 AutoCaller dhcpServerCaller(aDHCPServer);
4446 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4447
4448 m->allDHCPServers.removeChild(aDHCPServer);
4449
4450 HRESULT rc = S_OK;
4451
4452 if (aSaveRegistry)
4453 {
4454 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4455 rc = saveSettings();
4456 vboxLock.release();
4457
4458 if (FAILED(rc))
4459 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4460 }
4461
4462 return rc;
4463}
4464
4465/* 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