VirtualBox

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

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

Main: storage controller/attachment cleanup

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

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