VirtualBox

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

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

Main/VirtualBox: handle objects with limited functionality consistently when searching by name or uuid - skip them.

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

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