VirtualBox

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

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

default dhcp server

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

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