VirtualBox

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

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

first draft of OLE events support

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

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