VirtualBox

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

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

Main: Reapplied r62200 with bug fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 150.0 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 29937 2010-06-01 08:41:32Z 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 /* get the teleporter enable state for the progress object init. */
1998 BOOL fTeleporterEnabled;
1999 rc = machine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
2000 if (FAILED(rc))
2001 return rc;
2002
2003 /* create a progress object */
2004 ComObjPtr<ProgressProxy> progress;
2005 progress.createObject();
2006 rc = progress->init(this,
2007 static_cast<IMachine *>(machine),
2008 Bstr(tr("Spawning session")),
2009 TRUE /* aCancelable */,
2010 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
2011 Bstr(tr("Spawning session")),
2012 2 /* uFirstOperationWeight */,
2013 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
2014 if (SUCCEEDED(rc))
2015 {
2016 rc = machine->openRemoteSession(control, aType, aEnvironment, progress);
2017 if (SUCCEEDED(rc))
2018 {
2019 progress.queryInterfaceTo(aProgress);
2020
2021 /* signal the client watcher thread */
2022 updateClientWatcher();
2023
2024 /* fire an event */
2025 onSessionStateChange(id, SessionState_Spawning);
2026 }
2027 }
2028
2029 return rc;
2030}
2031
2032/**
2033 * @note Locks objects!
2034 */
2035STDMETHODIMP VirtualBox::OpenExistingSession(ISession *aSession,
2036 IN_BSTR aMachineId)
2037{
2038 CheckComArgNotNull(aSession);
2039
2040 AutoCaller autoCaller(this);
2041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2042
2043 Guid id(aMachineId);
2044 ComObjPtr<Machine> machine;
2045
2046 HRESULT rc = findMachine(id, true /* setError */, &machine);
2047 if (FAILED(rc)) return rc;
2048
2049 /* check the session state */
2050 SessionState_T state;
2051 rc = aSession->COMGETTER(State)(&state);
2052 if (FAILED(rc)) return rc;
2053
2054 if (state != SessionState_Closed)
2055 return setError(VBOX_E_INVALID_OBJECT_STATE,
2056 tr("The given session is already open or being opened"));
2057
2058 /* get the IInternalSessionControl interface */
2059 ComPtr<IInternalSessionControl> control = aSession;
2060 ComAssertMsgRet(!!control, ("No IInternalSessionControl interface"),
2061 E_INVALIDARG);
2062
2063 rc = machine->openExistingSession(control);
2064
2065 return rc;
2066}
2067
2068/**
2069 * @note Locks this object for writing.
2070 */
2071STDMETHODIMP VirtualBox::RegisterCallback(IVirtualBoxCallback *aCallback)
2072{
2073 LogFlowThisFunc(("aCallback=%p\n", aCallback));
2074
2075 CheckComArgNotNull(aCallback);
2076
2077 AutoCaller autoCaller(this);
2078 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2079
2080 /* Query the interface we associate with IVirtualBoxCallback as the caller
2081 might've been compiled against a different SDK. */
2082 void *pvCallback;
2083 HRESULT hrc = aCallback->QueryInterface(COM_IIDOF(IVirtualBoxCallback), &pvCallback);
2084 if (FAILED(hrc))
2085 return setError(hrc, tr("Incompatible IVirtualBoxCallback interface - version mismatch?"));
2086 aCallback = (IVirtualBoxCallback *)pvCallback;
2087
2088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2089 m->llCallbacks.push_back(CallbackList::value_type(aCallback));
2090
2091 aCallback->Release();
2092 return S_OK;
2093}
2094
2095/**
2096 * @note Locks this object for writing.
2097 */
2098STDMETHODIMP VirtualBox::UnregisterCallback(IVirtualBoxCallback *aCallback)
2099{
2100 CheckComArgNotNull(aCallback);
2101
2102 AutoCaller autoCaller(this);
2103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2104
2105 HRESULT rc = S_OK;
2106
2107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2108
2109 CallbackList::iterator it;
2110 it = std::find(m->llCallbacks.begin(),
2111 m->llCallbacks.end(),
2112 CallbackList::value_type(aCallback));
2113 if (it == m->llCallbacks.end())
2114 rc = E_INVALIDARG;
2115 else
2116 m->llCallbacks.erase(it);
2117
2118 LogFlowThisFunc(("aCallback=%p, rc=%08X\n", aCallback, rc));
2119 return rc;
2120}
2121
2122
2123STDMETHODIMP VirtualBox::WaitForPropertyChange(IN_BSTR /* aWhat */,
2124 ULONG /* aTimeout */,
2125 BSTR * /* aChanged */,
2126 BSTR * /* aValues */)
2127{
2128 ReturnComNotImplemented();
2129}
2130
2131// public methods only for internal purposes
2132/////////////////////////////////////////////////////////////////////////////
2133
2134#ifdef DEBUG
2135void VirtualBox::dumpAllBackRefs()
2136{
2137 {
2138 AutoReadLock al(m->ollHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2139 for (MediaList::const_iterator mt = m->ollHardDisks.begin();
2140 mt != m->ollHardDisks.end();
2141 ++mt)
2142 {
2143 ComObjPtr<Medium> pMedium = *mt;
2144 pMedium->dumpBackRefs();
2145 }
2146 }
2147 {
2148 AutoReadLock al(m->ollDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2149 for (MediaList::const_iterator mt = m->ollDVDImages.begin();
2150 mt != m->ollDVDImages.end();
2151 ++mt)
2152 {
2153 ComObjPtr<Medium> pMedium = *mt;
2154 pMedium->dumpBackRefs();
2155 }
2156 }
2157}
2158#endif
2159
2160/**
2161 * Posts an event to the event queue that is processed asynchronously
2162 * on a dedicated thread.
2163 *
2164 * Posting events to the dedicated event queue is useful to perform secondary
2165 * actions outside any object locks -- for example, to iterate over a list
2166 * of callbacks and inform them about some change caused by some object's
2167 * method call.
2168 *
2169 * @param event event to post
2170 * (must be allocated using |new|, will be deleted automatically
2171 * by the event thread after processing)
2172 *
2173 * @note Doesn't lock any object.
2174 */
2175HRESULT VirtualBox::postEvent(Event *event)
2176{
2177 AutoCaller autoCaller(this);
2178 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2179
2180 if (autoCaller.state() != Ready)
2181 {
2182 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n",
2183 autoCaller.state()));
2184 return S_OK;
2185 }
2186
2187 AssertReturn(event, E_FAIL);
2188 AssertReturn(m->pAsyncEventQ, E_FAIL);
2189
2190 if (m->pAsyncEventQ->postEvent(event))
2191 return S_OK;
2192
2193 return E_FAIL;
2194}
2195
2196/**
2197 * Adds a progress to the global collection of pending operations.
2198 * Usually gets called upon progress object initialization.
2199 *
2200 * @param aProgress Operation to add to the collection.
2201 *
2202 * @note Doesn't lock objects.
2203 */
2204HRESULT VirtualBox::addProgress(IProgress *aProgress)
2205{
2206 CheckComArgNotNull(aProgress);
2207
2208 AutoCaller autoCaller(this);
2209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2210
2211 Bstr id;
2212 HRESULT rc = aProgress->COMGETTER(Id)(id.asOutParam());
2213 AssertComRCReturnRC(rc);
2214
2215 /* protect mProgressOperations */
2216 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2217
2218 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress));
2219 return S_OK;
2220}
2221
2222/**
2223 * Removes the progress from the global collection of pending operations.
2224 * Usually gets called upon progress completion.
2225 *
2226 * @param aId UUID of the progress operation to remove
2227 *
2228 * @note Doesn't lock objects.
2229 */
2230HRESULT VirtualBox::removeProgress(IN_GUID aId)
2231{
2232 AutoCaller autoCaller(this);
2233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2234
2235 ComPtr<IProgress> progress;
2236
2237 /* protect mProgressOperations */
2238 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);
2239
2240 size_t cnt = m->mapProgressOperations.erase(aId);
2241 Assert(cnt == 1);
2242 NOREF(cnt);
2243
2244 return S_OK;
2245}
2246
2247#ifdef RT_OS_WINDOWS
2248
2249struct StartSVCHelperClientData
2250{
2251 ComObjPtr<VirtualBox> that;
2252 ComObjPtr<Progress> progress;
2253 bool privileged;
2254 VirtualBox::SVCHelperClientFunc func;
2255 void *user;
2256};
2257
2258/**
2259 * Helper method that starts a worker thread that:
2260 * - creates a pipe communication channel using SVCHlpClient;
2261 * - starts an SVC Helper process that will inherit this channel;
2262 * - executes the supplied function by passing it the created SVCHlpClient
2263 * and opened instance to communicate to the Helper process and the given
2264 * Progress object.
2265 *
2266 * The user function is supposed to communicate to the helper process
2267 * using the \a aClient argument to do the requested job and optionally expose
2268 * the progress through the \a aProgress object. The user function should never
2269 * call notifyComplete() on it: this will be done automatically using the
2270 * result code returned by the function.
2271 *
2272 * Before the user function is started, the communication channel passed to
2273 * the \a aClient argument is fully set up, the function should start using
2274 * its write() and read() methods directly.
2275 *
2276 * The \a aVrc parameter of the user function may be used to return an error
2277 * code if it is related to communication errors (for example, returned by
2278 * the SVCHlpClient members when they fail). In this case, the correct error
2279 * message using this value will be reported to the caller. Note that the
2280 * value of \a aVrc is inspected only if the user function itself returns
2281 * success.
2282 *
2283 * If a failure happens anywhere before the user function would be normally
2284 * called, it will be called anyway in special "cleanup only" mode indicated
2285 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
2286 * all the function is supposed to do is to cleanup its aUser argument if
2287 * necessary (it's assumed that the ownership of this argument is passed to
2288 * the user function once #startSVCHelperClient() returns a success, thus
2289 * making it responsible for the cleanup).
2290 *
2291 * After the user function returns, the thread will send the SVCHlpMsg::Null
2292 * message to indicate a process termination.
2293 *
2294 * @param aPrivileged |true| to start the SVC Helper process as a privileged
2295 * user that can perform administrative tasks
2296 * @param aFunc user function to run
2297 * @param aUser argument to the user function
2298 * @param aProgress progress object that will track operation completion
2299 *
2300 * @note aPrivileged is currently ignored (due to some unsolved problems in
2301 * Vista) and the process will be started as a normal (unprivileged)
2302 * process.
2303 *
2304 * @note Doesn't lock anything.
2305 */
2306HRESULT VirtualBox::startSVCHelperClient(bool aPrivileged,
2307 SVCHelperClientFunc aFunc,
2308 void *aUser, Progress *aProgress)
2309{
2310 AssertReturn(aFunc, E_POINTER);
2311 AssertReturn(aProgress, E_POINTER);
2312
2313 AutoCaller autoCaller(this);
2314 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2315
2316 /* create the SVCHelperClientThread() argument */
2317 std::auto_ptr <StartSVCHelperClientData>
2318 d(new StartSVCHelperClientData());
2319 AssertReturn(d.get(), E_OUTOFMEMORY);
2320
2321 d->that = this;
2322 d->progress = aProgress;
2323 d->privileged = aPrivileged;
2324 d->func = aFunc;
2325 d->user = aUser;
2326
2327 RTTHREAD tid = NIL_RTTHREAD;
2328 int vrc = RTThreadCreate(&tid, SVCHelperClientThread,
2329 static_cast <void *>(d.get()),
2330 0, RTTHREADTYPE_MAIN_WORKER,
2331 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2332
2333 ComAssertMsgRCRet(vrc, ("Could not create SVCHelper thread (%Rrc)", vrc),
2334 E_FAIL);
2335
2336 /* d is now owned by SVCHelperClientThread(), so release it */
2337 d.release();
2338
2339 return S_OK;
2340}
2341
2342/**
2343 * Worker thread for startSVCHelperClient().
2344 */
2345/* static */
2346DECLCALLBACK(int)
2347VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser)
2348{
2349 LogFlowFuncEnter();
2350
2351 std::auto_ptr <StartSVCHelperClientData>
2352 d(static_cast <StartSVCHelperClientData *>(aUser));
2353
2354 HRESULT rc = S_OK;
2355 bool userFuncCalled = false;
2356
2357 do
2358 {
2359 AssertBreakStmt(d.get(), rc = E_POINTER);
2360 AssertReturn(!d->progress.isNull(), E_POINTER);
2361
2362 /* protect VirtualBox from uninitialization */
2363 AutoCaller autoCaller(d->that);
2364 if (!autoCaller.isOk())
2365 {
2366 /* it's too late */
2367 rc = autoCaller.rc();
2368 break;
2369 }
2370
2371 int vrc = VINF_SUCCESS;
2372
2373 Guid id;
2374 id.create();
2375 SVCHlpClient client;
2376 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}",
2377 id.raw()).c_str());
2378 if (RT_FAILURE(vrc))
2379 {
2380 rc = setError(E_FAIL,
2381 tr("Could not create the communication channel (%Rrc)"), vrc);
2382 break;
2383 }
2384
2385 /* get the path to the executable */
2386 char exePathBuf[RTPATH_MAX];
2387 char *exePath = RTProcGetExecutableName(exePathBuf, RTPATH_MAX);
2388 ComAssertBreak(exePath, E_FAIL);
2389
2390 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().raw());
2391
2392 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
2393
2394 RTPROCESS pid = NIL_RTPROCESS;
2395
2396 if (d->privileged)
2397 {
2398 /* Attempt to start a privileged process using the Run As dialog */
2399
2400 Bstr file = exePath;
2401 Bstr parameters = argsStr;
2402
2403 SHELLEXECUTEINFO shExecInfo;
2404
2405 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
2406
2407 shExecInfo.fMask = NULL;
2408 shExecInfo.hwnd = NULL;
2409 shExecInfo.lpVerb = L"runas";
2410 shExecInfo.lpFile = file;
2411 shExecInfo.lpParameters = parameters;
2412 shExecInfo.lpDirectory = NULL;
2413 shExecInfo.nShow = SW_NORMAL;
2414 shExecInfo.hInstApp = NULL;
2415
2416 if (!ShellExecuteEx(&shExecInfo))
2417 {
2418 int vrc2 = RTErrConvertFromWin32(GetLastError());
2419 /* hide excessive details in case of a frequent error
2420 * (pressing the Cancel button to close the Run As dialog) */
2421 if (vrc2 == VERR_CANCELLED)
2422 rc = setError(E_FAIL,
2423 tr("Operation cancelled by the user"));
2424 else
2425 rc = setError(E_FAIL,
2426 tr("Could not launch a privileged process '%s' (%Rrc)"),
2427 exePath, vrc2);
2428 break;
2429 }
2430 }
2431 else
2432 {
2433 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 };
2434 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid);
2435 if (RT_FAILURE(vrc))
2436 {
2437 rc = setError(E_FAIL,
2438 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2439 break;
2440 }
2441 }
2442
2443 /* wait for the client to connect */
2444 vrc = client.connect();
2445 if (RT_SUCCESS(vrc))
2446 {
2447 /* start the user supplied function */
2448 rc = d->func(&client, d->progress, d->user, &vrc);
2449 userFuncCalled = true;
2450 }
2451
2452 /* send the termination signal to the process anyway */
2453 {
2454 int vrc2 = client.write(SVCHlpMsg::Null);
2455 if (RT_SUCCESS(vrc))
2456 vrc = vrc2;
2457 }
2458
2459 if (SUCCEEDED(rc) && RT_FAILURE(vrc))
2460 {
2461 rc = setError(E_FAIL,
2462 tr("Could not operate the communication channel (%Rrc)"), vrc);
2463 break;
2464 }
2465 }
2466 while (0);
2467
2468 if (FAILED(rc) && !userFuncCalled)
2469 {
2470 /* call the user function in the "cleanup only" mode
2471 * to let it free resources passed to in aUser */
2472 d->func(NULL, NULL, d->user, NULL);
2473 }
2474
2475 d->progress->notifyComplete(rc);
2476
2477 LogFlowFuncLeave();
2478 return 0;
2479}
2480
2481#endif /* RT_OS_WINDOWS */
2482
2483/**
2484 * Sends a signal to the client watcher thread to rescan the set of machines
2485 * that have open sessions.
2486 *
2487 * @note Doesn't lock anything.
2488 */
2489void VirtualBox::updateClientWatcher()
2490{
2491 AutoCaller autoCaller(this);
2492 AssertComRCReturnVoid(autoCaller.rc());
2493
2494 AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD);
2495
2496 /* sent an update request */
2497#if defined(RT_OS_WINDOWS)
2498 ::SetEvent(m->updateReq);
2499#elif defined(RT_OS_OS2)
2500 RTSemEventSignal(m->updateReq);
2501#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2502 RTSemEventSignal(m->updateReq);
2503#else
2504# error "Port me!"
2505#endif
2506}
2507
2508/**
2509 * Adds the given child process ID to the list of processes to be reaped.
2510 * This call should be followed by #updateClientWatcher() to take the effect.
2511 */
2512void VirtualBox::addProcessToReap(RTPROCESS pid)
2513{
2514 AutoCaller autoCaller(this);
2515 AssertComRCReturnVoid(autoCaller.rc());
2516
2517 /// @todo (dmik) Win32?
2518#ifndef RT_OS_WINDOWS
2519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2520 m->llProcesses.push_back(pid);
2521#endif
2522}
2523
2524/**
2525 * Removes a dead callback.
2526 * @param aCallback The reference to the registered callback interface.
2527 */
2528void VirtualBox::removeDeadCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2529{
2530 /* find and delete */
2531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2532 CallbackList::iterator it = std::find(m->llCallbacks.begin(),
2533 m->llCallbacks.end(),
2534 CallbackList::value_type(aCallback));
2535 if (it != m->llCallbacks.end())
2536 {
2537 LogRel(("Removing dead callback: %p\n", &*it));
2538 m->llCallbacks.erase(it);
2539 }
2540}
2541
2542/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2543struct MachineEvent : public VirtualBox::CallbackEvent
2544{
2545 MachineEvent(VirtualBox *aVB, const Guid &aId)
2546 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineDataChange), id(aId)
2547 {}
2548
2549 MachineEvent(VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2550 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineStateChange), id(aId)
2551 , state(aState)
2552 {}
2553
2554 MachineEvent(VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2555 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnMachineRegistered), id(aId)
2556 , registered(aRegistered)
2557 {}
2558
2559 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2560 {
2561 switch (mWhat)
2562 {
2563 case VirtualBoxCallbackRegistration::kOnMachineDataChange:
2564 LogFlow(("OnMachineDataChange: id={%RTuuid}\n", id.ptr()));
2565 return aCallback->OnMachineDataChange(id.toUtf16());
2566
2567 case VirtualBoxCallbackRegistration::kOnMachineStateChange:
2568 LogFlow(("OnMachineStateChange: id={%RTuuid}, state=%d\n",
2569 id.ptr(), state));
2570 return aCallback->OnMachineStateChange(id.toUtf16(), state);
2571
2572 case VirtualBoxCallbackRegistration::kOnMachineRegistered:
2573 LogFlow(("OnMachineRegistered: id={%RTuuid}, registered=%d\n",
2574 id.ptr(), registered));
2575 return aCallback->OnMachineRegistered(id.toUtf16(), registered);
2576
2577 default:
2578 AssertFailedReturn(S_OK);
2579 }
2580 }
2581
2582 Guid id;
2583 MachineState_T state;
2584 BOOL registered;
2585};
2586
2587/**
2588 * @note Doesn't lock any object.
2589 */
2590void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState)
2591{
2592 postEvent(new MachineEvent(this, aId, aState));
2593}
2594
2595/**
2596 * @note Doesn't lock any object.
2597 */
2598void VirtualBox::onMachineDataChange(const Guid &aId)
2599{
2600 postEvent(new MachineEvent(this, aId));
2601}
2602
2603/**
2604 * @note Locks this object for reading.
2605 */
2606BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2607 Bstr &aError)
2608{
2609 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n",
2610 aId.toString().raw(), aKey, aValue));
2611
2612 AutoCaller autoCaller(this);
2613 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2614
2615 CallbackList list;
2616 {
2617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2618 list = m->llCallbacks;
2619 }
2620
2621 BOOL allowChange = TRUE;
2622 CallbackList::iterator it = list.begin();
2623 Bstr id = aId.toUtf16();
2624 while ((it != list.end()) && allowChange)
2625 {
2626 if (it->isWanted(VirtualBoxCallbackRegistration::kOnExtraDataCanChange))
2627 {
2628 HRESULT rc = it->ptrICb->OnExtraDataCanChange(id, aKey, aValue,
2629 aError.asOutParam(),
2630 &allowChange);
2631 if (FAILED(rc))
2632 {
2633 if (rc == VBOX_E_DONT_CALL_AGAIN)
2634 {
2635 /* Have to update the original. */
2636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2637 CallbackList::iterator itOrg;
2638 itOrg = std::find(m->llCallbacks.begin(),
2639 m->llCallbacks.end(),
2640 CallbackList::value_type(it->ptrICb));
2641 if (itOrg != m->llCallbacks.end())
2642 itOrg->setDontCallAgain(VirtualBoxCallbackRegistration::kOnExtraDataCanChange);
2643 }
2644 else if (FAILED_DEAD_INTERFACE(rc))
2645 removeDeadCallback(it->ptrICb);
2646
2647 /* if a call to this method fails for some reason (for ex., because
2648 * the other side is dead), we ensure allowChange stays true
2649 * (MS COM RPC implementation seems to zero all output vars before
2650 * issuing an IPC call or after a failure, so it's essential
2651 * there) */
2652 allowChange = TRUE;
2653 }
2654 }
2655 ++it;
2656 }
2657
2658 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange));
2659 return allowChange;
2660}
2661
2662/** Event for onExtraDataChange() */
2663struct ExtraDataEvent : public VirtualBox::CallbackEvent
2664{
2665 ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId,
2666 IN_BSTR aKey, IN_BSTR aVal)
2667 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnExtraDataChange)
2668 , machineId(aMachineId), key(aKey), val(aVal)
2669 {}
2670
2671 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2672 {
2673 LogFlow(("OnExtraDataChange: machineId={%RTuuid}, key='%ls', val='%ls'\n",
2674 machineId.ptr(), key.raw(), val.raw()));
2675 return aCallback->OnExtraDataChange(machineId.toUtf16(), key, val);
2676 }
2677
2678 Guid machineId;
2679 Bstr key, val;
2680};
2681
2682/**
2683 * @note Doesn't lock any object.
2684 */
2685void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2686{
2687 postEvent(new ExtraDataEvent(this, aId, aKey, aValue));
2688}
2689
2690/**
2691 * @note Doesn't lock any object.
2692 */
2693void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered)
2694{
2695 postEvent(new MachineEvent(this, aId, aRegistered));
2696}
2697
2698/** Event for onSessionStateChange() */
2699struct SessionEvent : public VirtualBox::CallbackEvent
2700{
2701 SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2702 : CallbackEvent(aVB, VirtualBoxCallbackRegistration::kOnSessionStateChange)
2703 , machineId(aMachineId), sessionState(aState)
2704 {}
2705
2706 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2707 {
2708 LogFlow(("OnSessionStateChange: machineId={%RTuuid}, sessionState=%d\n",
2709 machineId.ptr(), sessionState));
2710 return aCallback->OnSessionStateChange(machineId.toUtf16(), sessionState);
2711 }
2712
2713 Guid machineId;
2714 SessionState_T sessionState;
2715};
2716
2717/**
2718 * @note Doesn't lock any object.
2719 */
2720void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState)
2721{
2722 postEvent(new SessionEvent(this, aId, aState));
2723}
2724
2725/** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */
2726struct SnapshotEvent : public VirtualBox::CallbackEvent
2727{
2728 SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2729 VirtualBoxCallbackRegistration::CallbackBit aWhat)
2730 : CallbackEvent(aVB, aWhat)
2731 , machineId(aMachineId), snapshotId(aSnapshotId)
2732 {}
2733
2734 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2735 {
2736 Bstr mid = machineId.toUtf16();
2737 Bstr sid = snapshotId.toUtf16();
2738
2739 switch (mWhat)
2740 {
2741 case VirtualBoxCallbackRegistration::kOnSnapshotTaken:
2742 LogFlow(("OnSnapshotTaken: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2743 machineId.ptr(), snapshotId.ptr()));
2744 return aCallback->OnSnapshotTaken(mid, sid);
2745
2746 case VirtualBoxCallbackRegistration::kOnSnapshotDeleted:
2747 LogFlow(("OnSnapshotDeleted: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2748 machineId.ptr(), snapshotId.ptr()));
2749 return aCallback->OnSnapshotDeleted(mid, sid);
2750
2751 case VirtualBoxCallbackRegistration::kOnSnapshotChange:
2752 LogFlow(("OnSnapshotChange: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2753 machineId.ptr(), snapshotId.ptr()));
2754 return aCallback->OnSnapshotChange(mid, sid);
2755
2756 default:
2757 AssertFailedReturn(S_OK);
2758 }
2759 }
2760
2761 Guid machineId;
2762 Guid snapshotId;
2763};
2764
2765/**
2766 * @note Doesn't lock any object.
2767 */
2768void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId)
2769{
2770 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2771 VirtualBoxCallbackRegistration::kOnSnapshotTaken));
2772}
2773
2774/**
2775 * @note Doesn't lock any object.
2776 */
2777void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId)
2778{
2779 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2780 VirtualBoxCallbackRegistration::kOnSnapshotDeleted));
2781}
2782
2783/**
2784 * @note Doesn't lock any object.
2785 */
2786void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId)
2787{
2788 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId,
2789 VirtualBoxCallbackRegistration::kOnSnapshotChange));
2790}
2791
2792/** Event for onGuestPropertyChange() */
2793struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2794{
2795 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId,
2796 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2797 : CallbackEvent(aVBox, VirtualBoxCallbackRegistration::kOnGuestPropertyChange),
2798 machineId(aMachineId),
2799 name(aName),
2800 value(aValue),
2801 flags(aFlags)
2802 {}
2803
2804 HRESULT handleCallback(const ComPtr<IVirtualBoxCallback> &aCallback)
2805 {
2806 LogFlow(("OnGuestPropertyChange: machineId={%RTuuid}, name='%ls', value='%ls', flags='%ls'\n",
2807 machineId.ptr(), name.raw(), value.raw(), flags.raw()));
2808 return aCallback->OnGuestPropertyChange(machineId.toUtf16(), name, value, flags);
2809 }
2810
2811 Guid machineId;
2812 Bstr name, value, flags;
2813};
2814
2815/**
2816 * @note Doesn't lock any object.
2817 */
2818void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName,
2819 IN_BSTR aValue, IN_BSTR aFlags)
2820{
2821 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags));
2822}
2823
2824/**
2825 * @note Locks this object for reading.
2826 */
2827ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType()
2828{
2829 ComObjPtr<GuestOSType> type;
2830 AutoCaller autoCaller(this);
2831 AssertComRCReturn(autoCaller.rc(), type);
2832
2833 /* unknown type must always be the first */
2834 ComAssertRet(m->ollGuestOSTypes.size() > 0, type);
2835
2836 return m->ollGuestOSTypes.front();
2837}
2838
2839/**
2840 * Returns the list of opened machines (machines having direct sessions opened
2841 * by client processes) and optionally the list of direct session controls.
2842 *
2843 * @param aMachines Where to put opened machines (will be empty if none).
2844 * @param aControls Where to put direct session controls (optional).
2845 *
2846 * @note The returned lists contain smart pointers. So, clear it as soon as
2847 * it becomes no more necessary to release instances.
2848 *
2849 * @note It can be possible that a session machine from the list has been
2850 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2851 * when accessing unprotected data directly.
2852 *
2853 * @note Locks objects for reading.
2854 */
2855void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines,
2856 InternalControlList *aControls /*= NULL*/)
2857{
2858 AutoCaller autoCaller(this);
2859 AssertComRCReturnVoid(autoCaller.rc());
2860
2861 aMachines.clear();
2862 if (aControls)
2863 aControls->clear();
2864
2865 AutoReadLock alock(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2866
2867 for (MachinesOList::iterator it = m->ollMachines.begin();
2868 it != m->ollMachines.end();
2869 ++it)
2870 {
2871 ComObjPtr<SessionMachine> sm;
2872 ComPtr<IInternalSessionControl> ctl;
2873 if ((*it)->isSessionOpen(sm, &ctl))
2874 {
2875 aMachines.push_back(sm);
2876 if (aControls)
2877 aControls->push_back(ctl);
2878 }
2879 }
2880}
2881
2882/**
2883 * Searches for a Machine object with the given ID in the collection
2884 * of registered machines.
2885 *
2886 * @param id
2887 * ID of the machine
2888 * @param doSetError
2889 * if TRUE, the appropriate error info is set in case when the machine
2890 * is not found
2891 * @param machine
2892 * where to store the found machine object (can be NULL)
2893 *
2894 * @return
2895 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
2896 *
2897 * @note Locks this object for reading.
2898 */
2899HRESULT VirtualBox::findMachine(const Guid &aId,
2900 bool aSetError,
2901 ComObjPtr<Machine> *aMachine /* = NULL */)
2902{
2903 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
2904
2905 AutoCaller autoCaller(this);
2906 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
2907
2908 {
2909 AutoReadLock al(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2910
2911 for (MachinesOList::iterator it = m->ollMachines.begin();
2912 it != m->ollMachines.end();
2913 ++it)
2914 {
2915 ComObjPtr<Machine> pMachine2 = *it;
2916 /* sanity */
2917 AutoLimitedCaller machCaller(pMachine2);
2918 AssertComRC(machCaller.rc());
2919
2920 if (pMachine2->getId() == aId)
2921 {
2922 rc = S_OK;
2923 if (aMachine)
2924 *aMachine = pMachine2;
2925 break;
2926 }
2927 }
2928 }
2929
2930 if (aSetError && FAILED(rc))
2931 setError(VBOX_E_OBJECT_NOT_FOUND,
2932 tr("Could not find a registered machine with UUID {%RTuuid}"),
2933 aId.raw());
2934
2935 return rc;
2936}
2937
2938/**
2939 * Searches for a Medium object with the given ID or location in the list of
2940 * registered hard disks. If both ID and location are specified, the first
2941 * object that matches either of them (not necessarily both) is returned.
2942 *
2943 * @param aId ID of the hard disk (unused when NULL).
2944 * @param aLocation Full location specification (unused NULL).
2945 * @param aSetError If @c true , the appropriate error info is set in case
2946 * when the hard disk is not found.
2947 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2948 *
2949 * @return S_OK when found or E_INVALIDARG when not found.
2950 *
2951 * @note Locks the media tree for reading.
2952 */
2953HRESULT VirtualBox::findHardDisk(const Guid *aId,
2954 CBSTR aLocation,
2955 bool aSetError,
2956 ComObjPtr<Medium> *aHardDisk /*= NULL*/)
2957{
2958 AssertReturn(aId || aLocation, E_INVALIDARG);
2959
2960 // we use the hard disks map, but it is protected by the
2961 // hard disk _list_ lock handle
2962 AutoReadLock alock(m->ollHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);
2963
2964 /* first, look up by UUID in the map if UUID is provided */
2965 if (aId)
2966 {
2967 HardDiskMap::const_iterator it = m->mapHardDisks.find(*aId);
2968 if (it != m->mapHardDisks.end())
2969 {
2970 if (aHardDisk)
2971 *aHardDisk = (*it).second;
2972 return S_OK;
2973 }
2974 }
2975
2976 /* then iterate and search by location */
2977 int result = -1;
2978 if (aLocation)
2979 {
2980 Utf8Str location = aLocation;
2981
2982 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin();
2983 it != m->mapHardDisks.end();
2984 ++ it)
2985 {
2986 const ComObjPtr<Medium> &hd = (*it).second;
2987
2988 HRESULT rc = hd->compareLocationTo(location.c_str(), result);
2989 if (FAILED(rc)) return rc;
2990
2991 if (result == 0)
2992 {
2993 if (aHardDisk)
2994 *aHardDisk = hd;
2995 break;
2996 }
2997 }
2998 }
2999
3000 HRESULT rc = result == 0 ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3001
3002 if (aSetError && result != 0)
3003 {
3004 if (aId)
3005 setError(rc,
3006 tr("Could not find a hard disk with UUID {%RTuuid} in the media registry ('%s')"),
3007 aId->raw(),
3008 m->strSettingsFilePath.raw());
3009 else
3010 setError(rc,
3011 tr("Could not find a hard disk with location '%ls' in the media registry ('%s')"),
3012 aLocation,
3013 m->strSettingsFilePath.raw());
3014 }
3015
3016 return rc;
3017}
3018
3019/**
3020 * Searches for a Medium object with the given ID or location in the list of
3021 * registered DVD images. If both ID and file path are specified, the first
3022 * object that matches either of them (not necessarily both) is returned.
3023 *
3024 * @param aId ID of the DVD image (unused when NULL).
3025 * @param aLocation Full path to the image file (unused when NULL).
3026 * @param aSetError If @c true, the appropriate error info is set in case when
3027 * the image is not found.
3028 * @param aImage Where to store the found image object (can be NULL).
3029 *
3030 * @return S_OK when found or E_INVALIDARG when not found.
3031 *
3032 * @note Locks the media tree for reading.
3033 */
3034HRESULT VirtualBox::findDVDImage(const Guid *aId,
3035 CBSTR aLocation,
3036 bool aSetError,
3037 ComObjPtr<Medium> *aImage /* = NULL */)
3038{
3039 AssertReturn(aId || aLocation, E_INVALIDARG);
3040
3041 Utf8Str location;
3042
3043 if (aLocation != NULL)
3044 {
3045 int vrc = calculateFullPath(Utf8Str(aLocation), location);
3046 if (RT_FAILURE(vrc))
3047 return setError(VBOX_E_FILE_ERROR,
3048 tr("Invalid image file location '%ls' (%Rrc)"),
3049 aLocation,
3050 vrc);
3051 }
3052
3053 AutoReadLock alock(m->ollDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3054
3055 bool found = false;
3056
3057 for (MediaList::const_iterator it = m->ollDVDImages.begin();
3058 it != m->ollDVDImages.end();
3059 ++ it)
3060 {
3061 /* no AutoCaller, registered image life time is bound to this */
3062 AutoReadLock imageLock(*it COMMA_LOCKVAL_SRC_POS);
3063
3064 found = (aId && (*it)->getId() == *aId) ||
3065 (aLocation != NULL &&
3066 RTPathCompare(location.c_str(),
3067 (*it)->getLocationFull().c_str()
3068 ) == 0);
3069 if (found)
3070 {
3071 if (aImage)
3072 *aImage = *it;
3073 break;
3074 }
3075 }
3076
3077 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3078
3079 if (aSetError && !found)
3080 {
3081 if (aId)
3082 setError(rc,
3083 tr("Could not find a CD/DVD image with UUID {%RTuuid} in the media registry ('%s')"),
3084 aId->raw(),
3085 m->strSettingsFilePath.raw());
3086 else
3087 setError(rc,
3088 tr("Could not find a CD/DVD image with location '%ls' in the media registry ('%s')"),
3089 aLocation,
3090 m->strSettingsFilePath.raw());
3091 }
3092
3093 return rc;
3094}
3095
3096/**
3097 * Searches for a Medium object with the given ID or location in the
3098 * collection of registered DVD images. If both ID and file path are specified,
3099 * the first object that matches either of them (not necessarily both) is
3100 * returned.
3101 *
3102 * @param aId ID of the DVD image (unused when NULL).
3103 * @param aLocation Full path to the image file (unused when NULL).
3104 * @param aSetError If @c true, the appropriate error info is set in case when
3105 * the image is not found.
3106 * @param aImage Where to store the found image object (can be NULL).
3107 *
3108 * @return S_OK when found or E_INVALIDARG when not found.
3109 *
3110 * @note Locks the media tree for reading.
3111 */
3112HRESULT VirtualBox::findFloppyImage(const Guid *aId,
3113 CBSTR aLocation,
3114 bool aSetError,
3115 ComObjPtr<Medium> *aImage /* = NULL */)
3116{
3117 AssertReturn(aId || aLocation, E_INVALIDARG);
3118
3119 Utf8Str location;
3120
3121 if (aLocation != NULL)
3122 {
3123 int vrc = calculateFullPath(Utf8Str(aLocation), location);
3124 if (RT_FAILURE(vrc))
3125 return setError(VBOX_E_FILE_ERROR,
3126 tr("Invalid image file location '%ls' (%Rrc)"),
3127 aLocation, vrc);
3128 }
3129
3130 AutoReadLock alock(m->ollFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3131
3132 bool found = false;
3133
3134 for (MediaList::const_iterator it = m->ollFloppyImages.begin();
3135 it != m->ollFloppyImages.end();
3136 ++ it)
3137 {
3138 /* no AutoCaller, registered image life time is bound to this */
3139 AutoReadLock imageLock(*it COMMA_LOCKVAL_SRC_POS);
3140
3141 found = (aId && (*it)->getId() == *aId) ||
3142 (aLocation != NULL &&
3143 RTPathCompare(location.c_str(),
3144 (*it)->getLocationFull().c_str()
3145 ) == 0);
3146 if (found)
3147 {
3148 if (aImage)
3149 *aImage = *it;
3150 break;
3151 }
3152 }
3153
3154 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
3155
3156 if (aSetError && !found)
3157 {
3158 if (aId)
3159 setError(rc,
3160 tr("Could not find a floppy image with UUID {%RTuuid} in the media registry ('%s')"),
3161 aId->raw(),
3162 m->strSettingsFilePath.raw());
3163 else
3164 setError(rc,
3165 tr("Could not find a floppy image with location '%ls' in the media registry ('%s')"),
3166 aLocation,
3167 m->strSettingsFilePath.raw());
3168 }
3169
3170 return rc;
3171}
3172
3173HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType,
3174 GuestOSType*& pGuestOSType)
3175{
3176 /* Look for a GuestOSType object */
3177 AssertMsg(m->ollGuestOSTypes.size() != 0,
3178 ("Guest OS types array must be filled"));
3179
3180 if (bstrOSType.isEmpty())
3181 {
3182 pGuestOSType = NULL;
3183 return S_OK;
3184 }
3185
3186 AutoReadLock alock(m->ollGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3187 for (GuestOSTypesOList::const_iterator it = m->ollGuestOSTypes.begin();
3188 it != m->ollGuestOSTypes.end();
3189 ++it)
3190 {
3191 if ((*it)->id() == bstrOSType)
3192 {
3193 pGuestOSType = *it;
3194 return S_OK;
3195 }
3196 }
3197
3198 return setError(VBOX_E_OBJECT_NOT_FOUND,
3199 tr("Guest OS type '%ls' is invalid"),
3200 bstrOSType.raw());
3201}
3202
3203const ComObjPtr<Host>& VirtualBox::host() const
3204{
3205 return m->pHost;
3206}
3207
3208const ComObjPtr<SystemProperties>& VirtualBox::systemProperties() const
3209{
3210 return m->pSystemProperties;
3211}
3212
3213#ifdef VBOX_WITH_RESOURCE_USAGE_API
3214const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const
3215{
3216 return m->pPerformanceCollector;
3217}
3218#endif /* VBOX_WITH_RESOURCE_USAGE_API */
3219
3220/**
3221 * Returns the default machine folder from the system properties
3222 * with proper locking.
3223 * @return
3224 */
3225const Utf8Str& VirtualBox::getDefaultMachineFolder() const
3226{
3227 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3228 return m->pSystemProperties->m_strDefaultMachineFolder;
3229}
3230
3231/**
3232 * Returns the default hard disk folder from the system properties
3233 * with proper locking.
3234 * @return
3235 */
3236const Utf8Str& VirtualBox::getDefaultHardDiskFolder() const
3237{
3238 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3239 return m->pSystemProperties->m_strDefaultHardDiskFolder;
3240}
3241
3242/**
3243 * Returns the default hard disk format from the system properties
3244 * with proper locking.
3245 * @return
3246 */
3247const Utf8Str& VirtualBox::getDefaultHardDiskFormat() const
3248{
3249 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS);
3250 return m->pSystemProperties->m_strDefaultHardDiskFormat;
3251}
3252
3253const Utf8Str& VirtualBox::homeDir() const
3254{
3255 return m->strHomeDir;
3256}
3257
3258/**
3259 * Calculates the absolute path of the given path taking the VirtualBox home
3260 * directory as the current directory.
3261 *
3262 * @param aPath Path to calculate the absolute path for.
3263 * @param aResult Where to put the result (used only on success, can be the
3264 * same Utf8Str instance as passed in @a aPath).
3265 * @return IPRT result.
3266 *
3267 * @note Doesn't lock any object.
3268 */
3269int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
3270{
3271 AutoCaller autoCaller(this);
3272 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE);
3273
3274 /* no need to lock since mHomeDir is const */
3275
3276 char folder[RTPATH_MAX];
3277 int vrc = RTPathAbsEx(m->strHomeDir.c_str(), strPath.c_str(), folder, sizeof(folder));
3278 if (RT_SUCCESS(vrc))
3279 aResult = folder;
3280
3281 return vrc;
3282}
3283
3284/**
3285 * Tries to calculate the relative path of the given absolute path using the
3286 * directory of the VirtualBox settings file as the base directory.
3287 *
3288 * @param aPath Absolute path to calculate the relative path for.
3289 * @param aResult Where to put the result (used only when it's possible to
3290 * make a relative path from the given absolute path; otherwise
3291 * left untouched).
3292 *
3293 * @note Doesn't lock any object.
3294 */
3295void VirtualBox::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
3296{
3297 AutoCaller autoCaller(this);
3298 AssertComRCReturnVoid(autoCaller.rc());
3299
3300 /* no need to lock since mHomeDir is const */
3301
3302 Utf8Str settingsDir = m->strHomeDir;
3303
3304 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
3305 {
3306 /* when assigning, we create a separate Utf8Str instance because both
3307 * aPath and aResult can point to the same memory location when this
3308 * func is called (if we just do aResult = aPath, aResult will be freed
3309 * first, and since its the same as aPath, an attempt to copy garbage
3310 * will be made. */
3311 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
3312 }
3313}
3314
3315// private methods
3316/////////////////////////////////////////////////////////////////////////////
3317
3318/**
3319 * Checks if there is a hard disk, DVD or floppy image with the given ID or
3320 * location already registered.
3321 *
3322 * On return, sets @a aConflict to the string describing the conflicting medium,
3323 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
3324 * either case. A failure is unexpected.
3325 *
3326 * @param aId UUID to check.
3327 * @param aLocation Location to check.
3328 * @param aConflict Where to return parameters of the conflicting medium.
3329 *
3330 * @note Locks the media tree and media objects for reading.
3331 */
3332HRESULT VirtualBox::checkMediaForConflicts2(const Guid &aId,
3333 const Utf8Str &aLocation,
3334 Utf8Str &aConflict)
3335{
3336 aConflict.setNull();
3337
3338 AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
3339
3340 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3341
3342 HRESULT rc = S_OK;
3343
3344 Bstr bstrLocation(aLocation);
3345
3346 {
3347 ComObjPtr<Medium> hardDisk;
3348 rc = findHardDisk(&aId, bstrLocation, false /* aSetError */, &hardDisk);
3349 if (SUCCEEDED(rc))
3350 {
3351 /* Note: no AutoCaller since bound to this */
3352 AutoReadLock mediaLock(hardDisk COMMA_LOCKVAL_SRC_POS);
3353 aConflict = Utf8StrFmt(tr("hard disk '%s' with UUID {%RTuuid}"),
3354 hardDisk->getLocationFull().raw(),
3355 hardDisk->getId().raw());
3356 return S_OK;
3357 }
3358 }
3359
3360 {
3361 ComObjPtr<Medium> image;
3362 rc = findDVDImage(&aId, bstrLocation, false /* aSetError */, &image);
3363 if (SUCCEEDED(rc))
3364 {
3365 /* Note: no AutoCaller since bound to this */
3366 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
3367 aConflict = Utf8StrFmt(tr("CD/DVD image '%s' with UUID {%RTuuid}"),
3368 image->getLocationFull().raw(),
3369 image->getId().raw());
3370 return S_OK;
3371 }
3372 }
3373
3374 {
3375 ComObjPtr<Medium> image;
3376 rc = findFloppyImage(&aId, bstrLocation, false /* aSetError */, &image);
3377 if (SUCCEEDED(rc))
3378 {
3379 /* Note: no AutoCaller since bound to this */
3380 AutoReadLock mediaLock(image COMMA_LOCKVAL_SRC_POS);
3381 aConflict = Utf8StrFmt(tr("floppy image '%s' with UUID {%RTuuid}"),
3382 image->getLocationFull().raw(),
3383 image->getId().raw());
3384 return S_OK;
3385 }
3386 }
3387
3388 return S_OK;
3389}
3390
3391/**
3392 * Called from Machine::prepareSaveSettings() when it has detected
3393 * that a machine has been renamed. Such renames will require
3394 * updating the global media registry during the
3395 * VirtualBox::saveSettings() that follows later.
3396*
3397 * When a machine is renamed, there may well be media (in particular,
3398 * diff images for snapshots) in the global registry that will need
3399 * to have their paths updated. Before 3.2, Machine::saveSettings
3400 * used to call VirtualBox::saveSettings implicitly, which was both
3401 * unintuitive and caused locking order problems. Now, we remeber
3402 * such pending name changes with this method so that
3403 * VirtualBox::saveSettings() can process them properly.
3404 */
3405void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir,
3406 const Utf8Str &strNewConfigDir)
3407{
3408 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3409
3410 Data::PendingMachineRename pmr;
3411 pmr.strConfigDirOld = strOldConfigDir;
3412 pmr.strConfigDirNew = strNewConfigDir;
3413 m->llPendingMachineRenames.push_back(pmr);
3414}
3415
3416/**
3417 * Helper function to write out the configuration tree.
3418 *
3419 * @note Caller must have locked the VirtualBox object for writing!
3420 */
3421HRESULT VirtualBox::saveSettings()
3422{
3423 AutoCaller autoCaller(this);
3424 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3425
3426 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
3427 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL);
3428
3429 HRESULT rc = S_OK;
3430
3431 try
3432 {
3433 // lock the lists while we're here
3434 AutoReadLock machinesLock(m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3435
3436 // machines
3437 settings::MachinesRegistry machinesTemp;
3438 {
3439 for (MachinesOList::iterator it = m->ollMachines.begin();
3440 it != m->ollMachines.end();
3441 ++it)
3442 {
3443 Machine *pMachine = *it;
3444 // save actual machine registry entry
3445 settings::MachineRegistryEntry mre;
3446 rc = pMachine->saveRegistryEntry(mre);
3447 machinesTemp.push_back(mre);
3448 }
3449 }
3450
3451 // lock all media for the following; use a write lock because we're
3452 // modifying the PendingMachineRenamesList, which is protected by this
3453 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3454
3455 // if a machine was renamed, then we'll need to refresh media paths
3456 if (m->llPendingMachineRenames.size())
3457 {
3458 // make a single list from the three media lists so we don't need three loops
3459 MediaList llAllMedia;
3460 // with hard disks, we must use the map, not the list, because the list only has base images
3461 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it)
3462 llAllMedia.push_back(it->second);
3463 for (MediaList::iterator it = m->ollDVDImages.begin(); it != m->ollDVDImages.end(); ++it)
3464 llAllMedia.push_back(*it);
3465 for (MediaList::iterator it = m->ollFloppyImages.begin(); it != m->ollFloppyImages.end(); ++it)
3466 llAllMedia.push_back(*it);
3467
3468 for (MediaList::iterator it = llAllMedia.begin();
3469 it != llAllMedia.end();
3470 ++it)
3471 {
3472 Medium *pMedium = *it;
3473 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin();
3474 it2 != m->llPendingMachineRenames.end();
3475 ++it2)
3476 {
3477 const Data::PendingMachineRename &pmr = *it2;
3478 const char *pcszOld = pmr.strConfigDirOld.c_str();
3479 const char *pcszNew = pmr.strConfigDirNew.c_str();
3480 pMedium->updatePath(pcszOld, pcszNew);
3481 }
3482 }
3483 // done, don't do it again until we have more machine renames
3484 m->llPendingMachineRenames.clear();
3485 }
3486
3487 // hard disks
3488 settings::MediaList hardDisksTemp;
3489 for (MediaList::const_iterator it = m->ollHardDisks.begin();
3490 it != m->ollHardDisks.end();
3491 ++it)
3492 {
3493 settings::Medium med;
3494 rc = (*it)->saveSettings(med); // this recurses into its children
3495 if (FAILED(rc)) throw rc;
3496 hardDisksTemp.push_back(med);
3497 }
3498
3499 /* CD/DVD images */
3500 settings::MediaList dvdsTemp;
3501 for (MediaList::const_iterator it = m->ollDVDImages.begin();
3502 it != m->ollDVDImages.end();
3503 ++it)
3504 {
3505 settings::Medium med;
3506 rc = (*it)->saveSettings(med);
3507 if (FAILED(rc)) throw rc;
3508 dvdsTemp.push_back(med);
3509 }
3510
3511 /* floppy images */
3512 settings::MediaList floppiesTemp;
3513 for (MediaList::const_iterator it = m->ollFloppyImages.begin();
3514 it != m->ollFloppyImages.end();
3515 ++it)
3516 {
3517 settings::Medium med;
3518 rc = (*it)->saveSettings(med);
3519 if (FAILED(rc)) throw rc;
3520 floppiesTemp.push_back(med);
3521 }
3522
3523 settings::DHCPServersList dhcpServersTemp;
3524 {
3525 AutoReadLock dhcpLock(m->ollDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3526 for (DHCPServersOList::const_iterator it = m->ollDHCPServers.begin();
3527 it != m->ollDHCPServers.end();
3528 ++it)
3529 {
3530 settings::DHCPServer d;
3531 rc = (*it)->saveSettings(d);
3532 if (FAILED(rc)) throw rc;
3533 dhcpServersTemp.push_back(d);
3534 }
3535 }
3536
3537 /* now copy the temp data to the config file under the VirtualBox lock */
3538 m->pMainConfigFile->llMachines = machinesTemp;
3539 m->pMainConfigFile->llHardDisks = hardDisksTemp;
3540 m->pMainConfigFile->llDvdImages = dvdsTemp;
3541 m->pMainConfigFile->llFloppyImages = floppiesTemp;
3542 m->pMainConfigFile->llDhcpServers = dhcpServersTemp;
3543
3544 // leave extra data alone, it's still in the config file
3545
3546 /* host data (USB filters) */
3547 rc = m->pHost->saveSettings(m->pMainConfigFile->host);
3548 if (FAILED(rc)) throw rc;
3549
3550 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties);
3551 if (FAILED(rc)) throw rc;
3552
3553 // and write out the XML, still under the lock
3554 m->pMainConfigFile->write(m->strSettingsFilePath);
3555 }
3556 catch (HRESULT err)
3557 {
3558 /* we assume that error info is set by the thrower */
3559 rc = err;
3560 }
3561 catch (...)
3562 {
3563 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
3564 }
3565
3566 return rc;
3567}
3568
3569/**
3570 * Helper to register the machine.
3571 *
3572 * When called during VirtualBox startup, adds the given machine to the
3573 * collection of registered machines. Otherwise tries to mark the machine
3574 * as registered, and, if succeeded, adds it to the collection and
3575 * saves global settings.
3576 *
3577 * @note The caller must have added itself as a caller of the @a aMachine
3578 * object if calls this method not on VirtualBox startup.
3579 *
3580 * @param aMachine machine to register
3581 *
3582 * @note Locks objects!
3583 */
3584HRESULT VirtualBox::registerMachine(Machine *aMachine)
3585{
3586 ComAssertRet(aMachine, E_INVALIDARG);
3587
3588 AutoCaller autoCaller(this);
3589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3590
3591 HRESULT rc = S_OK;
3592
3593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3594
3595 {
3596 ComObjPtr<Machine> pMachine;
3597 rc = findMachine(aMachine->getId(), false /* aDoSetError */, &pMachine);
3598 if (SUCCEEDED(rc))
3599 {
3600 /* sanity */
3601 AutoLimitedCaller machCaller(pMachine);
3602 AssertComRC(machCaller.rc());
3603
3604 return setError(E_INVALIDARG,
3605 tr("Registered machine with UUID {%RTuuid} ('%ls') already exists"),
3606 aMachine->getId().raw(),
3607 pMachine->getSettingsFileFull().raw());
3608 }
3609
3610 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3611 rc = S_OK;
3612 }
3613
3614 if (autoCaller.state() != InInit)
3615 {
3616 rc = aMachine->trySetRegistered(TRUE);
3617 if (FAILED(rc)) return rc;
3618 }
3619
3620 /* add to the collection of registered machines */
3621 m->ollMachines.addChild(aMachine);
3622
3623 if (autoCaller.state() != InInit)
3624 rc = saveSettings();
3625
3626 return rc;
3627}
3628
3629/**
3630 * Remembers the given hard disk by storing it in the hard disk registry.
3631 *
3632 * @param aHardDisk Hard disk object to remember.
3633 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3634 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3635 *
3636 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3637 */
3638HRESULT VirtualBox::registerHardDisk(Medium *aHardDisk,
3639 bool *pfNeedsSaveSettings)
3640{
3641 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3642
3643 AutoCaller autoCaller(this);
3644 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3645
3646 AutoCaller hardDiskCaller(aHardDisk);
3647 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3648
3649 // caller must hold the media tree write lock
3650 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3651
3652 Guid id;
3653 Utf8Str strLocationFull;
3654 ComObjPtr<Medium> pParent;
3655 {
3656 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3657 id = aHardDisk->getId();
3658 strLocationFull = aHardDisk->getLocationFull();
3659 pParent = aHardDisk->getParent();
3660 }
3661
3662 HRESULT rc;
3663
3664 Utf8Str strConflict;
3665 rc = checkMediaForConflicts2(id,
3666 strLocationFull,
3667 strConflict);
3668 if (FAILED(rc)) return rc;
3669
3670 if (strConflict.length())
3671 return setError(E_INVALIDARG,
3672 tr("Cannot register the hard disk '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3673 strLocationFull.raw(),
3674 id.raw(),
3675 strConflict.raw(),
3676 m->strSettingsFilePath.raw());
3677
3678 // store base (root) hard disks in the list
3679 if (pParent.isNull())
3680 m->ollHardDisks.getList().push_back(aHardDisk);
3681 // access the list directly because we already locked the list above
3682
3683 // store all hard disks (even differencing images) in the map
3684 m->mapHardDisks[id] = aHardDisk;
3685
3686 if (pfNeedsSaveSettings)
3687 *pfNeedsSaveSettings = true;
3688
3689 return rc;
3690}
3691
3692/**
3693 * Removes the given hard disk from the hard disk registry.
3694 *
3695 * @param aHardDisk Hard disk object to remove.
3696 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3697 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3698 *
3699 * @note Caller must hold the media tree lock for writing; in addition, this locks @a aHardDisk for reading
3700 */
3701HRESULT VirtualBox::unregisterHardDisk(Medium *aHardDisk,
3702 bool *pfNeedsSaveSettings)
3703{
3704 AssertReturn(aHardDisk != NULL, E_INVALIDARG);
3705
3706 AutoCaller autoCaller(this);
3707 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3708
3709 AutoCaller hardDiskCaller(aHardDisk);
3710 AssertComRCReturn(hardDiskCaller.rc(), hardDiskCaller.rc());
3711
3712 // caller must hold the media tree write lock
3713 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3714
3715 Guid id;
3716 ComObjPtr<Medium> pParent;
3717 {
3718 AutoReadLock hardDiskLock(aHardDisk COMMA_LOCKVAL_SRC_POS);
3719 id = aHardDisk->getId();
3720 pParent = aHardDisk->getParent();
3721 }
3722
3723 // remove base (root) hard disks from the list
3724 if (pParent.isNull())
3725 m->ollHardDisks.getList().remove(aHardDisk);
3726 // access the list directly because caller must have locked the list
3727
3728 // remove all hard disks (even differencing images) from map
3729 size_t cnt = m->mapHardDisks.erase(id);
3730 Assert(cnt == 1);
3731 NOREF(cnt);
3732
3733 if (pfNeedsSaveSettings)
3734 *pfNeedsSaveSettings = true;
3735
3736 return S_OK;
3737}
3738
3739/**
3740 * Remembers the given image by storing it in the CD/DVD or floppy image registry.
3741 *
3742 * @param argImage Image object to remember.
3743 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3744 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3745 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3746 *
3747 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3748 */
3749HRESULT VirtualBox::registerImage(Medium *argImage,
3750 DeviceType_T argType,
3751 bool *pfNeedsSaveSettings)
3752{
3753 AssertReturn(argImage != NULL, E_INVALIDARG);
3754
3755 AutoCaller autoCaller(this);
3756 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3757
3758 AutoCaller imageCaller(argImage);
3759 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3760
3761 // caller must hold the media tree write lock
3762 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3763
3764 Guid id;
3765 Utf8Str strLocationFull;
3766 ComObjPtr<Medium> pParent;
3767 {
3768 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3769 id = argImage->getId();
3770 strLocationFull = argImage->getLocationFull();
3771 pParent = argImage->getParent();
3772 }
3773
3774 // work on DVDs or floppies list?
3775 ObjectsList<Medium> &oll = (argType == DeviceType_DVD) ? m->ollDVDImages : m->ollFloppyImages;
3776
3777 HRESULT rc;
3778 // lock the images lists (list + map) while checking for conflicts
3779 AutoWriteLock al(oll.getLockHandle() COMMA_LOCKVAL_SRC_POS);
3780
3781 Utf8Str strConflict;
3782 rc = checkMediaForConflicts2(id,
3783 strLocationFull,
3784 strConflict);
3785 if (FAILED(rc)) return rc;
3786
3787 if (strConflict.length())
3788 return setError(VBOX_E_INVALID_OBJECT_STATE,
3789 tr("Cannot register the image '%s' with UUID {%RTuuid} because a %s already exists in the media registry ('%s')"),
3790 strLocationFull.raw(),
3791 id.raw(),
3792 strConflict.raw(),
3793 m->strSettingsFilePath.raw());
3794
3795 // add to the collection
3796 oll.getList().push_back(argImage);
3797 // access the list directly because we already locked the list above
3798
3799 if (pfNeedsSaveSettings)
3800 *pfNeedsSaveSettings = true;
3801
3802 return rc;
3803}
3804
3805/**
3806 * Removes the given image from the CD/DVD or floppy image registry.
3807 *
3808 * @param argImage Image object to remove.
3809 * @param argType Either DeviceType_DVD or DeviceType_Floppy.
3810 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
3811 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
3812 *
3813 * @note Caller must hold the media tree lock for writing; in addition, this locks @a argImage for reading
3814 */
3815HRESULT VirtualBox::unregisterImage(Medium *argImage,
3816 DeviceType_T argType,
3817 bool *pfNeedsSaveSettings)
3818{
3819 AssertReturn(argImage != NULL, E_INVALIDARG);
3820
3821 AutoCaller autoCaller(this);
3822 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3823
3824 AutoCaller imageCaller(argImage);
3825 AssertComRCReturn(imageCaller.rc(), imageCaller.rc());
3826
3827 // caller must hold the media tree write lock
3828 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3829
3830 Guid id;
3831 ComObjPtr<Medium> pParent;
3832 {
3833 AutoReadLock al(argImage COMMA_LOCKVAL_SRC_POS);
3834 id = argImage->getId();
3835 pParent = argImage->getParent();
3836 }
3837
3838 // work on DVDs or floppies list?
3839 ObjectsList<Medium> &oll = (argType == DeviceType_DVD) ? m->ollDVDImages : m->ollFloppyImages;
3840
3841 // access the list directly because the caller must have requested the lock
3842 oll.getList().remove(argImage);
3843
3844 HRESULT rc = S_OK;
3845
3846 if (pfNeedsSaveSettings)
3847 *pfNeedsSaveSettings = true;
3848
3849 return rc;
3850}
3851
3852/**
3853 * Creates the path to the specified file according to the path information
3854 * present in the file name.
3855 *
3856 * Note that the given file name must contain the full path otherwise the
3857 * extracted relative path will be created based on the current working
3858 * directory which is normally unknown.
3859 *
3860 * @param aFileName Full file name which path needs to be created.
3861 *
3862 * @return Extended error information on failure to create the path.
3863 */
3864/* static */
3865HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName)
3866{
3867 Utf8Str strDir(strFileName);
3868 strDir.stripFilename();
3869 if (!RTDirExists(strDir.c_str()))
3870 {
3871 int vrc = RTDirCreateFullPath(strDir.c_str(), 0777);
3872 if (RT_FAILURE(vrc))
3873 return setError(E_FAIL,
3874 tr("Could not create the directory '%s' (%Rrc)"),
3875 strDir.c_str(),
3876 vrc);
3877 }
3878
3879 return S_OK;
3880}
3881
3882/**
3883 * Handles unexpected exceptions by turning them into COM errors in release
3884 * builds or by hitting a breakpoint in the release builds.
3885 *
3886 * Usage pattern:
3887 * @code
3888 try
3889 {
3890 // ...
3891 }
3892 catch (LaLalA)
3893 {
3894 // ...
3895 }
3896 catch (...)
3897 {
3898 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3899 }
3900 * @endcode
3901 *
3902 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
3903 */
3904/* static */
3905HRESULT VirtualBox::handleUnexpectedExceptions(RT_SRC_POS_DECL)
3906{
3907 try
3908 {
3909 /* re-throw the current exception */
3910 throw;
3911 }
3912 catch (const xml::Error &err)
3913 {
3914 return setError(E_FAIL, tr("%s.\n%s[%d] (%s)"),
3915 err.what(),
3916 pszFile, iLine, pszFunction);
3917 }
3918 catch (const std::exception &err)
3919 {
3920 return setError(E_FAIL, tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
3921 err.what(), typeid(err).name(),
3922 pszFile, iLine, pszFunction);
3923 }
3924 catch (...)
3925 {
3926 return setError(E_FAIL, tr("Unknown exception\n%s[%d] (%s)"),
3927 pszFile, iLine, pszFunction);
3928 }
3929
3930 /* should not get here */
3931 AssertFailed();
3932 return E_FAIL;
3933}
3934
3935const Utf8Str& VirtualBox::settingsFilePath()
3936{
3937 return m->strSettingsFilePath;
3938}
3939
3940/**
3941 * Returns the lock handle which protects the media trees (hard disks,
3942 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
3943 * are no longer protected by the VirtualBox lock, but by this more
3944 * specialized lock. Mind the locking order: always request this lock
3945 * after the VirtualBox object lock but before the locks of the media
3946 * objects contained in these lists. See AutoLock.h.
3947 */
3948RWLockHandle& VirtualBox::getMediaTreeLockHandle()
3949{
3950 return m->lockMedia;
3951}
3952
3953/**
3954 * Thread function that watches the termination of all client processes
3955 * that have opened sessions using IVirtualBox::OpenSession()
3956 */
3957// static
3958DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)
3959{
3960 LogFlowFuncEnter();
3961
3962 VirtualBox *that = (VirtualBox*)pvUser;
3963 Assert(that);
3964
3965 typedef std::vector< ComObjPtr<Machine> > MachineVector;
3966 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;
3967
3968 SessionMachineVector machines;
3969 MachineVector spawnedMachines;
3970
3971 size_t cnt = 0;
3972 size_t cntSpawned = 0;
3973
3974#if defined(RT_OS_WINDOWS)
3975
3976 HRESULT hrc = CoInitializeEx(NULL,
3977 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
3978 COINIT_SPEED_OVER_MEMORY);
3979 AssertComRC(hrc);
3980
3981 /// @todo (dmik) processes reaping!
3982
3983 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3984 handles[0] = that->m->updateReq;
3985
3986 do
3987 {
3988 AutoCaller autoCaller(that);
3989 /* VirtualBox has been early uninitialized, terminate */
3990 if (!autoCaller.isOk())
3991 break;
3992
3993 do
3994 {
3995 /* release the caller to let uninit() ever proceed */
3996 autoCaller.release();
3997
3998 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),
3999 handles,
4000 FALSE,
4001 INFINITE);
4002
4003 /* Restore the caller before using VirtualBox. If it fails, this
4004 * means VirtualBox is being uninitialized and we must terminate. */
4005 autoCaller.add();
4006 if (!autoCaller.isOk())
4007 break;
4008
4009 bool update = false;
4010
4011 if (rc == WAIT_OBJECT_0)
4012 {
4013 /* update event is signaled */
4014 update = true;
4015 }
4016 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4017 {
4018 /* machine mutex is released */
4019 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4020 update = true;
4021 }
4022 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4023 {
4024 /* machine mutex is abandoned due to client process termination */
4025 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4026 update = true;
4027 }
4028 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
4029 {
4030 /* spawned VM process has terminated (normally or abnormally) */
4031 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->
4032 checkForSpawnFailure();
4033 update = true;
4034 }
4035
4036 if (update)
4037 {
4038 /* close old process handles */
4039 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)
4040 CloseHandle(handles[i]);
4041
4042 // lock the machines list for reading
4043 AutoReadLock thatLock(that->m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4044
4045 /* obtain a new set of opened machines */
4046 cnt = 0;
4047 machines.clear();
4048
4049 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4050 it != that->m->ollMachines.end();
4051 ++it)
4052 {
4053 /// @todo handle situations with more than 64 objects
4054 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
4055 ("MAXIMUM_WAIT_OBJECTS reached"));
4056
4057 ComObjPtr<SessionMachine> sm;
4058 HANDLE ipcSem;
4059 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4060 {
4061 machines.push_back(sm);
4062 handles[1 + cnt] = ipcSem;
4063 ++cnt;
4064 }
4065 }
4066
4067 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4068
4069 /* obtain a new set of spawned machines */
4070 cntSpawned = 0;
4071 spawnedMachines.clear();
4072
4073 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4074 it != that->m->ollMachines.end();
4075 ++it)
4076 {
4077 /// @todo handle situations with more than 64 objects
4078 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
4079 ("MAXIMUM_WAIT_OBJECTS reached"));
4080
4081 RTPROCESS pid;
4082 if ((*it)->isSessionSpawning(&pid))
4083 {
4084 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);
4085 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
4086 pid, GetLastError()));
4087 if (rc == 0)
4088 {
4089 spawnedMachines.push_back(*it);
4090 handles[1 + cnt + cntSpawned] = ph;
4091 ++cntSpawned;
4092 }
4093 }
4094 }
4095
4096 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4097
4098 // machines lock unwinds here
4099 }
4100 }
4101 while (true);
4102 }
4103 while (0);
4104
4105 /* close old process handles */
4106 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4107 CloseHandle(handles[i]);
4108
4109 /* release sets of machines if any */
4110 machines.clear();
4111 spawnedMachines.clear();
4112
4113 ::CoUninitialize();
4114
4115#elif defined(RT_OS_OS2)
4116
4117 /// @todo (dmik) processes reaping!
4118
4119 /* according to PMREF, 64 is the maximum for the muxwait list */
4120 SEMRECORD handles[64];
4121
4122 HMUX muxSem = NULLHANDLE;
4123
4124 do
4125 {
4126 AutoCaller autoCaller(that);
4127 /* VirtualBox has been early uninitialized, terminate */
4128 if (!autoCaller.isOk())
4129 break;
4130
4131 do
4132 {
4133 /* release the caller to let uninit() ever proceed */
4134 autoCaller.release();
4135
4136 int vrc = RTSemEventWait(that->m->updateReq, 500);
4137
4138 /* Restore the caller before using VirtualBox. If it fails, this
4139 * means VirtualBox is being uninitialized and we must terminate. */
4140 autoCaller.add();
4141 if (!autoCaller.isOk())
4142 break;
4143
4144 bool update = false;
4145 bool updateSpawned = false;
4146
4147 if (RT_SUCCESS(vrc))
4148 {
4149 /* update event is signaled */
4150 update = true;
4151 updateSpawned = true;
4152 }
4153 else
4154 {
4155 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4156 ("RTSemEventWait returned %Rrc\n", vrc));
4157
4158 /* are there any mutexes? */
4159 if (cnt > 0)
4160 {
4161 /* figure out what's going on with machines */
4162
4163 unsigned long semId = 0;
4164 APIRET arc = ::DosWaitMuxWaitSem(muxSem,
4165 SEM_IMMEDIATE_RETURN, &semId);
4166
4167 if (arc == NO_ERROR)
4168 {
4169 /* machine mutex is normally released */
4170 Assert(semId >= 0 && semId < cnt);
4171 if (semId >= 0 && semId < cnt)
4172 {
4173#ifdef DEBUG
4174 {
4175 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4176 LogFlowFunc(("released mutex: machine='%ls'\n",
4177 machines[semId]->name().raw()));
4178 }
4179#endif
4180 machines[semId]->checkForDeath();
4181 }
4182 update = true;
4183 }
4184 else if (arc == ERROR_SEM_OWNER_DIED)
4185 {
4186 /* machine mutex is abandoned due to client process
4187 * termination; find which mutex is in the Owner Died
4188 * state */
4189 for (size_t i = 0; i < cnt; ++ i)
4190 {
4191 PID pid; TID tid;
4192 unsigned long reqCnt;
4193 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);
4194 if (arc == ERROR_SEM_OWNER_DIED)
4195 {
4196 /* close the dead mutex as asked by PMREF */
4197 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);
4198
4199 Assert(i >= 0 && i < cnt);
4200 if (i >= 0 && i < cnt)
4201 {
4202#ifdef DEBUG
4203 {
4204 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);
4205 LogFlowFunc(("mutex owner dead: machine='%ls'\n",
4206 machines[i]->name().raw()));
4207 }
4208#endif
4209 machines[i]->checkForDeath();
4210 }
4211 }
4212 }
4213 update = true;
4214 }
4215 else
4216 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4217 ("DosWaitMuxWaitSem returned %d\n", arc));
4218 }
4219
4220 /* are there any spawning sessions? */
4221 if (cntSpawned > 0)
4222 {
4223 for (size_t i = 0; i < cntSpawned; ++ i)
4224 updateSpawned |= (spawnedMachines[i])->
4225 checkForSpawnFailure();
4226 }
4227 }
4228
4229 if (update || updateSpawned)
4230 {
4231 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);
4232
4233 if (update)
4234 {
4235 /* close the old muxsem */
4236 if (muxSem != NULLHANDLE)
4237 ::DosCloseMuxWaitSem(muxSem);
4238
4239 /* obtain a new set of opened machines */
4240 cnt = 0;
4241 machines.clear();
4242
4243 for (MachinesOList::iterator it = that->m->llMachines.begin();
4244 it != that->m->llMachines.end(); ++ it)
4245 {
4246 /// @todo handle situations with more than 64 objects
4247 AssertMsg(cnt <= 64 /* according to PMREF */,
4248 ("maximum of 64 mutex semaphores reached (%d)",
4249 cnt));
4250
4251 ComObjPtr<SessionMachine> sm;
4252 HMTX ipcSem;
4253 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))
4254 {
4255 machines.push_back(sm);
4256 handles[cnt].hsemCur = (HSEM)ipcSem;
4257 handles[cnt].ulUser = cnt;
4258 ++ cnt;
4259 }
4260 }
4261
4262 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4263
4264 if (cnt > 0)
4265 {
4266 /* create a new muxsem */
4267 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,
4268 handles,
4269 DCMW_WAIT_ANY);
4270 AssertMsg(arc == NO_ERROR,
4271 ("DosCreateMuxWaitSem returned %d\n", arc));
4272 NOREF(arc);
4273 }
4274 }
4275
4276 if (updateSpawned)
4277 {
4278 /* obtain a new set of spawned machines */
4279 spawnedMachines.clear();
4280
4281 for (MachinesOList::iterator it = that->m->llMachines.begin();
4282 it != that->m->llMachines.end(); ++ it)
4283 {
4284 if ((*it)->isSessionSpawning())
4285 spawnedMachines.push_back(*it);
4286 }
4287
4288 cntSpawned = spawnedMachines.size();
4289 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4290 }
4291 }
4292 }
4293 while (true);
4294 }
4295 while (0);
4296
4297 /* close the muxsem */
4298 if (muxSem != NULLHANDLE)
4299 ::DosCloseMuxWaitSem(muxSem);
4300
4301 /* release sets of machines if any */
4302 machines.clear();
4303 spawnedMachines.clear();
4304
4305#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4306
4307 bool update = false;
4308 bool updateSpawned = false;
4309
4310 do
4311 {
4312 AutoCaller autoCaller(that);
4313 if (!autoCaller.isOk())
4314 break;
4315
4316 do
4317 {
4318 /* release the caller to let uninit() ever proceed */
4319 autoCaller.release();
4320
4321 int rc = RTSemEventWait(that->m->updateReq, 500);
4322
4323 /*
4324 * Restore the caller before using VirtualBox. If it fails, this
4325 * means VirtualBox is being uninitialized and we must terminate.
4326 */
4327 autoCaller.add();
4328 if (!autoCaller.isOk())
4329 break;
4330
4331 if (RT_SUCCESS(rc) || update || updateSpawned)
4332 {
4333 /* RT_SUCCESS(rc) means an update event is signaled */
4334
4335 // lock the machines list for reading
4336 AutoReadLock thatLock(that->m->ollMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4337
4338 if (RT_SUCCESS(rc) || update)
4339 {
4340 /* obtain a new set of opened machines */
4341 machines.clear();
4342
4343 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4344 it != that->m->ollMachines.end();
4345 ++it)
4346 {
4347 ComObjPtr<SessionMachine> sm;
4348 if ((*it)->isSessionOpenOrClosing(sm))
4349 machines.push_back(sm);
4350 }
4351
4352 cnt = machines.size();
4353 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));
4354 }
4355
4356 if (RT_SUCCESS(rc) || updateSpawned)
4357 {
4358 /* obtain a new set of spawned machines */
4359 spawnedMachines.clear();
4360
4361 for (MachinesOList::iterator it = that->m->ollMachines.begin();
4362 it != that->m->ollMachines.end();
4363 ++it)
4364 {
4365 if ((*it)->isSessionSpawning())
4366 spawnedMachines.push_back(*it);
4367 }
4368
4369 cntSpawned = spawnedMachines.size();
4370 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));
4371 }
4372
4373 // machines lock unwinds here
4374 }
4375
4376 update = false;
4377 for (size_t i = 0; i < cnt; ++ i)
4378 update |= (machines[i])->checkForDeath();
4379
4380 updateSpawned = false;
4381 for (size_t i = 0; i < cntSpawned; ++ i)
4382 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();
4383
4384 /* reap child processes */
4385 {
4386 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
4387 if (that->m->llProcesses.size())
4388 {
4389 LogFlowFunc(("UPDATE: child process count = %d\n",
4390 that->m->llProcesses.size()));
4391 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();
4392 while (it != that->m->llProcesses.end())
4393 {
4394 RTPROCESS pid = *it;
4395 RTPROCSTATUS status;
4396 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);
4397 if (vrc == VINF_SUCCESS)
4398 {
4399 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",
4400 pid, pid, status.iStatus,
4401 status.enmReason));
4402 it = that->m->llProcesses.erase(it);
4403 }
4404 else
4405 {
4406 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4407 pid, pid, vrc));
4408 if (vrc != VERR_PROCESS_RUNNING)
4409 {
4410 /* remove the process if it is not already running */
4411 it = that->m->llProcesses.erase(it);
4412 }
4413 else
4414 ++ it;
4415 }
4416 }
4417 }
4418 }
4419 }
4420 while (true);
4421 }
4422 while (0);
4423
4424 /* release sets of machines if any */
4425 machines.clear();
4426 spawnedMachines.clear();
4427
4428#else
4429# error "Port me!"
4430#endif
4431
4432 LogFlowFuncLeave();
4433 return 0;
4434}
4435
4436/**
4437 * Thread function that handles custom events posted using #postEvent().
4438 */
4439// static
4440DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)
4441{
4442 LogFlowFuncEnter();
4443
4444 AssertReturn(pvUser, VERR_INVALID_POINTER);
4445
4446 // create an event queue for the current thread
4447 EventQueue *eventQ = new EventQueue();
4448 AssertReturn(eventQ, VERR_NO_MEMORY);
4449
4450 // return the queue to the one who created this thread
4451 *(static_cast <EventQueue **>(pvUser)) = eventQ;
4452 // signal that we're ready
4453 RTThreadUserSignal(thread);
4454
4455 BOOL ok = TRUE;
4456 Event *event = NULL;
4457
4458 while ((ok = eventQ->waitForEvent(&event)) && event)
4459 eventQ->handleEvent(event);
4460
4461 AssertReturn(ok, VERR_GENERAL_FAILURE);
4462
4463 delete eventQ;
4464
4465 LogFlowFuncLeave();
4466
4467 return 0;
4468}
4469
4470
4471////////////////////////////////////////////////////////////////////////////////
4472
4473/**
4474 * Takes the current list of registered callbacks of the managed VirtualBox
4475 * instance, and calls #handleCallback() for every callback item from the
4476 * list, passing the item as an argument.
4477 *
4478 * @note Locks the managed VirtualBox object for reading but leaves the lock
4479 * before iterating over callbacks and calling their methods.
4480 */
4481void *VirtualBox::CallbackEvent::handler()
4482{
4483 if (!mVirtualBox)
4484 return NULL;
4485
4486 AutoCaller autoCaller(mVirtualBox);
4487 if (!autoCaller.isOk())
4488 {
4489 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",
4490 autoCaller.state()));
4491 /* We don't need mVirtualBox any more, so release it */
4492 mVirtualBox = NULL;
4493 return NULL;
4494 }
4495
4496 CallbackList callbacks;
4497 {
4498 /* Make a copy to release the lock before iterating */
4499 AutoReadLock alock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
4500 callbacks = mVirtualBox->m->llCallbacks;
4501 }
4502
4503#ifdef RT_OS_WINDOWS
4504#if 0
4505 // WIP
4506
4507 LPTYPEINFO ptinfo;
4508 HRESULT hr;
4509 LPTYPELIB ptlib;
4510 DISPID dispid;
4511
4512 /* Real solution must cache all needed dispids once, ofc */
4513 hr = ::LoadRegTypeLib(LIBID_VirtualBox, kTypeLibraryMajorVersion, kTypeLibraryMinorVersion, LOCALE_SYSTEM_DEFAULT, &ptlib);
4514 hr = ptlib->GetTypeInfoOfGuid(IID_IVirtualBoxCallback, &ptinfo);
4515 ptlib->Release();
4516
4517 OLECHAR FAR* szMember = L"OnMachineStateChange";
4518
4519 hr = ::DispGetIDsOfNames(ptinfo, &szMember, 1, &dispid);
4520 ptinfo->Release();
4521
4522 int nConnections = mVirtualBox->m_vec.GetSize();
4523 for (int i=0; i<nConnections; i++)
4524 {
4525 ComPtr<IUnknown> sp = mVirtualBox->m_vec.GetAt(i);
4526 ComPtr<IVirtualBoxCallback> cbI;
4527 ComPtr<IDispatch> cbD;
4528
4529 cbI = sp;
4530 cbD = sp;
4531
4532 /**
4533 * Would be like this in ideal world, unfortunately our consumers want to be invoked via IDispatch,
4534 * thus going the hard way.
4535 */
4536#if 0
4537 if (cbI != NULL)
4538 {
4539 HRESULT hrc = handleCallback(cbI);
4540 if (hrc == VBOX_E_DONT_CALL_AGAIN)
4541 {
4542 // need to handle that somehow, maybe just set element to 0
4543 }
4544 }
4545#endif
4546 if (cbI != NULL && cbD != NULL)
4547 {
4548 CComVariant varResult, arg1, arg2;
4549
4550 ::VariantClear(&varResult);
4551 ::VariantClear(&arg1);
4552 ::VariantClear(&arg2);
4553
4554 VARIANTARG args[] = {arg1, arg2};
4555 DISPPARAMS disp = { args, NULL, sizeof(args)/sizeof(args[0]), 0};
4556
4557 cbD->Invoke(dispid, IID_NULL,
4558 LOCALE_USER_DEFAULT,
4559 DISPATCH_METHOD,
4560 &disp, &varResult,
4561 NULL, NULL);
4562 }
4563 }
4564#endif
4565#endif
4566
4567 for (CallbackList::const_iterator it = callbacks.begin();
4568 it != callbacks.end();
4569 ++it)
4570 {
4571 if (it->isWanted(mWhat))
4572 {
4573 HRESULT hrc = handleCallback(it->ptrICb);
4574 if (hrc == VBOX_E_DONT_CALL_AGAIN)
4575 {
4576 /* Have to update the original. */
4577 AutoReadLock alock(mVirtualBox COMMA_LOCKVAL_SRC_POS);
4578 CallbackList::iterator itOrg;
4579 itOrg = std::find(mVirtualBox->m->llCallbacks.begin(),
4580 mVirtualBox->m->llCallbacks.end(),
4581 CallbackList::value_type(it->ptrICb));
4582 if (itOrg != mVirtualBox->m->llCallbacks.end())
4583 itOrg->setDontCallAgain(mWhat);
4584 }
4585 else if (FAILED_DEAD_INTERFACE(hrc))
4586 mVirtualBox->removeDeadCallback(it->ptrICb);
4587 }
4588 }
4589
4590 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */
4591 return NULL;
4592}
4593
4594//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4595//{
4596// return E_NOTIMPL;
4597//}
4598
4599STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)
4600{
4601 CheckComArgStrNotEmptyOrNull(aName);
4602 CheckComArgNotNull(aServer);
4603
4604 AutoCaller autoCaller(this);
4605 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4606
4607 ComObjPtr<DHCPServer> dhcpServer;
4608 dhcpServer.createObject();
4609 HRESULT rc = dhcpServer->init(this, aName);
4610 if (FAILED(rc)) return rc;
4611
4612 rc = registerDHCPServer(dhcpServer, true);
4613 if (FAILED(rc)) return rc;
4614
4615 dhcpServer.queryInterfaceTo(aServer);
4616
4617 return rc;
4618}
4619
4620STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)
4621{
4622 CheckComArgStrNotEmptyOrNull(aName);
4623 CheckComArgNotNull(aServer);
4624
4625 AutoCaller autoCaller(this);
4626 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4627
4628 HRESULT rc;
4629 Bstr bstr;
4630 ComPtr<DHCPServer> found;
4631
4632 AutoReadLock alock(m->ollDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);
4633
4634 for (DHCPServersOList::const_iterator it = m->ollDHCPServers.begin();
4635 it != m->ollDHCPServers.end();
4636 ++it)
4637 {
4638 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());
4639 if (FAILED(rc)) throw rc;
4640
4641 if (bstr == aName)
4642 {
4643 found = *it;
4644 break;
4645 }
4646 }
4647
4648 if (!found)
4649 return E_INVALIDARG;
4650
4651 return found.queryInterfaceTo(aServer);
4652}
4653
4654STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)
4655{
4656 CheckComArgNotNull(aServer);
4657
4658 AutoCaller autoCaller(this);
4659 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4660
4661 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4662
4663 return rc;
4664}
4665
4666/**
4667 * Remembers the given dhcp server by storing it in the hard disk registry.
4668 *
4669 * @param aDHCPServer Dhcp Server object to remember.
4670 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4671 *
4672 * When @a aSaveRegistry is @c true, this operation may fail because of the
4673 * failed #saveSettings() method it calls. In this case, the dhcp server object
4674 * will not be remembered. It is therefore the responsibility of the caller to
4675 * call this method as the last step of some action that requires registration
4676 * in order to make sure that only fully functional dhcp server objects get
4677 * registered.
4678 *
4679 * @note Locks this object for writing and @a aDHCPServer for reading.
4680 */
4681HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4682 bool aSaveRegistry /*= true*/)
4683{
4684 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4685
4686 AutoCaller autoCaller(this);
4687 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4688
4689 AutoCaller dhcpServerCaller(aDHCPServer);
4690 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4691
4692 Bstr name;
4693 HRESULT rc;
4694 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());
4695 if (FAILED(rc)) return rc;
4696
4697 ComPtr<IDHCPServer> existing;
4698 rc = FindDHCPServerByNetworkName(name, existing.asOutParam());
4699 if (SUCCEEDED(rc))
4700 {
4701 return E_INVALIDARG;
4702 }
4703
4704 rc = S_OK;
4705
4706 m->ollDHCPServers.addChild(aDHCPServer);
4707
4708 if (aSaveRegistry)
4709 {
4710 rc = saveSettings();
4711 if (FAILED(rc))
4712 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4713 }
4714
4715 return rc;
4716}
4717
4718/**
4719 * Removes the given hard disk from the hard disk registry.
4720 *
4721 * @param aHardDisk Hard disk object to remove.
4722 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4723 *
4724 * When @a aSaveRegistry is @c true, this operation may fail because of the
4725 * failed #saveSettings() method it calls. In this case, the hard disk object
4726 * will NOT be removed from the registry when this method returns. It is
4727 * therefore the responsibility of the caller to call this method as the first
4728 * step of some action that requires unregistration, before calling uninit() on
4729 * @a aHardDisk.
4730 *
4731 * @note Locks this object for writing and @a aHardDisk for reading.
4732 */
4733HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4734 bool aSaveRegistry /*= true*/)
4735{
4736 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);
4737
4738 AutoCaller autoCaller(this);
4739 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
4740
4741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4742
4743 AutoCaller dhcpServerCaller(aDHCPServer);
4744 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());
4745
4746 m->ollDHCPServers.removeChild(aDHCPServer);
4747
4748 HRESULT rc = S_OK;
4749
4750 if (aSaveRegistry)
4751 {
4752 rc = saveSettings();
4753 if (FAILED(rc))
4754 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4755 }
4756
4757 return rc;
4758}
4759
4760/* 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