VirtualBox

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

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

VBoxSVC: Lock order fixes (+assert) for weird shutdown case where we have to clean up a SessionMachine or two. Needs reviewing.

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

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