VirtualBox

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

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

Main: enabled -Wunused; fixed some warnings

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