VirtualBox

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

Last change on this file since 8707 was 8569, checked in by vboxsync, 17 years ago

AssertBreak -> AssertBreakStmt.

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