VirtualBox

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

Last change on this file since 28098 was 28091, checked in by vboxsync, 15 years ago

Main: clean up Machine::init() and instance data

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

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