VirtualBox

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

Last change on this file since 33712 was 33691, checked in by vboxsync, 14 years ago

Main: openMedium() should return the same IMedium when called twice with the same path

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