VirtualBox

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

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

Main: fix win

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