VirtualBox

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

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

Main: reorganize session APIs

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette