VirtualBox

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

Last change on this file since 28851 was 28825, checked in by vboxsync, 14 years ago

Make SATA the default harddisk controller for Windows guests (Vista and up) and 2.6 Linux guests

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