VirtualBox

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

Last change on this file since 33598 was 33567, checked in by vboxsync, 14 years ago

VD: Move the generic virtual disk framework + backends to src/VBox/Storage and rename the files to get rid of the HDD part because it supports floppy and DVD images too

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 145.4 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 33567 2010-10-28 15:37:21Z 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 switch (deviceType)
1428 {
1429 case DeviceType_HardDisk:
1430 case DeviceType_Floppy:
1431 break;
1432
1433 case DeviceType_DVD:
1434 accessMode = AccessMode_ReadOnly;
1435 break;
1436
1437 default:
1438 return setError(E_INVALIDARG, "Device type must be HardDisk, DVD or Floppy");
1439 }
1440
1441 pMedium.createObject();
1442 HRESULT rc = pMedium->init(this,
1443 aLocation,
1444 (accessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly,
1445 deviceType);
1446
1447 if (SUCCEEDED(rc))
1448 {
1449 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1450
1451 switch (deviceType)
1452 {
1453 case DeviceType_HardDisk:
1454 rc = registerHardDisk(pMedium,
1455 &fNeedsGlobalSaveSettings);
1456 break;
1457
1458 case DeviceType_DVD:
1459 case DeviceType_Floppy:
1460 rc = registerImage(pMedium,
1461 deviceType,
1462 &fNeedsGlobalSaveSettings);
1463 break;
1464 }
1465
1466 treeLock.release();
1467
1468 /* Note that it's important to call uninit() on failure to register
1469 * because the differencing hard disk would have been already associated
1470 * with the parent and this association needs to be broken. */
1471
1472 if (FAILED(rc))
1473 pMedium->uninit();
1474 }
1475
1476 if (SUCCEEDED(rc))
1477 pMedium.queryInterfaceTo(aMedium);
1478
1479 if (fNeedsGlobalSaveSettings)
1480 {
1481 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
1482 saveSettings();
1483 }
1484
1485 return rc;
1486}
1487
1488STDMETHODIMP VirtualBox::FindMedium(IN_BSTR aLocation,
1489 DeviceType_T aDeviceType,
1490 IMedium **aMedium)
1491{
1492 CheckComArgOutSafeArrayPointerValid(aMedium);
1493
1494 AutoCaller autoCaller(this);
1495 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1496
1497 Guid id(aLocation);
1498 Utf8Str strLocation(aLocation);
1499
1500 HRESULT rc;
1501 ComObjPtr<Medium> pMedium;
1502
1503 switch (aDeviceType)
1504 {
1505 case DeviceType_HardDisk:
1506 if (!id.isEmpty())
1507 rc = findHardDiskById(id, true /* setError */, &pMedium);
1508 else
1509 rc = findHardDiskByLocation(strLocation, true /* setError */, &pMedium);
1510 break;
1511
1512 case DeviceType_Floppy:
1513 case DeviceType_DVD:
1514 if (!id.isEmpty())
1515 rc = findDVDOrFloppyImage(aDeviceType, &id, Utf8Str::Empty, true /* setError */, &pMedium);
1516 else
1517 rc = findDVDOrFloppyImage(aDeviceType, NULL, strLocation, true /* setError */, &pMedium);
1518 break;
1519
1520 default:
1521 return setError(E_INVALIDARG,
1522 tr("Invalid device type %d"), aDeviceType);
1523 }
1524
1525 /* the below will set *aHardDisk to NULL if hardDisk is null */
1526 pMedium.queryInterfaceTo(aMedium);
1527
1528 return rc;
1529}
1530
1531/** @note Locks this object for reading. */
1532STDMETHODIMP VirtualBox::GetGuestOSType(IN_BSTR aId, IGuestOSType **aType)
1533{
1534 /* Old ID to new ID conversion table. See r39691 for a source */
1535 static const wchar_t *kOldNewIDs[] =
1536 {
1537 L"unknown", L"Other",
1538 L"win31", L"Windows31",
1539 L"win95", L"Windows95",
1540 L"win98", L"Windows98",
1541 L"winme", L"WindowsMe",
1542 L"winnt4", L"WindowsNT4",
1543 L"win2k", L"Windows2000",
1544 L"winxp", L"WindowsXP",
1545 L"win2k3", L"Windows2003",
1546 L"winvista", L"WindowsVista",
1547 L"win2k8", L"Windows2008",
1548 L"ecs", L"OS2eCS",
1549 L"fedoracore", L"Fedora",
1550 /* the rest is covered by the case-insensitive comparison */
1551 };
1552
1553 CheckComArgNotNull(aType);
1554
1555 AutoCaller autoCaller(this);
1556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1557
1558 /* first, look for a substitution */
1559 Bstr id = aId;
1560 for (size_t i = 0; i < RT_ELEMENTS(kOldNewIDs) / 2; i += 2)
1561 {
1562 if (id == kOldNewIDs[i])
1563 {
1564 id = kOldNewIDs[i + 1];
1565 break;
1566 }
1567 }
1568
1569 *aType = NULL;
1570
1571 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1572 for (GuestOSTypesOList::iterator it = m->allGuestOSTypes.begin();
1573 it != m->allGuestOSTypes.end();
1574 ++it)
1575 {
1576 const Bstr &typeId = (*it)->id();
1577 AssertMsg(!typeId.isEmpty(), ("ID must not be NULL"));
1578 if (typeId.compare(id, Bstr::CaseInsensitive) == 0)
1579 {
1580 (*it).queryInterfaceTo(aType);
1581 break;
1582 }
1583 }
1584
1585 return (*aType) ? S_OK :
1586 setError(E_INVALIDARG,
1587 tr("'%ls' is not a valid Guest OS type"),
1588 aId);
1589}
1590
1591STDMETHODIMP VirtualBox::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath,
1592 BOOL /* aWritable */, BOOL /* aAutoMount */)
1593{
1594 CheckComArgStrNotEmptyOrNull(aName);
1595 CheckComArgStrNotEmptyOrNull(aHostPath);
1596
1597 AutoCaller autoCaller(this);
1598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1599
1600 return setError(E_NOTIMPL, "Not yet implemented");
1601}
1602
1603STDMETHODIMP VirtualBox::RemoveSharedFolder(IN_BSTR aName)
1604{
1605 CheckComArgStrNotEmptyOrNull(aName);
1606
1607 AutoCaller autoCaller(this);
1608 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1609
1610 return setError(E_NOTIMPL, "Not yet implemented");
1611}
1612
1613/**
1614 * @note Locks this object for reading.
1615 */
1616STDMETHODIMP VirtualBox::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
1617{
1618 using namespace settings;
1619
1620 if (ComSafeArrayOutIsNull(aKeys))
1621 return E_POINTER;
1622
1623 AutoCaller autoCaller(this);
1624 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1625
1626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1627
1628 com::SafeArray<BSTR> saKeys(m->pMainConfigFile->mapExtraDataItems.size());
1629 int i = 0;
1630 for (StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin();
1631 it != m->pMainConfigFile->mapExtraDataItems.end();
1632 ++it, ++i)
1633 {
1634 const Utf8Str &strName = it->first; // the key
1635 strName.cloneTo(&saKeys[i]);
1636 }
1637 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
1638
1639 return S_OK;
1640}
1641
1642/**
1643 * @note Locks this object for reading.
1644 */
1645STDMETHODIMP VirtualBox::GetExtraData(IN_BSTR aKey,
1646 BSTR *aValue)
1647{
1648 CheckComArgStrNotEmptyOrNull(aKey);
1649 CheckComArgNotNull(aValue);
1650
1651 AutoCaller autoCaller(this);
1652 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1653
1654 /* start with nothing found */
1655 Utf8Str strKey(aKey);
1656 Bstr bstrResult;
1657
1658 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
1659 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1660 // found:
1661 bstrResult = it->second; // source is a Utf8Str
1662
1663 /* return the result to caller (may be empty) */
1664 bstrResult.cloneTo(aValue);
1665
1666 return S_OK;
1667}
1668
1669/**
1670 * @note Locks this object for writing.
1671 */
1672STDMETHODIMP VirtualBox::SetExtraData(IN_BSTR aKey,
1673 IN_BSTR aValue)
1674{
1675 CheckComArgStrNotEmptyOrNull(aKey);
1676
1677 AutoCaller autoCaller(this);
1678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1679
1680 Utf8Str strKey(aKey);
1681 Utf8Str strValue(aValue);
1682 Utf8Str strOldValue; // empty
1683
1684 // locking note: we only hold the read lock briefly to look up the old value,
1685 // then release it and call the onExtraCanChange callbacks. There is a small
1686 // chance of a race insofar as the callback might be called twice if two callers
1687 // change the same key at the same time, but that's a much better solution
1688 // than the deadlock we had here before. The actual changing of the extradata
1689 // is then performed under the write lock and race-free.
1690
1691 // look up the old value first; if nothing has changed then we need not do anything
1692 {
1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
1694 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey);
1695 if (it != m->pMainConfigFile->mapExtraDataItems.end())
1696 strOldValue = it->second;
1697 }
1698
1699 bool fChanged;
1700 if ((fChanged = (strOldValue != strValue)))
1701 {
1702 // ask for permission from all listeners outside the locks;
1703 // onExtraDataCanChange() only briefly requests the VirtualBox
1704 // lock to copy the list of callbacks to invoke
1705 Bstr error;
1706 Bstr bstrValue(aValue);
1707
1708 if (!onExtraDataCanChange(Guid::Empty, aKey, bstrValue.raw(), error))
1709 {
1710 const char *sep = error.isEmpty() ? "" : ": ";
1711 CBSTR err = error.raw();
1712 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
1713 sep, err));
1714 return setError(E_ACCESSDENIED,
1715 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
1716 aKey,
1717 bstrValue.raw(),
1718 sep,
1719 err);
1720 }
1721
1722 // data is changing and change not vetoed: then write it out under the lock
1723
1724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1725
1726 if (strValue.isEmpty())
1727 m->pMainConfigFile->mapExtraDataItems.erase(strKey);
1728 else
1729 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue;
1730 // creates a new key if needed
1731
1732 /* save settings on success */
1733 HRESULT rc = saveSettings();
1734 if (FAILED(rc)) return rc;
1735 }
1736
1737 // fire notification outside the lock
1738 if (fChanged)
1739 onExtraDataChange(Guid::Empty, aKey, aValue);
1740
1741 return S_OK;
1742}
1743
1744// public methods only for internal purposes
1745/////////////////////////////////////////////////////////////////////////////
1746
1747#ifdef DEBUG
1748void VirtualBox::dumpAllBackRefs()
1749{
1750 {
1751 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1752 for (MediaList::const_iterator mt = m->allHardDisks.begin();
1753 mt != m->allHardDisks.end();
1754 ++mt)
1755 {
1756 ComObjPtr<Medium> pMedium = *mt;
1757 pMedium->dumpBackRefs();
1758 }
1759 }
1760 {
1761 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
1762 for (MediaList::const_iterator mt = m->allDVDImages.begin();
1763 mt != m->allDVDImages.end();
1764 ++mt)
1765 {
1766 ComObjPtr<Medium> pMedium = *mt;
1767 pMedium->dumpBackRefs();
1768 }
1769 }
1770}
1771#endif
1772
1773/**
1774 * Posts an event to the event queue that is processed asynchronously
1775 * on a dedicated thread.
1776 *
1777 * Posting events to the dedicated event queue is useful to perform secondary
1778 * actions outside any object locks -- for example, to iterate over a list
1779 * of callbacks and inform them about some change caused by some object's
1780 * method call.
1781 *
1782 * @param event event to post; must have been allocated using |new|, will
1783 * be deleted automatically by the event thread after processing
1784 *
1785 * @note Doesn't lock any object.
1786 */
1787HRESULT VirtualBox::postEvent(Event *event)
1788{
1789 AssertReturn(event, E_FAIL);
1790
1791 HRESULT rc;
1792 AutoCaller autoCaller(this);
1793 if (SUCCEEDED((rc = autoCaller.rc())))
1794 {
1795 if (autoCaller.state() != Ready)
1796 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
1797 autoCaller.state()));
1798 // return S_OK
1799 else if ( (m->pAsyncEventQ)
1800 && (m->pAsyncEventQ->postEvent(event))
1801 )
1802 return S_OK;
1803 else
1804 rc = E_FAIL;
1805 }
1806
1807 // in any event of failure, we must clean up here, or we'll leak;
1808 // the caller has allocated the object using new()
1809 delete event;
1810 return rc;
1811}
1812
1813/**
1814 * Adds a progress to the global collection of pending operations.
1815 * Usually gets called upon progress object initialization.
1816 *
1817 * @param aProgress Operation to add to the collection.
1818 *
1819 * @note Doesn't lock objects.
1820 */
1821HRESULT VirtualBox::addProgress(IProgress *aProgress)
1822{
1823 CheckComArgNotNull(aProgress);
1824
1825 AutoCaller autoCaller(this);
1826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1827
1828 Bstr id;
1829 HRESULT rc = aProgress->COMGETTER(Id)(id.asOutParam());
1830 AssertComRCReturnRC(rc);
1831
1832 /* protect mProgressOperations */
1833 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1834
1835 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
1836 return S_OK;
1837}
1838
1839/**
1840 * Removes the progress from the global collection of pending operations.
1841 * Usually gets called upon progress completion.
1842 *
1843 * @param aId UUID of the progress operation to remove
1844 *
1845 * @note Doesn't lock objects.
1846 */
1847HRESULT VirtualBox::removeProgress(IN_GUID aId)
1848{
1849 AutoCaller autoCaller(this);
1850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1851
1852 ComPtr<IProgress> progress;
1853
1854 /* protect mProgressOperations */
1855 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
1856
1857 size_t cnt = m->mapProgressOperations.erase(aId);
1858 Assert(cnt == 1);
1859 NOREF(cnt);
1860
1861 return S_OK;
1862}
1863
1864#ifdef RT_OS_WINDOWS
1865
1866struct StartSVCHelperClientData
1867{
1868 ComObjPtr<VirtualBox> that;
1869 ComObjPtr<Progress> progress;
1870 bool privileged;
1871 VirtualBox::SVCHelperClientFunc func;
1872 void *user;
1873};
1874
1875/**
1876 * Helper method that starts a worker thread that:
1877 * - creates a pipe communication channel using SVCHlpClient;
1878 * - starts an SVC Helper process that will inherit this channel;
1879 * - executes the supplied function by passing it the created SVCHlpClient
1880 * and opened instance to communicate to the Helper process and the given
1881 * Progress object.
1882 *
1883 * The user function is supposed to communicate to the helper process
1884 * using the \a aClient argument to do the requested job and optionally expose
1885 * the progress through the \a aProgress object. The user function should never
1886 * call notifyComplete() on it: this will be done automatically using the
1887 * result code returned by the function.
1888 *
1889 * Before the user function is started, the communication channel passed to
1890 * the \a aClient argument is fully set up, the function should start using
1891 * its write() and read() methods directly.
1892 *
1893 * The \a aVrc parameter of the user function may be used to return an error
1894 * code if it is related to communication errors (for example, returned by
1895 * the SVCHlpClient members when they fail). In this case, the correct error
1896 * message using this value will be reported to the caller. Note that the
1897 * value of \a aVrc is inspected only if the user function itself returns
1898 * success.
1899 *
1900 * If a failure happens anywhere before the user function would be normally
1901 * called, it will be called anyway in special "cleanup only" mode indicated
1902 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
1903 * all the function is supposed to do is to cleanup its aUser argument if
1904 * necessary (it's assumed that the ownership of this argument is passed to
1905 * the user function once #startSVCHelperClient() returns a success, thus
1906 * making it responsible for the cleanup).
1907 *
1908 * After the user function returns, the thread will send the SVCHlpMsg::Null
1909 * message to indicate a process termination.
1910 *
1911 * @param aPrivileged |true| to start the SVC Helper process as a privileged
1912 * user that can perform administrative tasks
1913 * @param aFunc user function to run
1914 * @param aUser argument to the user function
1915 * @param aProgress progress object that will track operation completion
1916 *
1917 * @note aPrivileged is currently ignored (due to some unsolved problems in
1918 * Vista) and the process will be started as a normal (unprivileged)
1919 * process.
1920 *
1921 * @note Doesn't lock anything.
1922 */
1923HRESULT VirtualBox::startSVCHelperClient(bool aPrivileged,
1924 SVCHelperClientFunc aFunc,
1925 void *aUser, Progress *aProgress)
1926{
1927 AssertReturn(aFunc, E_POINTER);
1928 AssertReturn(aProgress, E_POINTER);
1929
1930 AutoCaller autoCaller(this);
1931 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1932
1933 /* create the SVCHelperClientThread() argument */
1934 std::auto_ptr <StartSVCHelperClientData>
1935 d(new StartSVCHelperClientData());
1936 AssertReturn(d.get(), E_OUTOFMEMORY);
1937
1938 d->that = this;
1939 d->progress = aProgress;
1940 d->privileged = aPrivileged;
1941 d->func = aFunc;
1942 d->user = aUser;
1943
1944 RTTHREAD tid = NIL_RTTHREAD;
1945 int vrc = RTThreadCreate(&tid, SVCHelperClientThread,
1946 static_cast <void *>(d.get()),
1947 0, RTTHREADTYPE_MAIN_WORKER,
1948 RTTHREADFLAGS_WAITABLE, "SVCHelper");
1949 if (RT_FAILURE(vrc))
1950 return setError(E_FAIL, "Could not create SVCHelper thread (%Rrc)", vrc);
1951
1952 /* d is now owned by SVCHelperClientThread(), so release it */
1953 d.release();
1954
1955 return S_OK;
1956}
1957
1958/**
1959 * Worker thread for startSVCHelperClient().
1960 */
1961/* static */
1962DECLCALLBACK(int)
1963VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser)
1964{
1965 LogFlowFuncEnter();
1966
1967 std::auto_ptr<StartSVCHelperClientData>
1968 d(static_cast<StartSVCHelperClientData*>(aUser));
1969
1970 HRESULT rc = S_OK;
1971 bool userFuncCalled = false;
1972
1973 do
1974 {
1975 AssertBreakStmt(d.get(), rc = E_POINTER);
1976 AssertReturn(!d->progress.isNull(), E_POINTER);
1977
1978 /* protect VirtualBox from uninitialization */
1979 AutoCaller autoCaller(d->that);
1980 if (!autoCaller.isOk())
1981 {
1982 /* it's too late */
1983 rc = autoCaller.rc();
1984 break;
1985 }
1986
1987 int vrc = VINF_SUCCESS;
1988
1989 Guid id;
1990 id.create();
1991 SVCHlpClient client;
1992 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
1993 id.raw()).c_str());
1994 if (RT_FAILURE(vrc))
1995 {
1996 rc = d->that->setError(E_FAIL,
1997 tr("Could not create the communication channel (%Rrc)"), vrc);
1998 break;
1999 }
2000
2001 /* get the path to the executable */
2002 char exePathBuf[RTPATH_MAX];
2003 char *exePath = RTProcGetExecutableName(exePathBuf, RTPATH_MAX);
2004 if (!exePath)
2005 {
2006 rc = d->that->setError(E_FAIL, tr("Cannot get executable name"));
2007 break;
2008 }
2009
2010 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().c_str());
2011
2012 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.c_str()));
2013
2014 RTPROCESS pid = NIL_RTPROCESS;
2015
2016 if (d->privileged)
2017 {
2018 /* Attempt to start a privileged process using the Run As dialog */
2019
2020 Bstr file = exePath;
2021 Bstr parameters = argsStr;
2022
2023 SHELLEXECUTEINFO shExecInfo;
2024
2025 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
2026
2027 shExecInfo.fMask = NULL;
2028 shExecInfo.hwnd = NULL;
2029 shExecInfo.lpVerb = L"runas";
2030 shExecInfo.lpFile = file.raw();
2031 shExecInfo.lpParameters = parameters.raw();
2032 shExecInfo.lpDirectory = NULL;
2033 shExecInfo.nShow = SW_NORMAL;
2034 shExecInfo.hInstApp = NULL;
2035
2036 if (!ShellExecuteEx(&shExecInfo))
2037 {
2038 int vrc2 = RTErrConvertFromWin32(GetLastError());
2039 /* hide excessive details in case of a frequent error
2040 * (pressing the Cancel button to close the Run As dialog) */
2041 if (vrc2 == VERR_CANCELLED)
2042 rc = d->that->setError(E_FAIL,
2043 tr("Operation canceled by the user"));
2044 else
2045 rc = d->that->setError(E_FAIL,
2046 tr("Could not launch a privileged process '%s' (%Rrc)"),
2047 exePath, vrc2);
2048 break;
2049 }
2050 }
2051 else
2052 {
2053 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2054 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
2055 if (RT_FAILURE(vrc))
2056 {
2057 rc = d->that->setError(E_FAIL,
2058 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2059 break;
2060 }
2061 }
2062
2063 /* wait for the client to connect */
2064 vrc = client.connect();
2065 if (RT_SUCCESS(vrc))
2066 {
2067 /* start the user supplied function */
2068 rc = d->func(&client, d->progress, d->user, &vrc);
2069 userFuncCalled = true;
2070 }
2071
2072 /* send the termination signal to the process anyway */
2073 {
2074 int vrc2 = client.write(SVCHlpMsg::Null);
2075 if (RT_SUCCESS(vrc))
2076 vrc = vrc2;
2077 }
2078
2079 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2080 {
2081 rc = d->that->setError(E_FAIL,
2082 tr("Could not operate the communication channel (%Rrc)"), vrc);
2083 break;
2084 }
2085 }
2086 while (0);
2087
2088 if (FAILED(rc) && !userFuncCalled)
2089 {
2090 /* call the user function in the "cleanup only" mode
2091 * to let it free resources passed to in aUser */
2092 d->func(NULL, NULL, d->user, NULL);
2093 }
2094
2095 d->progress->notifyComplete(rc);
2096
2097 LogFlowFuncLeave();
2098 return 0;
2099}
2100
2101#endif /* RT_OS_WINDOWS */
2102
2103/**
2104 * Sends a signal to the client watcher thread to rescan the set of machines
2105 * that have open sessions.
2106 *
2107 * @note Doesn't lock anything.
2108 */
2109void VirtualBox::updateClientWatcher()
2110{
2111 AutoCaller autoCaller(this);
2112 AssertComRCReturnVoid(autoCaller.rc());
2113
2114 AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD);
2115
2116 /* sent an update request */
2117#if defined(RT_OS_WINDOWS)
2118 ::SetEvent(m->updateReq);
2119#elif defined(RT_OS_OS2)
2120 RTSemEventSignal(m->updateReq);
2121#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2122 RTSemEventSignal(m->updateReq);
2123#else
2124# error "Port me!"
2125#endif
2126}
2127
2128/**
2129 * Adds the given child process ID to the list of processes to be reaped.
2130 * This call should be followed by #updateClientWatcher() to take the effect.
2131 */
2132void VirtualBox::addProcessToReap(RTPROCESS pid)
2133{
2134 AutoCaller autoCaller(this);
2135 AssertComRCReturnVoid(autoCaller.rc());
2136
2137 /// @todo (dmik) Win32?
2138#ifndef RT_OS_WINDOWS
2139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2140 m->llProcesses.push_back(pid);
2141#endif
2142}
2143
2144/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2145struct MachineEvent : public VirtualBox::CallbackEvent
2146{
2147 MachineEvent(VirtualBox *aVB, const Guid &aId)
2148 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineDataChanged), id(aId.toUtf16())
2149 {}
2150
2151 MachineEvent(VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2152 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineStateChanged), id(aId.toUtf16())
2153 , state(aState)
2154 {}
2155
2156 MachineEvent(VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2157 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineRegistered), id(aId.toUtf16())
2158 , registered(aRegistered)
2159 {}
2160
2161 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2162 {
2163 switch (mWhat)
2164 {
2165 case VirtualBoxCallbackRegistration::kOnMachineDataChanged:
2166 aEvDesc.init(aSource, VBoxEventType_OnMachineDataChanged, id.raw());
2167 break;
2168
2169 case VirtualBoxCallbackRegistration::kOnMachineStateChanged:
2170 aEvDesc.init(aSource, VBoxEventType_OnMachineStateChanged, id.raw(), state);
2171 break;
2172
2173 case VirtualBoxCallbackRegistration::kOnMachineRegistered:
2174 aEvDesc.init(aSource, VBoxEventType_OnMachineRegistered, id.raw(), registered);
2175 break;
2176
2177 default:
2178 AssertFailedReturn(S_OK);
2179 }
2180 return S_OK;
2181 }
2182
2183 Bstr id;
2184 MachineState_T state;
2185 BOOL registered;
2186};
2187
2188/**
2189 * @note Doesn't lock any object.
2190 */
2191void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2192{
2193 postEvent(new MachineEvent(this, aId, aState));
2194}
2195
2196/**
2197 * @note Doesn't lock any object.
2198 */
2199void VirtualBox::onMachineDataChange(const Guid &aId)
2200{
2201 postEvent(new MachineEvent(this, aId));
2202}
2203
2204/**
2205 * @note Locks this object for reading.
2206 */
2207BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2208 Bstr &aError)
2209{
2210 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2211 aId.toString().c_str(), aKey, aValue));
2212
2213 AutoCaller autoCaller(this);
2214 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2215
2216 BOOL allowChange = TRUE;
2217 Bstr id = aId.toUtf16();
2218
2219 VBoxEventDesc evDesc;
2220 evDesc.init(m->pEventSource, VBoxEventType_OnExtraDataCanChange, id.raw(), aKey, aValue);
2221 BOOL fDelivered = evDesc.fire(3000); /* Wait up to 3 secs for delivery */
2222 //Assert(fDelivered);
2223 if (fDelivered)
2224 {
2225 ComPtr<IEvent> aEvent;
2226 evDesc.getEvent(aEvent.asOutParam());
2227 ComPtr<IExtraDataCanChangeEvent> aCanChangeEvent = aEvent;
2228 Assert(aCanChangeEvent);
2229 BOOL fVetoed = FALSE;
2230 aCanChangeEvent->IsVetoed(&fVetoed);
2231 allowChange = !fVetoed;
2232
2233 if (!allowChange)
2234 {
2235 SafeArray<BSTR> aVetos;
2236 aCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos));
2237 if (aVetos.size() > 0)
2238 aError = aVetos[0];
2239 }
2240 }
2241 else
2242 allowChange = TRUE;
2243
2244 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2245 return allowChange;
2246}
2247
2248/** Event for onExtraDataChange() */
2249struct ExtraDataEvent : public VirtualBox::CallbackEvent
2250{
2251 ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId,
2252 IN_BSTR aKey, IN_BSTR aVal)
2253 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnExtraDataChanged)
2254 , machineId(aMachineId.toUtf16()), key(aKey), val(aVal)
2255 {}
2256
2257 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2258 {
2259 return aEvDesc.init(aSource, VBoxEventType_OnExtraDataChanged, machineId.raw(), key.raw(), val.raw());
2260 }
2261
2262 Bstr machineId, key, val;
2263};
2264
2265/**
2266 * @note Doesn't lock any object.
2267 */
2268void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2269{
2270 postEvent(new ExtraDataEvent(this, aId, aKey, aValue));
2271}
2272
2273/**
2274 * @note Doesn't lock any object.
2275 */
2276void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered)
2277{
2278 postEvent(new MachineEvent(this, aId, aRegistered));
2279}
2280
2281/** Event for onSessionStateChange() */
2282struct SessionEvent : public VirtualBox::CallbackEvent
2283{
2284 SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2285 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnSessionStateChanged)
2286 , machineId(aMachineId.toUtf16()), sessionState(aState)
2287 {}
2288
2289 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2290 {
2291 return aEvDesc.init(aSource, VBoxEventType_OnSessionStateChanged, machineId.raw(), sessionState);
2292 }
2293 Bstr machineId;
2294 SessionState_T sessionState;
2295};
2296
2297/**
2298 * @note Doesn't lock any object.
2299 */
2300void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState)
2301{
2302 postEvent(new SessionEvent(this, aId, aState));
2303}
2304
2305/** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */
2306struct SnapshotEvent : public VirtualBox::CallbackEvent
2307{
2308 SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2309 VirtualBoxCallbackRegistration::CallbackBit aWhat)
2310 : CallbackEvent(aVB, aWhat)
2311 , machineId(aMachineId), snapshotId(aSnapshotId)
2312 {}
2313
2314 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2315 {
2316 return aEvDesc.init(aSource, VBoxEventType_OnSnapshotTaken,
2317 machineId.toUtf16().raw(), snapshotId.toUtf16().raw());
2318 }
2319
2320 Guid machineId;
2321 Guid snapshotId;
2322};
2323
2324/**
2325 * @note Doesn't lock any object.
2326 */
2327void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
2328{
2329 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2330 VirtualBoxCallbackRegistration::kOnSnapshotTaken));
2331}
2332
2333/**
2334 * @note Doesn't lock any object.
2335 */
2336void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
2337{
2338 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2339 VirtualBoxCallbackRegistration::kOnSnapshotDeleted));
2340}
2341
2342/**
2343 * @note Doesn't lock any object.
2344 */
2345void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
2346{
2347 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2348 VirtualBoxCallbackRegistration::kOnSnapshotChanged));
2349}
2350
2351/** Event for onGuestPropertyChange() */
2352struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2353{
2354 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
2355 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2356 : CallbackEvent(aVBox, VirtualBoxCallbackRegistration::kOnGuestPropertyChanged),
2357 machineId(aMachineId),
2358 name(aName),
2359 value(aValue),
2360 flags(aFlags)
2361 {}
2362
2363 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc)
2364 {
2365 return aEvDesc.init(aSource, VBoxEventType_OnGuestPropertyChanged,
2366 machineId.toUtf16().raw(), name.raw(), value.raw(), flags.raw());
2367 }
2368
2369 Guid machineId;
2370 Bstr name, value, flags;
2371};
2372
2373/**
2374 * @note Doesn't lock any object.
2375 */
2376void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
2377 IN_BSTR aValue, IN_BSTR aFlags)
2378{
2379 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
2380}
2381
2382/** Event for onMachineUninit(), this is not a CallbackEvent */
2383class MachineUninitEvent : public Event
2384{
2385public:
2386
2387 MachineUninitEvent(VirtualBox *aVirtualBox, Machine *aMachine)
2388 : mVirtualBox(aVirtualBox), mMachine(aMachine)
2389 {
2390 Assert(aVirtualBox);
2391 Assert(aMachine);
2392 }
2393
2394 void *handler()
2395 {
2396#ifdef VBOX_WITH_RESOURCE_USAGE_API
2397 /* Handle unregistering metrics here, as it is not vital to get
2398 * it done immediately. It reduces the number of locks needed and
2399 * the lock contention in SessionMachine::uninit. */
2400 {
2401 AutoWriteLock mLock(mMachine COMMA_LOCKVAL_SRC_POS);
2402 mMachine->unregisterMetrics(mVirtualBox->performanceCollector(), mMachine);
2403 }
2404#endif /* VBOX_WITH_RESOURCE_USAGE_API */
2405
2406 return NULL;
2407 }
2408
2409private:
2410
2411 /**
2412 * Note that this is a weak ref -- the CallbackEvent handler thread
2413 * is bound to the lifetime of the VirtualBox instance, so it's safe.
2414 */
2415 VirtualBox *mVirtualBox;
2416
2417 /** Reference to the machine object. */
2418 ComObjPtr<Machine> mMachine;
2419};
2420
2421/**
2422 * Trigger internal event. This isn't meant to be signalled to clients.
2423 * @note Doesn't lock any object.
2424 */
2425void VirtualBox::onMachineUninit(Machine *aMachine)
2426{
2427 postEvent(new MachineUninitEvent(this, aMachine));
2428}
2429
2430/**
2431 * @note Locks this object for reading.
2432 */
2433ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2434{
2435 ComObjPtr<GuestOSType> type;
2436 AutoCaller autoCaller(this);
2437 AssertComRCReturn(autoCaller.rc(), type);
2438
2439 /* unknown type must always be the first */
2440 ComAssertRet(m->allGuestOSTypes.size() > 0, type);
2441
2442 return m->allGuestOSTypes.front();
2443}
2444
2445/**
2446 * Returns the list of opened machines (machines having direct sessions opened
2447 * by client processes) and optionally the list of direct session controls.
2448 *
2449 * @param aMachines Where to put opened machines (will be empty if none).
2450 * @param aControls Where to put direct session controls (optional).
2451 *
2452 * @note The returned lists contain smart pointers. So, clear it as soon as
2453 * it becomes no more necessary to release instances.
2454 *
2455 * @note It can be possible that a session machine from the list has been
2456 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2457 * when accessing unprotected data directly.
2458 *
2459 * @note Locks objects for reading.
2460 */
2461void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
2462 InternalControlList *aControls /*= NULL*/)
2463{
2464 AutoCaller autoCaller(this);
2465 AssertComRCReturnVoid(autoCaller.rc());
2466
2467 aMachines.clear();
2468 if (aControls)
2469 aControls->clear();
2470
2471 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2472
2473 for (MachinesOList::iterator it = m->allMachines.begin();
2474 it != m->allMachines.end();
2475 ++it)
2476 {
2477 ComObjPtr<SessionMachine> sm;
2478 ComPtr<IInternalSessionControl> ctl;
2479 if ((*it)->isSessionOpen(sm, &ctl))
2480 {
2481 aMachines.push_back(sm);
2482 if (aControls)
2483 aControls->push_back(ctl);
2484 }
2485 }
2486}
2487
2488/**
2489 * Searches for a machine object with the given ID in the collection
2490 * of registered machines.
2491 *
2492 * @param aId Machine UUID to look for.
2493 * @param aPermitInaccessible If true, inaccessible machines will be found;
2494 * if false, this will fail if the given machine is inaccessible.
2495 * @param aSetError If true, set errorinfo if the machine is not found.
2496 * @param aMachine Returned machine, if found.
2497 * @return
2498 */
2499HRESULT VirtualBox::findMachine(const Guid &aId,
2500 bool fPermitInaccessible,
2501 bool aSetError,
2502 ComObjPtr<Machine> *aMachine /* = NULL */)
2503{
2504 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
2505
2506 AutoCaller autoCaller(this);
2507 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2508
2509 {
2510 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2511
2512 for (MachinesOList::iterator it = m->allMachines.begin();
2513 it != m->allMachines.end();
2514 ++it)
2515 {
2516 ComObjPtr<Machine> pMachine2 = *it;
2517
2518 if (!fPermitInaccessible)
2519 {
2520 // skip inaccessible machines
2521 AutoCaller machCaller(pMachine2);
2522 if (FAILED(machCaller.rc()))
2523 continue;
2524 }
2525
2526 if (pMachine2->getId() == aId)
2527 {
2528 rc = S_OK;
2529 if (aMachine)
2530 *aMachine = pMachine2;
2531 break;
2532 }
2533 }
2534 }
2535
2536 if (aSetError && FAILED(rc))
2537 rc = setError(rc,
2538 tr("Could not find a registered machine with UUID {%RTuuid}"),
2539 aId.raw());
2540
2541 return rc;
2542}
2543
2544/**
2545 * Searches for a Medium object with the given ID in the list of registered
2546 * hard disks.
2547 *
2548 * @param aId ID of the hard disk. Must not be empty.
2549 * @param aSetError If @c true , the appropriate error info is set in case
2550 * when the hard disk is not found.
2551 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2552 *
2553 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2554 *
2555 * @note Locks the media tree for reading.
2556 */
2557HRESULT VirtualBox::findHardDiskById(const Guid &id,
2558 bool aSetError,
2559 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2560{
2561 AssertReturn(!id.isEmpty(), E_INVALIDARG);
2562
2563 // we use the hard disks map, but it is protected by the
2564 // hard disk _list_ lock handle
2565 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2566
2567 HardDiskMap::const_iterator it = m->mapHardDisks.find(id);
2568 if (it != m->mapHardDisks.end())
2569 {
2570 if (aHardDisk)
2571 *aHardDisk = (*it).second;
2572 return S_OK;
2573 }
2574
2575 if (aSetError)
2576 return setError(VBOX_E_OBJECT_NOT_FOUND,
2577 tr("Could not find an open hard disk with UUID {%RTuuid}"),
2578 id.raw());
2579
2580 return VBOX_E_OBJECT_NOT_FOUND;
2581}
2582
2583/**
2584 * Searches for a Medium object with the given ID or location in the list of
2585 * registered hard disks. If both ID and location are specified, the first
2586 * object that matches either of them (not necessarily both) is returned.
2587 *
2588 * @param aLocation Full location specification. Must not be empty.
2589 * @param aSetError If @c true , the appropriate error info is set in case
2590 * when the hard disk is not found.
2591 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2592 *
2593 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2594 *
2595 * @note Locks the media tree for reading.
2596 */
2597HRESULT VirtualBox::findHardDiskByLocation(const Utf8Str &strLocation,
2598 bool aSetError,
2599 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2600{
2601 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG);
2602
2603 // we use the hard disks map, but it is protected by the
2604 // hard disk _list_ lock handle
2605 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2606
2607 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
2608 it != m->mapHardDisks.end();
2609 ++it)
2610 {
2611 const ComObjPtr<Medium> &pHD = (*it).second;
2612
2613 AutoCaller autoCaller(pHD);
2614 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2615 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
2616
2617 Utf8Str strLocationFull = pHD->getLocationFull();
2618
2619 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str()))
2620 {
2621 if (aHardDisk)
2622 *aHardDisk = pHD;
2623 return S_OK;
2624 }
2625 }
2626
2627 if (aSetError)
2628 return setError(VBOX_E_OBJECT_NOT_FOUND,
2629 tr("Could not find an open hard disk with location '%s'"),
2630 strLocation.c_str());
2631
2632 return VBOX_E_OBJECT_NOT_FOUND;
2633}
2634
2635/**
2636 * Searches for a Medium object with the given ID or location in the list of
2637 * registered DVD or floppy images, depending on the @a mediumType argument.
2638 * If both ID and file path are specified, the first object that matches either
2639 * of them (not necessarily both) is returned.
2640 *
2641 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy.
2642 * @param aId ID of the image file (unused when NULL).
2643 * @param aLocation Full path to the image file (unused when NULL).
2644 * @param aSetError If @c true, the appropriate error info is set in case when
2645 * the image is not found.
2646 * @param aImage Where to store the found image object (can be NULL).
2647 *
2648 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found.
2649 *
2650 * @note Locks the media tree for reading.
2651 */
2652HRESULT VirtualBox::findDVDOrFloppyImage(DeviceType_T mediumType,
2653 const Guid *aId,
2654 const Utf8Str &aLocation,
2655 bool aSetError,
2656 ComObjPtr<Medium> *aImage /* = NULL */)
2657{
2658 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG);
2659
2660 Utf8Str location;
2661 if (!aLocation.isEmpty())
2662 {
2663 int vrc = calculateFullPath(aLocation, location);
2664 if (RT_FAILURE(vrc))
2665 return setError(VBOX_E_FILE_ERROR,
2666 tr("Invalid image file location '%ls' (%Rrc)"),
2667 aLocation.c_str(),
2668 vrc);
2669 }
2670
2671 MediaOList *pMediaList;
2672
2673 switch (mediumType)
2674 {
2675 case DeviceType_DVD:
2676 pMediaList = &m->allDVDImages;
2677 break;
2678
2679 case DeviceType_Floppy:
2680 pMediaList = &m->allFloppyImages;
2681 break;
2682
2683 default:
2684 return E_INVALIDARG;
2685 }
2686
2687 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS);
2688
2689 bool found = false;
2690
2691 for (MediaList::const_iterator it = pMediaList->begin();
2692 it != pMediaList->end();
2693 ++it)
2694 {
2695 // no AutoCaller, registered image life time is bound to this
2696 Medium *pMedium = *it;
2697 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS);
2698 const Utf8Str &strLocationFull = pMedium->getLocationFull();
2699
2700 found = ( aId
2701 && pMedium->getId() == *aId)
2702 || ( !aLocation.isEmpty()
2703 && RTPathCompare(location.c_str(),
2704 strLocationFull.c_str()) == 0);
2705 if (found)
2706 {
2707 if (pMedium->getDeviceType() != mediumType)
2708 {
2709 if (mediumType == DeviceType_DVD)
2710 return setError(E_INVALIDARG,
2711 "Cannot mount DVD medium '%s' as floppy", strLocationFull.c_str());
2712 else
2713 return setError(E_INVALIDARG,
2714 "Cannot mount floppy medium '%s' as DVD", strLocationFull.c_str());
2715 }
2716
2717 if (aImage)
2718 *aImage = pMedium;
2719 break;
2720 }
2721 }
2722
2723 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2724
2725 if (aSetError && !found)
2726 {
2727 if (aId)
2728 setError(rc,
2729 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"),
2730 aId->raw(),
2731 m->strSettingsFilePath.c_str());
2732 else
2733 setError(rc,
2734 tr("Could not find an image file with location '%ls' in the media registry ('%s')"),
2735 aLocation.c_str(),
2736 m->strSettingsFilePath.c_str());
2737 }
2738
2739 return rc;
2740}
2741
2742/**
2743 * Searches for an IMedium object that represents the given UUID.
2744 *
2745 * If the UUID is empty (indicating an empty drive), this sets pMedium
2746 * to NULL and returns S_OK.
2747 *
2748 * If the UUID refers to a host drive of the given device type, this
2749 * sets pMedium to the object from the list in IHost and returns S_OK.
2750 *
2751 * If the UUID is an image file, this sets pMedium to the object that
2752 * findDVDOrFloppyImage() returned.
2753 *
2754 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND.
2755 *
2756 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy.
2757 * @param uuid UUID to search for; must refer to a host drive or an image file or be null.
2758 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives())
2759 * @param pMedium out: IMedium object found.
2760 * @return
2761 */
2762HRESULT VirtualBox::findRemoveableMedium(DeviceType_T mediumType,
2763 const Guid &uuid,
2764 bool fRefresh,
2765 ComObjPtr<Medium> &pMedium)
2766{
2767 if (uuid.isEmpty())
2768 {
2769 // that's easy
2770 pMedium.setNull();
2771 return S_OK;
2772 }
2773
2774 // first search for host drive with that UUID
2775 HRESULT rc = m->pHost->findHostDrive(mediumType,
2776 uuid,
2777 fRefresh,
2778 pMedium);
2779 if (rc == VBOX_E_OBJECT_NOT_FOUND)
2780 // then search for an image with that UUID
2781 rc = findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, true /* aSetError */, &pMedium);
2782
2783 return rc;
2784}
2785
2786HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
2787 GuestOSType*& pGuestOSType)
2788{
2789 /* Look for a GuestOSType object */
2790 AssertMsg(m->allGuestOSTypes.size() != 0,
2791 ("Guest OS types array must be filled"));
2792
2793 if (bstrOSType.isEmpty())
2794 {
2795 pGuestOSType = NULL;
2796 return S_OK;
2797 }
2798
2799 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2800 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin();
2801 it != m->allGuestOSTypes.end();
2802 ++it)
2803 {
2804 if ((*it)->id() == bstrOSType)
2805 {
2806 pGuestOSType = *it;
2807 return S_OK;
2808 }
2809 }
2810
2811 return setError(VBOX_E_OBJECT_NOT_FOUND,
2812 tr("Guest OS type '%ls' is invalid"),
2813 bstrOSType.raw());
2814}
2815
2816/**
2817 * Returns the pseudo-machine UUID that is created on each VirtualBox startup
2818 * and that is used to identify the global media registry.
2819 *
2820 * Starting with VirtualBox 4.0 each medium remembers in its instance data
2821 * in which media registry it is saved (if any): this can either be a machine
2822 * UUID, if it's in a per-machine media registry, or this global ID.
2823 *
2824 * This UUID is only used to identify the VirtualBox object while VirtualBox
2825 * is running. It is not saved anywhere and thus gets recreated with every
2826 * VirtualBox startup.
2827 *
2828 * @return
2829 */
2830const Guid& VirtualBox::getGlobalRegistryId() const
2831{
2832 return m->uuidMediaRegistry;
2833}
2834
2835const ComObjPtr<Host>& VirtualBox::host() const
2836{
2837 return m->pHost;
2838}
2839
2840SystemProperties* VirtualBox::getSystemProperties() const
2841{
2842 return m->pSystemProperties;
2843}
2844
2845#ifdef VBOX_WITH_RESOURCE_USAGE_API
2846const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
2847{
2848 return m->pPerformanceCollector;
2849}
2850#endif /* VBOX_WITH_RESOURCE_USAGE_API */
2851
2852/**
2853 * Returns the default machine folder from the system properties
2854 * with proper locking.
2855 * @return
2856 */
2857void VirtualBox::getDefaultMachineFolder(Utf8Str &str) const
2858{
2859 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2860 str = m->pSystemProperties->m->strDefaultMachineFolder;
2861}
2862
2863/**
2864 * Returns the default hard disk format from the system properties
2865 * with proper locking.
2866 * @return
2867 */
2868void VirtualBox::getDefaultHardDiskFormat(Utf8Str &str) const
2869{
2870 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
2871 str = m->pSystemProperties->m->strDefaultHardDiskFormat;
2872}
2873
2874const Utf8Str& VirtualBox::homeDir() const
2875{
2876 return m->strHomeDir;
2877}
2878
2879/**
2880 * Calculates the absolute path of the given path taking the VirtualBox home
2881 * directory as the current directory.
2882 *
2883 * @param aPath Path to calculate the absolute path for.
2884 * @param aResult Where to put the result (used only on success, can be the
2885 * same Utf8Str instance as passed in @a aPath).
2886 * @return IPRT result.
2887 *
2888 * @note Doesn't lock any object.
2889 */
2890int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
2891{
2892 AutoCaller autoCaller(this);
2893 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
2894
2895 /* no need to lock since mHomeDir is const */
2896
2897 char folder[RTPATH_MAX];
2898 int vrc = RTPathAbsEx(m->strHomeDir.c_str(),
2899 strPath.c_str(),
2900 folder,
2901 sizeof(folder));
2902 if (RT_SUCCESS(vrc))
2903 aResult = folder;
2904
2905 return vrc;
2906}
2907
2908/**
2909 * Copies strSource to strTarget, making it relative to the VirtualBox config folder
2910 * if it is a subdirectory thereof, or simply copying it otherwise.
2911 *
2912 * @param strSource Path to evalue and copy.
2913 * @param strTarget Buffer to receive target path.
2914 */
2915void VirtualBox::copyPathRelativeToConfig(const Utf8Str &strSource,
2916 Utf8Str &strTarget)
2917{
2918 AutoCaller autoCaller(this);
2919 AssertComRCReturnVoid(autoCaller.rc());
2920
2921 // no need to lock since mHomeDir is const
2922
2923 // use strTarget as a temporary buffer to hold the machine settings dir
2924 strTarget = m->strHomeDir;
2925 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
2926 // is relative: then append what's left
2927 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'
2928 else
2929 // is not relative: then overwrite
2930 strTarget = strSource;
2931}
2932
2933// private methods
2934/////////////////////////////////////////////////////////////////////////////
2935
2936/**
2937 * Checks if there is a hard disk, DVD or floppy image with the given ID or
2938 * location already registered.
2939 *
2940 * On return, sets @a aConflict to the string describing the conflicting medium,
2941 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
2942 * either case. A failure is unexpected.
2943 *
2944 * @param aId UUID to check.
2945 * @param aLocation Location to check.
2946 * @param aConflict Where to return parameters of the conflicting medium.
2947 *
2948 * @note Locks the media tree and media objects for reading.
2949 */
2950HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId,
2951 const Utf8Str &aLocation,
2952 Utf8Str &aConflict,
2953 bool &fIdentical)
2954{
2955 aConflict.setNull();
2956
2957 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
2958
2959 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2960
2961 HRESULT rc = S_OK;
2962
2963 aConflict.setNull();
2964 fIdentical = false;
2965
2966 ComObjPtr<Medium> pMediumFound;
2967 const char *pcszType = NULL;
2968
2969 if (!aId.isEmpty())
2970 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound);
2971 if (FAILED(rc) && !aLocation.isEmpty())
2972 rc = findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound);
2973 if (SUCCEEDED(rc))
2974 pcszType = tr("hard disk");
2975
2976 if (!pcszType)
2977 {
2978 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound);
2979 if (SUCCEEDED(rc))
2980 pcszType = tr("CD/DVD image");
2981 }
2982
2983 if (!pcszType)
2984 {
2985 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound);
2986 if (SUCCEEDED(rc))
2987 pcszType = tr("floppy image");
2988 }
2989
2990 if (pcszType && pMediumFound)
2991 {
2992 /* Note: no AutoCaller since bound to this */
2993 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS);
2994
2995 Utf8Str strLocFound = pMediumFound->getLocationFull();
2996 Guid idFound = pMediumFound->getId();
2997
2998 if ( (strLocFound == aLocation)
2999 && (idFound == aId)
3000 )
3001 fIdentical = true;
3002
3003 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"),
3004 pcszType,
3005 strLocFound.c_str(),
3006 idFound.raw());
3007 }
3008
3009 return S_OK;
3010}
3011
3012/**
3013 * Called from Machine::prepareSaveSettings() when it has detected
3014 * that a machine has been renamed. Such renames will require
3015 * updating the global media registry during the
3016 * VirtualBox::saveSettings() that follows later.
3017*
3018 * When a machine is renamed, there may well be media (in particular,
3019 * diff images for snapshots) in the global registry that will need
3020 * to have their paths updated. Before 3.2, Machine::saveSettings
3021 * used to call VirtualBox::saveSettings implicitly, which was both
3022 * unintuitive and caused locking order problems. Now, we remember
3023 * such pending name changes with this method so that
3024 * VirtualBox::saveSettings() can process them properly.
3025 */
3026void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3027 const Utf8Str &strNewConfigDir)
3028{
3029 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3030
3031 Data::PendingMachineRename pmr;
3032 pmr.strConfigDirOld = strOldConfigDir;
3033 pmr.strConfigDirNew = strNewConfigDir;
3034 m->llPendingMachineRenames.push_back(pmr);
3035}
3036
3037/**
3038 * Goes through all known media (hard disks, floppies and DVDs) and saves
3039 * those into the given settings::MediaRegistry structures whose registry
3040 * ID match the given UUID.
3041 *
3042 * This gets called from two contexts:
3043 *
3044 * -- VirtualBox::saveSettings() with the UUID of the global registry
3045 * (VirtualBox::Data.uuidRegistry); this will save those media
3046 * which had been loaded from the global registry or have been
3047 * attached to a "legacy" machine which can't save its own registry;
3048 *
3049 * -- Machine::saveSettings() with the UUID of a machine, if a medium
3050 * has been attached to a machine created with VirtualBox 4.0 or later.
3051 *
3052 * Media which have only been temporarily opened without having been
3053 * attached to a machine have a NULL registry UUID and therefore don't
3054 * get saved.
3055 *
3056 * This throws HRESULT on errors!
3057 *
3058 * @param mediaRegistry Settings structure to fill.
3059 * @param uuidRegistry The UUID of the media registry; either a machine UUID (if machine registry) or the UUID of the global registry.
3060 * @param hardDiskFolder The machine folder for relative paths, if machine registry, or an empty string otherwise.
3061 */
3062void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry,
3063 const Guid &uuidRegistry,
3064 const Utf8Str &strMachineFolder)
3065{
3066 HRESULT rc;
3067 // hard disks
3068 mediaRegistry.llHardDisks.clear();
3069 for (MediaList::const_iterator it = m->allHardDisks.begin();
3070 it != m->allHardDisks.end();
3071 ++it)
3072 {
3073 Medium *pMedium = *it;
3074 if (pMedium->isInRegistry(uuidRegistry))
3075 {
3076 settings::Medium med;
3077 rc = pMedium->saveSettings(med, strMachineFolder); // this recurses into its children
3078 if (FAILED(rc)) throw rc;
3079 mediaRegistry.llHardDisks.push_back(med);
3080 }
3081 }
3082
3083 // CD/DVD images
3084 mediaRegistry.llDvdImages.clear();
3085 for (MediaList::const_iterator it = m->allDVDImages.begin();
3086 it != m->allDVDImages.end();
3087 ++it)
3088 {
3089 Medium *pMedium = *it;
3090 if (pMedium->isInRegistry(uuidRegistry))
3091 {
3092 settings::Medium med;
3093 rc = pMedium->saveSettings(med, strMachineFolder);
3094 if (FAILED(rc)) throw rc;
3095 mediaRegistry.llDvdImages.push_back(med);
3096 }
3097 }
3098
3099 // floppy images
3100 mediaRegistry.llFloppyImages.clear();
3101 for (MediaList::const_iterator it = m->allFloppyImages.begin();
3102 it != m->allFloppyImages.end();
3103 ++it)
3104 {
3105 Medium *pMedium = *it;
3106 if (pMedium->isInRegistry(uuidRegistry))
3107 {
3108 settings::Medium med;
3109 rc = pMedium->saveSettings(med, strMachineFolder);
3110 if (FAILED(rc)) throw rc;
3111 mediaRegistry.llFloppyImages.push_back(med);
3112 }
3113 }
3114}
3115
3116/**
3117 * Helper function which actually writes out VirtualBox.xml, the main configuration file.
3118 * Gets called from the public VirtualBox::SaveSettings() as well as from various other
3119 * places internally when settings need saving.
3120 *
3121 * @note Caller must have locked the VirtualBox object for writing and must not hold any
3122 * other locks since this locks all kinds of member objects and trees temporarily,
3123 * which could cause conflicts.
3124 */
3125HRESULT VirtualBox::saveSettings()
3126{
3127 AutoCaller autoCaller(this);
3128 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3129
3130 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3131 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3132
3133 HRESULT rc = S_OK;
3134
3135 try
3136 {
3137 // machines
3138 m->pMainConfigFile->llMachines.clear();
3139 {
3140 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3141 for (MachinesOList::iterator it = m->allMachines.begin();
3142 it != m->allMachines.end();
3143 ++it)
3144 {
3145 Machine *pMachine = *it;
3146 // save actual machine registry entry
3147 settings::MachineRegistryEntry mre;
3148 rc = pMachine->saveRegistryEntry(mre);
3149 m->pMainConfigFile->llMachines.push_back(mre);
3150 }
3151 }
3152
3153 // lock all media for the following; use a write lock because we're
3154 // modifying the PendingMachineRenamesList, which is protected by this
3155 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3156
3157 // if a machine was renamed, then we'll need to refresh media paths
3158 if (m->llPendingMachineRenames.size())
3159 {
3160 // make a single list from the three media lists so we don't need three loops
3161 MediaList llAllMedia;
3162 // with hard disks, we must use the map, not the list, because the list only has base images
3163 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3164 llAllMedia.push_back(it->second);
3165 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it)
3166 llAllMedia.push_back(*it);
3167 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it)
3168 llAllMedia.push_back(*it);
3169
3170 for (MediaList::iterator it = llAllMedia.begin();
3171 it != llAllMedia.end();
3172 ++it)
3173 {
3174 Medium *pMedium = *it;
3175 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3176 it2 != m->llPendingMachineRenames.end();
3177 ++it2)
3178 {
3179 // is medium in global registry:?
3180 if (pMedium->isInRegistry(m->uuidMediaRegistry))
3181 {
3182 const Data::PendingMachineRename &pmr = *it2;
3183 pMedium->updatePath(pmr.strConfigDirOld,
3184 pmr.strConfigDirNew);
3185 }
3186 }
3187 }
3188 // done, don't do it again until we have more machine renames
3189 m->llPendingMachineRenames.clear();
3190 }
3191
3192 saveMediaRegistry(m->pMainConfigFile->mediaRegistry,
3193 m->uuidMediaRegistry, // global media registry ID
3194 Utf8Str::Empty); // strMachineFolder
3195
3196 mediaLock.release();
3197
3198 m->pMainConfigFile->llDhcpServers.clear();
3199 {
3200 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3201 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
3202 it != m->allDHCPServers.end();
3203 ++it)
3204 {
3205 settings::DHCPServer d;
3206 rc = (*it)->saveSettings(d);
3207 if (FAILED(rc)) throw rc;
3208 m->pMainConfigFile->llDhcpServers.push_back(d);
3209 }
3210 }
3211
3212 // leave extra data alone, it's still in the config file
3213
3214 // host data (USB filters)
3215 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3216 if (FAILED(rc)) throw rc;
3217
3218 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3219 if (FAILED(rc)) throw rc;
3220
3221 // and write out the XML, still under the lock
3222 m->pMainConfigFile->write(m->strSettingsFilePath);
3223 }
3224 catch (HRESULT err)
3225 {
3226 /* we assume that error info is set by the thrower */
3227 rc = err;
3228 }
3229 catch (...)
3230 {
3231 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3232 }
3233
3234 return rc;
3235}
3236
3237/**
3238 * Helper to register the machine.
3239 *
3240 * When called during VirtualBox startup, adds the given machine to the
3241 * collection of registered machines. Otherwise tries to mark the machine
3242 * as registered, and, if succeeded, adds it to the collection and
3243 * saves global settings.
3244 *
3245 * @note The caller must have added itself as a caller of the @a aMachine
3246 * object if calls this method not on VirtualBox startup.
3247 *
3248 * @param aMachine machine to register
3249 *
3250 * @note Locks objects!
3251 */
3252HRESULT VirtualBox::registerMachine(Machine *aMachine)
3253{
3254 ComAssertRet(aMachine, E_INVALIDARG);
3255
3256 AutoCaller autoCaller(this);
3257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3258
3259 HRESULT rc = S_OK;
3260
3261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3262
3263 {
3264 ComObjPtr<Machine> pMachine;
3265 rc = findMachine(aMachine->getId(),
3266 true /* fPermitInaccessible */,
3267 false /* aDoSetError */,
3268 &pMachine);
3269 if (SUCCEEDED(rc))
3270 {
3271 /* sanity */
3272 AutoLimitedCaller machCaller(pMachine);
3273 AssertComRC(machCaller.rc());
3274
3275 return setError(E_INVALIDARG,
3276 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"),
3277 aMachine->getId().raw(),
3278 pMachine->getSettingsFileFull().c_str());
3279 }
3280
3281 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3282 rc = S_OK;
3283 }
3284
3285 if (autoCaller.state() != InInit)
3286 {
3287 rc = aMachine->prepareRegister();
3288 if (FAILED(rc)) return rc;
3289 }
3290
3291 /* add to the collection of registered machines */
3292 m->allMachines.addChild(aMachine);
3293
3294 if (autoCaller.state() != InInit)
3295 rc = saveSettings();
3296
3297 return rc;
3298}
3299
3300/**
3301 * Remembers the given hard disk by storing it in either the global hard disk registry
3302 * or a machine one.
3303 *
3304 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3305 *
3306 * @param aHardDisk Hard disk object to remember.
3307 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3308 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3309 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3310 * @return
3311 */
3312HRESULT VirtualBox::registerHardDisk(Medium *pMedium,
3313 bool *pfNeedsGlobalSaveSettings)
3314{
3315 AssertReturn(pMedium != NULL, E_INVALIDARG);
3316
3317 AutoCaller autoCaller(this);
3318 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3319
3320 AutoCaller hardDiskCaller(pMedium);
3321 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3322
3323 // caller must hold the media tree write lock
3324 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3325
3326 Guid id;
3327 Utf8Str strLocationFull;
3328 ComObjPtr<Medium> pParent;
3329 {
3330 AutoReadLock hardDiskLock(pMedium COMMA_LOCKVAL_SRC_POS);
3331 id = pMedium->getId();
3332 strLocationFull = pMedium->getLocationFull();
3333 pParent = pMedium->getParent();
3334 }
3335
3336 HRESULT rc;
3337
3338 Utf8Str strConflict;
3339 bool fIdentical;
3340 rc = checkMediaForConflicts(id,
3341 strLocationFull,
3342 strConflict,
3343 fIdentical);
3344 if (FAILED(rc)) return rc;
3345
3346 if (!fIdentical)
3347 {
3348 if (strConflict.length())
3349 return setError(E_INVALIDARG,
3350 tr("Cannot register the hard disk '%s' {%RTuuid} because a %s already exists"),
3351 strLocationFull.c_str(),
3352 id.raw(),
3353 strConflict.c_str(),
3354 m->strSettingsFilePath.c_str());
3355
3356 // store base (root) hard disks in the list
3357 if (pParent.isNull())
3358 m->allHardDisks.getList().push_back(pMedium);
3359 // access the list directly because we already locked the list above
3360
3361 // store all hard disks (even differencing images) in the map
3362 m->mapHardDisks[id] = pMedium;
3363
3364 if (pfNeedsGlobalSaveSettings)
3365 // global settings need saving only if the medium is to be saved in the global registry
3366 if (pMedium->isInRegistry(m->uuidMediaRegistry))
3367 *pfNeedsGlobalSaveSettings = true;
3368 }
3369
3370 return rc;
3371}
3372
3373/**
3374 * Removes the given hard disk from the hard disk registry.
3375 *
3376 * @param aHardDisk Hard disk object to remove.
3377 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3378 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3379 *
3380 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3381 */
3382HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3383 bool *pfNeedsGlobalSaveSettings)
3384{
3385 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3386
3387 AutoCaller autoCaller(this);
3388 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3389
3390 AutoCaller hardDiskCaller(aHardDisk);
3391 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3392
3393 // caller must hold the media tree write lock
3394 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3395
3396 Guid id;
3397 ComObjPtr<Medium> pParent;
3398 {
3399 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3400 id = aHardDisk->getId();
3401 pParent = aHardDisk->getParent();
3402 }
3403
3404 // remove base (root) hard disks from the list
3405 if (pParent.isNull())
3406 m->allHardDisks.getList().remove(aHardDisk);
3407 // access the list directly because caller must have locked the list
3408
3409 // remove all hard disks (even differencing images) from map
3410 size_t cnt = m->mapHardDisks.erase(id);
3411 Assert(cnt == 1);
3412 NOREF(cnt);
3413
3414 if (pfNeedsGlobalSaveSettings)
3415 // global settings need saving only if the medium is to be saved in the global registry
3416 if (aHardDisk->isInRegistry(m->uuidMediaRegistry))
3417 *pfNeedsGlobalSaveSettings = true;
3418
3419 return S_OK;
3420}
3421
3422/**
3423 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3424 *
3425 * @param argImage Image object to remember.
3426 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3427 * @param uuidMachineRegistry UUID of machine whose registry should be used, or a NULL UUID for the global registry.
3428 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3429 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3430 *
3431 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3432 */
3433HRESULT VirtualBox::registerImage(Medium *pMedium,
3434 DeviceType_T argType,
3435 bool *pfNeedsGlobalSaveSettings)
3436{
3437 AssertReturn(pMedium != NULL, E_INVALIDARG);
3438
3439 AutoCaller autoCaller(this);
3440 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3441
3442 AutoCaller imageCaller(pMedium);
3443 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3444
3445 // caller must hold the media tree write lock
3446 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3447
3448 Guid id;
3449 Utf8Str strLocationFull;
3450 ComObjPtr<Medium> pParent;
3451 {
3452 AutoReadLock al(pMedium COMMA_LOCKVAL_SRC_POS);
3453 id = pMedium->getId();
3454 strLocationFull = pMedium->getLocationFull();
3455 pParent = pMedium->getParent();
3456 }
3457
3458 // work on DVDs or floppies list?
3459 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3460
3461 HRESULT rc;
3462 // lock the images lists (list + map) while checking for conflicts
3463 AutoWriteLock al(all.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3464
3465 Utf8Str strConflict;
3466 bool fIdentical;
3467 rc = checkMediaForConflicts(id,
3468 strLocationFull,
3469 strConflict,
3470 fIdentical);
3471 if (FAILED(rc)) return rc;
3472
3473 if (!fIdentical)
3474 {
3475 if (strConflict.length())
3476 return setError(VBOX_E_INVALID_OBJECT_STATE,
3477 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists"),
3478 strLocationFull.c_str(),
3479 id.raw(),
3480 strConflict.c_str());
3481
3482 // add to the collection
3483 all.getList().push_back(pMedium);
3484 // access the list directly because we already locked the list above
3485
3486 if (pfNeedsGlobalSaveSettings)
3487 // global settings need saving only if the medium is to be saved in the global registry
3488 if (pMedium->isInRegistry(m->uuidMediaRegistry))
3489 *pfNeedsGlobalSaveSettings = true;
3490 }
3491
3492 return rc;
3493}
3494
3495/**
3496 * Removes the given image from the CD/DVD or floppy image registry.
3497 *
3498 * @param argImage Image object to remove.
3499 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3500 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3501 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3502 *
3503 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3504 */
3505HRESULT VirtualBox::unregisterImage(Medium *argImage,
3506 DeviceType_T argType,
3507 bool *pfNeedsGlobalSaveSettings)
3508{
3509 AssertReturn(argImage != NULL, E_INVALIDARG);
3510
3511 AutoCaller autoCaller(this);
3512 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3513
3514 AutoCaller imageCaller(argImage);
3515 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3516
3517 // caller must hold the media tree write lock
3518 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3519
3520 Guid id;
3521 ComObjPtr<Medium> pParent;
3522 {
3523 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3524 id = argImage->getId();
3525 pParent = argImage->getParent();
3526 }
3527
3528 // work on DVDs or floppies list?
3529 ObjectsList<Medium> &all = (argType == DeviceType_DVD) ? m->allDVDImages : m->allFloppyImages;
3530
3531 // access the list directly because the caller must have requested the lock
3532 all.getList().remove(argImage);
3533
3534 HRESULT rc = S_OK;
3535
3536 if (pfNeedsGlobalSaveSettings)
3537 // global settings need saving only if the medium is to be saved in the global registry
3538 if (argImage->isInRegistry(m->uuidMediaRegistry))
3539 *pfNeedsGlobalSaveSettings = true;
3540
3541 return rc;
3542}
3543
3544/**
3545 * Little helper called from unregisterMachineMedia() to recursively add media to the given list,
3546 * with children appearing before their parents.
3547 * @param llMedia
3548 * @param pMedium
3549 */
3550void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium)
3551{
3552 // recurse first, then add ourselves; this way children end up on the
3553 // list before their parents
3554
3555 const MediaList &llChildren = pMedium->getChildren();
3556 for (MediaList::const_iterator it = llChildren.begin();
3557 it != llChildren.end();
3558 ++it)
3559 {
3560 Medium *pChild = *it;
3561 pushMediumToListWithChildren(llMedia, pChild);
3562 }
3563
3564 Log(("Pushing medium %RTuuid\n", pMedium->getId().raw()));
3565 llMedia.push_back(pMedium);
3566}
3567
3568/**
3569 * Unregisters all Medium objects which belong to the given machine registry.
3570 * Gets called from Machine::uninit() just before the machine object dies
3571 * and must only be called with a machine UUID as the registry ID.
3572 *
3573 * Locks the media tree.
3574 *
3575 * @param uuidMachine Medium registry ID (always a machine UUID)
3576 * @return
3577 */
3578HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine)
3579{
3580 Assert(!uuidMachine.isEmpty());
3581
3582 LogFlowFuncEnter();
3583
3584 AutoCaller autoCaller(this);
3585 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3586
3587 MediaList llMedia2Close;
3588
3589 {
3590 AutoWriteLock mlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3591 for (MediaOList::iterator it = m->allHardDisks.getList().begin();
3592 it != m->allHardDisks.getList().end();
3593 ++it)
3594 {
3595 ComObjPtr<Medium> pMedium = *it;
3596
3597 if (pMedium->isInRegistry(uuidMachine))
3598 // recursively with children first
3599 pushMediumToListWithChildren(llMedia2Close, pMedium);
3600 }
3601 }
3602
3603 for (MediaList::iterator it = llMedia2Close.begin();
3604 it != llMedia2Close.end();
3605 ++it)
3606 {
3607 ComObjPtr<Medium> pMedium = *it;
3608 Log(("Closing medium %RTuuid\n", pMedium->getId().raw()));
3609 AutoCaller mac(pMedium);
3610 pMedium->close(NULL /* pfNeedsGlobalSaveSettings*/, mac);
3611 }
3612
3613 LogFlowFuncLeave();
3614
3615 return S_OK;
3616}
3617
3618/**
3619 * Removes the given machine object from the internal list of registered machines.
3620 * Called from Machine::Unregister().
3621 * @param pMachine
3622 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time.
3623 * @return
3624 */
3625HRESULT VirtualBox::unregisterMachine(Machine *pMachine,
3626 const Guid &id)
3627{
3628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3629
3630 // remove from the collection of registered machines
3631 m->allMachines.removeChild(pMachine);
3632
3633 // save the global registry
3634 HRESULT rc = saveSettings();
3635
3636 alock.release();
3637
3638 /* fire an event */
3639 onMachineRegistered(id, FALSE);
3640
3641 return rc;
3642}
3643
3644/**
3645 * Creates the path to the specified file according to the path information
3646 * present in the file name.
3647 *
3648 * Note that the given file name must contain the full path otherwise the
3649 * extracted relative path will be created based on the current working
3650 * directory which is normally unknown.
3651 *
3652 * @param aFileName Full file name which path needs to be created.
3653 *
3654 * @return Extended error information on failure to create the path.
3655 */
3656/* static */
3657HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3658{
3659 Utf8Str strDir(strFileName);
3660 strDir.stripFilename();
3661 if (!RTDirExists(strDir.c_str()))
3662 {
3663 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3664 if (RT_FAILURE(vrc))
3665 return setErrorStatic(E_FAIL,
3666 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"),
3667 strDir.c_str(),
3668 vrc));
3669 }
3670
3671 return S_OK;
3672}
3673
3674/**
3675 * Handles unexpected exceptions by turning them into COM errors in release
3676 * builds or by hitting a breakpoint in the release builds.
3677 *
3678 * Usage pattern:
3679 * @code
3680 try
3681 {
3682 // ...
3683 }
3684 catch (LaLalA)
3685 {
3686 // ...
3687 }
3688 catch (...)
3689 {
3690 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3691 }
3692 * @endcode
3693 *
3694 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3695 */
3696/* static */
3697HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3698{
3699 try
3700 {
3701 /* re-throw the current exception */
3702 throw;
3703 }
3704 catch (const iprt::Error &err) // includes all XML exceptions
3705 {
3706 return setErrorStatic(E_FAIL,
3707 Utf8StrFmt(tr("%s.\n%s[%d] (%s)"),
3708 err.what(),
3709 pszFile, iLine, pszFunction).c_str());
3710 }
3711 catch (const std::exception &err)
3712 {
3713 return setErrorStatic(E_FAIL,
3714 Utf8StrFmt(tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3715 err.what(), typeid(err).name(),
3716 pszFile, iLine, pszFunction).c_str());
3717 }
3718 catch (...)
3719 {
3720 return setErrorStatic(E_FAIL,
3721 Utf8StrFmt(tr("Unknown exception\n%s[%d] (%s)"),
3722 pszFile, iLine, pszFunction).c_str());
3723 }
3724
3725 /* should not get here */
3726 AssertFailed();
3727 return E_FAIL;
3728}
3729
3730const Utf8Str& VirtualBox::settingsFilePath()
3731{
3732 return m->strSettingsFilePath;
3733}
3734
3735/**
3736 * Returns the lock handle which protects the media trees (hard disks,
3737 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3738 * are no longer protected by the VirtualBox lock, but by this more
3739 * specialized lock. Mind the locking order: always request this lock
3740 * after the VirtualBox object lock but before the locks of the media
3741 * objects contained in these lists. See AutoLock.h.
3742 */
3743RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3744{
3745 return m->lockMedia;
3746}
3747
3748/**
3749 * Thread function that watches the termination of all client processes
3750 * that have opened sessions using IMachine::LockMachine()
3751 */
3752// static
3753DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3754{
3755 LogFlowFuncEnter();
3756
3757 VirtualBox *that = (VirtualBox*)pvUser;
3758 Assert(that);
3759
3760 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3761 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3762
3763 SessionMachineVector machines;
3764 MachineVector spawnedMachines;
3765
3766 size_t cnt = 0;
3767 size_t cntSpawned = 0;
3768
3769#if defined(RT_OS_WINDOWS)
3770
3771 HRESULT hrc = CoInitializeEx(NULL,
3772 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3773 COINIT_SPEED_OVER_MEMORY);
3774 AssertComRC(hrc);
3775
3776 /// @todo (dmik) processes reaping!
3777
3778 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3779 handles[0] = that->m->updateReq;
3780
3781 do
3782 {
3783 AutoCaller autoCaller(that);
3784 /* VirtualBox has been early uninitialized, terminate */
3785 if (!autoCaller.isOk())
3786 break;
3787
3788 do
3789 {
3790 /* release the caller to let uninit() ever proceed */
3791 autoCaller.release();
3792
3793 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3794 handles,
3795 FALSE,
3796 INFINITE);
3797
3798 /* Restore the caller before using VirtualBox. If it fails, this
3799 * means VirtualBox is being uninitialized and we must terminate. */
3800 autoCaller.add();
3801 if (!autoCaller.isOk())
3802 break;
3803
3804 bool update = false;
3805
3806 if (rc == WAIT_OBJECT_0)
3807 {
3808 /* update event is signaled */
3809 update = true;
3810 }
3811 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
3812 {
3813 /* machine mutex is released */
3814 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
3815 update = true;
3816 }
3817 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
3818 {
3819 /* machine mutex is abandoned due to client process termination */
3820 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
3821 update = true;
3822 }
3823 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
3824 {
3825 /* spawned VM process has terminated (normally or abnormally) */
3826 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
3827 checkForSpawnFailure();
3828 update = true;
3829 }
3830
3831 if (update)
3832 {
3833 /* close old process handles */
3834 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
3835 CloseHandle(handles[i]);
3836
3837 // lock the machines list for reading
3838 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3839
3840 /* obtain a new set of opened machines */
3841 cnt = 0;
3842 machines.clear();
3843
3844 for (MachinesOList::iterator it = that->m->allMachines.begin();
3845 it != that->m->allMachines.end();
3846 ++it)
3847 {
3848 /// @todo handle situations with more than 64 objects
3849 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
3850 ("MAXIMUM_WAIT_OBJECTS reached"));
3851
3852 ComObjPtr<SessionMachine> sm;
3853 HANDLE ipcSem;
3854 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
3855 {
3856 machines.push_back(sm);
3857 handles[1 + cnt] = ipcSem;
3858 ++cnt;
3859 }
3860 }
3861
3862 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
3863
3864 /* obtain a new set of spawned machines */
3865 cntSpawned = 0;
3866 spawnedMachines.clear();
3867
3868 for (MachinesOList::iterator it = that->m->allMachines.begin();
3869 it != that->m->allMachines.end();
3870 ++it)
3871 {
3872 /// @todo handle situations with more than 64 objects
3873 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
3874 ("MAXIMUM_WAIT_OBJECTS reached"));
3875
3876 RTPROCESS pid;
3877 if ((*it)->isSessionSpawning(&pid))
3878 {
3879 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
3880 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
3881 pid, GetLastError()));
3882 if (rc == 0)
3883 {
3884 spawnedMachines.push_back(*it);
3885 handles[1 + cnt + cntSpawned] = ph;
3886 ++cntSpawned;
3887 }
3888 }
3889 }
3890
3891 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
3892
3893 // machines lock unwinds here
3894 }
3895 }
3896 while (true);
3897 }
3898 while (0);
3899
3900 /* close old process handles */
3901 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
3902 CloseHandle(handles[i]);
3903
3904 /* release sets of machines if any */
3905 machines.clear();
3906 spawnedMachines.clear();
3907
3908 ::CoUninitialize();
3909
3910#elif defined(RT_OS_OS2)
3911
3912 /// @todo (dmik) processes reaping!
3913
3914 /* according to PMREF, 64 is the maximum for the muxwait list */
3915 SEMRECORD handles[64];
3916
3917 HMUX muxSem = NULLHANDLE;
3918
3919 do
3920 {
3921 AutoCaller autoCaller(that);
3922 /* VirtualBox has been early uninitialized, terminate */
3923 if (!autoCaller.isOk())
3924 break;
3925
3926 do
3927 {
3928 /* release the caller to let uninit() ever proceed */
3929 autoCaller.release();
3930
3931 int vrc = RTSemEventWait(that->m->updateReq, 500);
3932
3933 /* Restore the caller before using VirtualBox. If it fails, this
3934 * means VirtualBox is being uninitialized and we must terminate. */
3935 autoCaller.add();
3936 if (!autoCaller.isOk())
3937 break;
3938
3939 bool update = false;
3940 bool updateSpawned = false;
3941
3942 if (RT_SUCCESS(vrc))
3943 {
3944 /* update event is signaled */
3945 update = true;
3946 updateSpawned = true;
3947 }
3948 else
3949 {
3950 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
3951 ("RTSemEventWait returned %Rrc\n", vrc));
3952
3953 /* are there any mutexes? */
3954 if (cnt > 0)
3955 {
3956 /* figure out what's going on with machines */
3957
3958 unsigned long semId = 0;
3959 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
3960 SEM_IMMEDIATE_RETURN, &semId);
3961
3962 if (arc == NO_ERROR)
3963 {
3964 /* machine mutex is normally released */
3965 Assert(semId >= 0 && semId < cnt);
3966 if (semId >= 0 && semId < cnt)
3967 {
3968#if 0//def DEBUG
3969 {
3970 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
3971 LogFlowFunc(("released mutex: machine='%ls'\n",
3972 machines[semId]->name().raw()));
3973 }
3974#endif
3975 machines[semId]->checkForDeath();
3976 }
3977 update = true;
3978 }
3979 else if (arc == ERROR_SEM_OWNER_DIED)
3980 {
3981 /* machine mutex is abandoned due to client process
3982 * termination; find which mutex is in the Owner Died
3983 * state */
3984 for (size_t i = 0; i < cnt; ++ i)
3985 {
3986 PID pid; TID tid;
3987 unsigned long reqCnt;
3988 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
3989 if (arc == ERROR_SEM_OWNER_DIED)
3990 {
3991 /* close the dead mutex as asked by PMREF */
3992 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
3993
3994 Assert(i >= 0 && i < cnt);
3995 if (i >= 0 && i < cnt)
3996 {
3997#if 0//def DEBUG
3998 {
3999 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4000 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4001 machines[i]->name().raw()));
4002 }
4003#endif
4004 machines[i]->checkForDeath();
4005 }
4006 }
4007 }
4008 update = true;
4009 }
4010 else
4011 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4012 ("DosWaitMuxWaitSem returned %d\n", arc));
4013 }
4014
4015 /* are there any spawning sessions? */
4016 if (cntSpawned > 0)
4017 {
4018 for (size_t i = 0; i < cntSpawned; ++ i)
4019 updateSpawned |= (spawnedMachines[i])->
4020 checkForSpawnFailure();
4021 }
4022 }
4023
4024 if (update || updateSpawned)
4025 {
4026 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4027
4028 if (update)
4029 {
4030 /* close the old muxsem */
4031 if (muxSem != NULLHANDLE)
4032 ::DosCloseMuxWaitSem(muxSem);
4033
4034 /* obtain a new set of opened machines */
4035 cnt = 0;
4036 machines.clear();
4037
4038 for (MachinesOList::iterator it = that->m->allMachines.begin();
4039 it != that->m->allMachines.end(); ++ it)
4040 {
4041 /// @todo handle situations with more than 64 objects
4042 AssertMsg(cnt <= 64 /* according to PMREF */,
4043 ("maximum of 64 mutex semaphores reached (%d)",
4044 cnt));
4045
4046 ComObjPtr<SessionMachine> sm;
4047 HMTX ipcSem;
4048 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4049 {
4050 machines.push_back(sm);
4051 handles[cnt].hsemCur = (HSEM)ipcSem;
4052 handles[cnt].ulUser = cnt;
4053 ++ cnt;
4054 }
4055 }
4056
4057 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4058
4059 if (cnt > 0)
4060 {
4061 /* create a new muxsem */
4062 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4063 handles,
4064 DCMW_WAIT_ANY);
4065 AssertMsg(arc == NO_ERROR,
4066 ("DosCreateMuxWaitSem returned %d\n", arc));
4067 NOREF(arc);
4068 }
4069 }
4070
4071 if (updateSpawned)
4072 {
4073 /* obtain a new set of spawned machines */
4074 spawnedMachines.clear();
4075
4076 for (MachinesOList::iterator it = that->m->allMachines.begin();
4077 it != that->m->allMachines.end(); ++ it)
4078 {
4079 if ((*it)->isSessionSpawning())
4080 spawnedMachines.push_back(*it);
4081 }
4082
4083 cntSpawned = spawnedMachines.size();
4084 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4085 }
4086 }
4087 }
4088 while (true);
4089 }
4090 while (0);
4091
4092 /* close the muxsem */
4093 if (muxSem != NULLHANDLE)
4094 ::DosCloseMuxWaitSem(muxSem);
4095
4096 /* release sets of machines if any */
4097 machines.clear();
4098 spawnedMachines.clear();
4099
4100#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4101
4102 bool update = false;
4103 bool updateSpawned = false;
4104
4105 do
4106 {
4107 AutoCaller autoCaller(that);
4108 if (!autoCaller.isOk())
4109 break;
4110
4111 do
4112 {
4113 /* release the caller to let uninit() ever proceed */
4114 autoCaller.release();
4115
4116 int rc = RTSemEventWait(that->m->updateReq, 500);
4117
4118 /*
4119 * Restore the caller before using VirtualBox. If it fails, this
4120 * means VirtualBox is being uninitialized and we must terminate.
4121 */
4122 autoCaller.add();
4123 if (!autoCaller.isOk())
4124 break;
4125
4126 if (RT_SUCCESS(rc) || update || updateSpawned)
4127 {
4128 /* RT_SUCCESS(rc) means an update event is signaled */
4129
4130 // lock the machines list for reading
4131 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4132
4133 if (RT_SUCCESS(rc) || update)
4134 {
4135 /* obtain a new set of opened machines */
4136 machines.clear();
4137
4138 for (MachinesOList::iterator it = that->m->allMachines.begin();
4139 it != that->m->allMachines.end();
4140 ++it)
4141 {
4142 ComObjPtr<SessionMachine> sm;
4143 if ((*it)->isSessionOpenOrClosing(sm))
4144 machines.push_back(sm);
4145 }
4146
4147 cnt = machines.size();
4148 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4149 }
4150
4151 if (RT_SUCCESS(rc) || updateSpawned)
4152 {
4153 /* obtain a new set of spawned machines */
4154 spawnedMachines.clear();
4155
4156 for (MachinesOList::iterator it = that->m->allMachines.begin();
4157 it != that->m->allMachines.end();
4158 ++it)
4159 {
4160 if ((*it)->isSessionSpawning())
4161 spawnedMachines.push_back(*it);
4162 }
4163
4164 cntSpawned = spawnedMachines.size();
4165 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4166 }
4167
4168 // machines lock unwinds here
4169 }
4170
4171 update = false;
4172 for (size_t i = 0; i < cnt; ++ i)
4173 update |= (machines[i])->checkForDeath();
4174
4175 updateSpawned = false;
4176 for (size_t i = 0; i < cntSpawned; ++ i)
4177 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4178
4179 /* reap child processes */
4180 {
4181 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4182 if (that->m->llProcesses.size())
4183 {
4184 LogFlowFunc(("UPDATE: child process count = %d\n",
4185 that->m->llProcesses.size()));
4186 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4187 while (it != that->m->llProcesses.end())
4188 {
4189 RTPROCESS pid = *it;
4190 RTPROCSTATUS status;
4191 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4192 if (vrc == VINF_SUCCESS)
4193 {
4194 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4195 pid, pid, status.iStatus,
4196 status.enmReason));
4197 it = that->m->llProcesses.erase(it);
4198 }
4199 else
4200 {
4201 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4202 pid, pid, vrc));
4203 if (vrc != VERR_PROCESS_RUNNING)
4204 {
4205 /* remove the process if it is not already running */
4206 it = that->m->llProcesses.erase(it);
4207 }
4208 else
4209 ++ it;
4210 }
4211 }
4212 }
4213 }
4214 }
4215 while (true);
4216 }
4217 while (0);
4218
4219 /* release sets of machines if any */
4220 machines.clear();
4221 spawnedMachines.clear();
4222
4223#else
4224# error "Port me!"
4225#endif
4226
4227 LogFlowFuncLeave();
4228 return 0;
4229}
4230
4231/**
4232 * Thread function that handles custom events posted using #postEvent().
4233 */
4234// static
4235DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4236{
4237 LogFlowFuncEnter();
4238
4239 AssertReturn(pvUser, VERR_INVALID_POINTER);
4240
4241 // create an event queue for the current thread
4242 EventQueue *eventQ = new EventQueue();
4243 AssertReturn(eventQ, VERR_NO_MEMORY);
4244
4245 // return the queue to the one who created this thread
4246 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4247 // signal that we're ready
4248 RTThreadUserSignal(thread);
4249
4250 while (RT_SUCCESS(eventQ->processEventQueue(RT_INDEFINITE_WAIT)))
4251 /* nothing */ ;
4252
4253 delete eventQ;
4254
4255 LogFlowFuncLeave();
4256
4257 return 0;
4258}
4259
4260
4261////////////////////////////////////////////////////////////////////////////////
4262
4263/**
4264 * Takes the current list of registered callbacks of the managed VirtualBox
4265 * instance, and calls #handleCallback() for every callback item from the
4266 * list, passing the item as an argument.
4267 *
4268 * @note Locks the managed VirtualBox object for reading but leaves the lock
4269 * before iterating over callbacks and calling their methods.
4270 */
4271void *VirtualBox::CallbackEvent::handler()
4272{
4273 if (!mVirtualBox)
4274 return NULL;
4275
4276 AutoCaller autoCaller(mVirtualBox);
4277 if (!autoCaller.isOk())
4278 {
4279 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4280 autoCaller.state()));
4281 /* We don't need mVirtualBox any more, so release it */
4282 mVirtualBox = NULL;
4283 return NULL;
4284 }
4285
4286
4287 {
4288 VBoxEventDesc evDesc;
4289 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);
4290
4291 evDesc.fire(/* don't wait for delivery */0);
4292 }
4293
4294 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4295 return NULL;
4296}
4297
4298//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4299//{
4300// return E_NOTIMPL;
4301//}
4302
4303STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4304{
4305 CheckComArgStrNotEmptyOrNull(aName);
4306 CheckComArgNotNull(aServer);
4307
4308 AutoCaller autoCaller(this);
4309 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4310
4311 ComObjPtr<DHCPServer> dhcpServer;
4312 dhcpServer.createObject();
4313 HRESULT rc = dhcpServer->init(this, aName);
4314 if (FAILED(rc)) return rc;
4315
4316 rc = registerDHCPServer(dhcpServer, true);
4317 if (FAILED(rc)) return rc;
4318
4319 dhcpServer.queryInterfaceTo(aServer);
4320
4321 return rc;
4322}
4323
4324STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4325{
4326 CheckComArgStrNotEmptyOrNull(aName);
4327 CheckComArgNotNull(aServer);
4328
4329 AutoCaller autoCaller(this);
4330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4331
4332 HRESULT rc;
4333 Bstr bstr;
4334 ComPtr<DHCPServer> found;
4335
4336 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4337
4338 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();
4339 it != m->allDHCPServers.end();
4340 ++it)
4341 {
4342 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4343 if (FAILED(rc)) throw rc;
4344
4345 if (bstr == aName)
4346 {
4347 found = *it;
4348 break;
4349 }
4350 }
4351
4352 if (!found)
4353 return E_INVALIDARG;
4354
4355 return found.queryInterfaceTo(aServer);
4356}
4357
4358STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4359{
4360 CheckComArgNotNull(aServer);
4361
4362 AutoCaller autoCaller(this);
4363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4364
4365 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4366
4367 return rc;
4368}
4369
4370/**
4371 * Remembers the given dhcp server by storing it in the hard disk registry.
4372 *
4373 * @param aDHCPServer Dhcp Server object to remember.
4374 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4375 *
4376 * When @a aSaveRegistry is @c true, this operation may fail because of the
4377 * failed #saveSettings() method it calls. In this case, the dhcp server object
4378 * will not be remembered. It is therefore the responsibility of the caller to
4379 * call this method as the last step of some action that requires registration
4380 * in order to make sure that only fully functional dhcp server objects get
4381 * registered.
4382 *
4383 * @note Locks this object for writing and @a aDHCPServer for reading.
4384 */
4385HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4386 bool aSaveRegistry /*= true*/)
4387{
4388 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4389
4390 AutoCaller autoCaller(this);
4391 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4392
4393 AutoCaller dhcpServerCaller(aDHCPServer);
4394 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4395
4396 Bstr name;
4397 HRESULT rc;
4398 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4399 if (FAILED(rc)) return rc;
4400
4401 ComPtr<IDHCPServer> existing;
4402 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());
4403 if (SUCCEEDED(rc))
4404 return E_INVALIDARG;
4405
4406 rc = S_OK;
4407
4408 m->allDHCPServers.addChild(aDHCPServer);
4409
4410 if (aSaveRegistry)
4411 {
4412 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4413 rc = saveSettings();
4414 vboxLock.release();
4415
4416 if (FAILED(rc))
4417 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4418 }
4419
4420 return rc;
4421}
4422
4423/**
4424 * Removes the given hard disk from the hard disk registry.
4425 *
4426 * @param aHardDisk Hard disk object to remove.
4427 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4428 *
4429 * When @a aSaveRegistry is @c true, this operation may fail because of the
4430 * failed #saveSettings() method it calls. In this case, the hard disk object
4431 * will NOT be removed from the registry when this method returns. It is
4432 * therefore the responsibility of the caller to call this method as the first
4433 * step of some action that requires unregistration, before calling uninit() on
4434 * @a aHardDisk.
4435 *
4436 * @note Locks this object for writing and @a aHardDisk for reading.
4437 */
4438HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4439 bool aSaveRegistry /*= true*/)
4440{
4441 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4442
4443 AutoCaller autoCaller(this);
4444 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4445
4446 AutoCaller dhcpServerCaller(aDHCPServer);
4447 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4448
4449 m->allDHCPServers.removeChild(aDHCPServer);
4450
4451 HRESULT rc = S_OK;
4452
4453 if (aSaveRegistry)
4454 {
4455 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);
4456 rc = saveSettings();
4457 vboxLock.release();
4458
4459 if (FAILED(rc))
4460 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4461 }
4462
4463 return rc;
4464}
4465
4466STDMETHODIMP VirtualBox::VRDERegisterLibrary(IN_BSTR aName)
4467{
4468 CheckComArgStrNotEmptyOrNull(aName);
4469
4470 AutoCaller autoCaller(this);
4471 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4472
4473 Utf8Str strName(aName);
4474
4475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4476
4477 /* If the name already exists, there is nothing to do. */
4478 settings::VRDELibrariesList::const_iterator it;
4479
4480 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4481 it != m->pMainConfigFile->llVRDELibraries.end();
4482 ++it)
4483 {
4484 const Utf8Str &str = *it;
4485 if (strName == str)
4486 {
4487 break;
4488 }
4489 }
4490
4491 if (it == m->pMainConfigFile->llVRDELibraries.end())
4492 {
4493 /* Not found, add it. */
4494 m->pMainConfigFile->llVRDELibraries.push_back(strName);
4495
4496 /* Save settings on success. */
4497 HRESULT rc = saveSettings();
4498 if (FAILED(rc)) return rc;
4499 }
4500
4501 return S_OK;
4502}
4503
4504STDMETHODIMP VirtualBox::VRDEUnregisterLibrary(IN_BSTR aName)
4505{
4506 CheckComArgStrNotEmptyOrNull(aName);
4507
4508 AutoCaller autoCaller(this);
4509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4510
4511 Utf8Str strName(aName);
4512
4513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4514
4515 settings::VRDELibrariesList::const_iterator it;
4516
4517 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4518 it != m->pMainConfigFile->llVRDELibraries.end();
4519 ++it)
4520 {
4521 const Utf8Str &str = *it;
4522 if (strName == str)
4523 {
4524 break;
4525 }
4526 }
4527
4528 if (it != m->pMainConfigFile->llVRDELibraries.end())
4529 {
4530 /* Found, remove it. */
4531 m->pMainConfigFile->llVRDELibraries.remove(strName);
4532
4533 /* Save settings on success. */
4534 HRESULT rc = saveSettings();
4535 if (FAILED(rc)) return rc;
4536 }
4537
4538 return S_OK;
4539}
4540
4541STDMETHODIMP VirtualBox::VRDEListLibraries(ComSafeArrayOut(BSTR, aNames))
4542{
4543 using namespace settings;
4544
4545 if (ComSafeArrayOutIsNull(aNames))
4546 return E_POINTER;
4547
4548 AutoCaller autoCaller(this);
4549 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4550
4551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4552
4553 com::SafeArray<BSTR> saNames(m->pMainConfigFile->llVRDELibraries.size());
4554 int i = 0;
4555 for (VRDELibrariesList::const_iterator it = m->pMainConfigFile->llVRDELibraries.begin();
4556 it != m->pMainConfigFile->llVRDELibraries.end();
4557 ++it, ++i)
4558 {
4559 const Utf8Str &strName = *it;
4560 strName.cloneTo(&saNames[i]);
4561 }
4562 saNames.detachTo(ComSafeArrayOutArg(aNames));
4563
4564 return S_OK;
4565}
4566
4567STDMETHODIMP VirtualBox::VRDEIsLibraryRegistered(IN_BSTR aName, BOOL *aRegistered)
4568{
4569 CheckComArgStrNotEmptyOrNull(aName);
4570 CheckComArgOutPointerValid(aRegistered);
4571
4572 AutoCaller autoCaller(this);
4573 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4574
4575 Utf8Str strName(aName);
4576
4577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4578
4579 settings::VRDELibrariesList::const_iterator it;
4580
4581 for (it = m->pMainConfigFile->llVRDELibraries.begin();
4582 it != m->pMainConfigFile->llVRDELibraries.end();
4583 ++it)
4584 {
4585 const Utf8Str &str = *it;
4586 if (strName == str)
4587 {
4588 break;
4589 }
4590 }
4591
4592 *aRegistered = (it != m->pMainConfigFile->llVRDELibraries.end());
4593
4594 return S_OK;
4595}
4596
4597/* 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