VirtualBox

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

Last change on this file since 31598 was 31595, checked in by vboxsync, 14 years ago

Main: remove deprecated VirtualBox::createLegacyMachine() and corresponding VBoxManage --settingsfile option; new API docs for IMachine::attachDevice() (not yet implemented)

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

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