VirtualBox

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

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

#3551: “Main: Replace remaining collections with safe arrays”
Converted ostype collection. Tested with VBoxManage list ostypes.

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