VirtualBox

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

Last change on this file since 31570 was 31568, checked in by vboxsync, 14 years ago

Main: combine IVirtualBox::openHardDisk(), openDVDImage(), openFloppyImage() into new IVirtualBox::openMedium(); add new IMedium::setImageUUIDs() method for a rare use case of the old openHardDisk(); optimize client code now that code is mostly the same

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