VirtualBox

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

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

partial dhcp settings impl

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