VirtualBox

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

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

Main/VirtualBoxCallback: rename OnSnapshotDiscard to OnSnapshotDeleted for consistency, clean up wording and other minor issues

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

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