VirtualBox

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

Last change on this file since 17865 was 17865, checked in by vboxsync, 16 years ago

Main/dhcp API, E_NOTIMPL yet

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

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