VirtualBox

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

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

Main/Settigs: Disabled File (..., RTFILE aHandle, ...) constructor since it is not thread-safe when the handle is shared across different File instances (needs a handle duplicate operation in the IPRT File API).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 151.6 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 8020 2008-04-16 10:14:39Z vboxsync $ */
2/** @file
3 * Implmentation of IVirtualBox in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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
18#include "VirtualBoxImpl.h"
19#include "MachineImpl.h"
20#include "HardDiskImpl.h"
21#include "DVDImageImpl.h"
22#include "FloppyImageImpl.h"
23#include "SharedFolderImpl.h"
24#include "ProgressImpl.h"
25#include "HostImpl.h"
26#include "USBControllerImpl.h"
27#include "SystemPropertiesImpl.h"
28#include "GuestOSTypeImpl.h"
29
30#include "VirtualBoxXMLUtil.h"
31
32#include "Logging.h"
33
34#ifdef RT_OS_WINDOWS
35#include "win32/svchlp.h"
36#endif
37
38#include <stdio.h>
39#include <stdlib.h>
40
41#include <iprt/path.h>
42#include <iprt/dir.h>
43#include <iprt/file.h>
44#include <iprt/string.h>
45#include <iprt/uuid.h>
46#include <iprt/thread.h>
47#include <iprt/process.h>
48#include <iprt/env.h>
49#include <iprt/cpputils.h>
50
51#include <VBox/err.h>
52#include <VBox/param.h>
53#include <VBox/VBoxHDD.h>
54#include <VBox/VBoxHDD-new.h>
55#include <VBox/ostypes.h>
56#include <VBox/version.h>
57
58#include <VBox/com/com.h>
59#include <VBox/com/array.h>
60
61#include <algorithm>
62#include <set>
63#include <memory> // for auto_ptr
64
65#include <typeinfo>
66
67// defines
68/////////////////////////////////////////////////////////////////////////////
69
70#define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"
71
72// globals
73/////////////////////////////////////////////////////////////////////////////
74
75static const char DefaultGlobalConfig [] =
76{
77 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" RTFILE_LINEFEED
78 "<!-- innotek VirtualBox Global Configuration -->" RTFILE_LINEFEED
79 "<VirtualBox xmlns=\"" VBOX_XML_NAMESPACE "\" "
80 "version=\"" VBOX_XML_VERSION_FULL "\">" RTFILE_LINEFEED
81 " <Global>"RTFILE_LINEFEED
82 " <MachineRegistry/>"RTFILE_LINEFEED
83 " <DiskRegistry/>"RTFILE_LINEFEED
84 " <USBDeviceFilters/>"RTFILE_LINEFEED
85 " <SystemProperties/>"RTFILE_LINEFEED
86 " </Global>"RTFILE_LINEFEED
87 "</VirtualBox>"RTFILE_LINEFEED
88};
89
90// static
91Bstr VirtualBox::sVersion;
92
93// static
94Bstr VirtualBox::sSettingsFormatVersion;
95
96// constructor / destructor
97/////////////////////////////////////////////////////////////////////////////
98
99VirtualBox::VirtualBox()
100 : mAsyncEventThread (NIL_RTTHREAD)
101 , mAsyncEventQ (NULL)
102{}
103
104VirtualBox::~VirtualBox() {}
105
106HRESULT VirtualBox::FinalConstruct()
107{
108 LogFlowThisFunc (("\n"));
109
110 return init();
111}
112
113void VirtualBox::FinalRelease()
114{
115 LogFlowThisFunc (("\n"));
116
117 uninit();
118}
119
120VirtualBox::Data::Data()
121{
122}
123
124// public initializer/uninitializer for internal purposes only
125/////////////////////////////////////////////////////////////////////////////
126
127/**
128 * Initializes the VirtualBox object.
129 *
130 * @return COM result code
131 */
132HRESULT VirtualBox::init()
133{
134 /* Enclose the state transition NotReady->InInit->Ready */
135 AutoInitSpan autoInitSpan (this);
136 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
137
138 LogFlow (("===========================================================\n"));
139 LogFlowThisFuncEnter();
140
141 if (sVersion.isNull())
142 sVersion = VBOX_VERSION_STRING;
143 LogFlowThisFunc (("Version: %ls\n", sVersion.raw()));
144
145 if (sSettingsFormatVersion.isNull())
146 sSettingsFormatVersion = VBOX_XML_VERSION_FULL;
147 LogFlowThisFunc (("Settings Format Version: %ls\n",
148 sSettingsFormatVersion.raw()));
149
150 /* Get the VirtualBox home directory. */
151 {
152 char homeDir [RTPATH_MAX];
153 int vrc = com::GetVBoxUserHomeDirectory (homeDir, sizeof (homeDir));
154 if (VBOX_FAILURE (vrc))
155 return setError (E_FAIL,
156 tr ("Could not create the VirtualBox home directory '%s'"
157 "(%Vrc)"),
158 homeDir, vrc);
159
160 unconst (mData.mHomeDir) = homeDir;
161 }
162
163 /* compose the global config file name (always full path) */
164 Utf8StrFmt vboxConfigFile ("%s%c%s", mData.mHomeDir.raw(),
165 RTPATH_DELIMITER, VBOX_GLOBAL_SETTINGS_FILE);
166
167 /* store the config file name */
168 unconst (mData.mCfgFile.mName) = vboxConfigFile;
169
170 /* lock the config file */
171 HRESULT rc = lockConfig();
172 if (SUCCEEDED (rc))
173 {
174 if (!isConfigLocked())
175 {
176 /*
177 * This means the config file not found. This is not fatal --
178 * we just create an empty one.
179 */
180 RTFILE handle = NIL_RTFILE;
181 int vrc = RTFileOpen (&handle, vboxConfigFile,
182 RTFILE_O_READWRITE | RTFILE_O_CREATE |
183 RTFILE_O_DENY_WRITE);
184 if (VBOX_SUCCESS (vrc))
185 vrc = RTFileWrite (handle,
186 (void *) DefaultGlobalConfig,
187 sizeof (DefaultGlobalConfig), NULL);
188 if (VBOX_FAILURE (vrc))
189 {
190 rc = setError (E_FAIL, tr ("Could not create the default settings file "
191 "'%s' (%Vrc)"),
192 vboxConfigFile.raw(), vrc);
193 }
194 else
195 {
196 mData.mCfgFile.mHandle = handle;
197 /* we do not close the file to simulate lockConfig() */
198 }
199 }
200 }
201
202 if (SUCCEEDED (rc))
203 {
204 try
205 {
206 using namespace settings;
207
208#if 0
209 /// @todo disabled until made thread-safe by using handle duplicates
210 File file (File::ReadWrite, mData.mCfgFile.mHandle, vboxConfigFile);
211#else
212 File file (File::Read, vboxConfigFile);
213#endif
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 AutoReaderLock 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 AutoReaderLock 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 AutoReaderLock 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 AutoReaderLock 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 AutoReaderLock 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 AutoReaderLock 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 AutoReaderLock 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 AutoReaderLock 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 AutoReaderLock 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 AutoReaderLock 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 AutoReaderLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoReaderLock 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 AutoLock 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 AutoReaderLock 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 AutoLock 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 AutoReaderLock 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 config file access */
1709 AutoReaderLock alock (this);
1710
1711 try
1712 {
1713 using namespace settings;
1714
1715 /* load the config file */
1716#if 0
1717 /// @todo disabled until made thread-safe by using handle duplicates
1718 File file (File::ReadWrite, mData.mCfgFile.mHandle,
1719 Utf8Str (mData.mCfgFile.mName));
1720#else
1721 File file (File::Read, Utf8Str (mData.mCfgFile.mName));
1722#endif
1723 XmlTreeBackend tree;
1724
1725 rc = VirtualBox::loadSettingsTree_Again (tree, file);
1726 CheckComRCReturnRC (rc);
1727
1728 Key globalNode = tree.rootKey().key ("Global");
1729 Key extraDataNode = tree.rootKey().findKey ("ExtraData");
1730
1731 if (!extraDataNode.isNull())
1732 {
1733 Key::List items = extraDataNode.keys ("ExtraDataItem");
1734 if (items.size())
1735 {
1736 for (Key::List::const_iterator it = items.begin();
1737 it != items.end(); ++ it)
1738 {
1739 Bstr key = (*it).stringValue ("name");
1740
1741 /* if we're supposed to return the first one */
1742 if (aKey == NULL)
1743 {
1744 key.cloneTo (aNextKey);
1745 if (aNextValue)
1746 {
1747 Bstr val = (*it).stringValue ("value");
1748 val.cloneTo (aNextValue);
1749 }
1750 return S_OK;
1751 }
1752
1753 /* did we find the key we're looking for? */
1754 if (key == aKey)
1755 {
1756 ++ it;
1757 /* is there another item? */
1758 if (it != items.end())
1759 {
1760 Bstr key = (*it).stringValue ("name");
1761 key.cloneTo (aNextKey);
1762 if (aNextValue)
1763 {
1764 Bstr val = (*it).stringValue ("value");
1765 val.cloneTo (aNextValue);
1766 }
1767 }
1768 /* else it's the last one, arguments are already NULL */
1769 return S_OK;
1770 }
1771 }
1772 }
1773 }
1774
1775 /* Here we are when a) there are no items at all or b) there are items
1776 * but none of them equals to the requested non-NULL key. b) is an
1777 * error as well as a) if the key is non-NULL. When the key is NULL
1778 * (which is the case only when there are no items), we just fall
1779 * through to return NULLs and S_OK. */
1780
1781 if (aKey != NULL)
1782 return setError (E_FAIL,
1783 tr ("Could not find the extra data key '%ls'"), aKey);
1784 }
1785 catch (...)
1786 {
1787 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
1788 }
1789
1790 return rc;
1791}
1792
1793/**
1794 * @note Locks this object for reading.
1795 */
1796STDMETHODIMP VirtualBox::GetExtraData (INPTR BSTR aKey, BSTR *aValue)
1797{
1798 if (!aKey)
1799 return E_INVALIDARG;
1800 if (!aValue)
1801 return E_POINTER;
1802
1803 AutoCaller autoCaller (this);
1804 CheckComRCReturnRC (autoCaller.rc());
1805
1806 /* start with nothing found */
1807 *aValue = NULL;
1808
1809 HRESULT rc = S_OK;
1810
1811 /* serialize file access */
1812 AutoReaderLock alock (this);
1813
1814 try
1815 {
1816 using namespace settings;
1817
1818 /* load the config file */
1819#if 0
1820 /// @todo disabled until made thread-safe by using handle duplicates
1821 File file (File::ReadWrite, mData.mCfgFile.mHandle,
1822 Utf8Str (mData.mCfgFile.mName));
1823#else
1824 File file (File::Read, Utf8Str (mData.mCfgFile.mName));
1825#endif
1826 XmlTreeBackend tree;
1827
1828 rc = VirtualBox::loadSettingsTree_Again (tree, file);
1829 CheckComRCReturnRC (rc);
1830
1831 const Utf8Str key = aKey;
1832
1833 Key globalNode = tree.rootKey().key ("Global");
1834 Key extraDataNode = globalNode.findKey ("ExtraData");
1835
1836 if (!extraDataNode.isNull())
1837 {
1838 /* check if the key exists */
1839 Key::List items = extraDataNode.keys ("ExtraDataItem");
1840 for (Key::List::const_iterator it = items.begin();
1841 it != items.end(); ++ it)
1842 {
1843 if (key == (*it).stringValue ("name"))
1844 {
1845 Bstr val = (*it).stringValue ("value");
1846 val.cloneTo (aValue);
1847 break;
1848 }
1849 }
1850 }
1851 }
1852 catch (...)
1853 {
1854 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
1855 }
1856
1857 return rc;
1858}
1859
1860/**
1861 * @note Locks this object for writing.
1862 */
1863STDMETHODIMP VirtualBox::SetExtraData (INPTR BSTR aKey, INPTR BSTR aValue)
1864{
1865 if (!aKey)
1866 return E_INVALIDARG;
1867
1868 AutoCaller autoCaller (this);
1869 CheckComRCReturnRC (autoCaller.rc());
1870
1871 Guid emptyGuid;
1872
1873 bool changed = false;
1874 HRESULT rc = S_OK;
1875
1876 /* serialize file access */
1877 AutoLock alock (this);
1878
1879 try
1880 {
1881 using namespace settings;
1882
1883 /* load the config file */
1884#if 0
1885 /// @todo disabled until made thread-safe by using handle duplicates
1886 File file (File::ReadWrite, mData.mCfgFile.mHandle,
1887 Utf8Str (mData.mCfgFile.mName));
1888#else
1889 File file (File::ReadWrite, Utf8Str (mData.mCfgFile.mName));
1890#endif
1891 XmlTreeBackend tree;
1892
1893 rc = VirtualBox::loadSettingsTree_ForUpdate (tree, file);
1894 CheckComRCReturnRC (rc);
1895
1896 const Utf8Str key = aKey;
1897 Bstr oldVal;
1898
1899 Key globalNode = tree.rootKey().key ("Global");
1900 Key extraDataNode = globalNode.createKey ("ExtraData");
1901 Key extraDataItemNode;
1902
1903 Key::List items = extraDataNode.keys ("ExtraDataItem");
1904 for (Key::List::const_iterator it = items.begin();
1905 it != items.end(); ++ it)
1906 {
1907 if (key == (*it).stringValue ("name"))
1908 {
1909 extraDataItemNode = *it;
1910 oldVal = (*it).stringValue ("value");
1911 break;
1912 }
1913 }
1914
1915 /* When no key is found, oldVal is null */
1916 changed = oldVal != aValue;
1917
1918 if (changed)
1919 {
1920 /* ask for permission from all listeners */
1921 Bstr error;
1922 if (!onExtraDataCanChange (Guid::Empty, aKey, aValue, error))
1923 {
1924 const char *sep = error.isEmpty() ? "" : ": ";
1925 const BSTR err = error.isNull() ? (const BSTR) L"" : error.raw();
1926 LogWarningFunc (("Someone vetoed! Change refused%s%ls\n",
1927 sep, err));
1928 return setError (E_ACCESSDENIED,
1929 tr ("Could not set extra data because someone refused "
1930 "the requested change of '%ls' to '%ls'%s%ls"),
1931 aKey, aValue, sep, err);
1932 }
1933
1934 if (aValue != NULL)
1935 {
1936 if (extraDataItemNode.isNull())
1937 {
1938 extraDataItemNode = extraDataNode.appendKey ("ExtraDataItem");
1939 extraDataItemNode.setStringValue ("name", key);
1940 }
1941 extraDataItemNode.setStringValue ("value", Utf8Str (aValue));
1942 }
1943 else
1944 {
1945 /* an old value does for sure exist here (XML schema
1946 * guarantees that "value" may not absent in the
1947 * <ExtraDataItem> element) */
1948 Assert (!extraDataItemNode.isNull());
1949 extraDataItemNode.zap();
1950 }
1951
1952 /* save settings on success */
1953 rc = VirtualBox::saveSettingsTree (tree, file,
1954 mData.mSettingsFileVersion);
1955 CheckComRCReturnRC (rc);
1956 }
1957 }
1958 catch (...)
1959 {
1960 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
1961 }
1962
1963 /* fire a notification */
1964 if (SUCCEEDED (rc) && changed)
1965 onExtraDataChange (Guid::Empty, aKey, aValue);
1966
1967 return rc;
1968}
1969
1970/**
1971 * @note Locks objects!
1972 */
1973STDMETHODIMP VirtualBox::OpenSession (ISession *aSession, INPTR GUIDPARAM aMachineId)
1974{
1975 if (!aSession)
1976 return E_INVALIDARG;
1977
1978 AutoCaller autoCaller (this);
1979 CheckComRCReturnRC (autoCaller.rc());
1980
1981 Guid id = aMachineId;
1982 ComObjPtr <Machine> machine;
1983
1984 HRESULT rc = findMachine (id, true /* setError */, &machine);
1985 CheckComRCReturnRC (rc);
1986
1987 /* check the session state */
1988 SessionState_T state;
1989 rc = aSession->COMGETTER(State) (&state);
1990 CheckComRCReturnRC (rc);
1991
1992 if (state != SessionState_Closed)
1993 return setError (E_INVALIDARG,
1994 tr ("The given session is already open or being opened"));
1995
1996 /* get the IInternalSessionControl interface */
1997 ComPtr <IInternalSessionControl> control = aSession;
1998 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1999 E_INVALIDARG);
2000
2001 rc = machine->openSession (control);
2002
2003 if (SUCCEEDED (rc))
2004 {
2005 /*
2006 * tell the client watcher thread to update the set of
2007 * machines that have open sessions
2008 */
2009 updateClientWatcher();
2010
2011 /* fire an event */
2012 onSessionStateChange (aMachineId, SessionState_Open);
2013 }
2014
2015 return rc;
2016}
2017
2018/**
2019 * @note Locks objects!
2020 */
2021STDMETHODIMP VirtualBox::OpenRemoteSession (ISession *aSession,
2022 INPTR GUIDPARAM aMachineId,
2023 INPTR BSTR aType,
2024 INPTR BSTR aEnvironment,
2025 IProgress **aProgress)
2026{
2027 if (!aSession || !aType)
2028 return E_INVALIDARG;
2029 if (!aProgress)
2030 return E_POINTER;
2031
2032 AutoCaller autoCaller (this);
2033 CheckComRCReturnRC (autoCaller.rc());
2034
2035 Guid id = aMachineId;
2036 ComObjPtr <Machine> machine;
2037
2038 HRESULT rc = findMachine (id, true /* setError */, &machine);
2039 CheckComRCReturnRC (rc);
2040
2041 /* check the session state */
2042 SessionState_T state;
2043 rc = aSession->COMGETTER(State) (&state);
2044 CheckComRCReturnRC (rc);
2045
2046 if (state != SessionState_Closed)
2047 return setError (E_INVALIDARG,
2048 tr ("The given session is already open or being opened"));
2049
2050 /* get the IInternalSessionControl interface */
2051 ComPtr <IInternalSessionControl> control = aSession;
2052 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
2053 E_INVALIDARG);
2054
2055 /* create a progress object */
2056 ComObjPtr <Progress> progress;
2057 progress.createObject();
2058 progress->init (this, static_cast <IMachine *> (machine),
2059 Bstr (tr ("Spawning session")),
2060 FALSE /* aCancelable */);
2061
2062 rc = machine->openRemoteSession (control, aType, aEnvironment, progress);
2063
2064 if (SUCCEEDED (rc))
2065 {
2066 progress.queryInterfaceTo (aProgress);
2067
2068 /* fire an event */
2069 onSessionStateChange (aMachineId, SessionState_Spawning);
2070 }
2071
2072 return rc;
2073}
2074
2075/**
2076 * @note Locks objects!
2077 */
2078STDMETHODIMP VirtualBox::OpenExistingSession (ISession *aSession,
2079 INPTR GUIDPARAM aMachineId)
2080{
2081 if (!aSession)
2082 return E_POINTER;
2083
2084 AutoCaller autoCaller (this);
2085 CheckComRCReturnRC (autoCaller.rc());
2086
2087 Guid id = aMachineId;
2088 ComObjPtr <Machine> machine;
2089
2090 HRESULT rc = findMachine (id, true /* setError */, &machine);
2091 CheckComRCReturnRC (rc);
2092
2093 /* check the session state */
2094 SessionState_T state;
2095 rc = aSession->COMGETTER(State) (&state);
2096 CheckComRCReturnRC (rc);
2097
2098 if (state != SessionState_Closed)
2099 return setError (E_INVALIDARG,
2100 tr ("The given session is already open or being opened"));
2101
2102 /* get the IInternalSessionControl interface */
2103 ComPtr <IInternalSessionControl> control = aSession;
2104 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
2105 E_INVALIDARG);
2106
2107 rc = machine->openExistingSession (control);
2108
2109 return rc;
2110}
2111
2112/**
2113 * @note Locks this object for writing.
2114 */
2115STDMETHODIMP VirtualBox::RegisterCallback (IVirtualBoxCallback *aCallback)
2116{
2117 LogFlowThisFunc (("aCallback=%p\n", aCallback));
2118
2119 if (!aCallback)
2120 return E_INVALIDARG;
2121
2122 AutoCaller autoCaller (this);
2123 CheckComRCReturnRC (autoCaller.rc());
2124
2125 AutoLock alock (this);
2126 mData.mCallbacks.push_back (CallbackList::value_type (aCallback));
2127
2128 return S_OK;
2129}
2130
2131/**
2132 * @note Locks this object for writing.
2133 */
2134STDMETHODIMP VirtualBox::UnregisterCallback (IVirtualBoxCallback *aCallback)
2135{
2136 if (!aCallback)
2137 return E_INVALIDARG;
2138
2139 AutoCaller autoCaller (this);
2140 CheckComRCReturnRC (autoCaller.rc());
2141
2142 HRESULT rc = S_OK;
2143
2144 AutoLock alock (this);
2145
2146 CallbackList::iterator it;
2147 it = std::find (mData.mCallbacks.begin(),
2148 mData.mCallbacks.end(),
2149 CallbackList::value_type (aCallback));
2150 if (it == mData.mCallbacks.end())
2151 rc = E_INVALIDARG;
2152 else
2153 mData.mCallbacks.erase (it);
2154
2155 LogFlowThisFunc (("aCallback=%p, rc=%08X\n", aCallback, rc));
2156 return rc;
2157}
2158
2159STDMETHODIMP VirtualBox::WaitForPropertyChange (INPTR BSTR aWhat, ULONG aTimeout,
2160 BSTR *aChanged, BSTR *aValues)
2161{
2162 return E_NOTIMPL;
2163}
2164
2165STDMETHODIMP VirtualBox::SaveSettings()
2166{
2167 AutoCaller autoCaller (this);
2168 CheckComRCReturnRC (autoCaller.rc());
2169
2170 return saveSettings();
2171}
2172
2173STDMETHODIMP VirtualBox::SaveSettingsWithBackup (BSTR *aBakFileName)
2174{
2175 if (!aBakFileName)
2176 return E_POINTER;
2177
2178 AutoCaller autoCaller (this);
2179 CheckComRCReturnRC (autoCaller.rc());
2180
2181 /* saveSettings() needs write lock */
2182 AutoLock alock (this);
2183
2184 /* perform backup only when there was auto-conversion */
2185 if (mData.mSettingsFileVersion != VBOX_XML_VERSION_FULL)
2186 {
2187 Bstr bakFileName;
2188
2189 HRESULT rc = backupSettingsFile (mData.mCfgFile.mName,
2190 mData.mSettingsFileVersion,
2191 bakFileName);
2192 CheckComRCReturnRC (rc);
2193
2194 bakFileName.cloneTo (aBakFileName);
2195 }
2196
2197 return saveSettings();
2198}
2199
2200// public methods only for internal purposes
2201/////////////////////////////////////////////////////////////////////////////
2202
2203/**
2204 * Posts an event to the event queue that is processed asynchronously
2205 * on a dedicated thread.
2206 *
2207 * Posting events to the dedicated event queue is useful to perform secondary
2208 * actions outside any object locks -- for example, to iterate over a list
2209 * of callbacks and inform them about some change caused by some object's
2210 * method call.
2211 *
2212 * @param event event to post
2213 * (must be allocated using |new|, will be deleted automatically
2214 * by the event thread after processing)
2215 *
2216 * @note Doesn't lock any object.
2217 */
2218HRESULT VirtualBox::postEvent (Event *event)
2219{
2220 AutoCaller autoCaller (this);
2221 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2222
2223 if (autoCaller.state() != Ready)
2224 {
2225 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
2226 "the event is discarded!\n",
2227 autoCaller.state()));
2228 return S_OK;
2229 }
2230
2231 AssertReturn (event, E_FAIL);
2232 AssertReturn (mAsyncEventQ, E_FAIL);
2233
2234 AutoLock alock (mAsyncEventQLock);
2235 if (mAsyncEventQ->postEvent (event))
2236 return S_OK;
2237
2238 return E_FAIL;
2239}
2240
2241/**
2242 * Helper method to add a progress to the global collection of pending
2243 * operations.
2244 *
2245 * @param aProgress operation to add to the collection
2246 * @return COM status code
2247 *
2248 * @note Locks this object for writing.
2249 */
2250HRESULT VirtualBox::addProgress (IProgress *aProgress)
2251{
2252 if (!aProgress)
2253 return E_INVALIDARG;
2254
2255 AutoCaller autoCaller (this);
2256 CheckComRCReturnRC (autoCaller.rc());
2257
2258 AutoLock alock (this);
2259 mData.mProgressOperations.push_back (aProgress);
2260 return S_OK;
2261}
2262
2263/**
2264 * Helper method to remove the progress from the global collection of pending
2265 * operations. Usualy gets called upon progress completion.
2266 *
2267 * @param aId UUID of the progress operation to remove
2268 * @return COM status code
2269 *
2270 * @note Locks this object for writing.
2271 */
2272HRESULT VirtualBox::removeProgress (INPTR GUIDPARAM aId)
2273{
2274 AutoCaller autoCaller (this);
2275 CheckComRCReturnRC (autoCaller.rc());
2276
2277 ComPtr <IProgress> progress;
2278
2279 AutoLock alock (this);
2280
2281 for (ProgressList::iterator it = mData.mProgressOperations.begin();
2282 it != mData.mProgressOperations.end();
2283 ++ it)
2284 {
2285 Guid id;
2286 (*it)->COMGETTER(Id) (id.asOutParam());
2287 if (id == aId)
2288 {
2289 mData.mProgressOperations.erase (it);
2290 return S_OK;
2291 }
2292 }
2293
2294 AssertFailed(); /* should never happen */
2295
2296 return E_FAIL;
2297}
2298
2299#ifdef RT_OS_WINDOWS
2300
2301struct StartSVCHelperClientData
2302{
2303 ComObjPtr <VirtualBox> that;
2304 ComObjPtr <Progress> progress;
2305 bool privileged;
2306 VirtualBox::SVCHelperClientFunc func;
2307 void *user;
2308};
2309
2310/**
2311 * Helper method to that starts a worker thread that:
2312 * - creates a pipe communication channel using SVCHlpClient;
2313 * - starts a SVC Helper process that will inherit this channel;
2314 * - executes the supplied function by passing it the created SVCHlpClient
2315 * and opened instance to communicate to the Helper process and the given
2316 * Progress object.
2317 *
2318 * The user function is supposed to communicate to the helper process
2319 * using the \a aClient argument to do the requested job and optionally expose
2320 * the prgress through the \a aProgress object. The user function should never
2321 * call notifyComplete() on it: this will be done automatically using the
2322 * result code returned by the function.
2323 *
2324 * Before the user function is stared, the communication channel passed to in
2325 * the \a aClient argument, is fully set up, the function should start using
2326 * it's write() and read() methods directly.
2327 *
2328 * The \a aVrc parameter of the user function may be used to return an error
2329 * code if it is related to communication errors (for example, returned by
2330 * the SVCHlpClient members when they fail). In this case, the correct error
2331 * message using this value will be reported to the caller. Note that the
2332 * value of \a aVrc is inspected only if the user function itself returns
2333 * a success.
2334 *
2335 * If a failure happens anywhere before the user function would be normally
2336 * called, it will be called anyway in special "cleanup only" mode indicated
2337 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
2338 * all the function is supposed to do is to cleanup its aUser argument if
2339 * necessary (it's assumed that the ownership of this argument is passed to
2340 * the user function once #startSVCHelperClient() returns a success, thus
2341 * making it responsible for the cleanup).
2342 *
2343 * After the user function returns, the thread will send the SVCHlpMsg::Null
2344 * message to indicate a process termination.
2345 *
2346 * @param aPrivileged |true| to start the SVC Hepler process as a privlieged
2347 * user that can perform administrative tasks
2348 * @param aFunc user function to run
2349 * @param aUser argument to the user function
2350 * @param aProgress progress object that will track operation completion
2351 *
2352 * @note aPrivileged is currently ignored (due to some unsolved problems in
2353 * Vista) and the process will be started as a normal (unprivileged)
2354 * process.
2355 *
2356 * @note Doesn't lock anything.
2357 */
2358HRESULT VirtualBox::startSVCHelperClient (bool aPrivileged,
2359 SVCHelperClientFunc aFunc,
2360 void *aUser, Progress *aProgress)
2361{
2362 AssertReturn (aFunc, E_POINTER);
2363 AssertReturn (aProgress, E_POINTER);
2364
2365 AutoCaller autoCaller (this);
2366 CheckComRCReturnRC (autoCaller.rc());
2367
2368 /* create the SVCHelperClientThread() argument */
2369 std::auto_ptr <StartSVCHelperClientData>
2370 d (new StartSVCHelperClientData());
2371 AssertReturn (d.get(), E_OUTOFMEMORY);
2372
2373 d->that = this;
2374 d->progress = aProgress;
2375 d->privileged = aPrivileged;
2376 d->func = aFunc;
2377 d->user = aUser;
2378
2379 RTTHREAD tid = NIL_RTTHREAD;
2380 int vrc = RTThreadCreate (&tid, SVCHelperClientThread,
2381 static_cast <void *> (d.get()),
2382 0, RTTHREADTYPE_MAIN_WORKER,
2383 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2384
2385 ComAssertMsgRCRet (vrc, ("Could not create SVCHelper thread (%Vrc)\n", vrc),
2386 E_FAIL);
2387
2388 /* d is now owned by SVCHelperClientThread(), so release it */
2389 d.release();
2390
2391 return S_OK;
2392}
2393
2394/**
2395 * Worker thread for startSVCHelperClient().
2396 */
2397/* static */
2398DECLCALLBACK(int)
2399VirtualBox::SVCHelperClientThread (RTTHREAD aThread, void *aUser)
2400{
2401 LogFlowFuncEnter();
2402
2403 std::auto_ptr <StartSVCHelperClientData>
2404 d (static_cast <StartSVCHelperClientData *> (aUser));
2405
2406 HRESULT rc = S_OK;
2407 bool userFuncCalled = false;
2408
2409 do
2410 {
2411 AssertBreak (d.get(), rc = E_POINTER);
2412 AssertReturn (!d->progress.isNull(), E_POINTER);
2413
2414 /* protect VirtualBox from uninitialization */
2415 AutoCaller autoCaller (d->that);
2416 if (!autoCaller.isOk())
2417 {
2418 /* it's too late */
2419 rc = autoCaller.rc();
2420 break;
2421 }
2422
2423 int vrc = VINF_SUCCESS;
2424
2425 Guid id;
2426 id.create();
2427 SVCHlpClient client;
2428 vrc = client.create (Utf8StrFmt ("VirtualBox\\SVCHelper\\{%Vuuid}",
2429 id.raw()));
2430 if (VBOX_FAILURE (vrc))
2431 {
2432 rc = setError (E_FAIL,
2433 tr ("Could not create the communication channel (%Vrc)"), vrc);
2434 break;
2435 }
2436
2437 /* get the path to the executable */
2438 char exePathBuf [RTPATH_MAX];
2439 char *exePath = RTProcGetExecutableName (exePathBuf, RTPATH_MAX);
2440 ComAssertBreak (exePath, E_FAIL);
2441
2442 Utf8Str argsStr = Utf8StrFmt ("/Helper %s", client.name().raw());
2443
2444 LogFlowFunc (("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
2445
2446 RTPROCESS pid = NIL_RTPROCESS;
2447
2448 if (d->privileged)
2449 {
2450 /* Attempt to start a privileged process using the Run As dialog */
2451
2452 Bstr file = exePath;
2453 Bstr parameters = argsStr;
2454
2455 SHELLEXECUTEINFO shExecInfo;
2456
2457 shExecInfo.cbSize = sizeof (SHELLEXECUTEINFO);
2458
2459 shExecInfo.fMask = NULL;
2460 shExecInfo.hwnd = NULL;
2461 shExecInfo.lpVerb = L"runas";
2462 shExecInfo.lpFile = file;
2463 shExecInfo.lpParameters = parameters;
2464 shExecInfo.lpDirectory = NULL;
2465 shExecInfo.nShow = SW_NORMAL;
2466 shExecInfo.hInstApp = NULL;
2467
2468 if (!ShellExecuteEx (&shExecInfo))
2469 {
2470 int vrc2 = RTErrConvertFromWin32 (GetLastError());
2471 /* hide excessive details in case of a frequent error
2472 * (pressing the Cancel button to close the Run As dialog) */
2473 if (vrc2 == VERR_CANCELLED)
2474 rc = setError (E_FAIL,
2475 tr ("Operatiion cancelled by the user"));
2476 else
2477 rc = setError (E_FAIL,
2478 tr ("Could not launch a privileged process '%s' (%Vrc)"),
2479 exePath, vrc2);
2480 break;
2481 }
2482 }
2483 else
2484 {
2485 const char *args[] = { exePath, "/Helper", client.name(), 0 };
2486 vrc = RTProcCreate (exePath, args, RTENV_DEFAULT, 0, &pid);
2487 if (VBOX_FAILURE (vrc))
2488 {
2489 rc = setError (E_FAIL,
2490 tr ("Could not launch a process '%s' (%Vrc)"), exePath, vrc);
2491 break;
2492 }
2493 }
2494
2495 /* wait for the client to connect */
2496 vrc = client.connect();
2497 if (VBOX_SUCCESS (vrc))
2498 {
2499 /* start the user supplied function */
2500 rc = d->func (&client, d->progress, d->user, &vrc);
2501 userFuncCalled = true;
2502 }
2503
2504 /* send the termination signal to the process anyway */
2505 {
2506 int vrc2 = client.write (SVCHlpMsg::Null);
2507 if (VBOX_SUCCESS (vrc))
2508 vrc = vrc2;
2509 }
2510
2511 if (SUCCEEDED (rc) && VBOX_FAILURE (vrc))
2512 {
2513 rc = setError (E_FAIL,
2514 tr ("Could not operate the communication channel (%Vrc)"), vrc);
2515 break;
2516 }
2517 }
2518 while (0);
2519
2520 if (FAILED (rc) && !userFuncCalled)
2521 {
2522 /* call the user function in the "cleanup only" mode
2523 * to let it free resources passed to in aUser */
2524 d->func (NULL, NULL, d->user, NULL);
2525 }
2526
2527 d->progress->notifyComplete (rc);
2528
2529 LogFlowFuncLeave();
2530 return 0;
2531}
2532
2533#endif /* RT_OS_WINDOWS */
2534
2535/**
2536 * Sends a signal to the client watcher thread to rescan the set of machines
2537 * that have open sessions.
2538 *
2539 * @note Doesn't lock anything.
2540 */
2541void VirtualBox::updateClientWatcher()
2542{
2543 AutoCaller autoCaller (this);
2544 AssertComRCReturn (autoCaller.rc(), (void) 0);
2545
2546 AssertReturn (mWatcherData.mThread != NIL_RTTHREAD, (void) 0);
2547
2548 /* sent an update request */
2549#if defined(RT_OS_WINDOWS)
2550 ::SetEvent (mWatcherData.mUpdateReq);
2551#elif defined(RT_OS_OS2)
2552 RTSemEventSignal (mWatcherData.mUpdateReq);
2553#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2554 RTSemEventSignal (mWatcherData.mUpdateReq);
2555#else
2556# error "Port me!"
2557#endif
2558}
2559
2560/**
2561 * Adds the given child process ID to the list of processes to be reaped.
2562 * This call should be followed by #updateClientWatcher() to take the effect.
2563 */
2564void VirtualBox::addProcessToReap (RTPROCESS pid)
2565{
2566 AutoCaller autoCaller (this);
2567 AssertComRCReturn (autoCaller.rc(), (void) 0);
2568
2569 /// @todo (dmik) Win32?
2570#ifndef RT_OS_WINDOWS
2571 AutoLock alock (this);
2572 mWatcherData.mProcesses.push_back (pid);
2573#endif
2574}
2575
2576/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2577struct MachineEvent : public VirtualBox::CallbackEvent
2578{
2579 enum What { DataChanged, StateChanged, Registered };
2580
2581 MachineEvent (VirtualBox *aVB, const Guid &aId)
2582 : CallbackEvent (aVB), what (DataChanged), id (aId)
2583 {}
2584
2585 MachineEvent (VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2586 : CallbackEvent (aVB), what (StateChanged), id (aId)
2587 , state (aState)
2588 {}
2589
2590 MachineEvent (VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2591 : CallbackEvent (aVB), what (Registered), id (aId)
2592 , registered (aRegistered)
2593 {}
2594
2595 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2596 {
2597 switch (what)
2598 {
2599 case DataChanged:
2600 LogFlow (("OnMachineDataChange: id={%Vuuid}\n", id.ptr()));
2601 aCallback->OnMachineDataChange (id);
2602 break;
2603
2604 case StateChanged:
2605 LogFlow (("OnMachineStateChange: id={%Vuuid}, state=%d\n",
2606 id.ptr(), state));
2607 aCallback->OnMachineStateChange (id, state);
2608 break;
2609
2610 case Registered:
2611 LogFlow (("OnMachineRegistered: id={%Vuuid}, registered=%d\n",
2612 id.ptr(), registered));
2613 aCallback->OnMachineRegistered (id, registered);
2614 break;
2615 }
2616 }
2617
2618 const What what;
2619
2620 Guid id;
2621 MachineState_T state;
2622 BOOL registered;
2623};
2624
2625/**
2626 * @note Doesn't lock any object.
2627 */
2628void VirtualBox::onMachineStateChange (const Guid &aId, MachineState_T aState)
2629{
2630 postEvent (new MachineEvent (this, aId, aState));
2631}
2632
2633/**
2634 * @note Doesn't lock any object.
2635 */
2636void VirtualBox::onMachineDataChange (const Guid &aId)
2637{
2638 postEvent (new MachineEvent (this, aId));
2639}
2640
2641/**
2642 * @note Locks this object for reading.
2643 */
2644BOOL VirtualBox::onExtraDataCanChange (const Guid &aId, INPTR BSTR aKey, INPTR BSTR aValue,
2645 Bstr &aError)
2646{
2647 LogFlowThisFunc (("machine={%s} aKey={%ls} aValue={%ls}\n",
2648 aId.toString().raw(), aKey, aValue));
2649
2650 AutoCaller autoCaller (this);
2651 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2652
2653 CallbackList list;
2654 {
2655 AutoReaderLock alock (this);
2656 list = mData.mCallbacks;
2657 }
2658
2659 BOOL allowChange = TRUE;
2660 CallbackList::iterator it = list.begin();
2661 while ((it != list.end()) && allowChange)
2662 {
2663 HRESULT rc = (*it++)->OnExtraDataCanChange (aId, aKey, aValue,
2664 aError.asOutParam(), &allowChange);
2665 if (FAILED (rc))
2666 {
2667 /* if a call to this method fails for some reason (for ex., because
2668 * the other side is dead), we ensure allowChange stays true
2669 * (MS COM RPC implementation seems to zero all output vars before
2670 * issuing an IPC call or after a failure, so it's essential
2671 * there) */
2672 allowChange = TRUE;
2673 }
2674 }
2675
2676 LogFlowThisFunc (("allowChange=%RTbool\n", allowChange));
2677 return allowChange;
2678}
2679
2680/** Event for onExtraDataChange() */
2681struct ExtraDataEvent : public VirtualBox::CallbackEvent
2682{
2683 ExtraDataEvent (VirtualBox *aVB, const Guid &aMachineId,
2684 INPTR BSTR aKey, INPTR BSTR aVal)
2685 : CallbackEvent (aVB), machineId (aMachineId)
2686 , key (aKey), val (aVal)
2687 {}
2688
2689 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2690 {
2691 LogFlow (("OnExtraDataChange: machineId={%Vuuid}, key='%ls', val='%ls'\n",
2692 machineId.ptr(), key.raw(), val.raw()));
2693 aCallback->OnExtraDataChange (machineId, key, val);
2694 }
2695
2696 Guid machineId;
2697 Bstr key, val;
2698};
2699
2700/**
2701 * @note Doesn't lock any object.
2702 */
2703void VirtualBox::onExtraDataChange (const Guid &aId, INPTR BSTR aKey, INPTR BSTR aValue)
2704{
2705 postEvent (new ExtraDataEvent (this, aId, aKey, aValue));
2706}
2707
2708/**
2709 * @note Doesn't lock any object.
2710 */
2711void VirtualBox::onMachineRegistered (const Guid &aId, BOOL aRegistered)
2712{
2713 postEvent (new MachineEvent (this, aId, aRegistered));
2714}
2715
2716/** Event for onSessionStateChange() */
2717struct SessionEvent : public VirtualBox::CallbackEvent
2718{
2719 SessionEvent (VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2720 : CallbackEvent (aVB), machineId (aMachineId), sessionState (aState)
2721 {}
2722
2723 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2724 {
2725 LogFlow (("OnSessionStateChange: machineId={%Vuuid}, sessionState=%d\n",
2726 machineId.ptr(), sessionState));
2727 aCallback->OnSessionStateChange (machineId, sessionState);
2728 }
2729
2730 Guid machineId;
2731 SessionState_T sessionState;
2732};
2733
2734/**
2735 * @note Doesn't lock any object.
2736 */
2737void VirtualBox::onSessionStateChange (const Guid &aId, SessionState_T aState)
2738{
2739 postEvent (new SessionEvent (this, aId, aState));
2740}
2741
2742/** Event for onSnapshotTaken(), onSnapshotRemoved() and onSnapshotChange() */
2743struct SnapshotEvent : public VirtualBox::CallbackEvent
2744{
2745 enum What { Taken, Discarded, Changed };
2746
2747 SnapshotEvent (VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2748 What aWhat)
2749 : CallbackEvent (aVB)
2750 , what (aWhat)
2751 , machineId (aMachineId), snapshotId (aSnapshotId)
2752 {}
2753
2754 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2755 {
2756 switch (what)
2757 {
2758 case Taken:
2759 LogFlow (("OnSnapshotTaken: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
2760 machineId.ptr(), snapshotId.ptr()));
2761 aCallback->OnSnapshotTaken (machineId, snapshotId);
2762 break;
2763
2764 case Discarded:
2765 LogFlow (("OnSnapshotDiscarded: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
2766 machineId.ptr(), snapshotId.ptr()));
2767 aCallback->OnSnapshotDiscarded (machineId, snapshotId);
2768 break;
2769
2770 case Changed:
2771 LogFlow (("OnSnapshotChange: machineId={%Vuuid}, snapshotId={%Vuuid}\n",
2772 machineId.ptr(), snapshotId.ptr()));
2773 aCallback->OnSnapshotChange (machineId, snapshotId);
2774 break;
2775 }
2776 }
2777
2778 const What what;
2779
2780 Guid machineId;
2781 Guid snapshotId;
2782};
2783
2784/**
2785 * @note Doesn't lock any object.
2786 */
2787void VirtualBox::onSnapshotTaken (const Guid &aMachineId, const Guid &aSnapshotId)
2788{
2789 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Taken));
2790}
2791
2792/**
2793 * @note Doesn't lock any object.
2794 */
2795void VirtualBox::onSnapshotDiscarded (const Guid &aMachineId, const Guid &aSnapshotId)
2796{
2797 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Discarded));
2798}
2799
2800/**
2801 * @note Doesn't lock any object.
2802 */
2803void VirtualBox::onSnapshotChange (const Guid &aMachineId, const Guid &aSnapshotId)
2804{
2805 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Changed));
2806}
2807
2808/**
2809 * @note Locks this object for reading.
2810 */
2811ComObjPtr <GuestOSType> VirtualBox::getUnknownOSType()
2812{
2813 ComObjPtr <GuestOSType> type;
2814
2815 AutoCaller autoCaller (this);
2816 AssertComRCReturn (autoCaller.rc(), type);
2817
2818 AutoReaderLock alock (this);
2819
2820 /* unknown type must always be the first */
2821 ComAssertRet (mData.mGuestOSTypes.size() > 0, type);
2822
2823 type = mData.mGuestOSTypes.front();
2824 return type;
2825}
2826
2827/**
2828 * Returns the list of opened machines (i.e. machines having direct sessions
2829 * opened by client processes).
2830 *
2831 * @note the returned list contains smart pointers. So, clear it as soon as
2832 * it becomes no more necessary to release instances.
2833 * @note it can be possible that a session machine from the list has been
2834 * already uninitialized, so a) lock the instance and b) chheck for
2835 * instance->isReady() return value before manipulating the object directly
2836 * (i.e. not through COM methods).
2837 *
2838 * @note Locks objects for reading.
2839 */
2840void VirtualBox::getOpenedMachines (SessionMachineVector &aVector)
2841{
2842 AutoCaller autoCaller (this);
2843 AssertComRCReturn (autoCaller.rc(), (void) 0);
2844
2845 std::list <ComObjPtr <SessionMachine> > list;
2846
2847 {
2848 AutoReaderLock alock (this);
2849
2850 for (MachineList::iterator it = mData.mMachines.begin();
2851 it != mData.mMachines.end();
2852 ++ it)
2853 {
2854 ComObjPtr <SessionMachine> sm = (*it)->sessionMachine();
2855 /* SessionMachine is null when there are no open sessions */
2856 if (!sm.isNull())
2857 list.push_back (sm);
2858 }
2859 }
2860
2861 aVector = SessionMachineVector (list.begin(), list.end());
2862 return;
2863}
2864
2865/**
2866 * Helper to find machines that use the given DVD image.
2867 *
2868 * This method also checks whether an existing snapshot refers to the given
2869 * image. However, only the machine ID is returned in this case, not IDs of
2870 * individual snapshots.
2871 *
2872 * @param aId Image ID to get usage for.
2873 * @param aUsage Type of the check.
2874 * @param aMachineIDs Where to store the list of machine IDs (can be NULL)
2875 *
2876 * @return @c true if at least one machine or its snapshot found and @c false
2877 * otherwise.
2878 *
2879 * @note For now, we just scan all the machines. We can optimize this later
2880 * if required by adding the corresponding field to DVDImage and requiring all
2881 * IDVDImage instances to be DVDImage objects.
2882 *
2883 * @note Locks objects for reading.
2884 */
2885bool VirtualBox::getDVDImageUsage (const Guid &aId,
2886 ResourceUsage_T aUsage,
2887 Bstr *aMachineIDs)
2888{
2889 AutoCaller autoCaller (this);
2890 AssertComRCReturn (autoCaller.rc(), FALSE);
2891
2892 typedef std::set <Guid> Set;
2893 Set idSet;
2894
2895 {
2896 AutoReaderLock alock (this);
2897
2898 for (MachineList::const_iterator mit = mData.mMachines.begin();
2899 mit != mData.mMachines.end();
2900 ++ mit)
2901 {
2902 ComObjPtr <Machine> m = *mit;
2903
2904 AutoLimitedCaller machCaller (m);
2905 AssertComRC (machCaller.rc());
2906
2907 /* ignore inaccessible machines */
2908 if (machCaller.state() == Machine::Ready)
2909 {
2910 if (m->isDVDImageUsed (aId, aUsage))
2911 {
2912 /* if not interested in the list, return shortly */
2913 if (aMachineIDs == NULL)
2914 return true;
2915
2916 idSet.insert (m->uuid());
2917 }
2918 }
2919 }
2920 }
2921
2922 if (aMachineIDs)
2923 {
2924 if (!idSet.empty())
2925 {
2926 /* convert to a string of UUIDs */
2927 char *idList = (char *) RTMemTmpAllocZ (RTUUID_STR_LENGTH * idSet.size());
2928 char *idListPtr = idList;
2929 for (Set::iterator it = idSet.begin(); it != idSet.end(); ++ it)
2930 {
2931 RTUuidToStr (*it, idListPtr, RTUUID_STR_LENGTH);
2932 idListPtr += RTUUID_STR_LENGTH - 1;
2933 /* replace EOS with a space char */
2934 *(idListPtr ++) = ' ';
2935 }
2936 Assert (int (idListPtr - idList) == int (RTUUID_STR_LENGTH * idSet.size()));
2937 /* remove the trailing space */
2938 *(-- idListPtr) = 0;
2939 /* copy the string */
2940 *aMachineIDs = idList;
2941 RTMemTmpFree (idList);
2942 }
2943 else
2944 {
2945 (*aMachineIDs).setNull();
2946 }
2947 }
2948
2949 return !idSet.empty();
2950}
2951
2952/**
2953 * Helper to find machines that use the given Floppy image.
2954 *
2955 * This method also checks whether an existing snapshot refers to the given
2956 * image. However, only the machine ID is returned in this case, not IDs of
2957 * individual snapshots.
2958 *
2959 * @param aId Image ID to get usage for.
2960 * @param aUsage Type of the check.
2961 * @param aMachineIDs Where to store the list of machine IDs (can be NULL)
2962 *
2963 * @return @c true if at least one machine or its snapshot found and @c false
2964 * otherwise.
2965 *
2966 * @note For now, we just scan all the machines. We can optimize this later
2967 * if required by adding the corresponding field to FloppyImage and requiring all
2968 * FloppyImage instances to be FloppyImage objects.
2969 *
2970 * @note Locks objects for reading.
2971 */
2972bool VirtualBox::getFloppyImageUsage (const Guid &aId,
2973 ResourceUsage_T aUsage,
2974 Bstr *aMachineIDs)
2975{
2976 AutoCaller autoCaller (this);
2977 AssertComRCReturn (autoCaller.rc(), FALSE);
2978
2979 typedef std::set <Guid> Set;
2980 Set idSet;
2981
2982 {
2983 AutoReaderLock alock (this);
2984
2985 for (MachineList::const_iterator mit = mData.mMachines.begin();
2986 mit != mData.mMachines.end();
2987 ++ mit)
2988 {
2989 ComObjPtr <Machine> m = *mit;
2990
2991 AutoLimitedCaller machCaller (m);
2992 AssertComRC (machCaller.rc());
2993
2994 /* ignore inaccessible machines */
2995 if (machCaller.state() == Machine::Ready)
2996 {
2997 if (m->isFloppyImageUsed (aId, aUsage))
2998 {
2999 /* if not interested in the list, return shortly */
3000 if (aMachineIDs == NULL)
3001 return true;
3002
3003 idSet.insert (m->uuid());
3004 }
3005 }
3006 }
3007 }
3008
3009 if (aMachineIDs)
3010 {
3011 if (!idSet.empty())
3012 {
3013 /* convert to a string of UUIDs */
3014 char *idList = (char *) RTMemTmpAllocZ (RTUUID_STR_LENGTH * idSet.size());
3015 char *idListPtr = idList;
3016 for (Set::iterator it = idSet.begin(); it != idSet.end(); ++ it)
3017 {
3018 RTUuidToStr (*it, idListPtr, RTUUID_STR_LENGTH);
3019 idListPtr += RTUUID_STR_LENGTH - 1;
3020 /* replace EOS with a space char */
3021 *(idListPtr ++) = ' ';
3022 }
3023 Assert (int (idListPtr - idList) == int (RTUUID_STR_LENGTH * idSet.size()));
3024 /* remove the trailing space */
3025 *(-- idListPtr) = 0;
3026 /* copy the string */
3027 *aMachineIDs = idList;
3028 RTMemTmpFree (idList);
3029 }
3030 else
3031 {
3032 (*aMachineIDs).setNull();
3033 }
3034 }
3035
3036 return !idSet.empty();
3037}
3038
3039/**
3040 * Tries to calculate the relative path of the given absolute path using the
3041 * directory of the VirtualBox settings file as the base directory.
3042 *
3043 * @param aPath absolute path to calculate the relative path for
3044 * @param aResult where to put the result (used only when it's possible to
3045 * make a relative path from the given absolute path;
3046 * otherwise left untouched)
3047 *
3048 * @note Doesn't lock any object.
3049 */
3050void VirtualBox::calculateRelativePath (const char *aPath, Utf8Str &aResult)
3051{
3052 AutoCaller autoCaller (this);
3053 AssertComRCReturn (autoCaller.rc(), (void) 0);
3054
3055 /* no need to lock since mHomeDir is const */
3056
3057 Utf8Str settingsDir = mData.mHomeDir;
3058
3059 if (RTPathStartsWith (aPath, settingsDir))
3060 {
3061 /* when assigning, we create a separate Utf8Str instance because both
3062 * aPath and aResult can point to the same memory location when this
3063 * func is called (if we just do aResult = aPath, aResult will be freed
3064 * first, and since its the same as aPath, an attempt to copy garbage
3065 * will be made. */
3066 aResult = Utf8Str (aPath + settingsDir.length() + 1);
3067 }
3068}
3069
3070// private methods
3071/////////////////////////////////////////////////////////////////////////////
3072
3073/**
3074 * Searches for a Machine object with the given ID in the collection
3075 * of registered machines.
3076 *
3077 * @param id
3078 * ID of the machine
3079 * @param doSetError
3080 * if TRUE, the appropriate error info is set in case when the machine
3081 * is not found
3082 * @param machine
3083 * where to store the found machine object (can be NULL)
3084 *
3085 * @return
3086 * S_OK when found or E_INVALIDARG when not found
3087 *
3088 * @note Locks this object for reading.
3089 */
3090HRESULT VirtualBox::findMachine (const Guid &aId, bool aSetError,
3091 ComObjPtr <Machine> *aMachine /* = NULL */)
3092{
3093 AutoCaller autoCaller (this);
3094 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3095
3096 bool found = false;
3097
3098 {
3099 AutoReaderLock alock (this);
3100
3101 for (MachineList::iterator it = mData.mMachines.begin();
3102 !found && it != mData.mMachines.end();
3103 ++ it)
3104 {
3105 /* sanity */
3106 AutoLimitedCaller machCaller (*it);
3107 AssertComRC (machCaller.rc());
3108
3109 found = (*it)->uuid() == aId;
3110 if (found && aMachine)
3111 *aMachine = *it;
3112 }
3113 }
3114
3115 HRESULT rc = found ? S_OK : E_INVALIDARG;
3116
3117 if (aSetError && !found)
3118 {
3119 setError (E_INVALIDARG,
3120 tr ("Could not find a registered machine with UUID {%Vuuid}"),
3121 aId.raw());
3122 }
3123
3124 return rc;
3125}
3126
3127/**
3128 * Searches for a HardDisk object with the given ID or location specification
3129 * in the collection of registered hard disks. If both ID and location are
3130 * specified, the first object that matches either of them (not necessarily
3131 * both) is returned.
3132 *
3133 * @param aId ID of the hard disk (NULL when unused)
3134 * @param aLocation full location specification (NULL when unused)
3135 * @param aSetError if TRUE, the appropriate error info is set in case when
3136 * the disk is not found and only one search criteria (ID
3137 * or file name) is specified.
3138 * @param aHardDisk where to store the found hard disk object (can be NULL)
3139 *
3140 * @return
3141 * S_OK when found or E_INVALIDARG when not found
3142 *
3143 * @note Locks objects for reading!
3144 */
3145HRESULT VirtualBox::
3146findHardDisk (const Guid *aId, const BSTR aLocation,
3147 bool aSetError, ComObjPtr <HardDisk> *aHardDisk /* = NULL */)
3148{
3149 ComAssertRet (aId || aLocation, E_INVALIDARG);
3150
3151 AutoReaderLock alock (this);
3152
3153 /* first lookup the map by UUID if UUID is provided */
3154 if (aId)
3155 {
3156 HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
3157 if (it != mData.mHardDiskMap.end())
3158 {
3159 if (aHardDisk)
3160 *aHardDisk = (*it).second;
3161 return S_OK;
3162 }
3163 }
3164
3165 /* then iterate and find by location */
3166 bool found = false;
3167 if (aLocation)
3168 {
3169 Utf8Str location = aLocation;
3170
3171 for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3172 !found && it != mData.mHardDiskMap.end();
3173 ++ it)
3174 {
3175 const ComObjPtr <HardDisk> &hd = (*it).second;
3176 AutoReaderLock hdLock (hd);
3177
3178 if (hd->storageType() == HardDiskStorageType_VirtualDiskImage ||
3179 hd->storageType() == HardDiskStorageType_VMDKImage ||
3180 hd->storageType() == HardDiskStorageType_VHDImage)
3181 {
3182 /* locations of VDI and VMDK hard disks for now are just
3183 * file paths */
3184 found = RTPathCompare (location,
3185 Utf8Str (hd->toString
3186 (false /* aShort */))) == 0;
3187 }
3188 else
3189 {
3190 found = aLocation == hd->toString (false /* aShort */);
3191 }
3192
3193 if (found && aHardDisk)
3194 *aHardDisk = hd;
3195 }
3196 }
3197
3198 HRESULT rc = found ? S_OK : E_INVALIDARG;
3199
3200 if (aSetError && !found)
3201 {
3202 if (aId && !aLocation)
3203 setError (rc, tr ("Could not find a registered hard disk "
3204 "with UUID {%Vuuid}"), aId->raw());
3205 else if (aLocation && !aId)
3206 setError (rc, tr ("Could not find a registered hard disk "
3207 "with location '%ls'"), aLocation);
3208 }
3209
3210 return rc;
3211}
3212
3213/**
3214 * @deprecated Use #findHardDisk() instead.
3215 *
3216 * Searches for a HVirtualDiskImage object with the given ID or file path in the
3217 * collection of registered hard disks. If both ID and file path are specified,
3218 * the first object that matches either of them (not necessarily both)
3219 * is returned.
3220 *
3221 * @param aId ID of the hard disk (NULL when unused)
3222 * @param filePathFull full path to the image file (NULL when unused)
3223 * @param aSetError if TRUE, the appropriate error info is set in case when
3224 * the disk is not found and only one search criteria (ID
3225 * or file name) is specified.
3226 * @param aHardDisk where to store the found hard disk object (can be NULL)
3227 *
3228 * @return
3229 * S_OK when found or E_INVALIDARG when not found
3230 *
3231 * @note Locks objects for reading!
3232 */
3233HRESULT VirtualBox::
3234findVirtualDiskImage (const Guid *aId, const BSTR aFilePathFull,
3235 bool aSetError, ComObjPtr <HVirtualDiskImage> *aImage /* = NULL */)
3236{
3237 ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3238
3239 AutoReaderLock alock (this);
3240
3241 /* first lookup the map by UUID if UUID is provided */
3242 if (aId)
3243 {
3244 HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
3245 if (it != mData.mHardDiskMap.end())
3246 {
3247 AutoReaderLock hdLock ((*it).second);
3248 if ((*it).second->storageType() == HardDiskStorageType_VirtualDiskImage)
3249 {
3250 if (aImage)
3251 *aImage = (*it).second->asVDI();
3252 return S_OK;
3253 }
3254 }
3255 }
3256
3257 /* then iterate and find by name */
3258 bool found = false;
3259 if (aFilePathFull)
3260 {
3261 for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3262 !found && it != mData.mHardDiskMap.end();
3263 ++ it)
3264 {
3265 const ComObjPtr <HardDisk> &hd = (*it).second;
3266 AutoReaderLock hdLock (hd);
3267 if (hd->storageType() != HardDiskStorageType_VirtualDiskImage)
3268 continue;
3269
3270 found = RTPathCompare (Utf8Str (aFilePathFull),
3271 Utf8Str (hd->asVDI()->filePathFull())) == 0;
3272 if (found && aImage)
3273 *aImage = hd->asVDI();
3274 }
3275 }
3276
3277 HRESULT rc = found ? S_OK : E_INVALIDARG;
3278
3279 if (aSetError && !found)
3280 {
3281 if (aId && !aFilePathFull)
3282 setError (rc, tr ("Could not find a registered VDI hard disk "
3283 "with UUID {%Vuuid}"), aId->raw());
3284 else if (aFilePathFull && !aId)
3285 setError (rc, tr ("Could not find a registered VDI hard disk "
3286 "with the file path '%ls'"), aFilePathFull);
3287 }
3288
3289 return rc;
3290}
3291
3292/**
3293 * Searches for a DVDImage object with the given ID or file path in the
3294 * collection of registered DVD images. If both ID and file path are specified,
3295 * the first object that matches either of them (not necessarily both)
3296 * is returned.
3297 *
3298 * @param aId
3299 * ID of the DVD image (unused when NULL)
3300 * @param aFilePathFull
3301 * full path to the image file (unused when NULL)
3302 * @param aSetError
3303 * if TRUE, the appropriate error info is set in case when the image is not
3304 * found and only one search criteria (ID or file name) is specified.
3305 * @param aImage
3306 * where to store the found DVD image object (can be NULL)
3307 *
3308 * @return
3309 * S_OK when found or E_INVALIDARG when not found
3310 *
3311 * @note Locks this object for reading.
3312 */
3313HRESULT VirtualBox::findDVDImage (const Guid *aId, const BSTR aFilePathFull,
3314 bool aSetError,
3315 ComObjPtr <DVDImage> *aImage /* = NULL */)
3316{
3317 ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3318
3319 bool found = false;
3320
3321 {
3322 AutoReaderLock alock (this);
3323
3324 for (DVDImageList::const_iterator it = mData.mDVDImages.begin();
3325 !found && it != mData.mDVDImages.end();
3326 ++ it)
3327 {
3328 /* DVDImage fields are constant, so no need to lock */
3329 found = (aId && (*it)->id() == *aId) ||
3330 (aFilePathFull &&
3331 RTPathCompare (Utf8Str (aFilePathFull),
3332 Utf8Str ((*it)->filePathFull())) == 0);
3333 if (found && aImage)
3334 *aImage = *it;
3335 }
3336 }
3337
3338 HRESULT rc = found ? S_OK : E_INVALIDARG;
3339
3340 if (aSetError && !found)
3341 {
3342 if (aId && !aFilePathFull)
3343 setError (rc, tr ("Could not find a registered CD/DVD image "
3344 "with UUID {%s}"), aId->toString().raw());
3345 else if (aFilePathFull && !aId)
3346 setError (rc, tr ("Could not find a registered CD/DVD image "
3347 "with the file path '%ls'"), aFilePathFull);
3348 }
3349
3350 return rc;
3351}
3352
3353/**
3354 * Searches for a FloppyImage object with the given ID or file path in the
3355 * collection of registered floppy images. If both ID and file path are specified,
3356 * the first object that matches either of them (not necessarily both)
3357 * is returned.
3358 *
3359 * @param aId
3360 * ID of the floppy image (unused when NULL)
3361 * @param aFilePathFull
3362 * full path to the image file (unused when NULL)
3363 * @param aSetError
3364 * if TRUE, the appropriate error info is set in case when the image is not
3365 * found and only one search criteria (ID or file name) is specified.
3366 * @param aImage
3367 * where to store the found floppy image object (can be NULL)
3368 *
3369 * @return
3370 * S_OK when found or E_INVALIDARG when not found
3371 *
3372 * @note Locks this object for reading.
3373 */
3374HRESULT VirtualBox::findFloppyImage (const Guid *aId, const BSTR aFilePathFull,
3375 bool aSetError,
3376 ComObjPtr <FloppyImage> *aImage /* = NULL */)
3377{
3378 ComAssertRet (aId || aFilePathFull, E_INVALIDARG);
3379
3380 bool found = false;
3381
3382 {
3383 AutoReaderLock alock (this);
3384
3385 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3386 !found && it != mData.mFloppyImages.end();
3387 ++ it)
3388 {
3389 /* FloppyImage fields are constant, so no need to lock */
3390 found = (aId && (*it)->id() == *aId) ||
3391 (aFilePathFull &&
3392 RTPathCompare (Utf8Str (aFilePathFull),
3393 Utf8Str ((*it)->filePathFull())) == 0);
3394 if (found && aImage)
3395 *aImage = *it;
3396 }
3397 }
3398
3399 HRESULT rc = found ? S_OK : E_INVALIDARG;
3400
3401 if (aSetError && !found)
3402 {
3403 if (aId && !aFilePathFull)
3404 setError (rc, tr ("Could not find a registered floppy image "
3405 "with UUID {%s}"), aId->toString().raw());
3406 else if (aFilePathFull && !aId)
3407 setError (rc, tr ("Could not find a registered floppy image "
3408 "with the file path '%ls'"), aFilePathFull);
3409 }
3410
3411 return rc;
3412}
3413
3414/**
3415 * When \a aHardDisk is not NULL, searches for an object equal to the given
3416 * hard disk in the collection of registered hard disks, or, if the given hard
3417 * disk is HVirtualDiskImage, for an object with the given file path in the
3418 * collection of all registered non-hard disk images (DVDs and floppies).
3419 * Other parameters are unused.
3420 *
3421 * When \a aHardDisk is NULL, searches for an object with the given ID or file
3422 * path in the collection of all registered images (VDIs, DVDs and floppies).
3423 * If both ID and file path are specified, matching either of them will satisfy
3424 * the search.
3425 *
3426 * If a matching object is found, this method returns E_INVALIDARG and sets the
3427 * appropriate error info. Otherwise, S_OK is returned.
3428 *
3429 * @param aHardDisk hard disk object to check against registered media
3430 * (NULL when unused)
3431 * @param aId UUID of the media to check (NULL when unused)
3432 * @param aFilePathFull full path to the image file (NULL when unused)
3433 *
3434 * @note Locks objects!
3435 */
3436HRESULT VirtualBox::checkMediaForConflicts (HardDisk *aHardDisk,
3437 const Guid *aId,
3438 const BSTR aFilePathFull)
3439{
3440 AssertReturn (aHardDisk || aId || aFilePathFull, E_FAIL);
3441
3442 HRESULT rc = S_OK;
3443
3444 AutoReaderLock alock (this);
3445
3446 if (aHardDisk)
3447 {
3448 for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
3449 it != mData.mHardDiskMap.end();
3450 ++ it)
3451 {
3452 const ComObjPtr <HardDisk> &hd = (*it).second;
3453 if (hd->sameAs (aHardDisk))
3454 return setError (E_INVALIDARG,
3455 tr ("A hard disk with UUID {%Vuuid} or with the same properties "
3456 "('%ls') is already registered"),
3457 aHardDisk->id().raw(), aHardDisk->toString().raw());
3458 }
3459
3460 aId = &aHardDisk->id();
3461 if (aHardDisk->storageType() == HardDiskStorageType_VirtualDiskImage)
3462#if !defined (VBOX_WITH_XPCOM)
3463 /// @todo (dmik) stupid BSTR declaration lacks the BCSTR counterpart
3464 const_cast <BSTR> (aFilePathFull) = aHardDisk->asVDI()->filePathFull();
3465#else
3466 aFilePathFull = aHardDisk->asVDI()->filePathFull();
3467#endif
3468 }
3469
3470 bool found = false;
3471
3472 if (aId || aFilePathFull) do
3473 {
3474 if (!aHardDisk)
3475 {
3476 rc = findHardDisk (aId, aFilePathFull, false /* aSetError */);
3477 found = SUCCEEDED (rc);
3478 if (found)
3479 break;
3480 }
3481
3482 rc = findDVDImage (aId, aFilePathFull, false /* aSetError */);
3483 found = SUCCEEDED (rc);
3484 if (found)
3485 break;
3486
3487 rc = findFloppyImage (aId, aFilePathFull, false /* aSetError */);
3488 found = SUCCEEDED (rc);
3489 if (found)
3490 break;
3491 }
3492 while (0);
3493
3494 if (found)
3495 {
3496 if (aId && !aFilePathFull)
3497 rc = setError (E_INVALIDARG,
3498 tr ("A disk image with UUID {%Vuuid} is already registered"),
3499 aId->raw());
3500 else if (aFilePathFull && !aId)
3501 rc = setError (E_INVALIDARG,
3502 tr ("A disk image with file path '%ls' is already registered"),
3503 aFilePathFull);
3504 else
3505 rc = setError (E_INVALIDARG,
3506 tr ("A disk image with UUID {%Vuuid} or file path '%ls' "
3507 "is already registered"), aId->raw(), aFilePathFull);
3508 }
3509 else
3510 rc = S_OK;
3511
3512 return rc;
3513}
3514
3515/**
3516 * Reads in the machine definitions from the configuration loader
3517 * and creates the relevant objects.
3518 *
3519 * @param aGlobal <Global> node.
3520 *
3521 * @note Can be called only from #init().
3522 * @note Doesn't lock anything.
3523 */
3524HRESULT VirtualBox::loadMachines (const settings::Key &aGlobal)
3525{
3526 using namespace settings;
3527
3528 AutoCaller autoCaller (this);
3529 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3530
3531 HRESULT rc = S_OK;
3532
3533 Key::List machines = aGlobal.key ("MachineRegistry").keys ("MachineEntry");
3534 for (Key::List::const_iterator it = machines.begin();
3535 it != machines.end(); ++ it)
3536 {
3537 /* required */
3538 Guid uuid = (*it).value <Guid> ("uuid");
3539 /* required */
3540 Bstr src = (*it).stringValue ("src");
3541
3542 /* create a new machine object */
3543 ComObjPtr <Machine> machine;
3544 rc = machine.createObject();
3545 if (SUCCEEDED (rc))
3546 {
3547 /* initialize the machine object and register it */
3548 rc = machine->init (this, src, Machine::Init_Registered,
3549 NULL, FALSE, &uuid);
3550 if (SUCCEEDED (rc))
3551 rc = registerMachine (machine);
3552 }
3553 }
3554
3555 return rc;
3556}
3557
3558/**
3559 * Reads in the disk registration entries from the global settings file
3560 * and creates the relevant objects
3561 *
3562 * @param aGlobal <Global> node
3563 *
3564 * @note Can be called only from #init().
3565 * @note Doesn't lock anything.
3566 */
3567HRESULT VirtualBox::loadDisks (const settings::Key &aGlobal)
3568{
3569 using namespace settings;
3570
3571 AutoCaller autoCaller (this);
3572 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3573
3574 HRESULT rc = S_OK;
3575
3576 Key registry = aGlobal.key ("DiskRegistry");
3577
3578 const char *kMediaNodes[] = { "HardDisks", "DVDImages", "FloppyImages" };
3579
3580 for (size_t n = 0; n < ELEMENTS (kMediaNodes); ++ n)
3581 {
3582 /* All three media nodes are optional */
3583 Key node = registry.findKey (kMediaNodes [n]);
3584 if (node.isNull())
3585 continue;
3586
3587 if (n == 0)
3588 {
3589 /* HardDisks node */
3590 rc = loadHardDisks (node);
3591 continue;
3592 }
3593
3594 Key::List images = node.keys ("Image");
3595 for (Key::List::const_iterator it = images.begin();
3596 it != images.end(); ++ it)
3597 {
3598 /* required */
3599 Guid uuid = (*it).value <Guid> ("uuid");
3600 /* required */
3601 Bstr src = (*it).stringValue ("src");
3602
3603 switch (n)
3604 {
3605 case 1: /* DVDImages */
3606 {
3607 ComObjPtr <DVDImage> image;
3608 image.createObject();
3609 rc = image->init (this, src, TRUE /* isRegistered */, uuid);
3610 if (SUCCEEDED (rc))
3611 rc = registerDVDImage (image, TRUE /* aOnStartUp */);
3612
3613 break;
3614 }
3615 case 2: /* FloppyImages */
3616 {
3617 ComObjPtr <FloppyImage> image;
3618 image.createObject();
3619 rc = image->init (this, src, TRUE /* isRegistered */, uuid);
3620 if (SUCCEEDED (rc))
3621 rc = registerFloppyImage (image, TRUE /* aOnStartUp */);
3622
3623 break;
3624 }
3625 default:
3626 AssertFailed();
3627 }
3628
3629 CheckComRCBreakRC (rc);
3630 }
3631
3632 CheckComRCBreakRC (rc);
3633 }
3634
3635 return rc;
3636}
3637
3638/**
3639 * Loads all hard disks from the given <HardDisks> node.
3640 * Note that all loaded hard disks register themselves within this VirtualBox.
3641 *
3642 * @param aNode <HardDisks> node.
3643 *
3644 * @note Can be called only from #init().
3645 * @note Doesn't lock anything.
3646 */
3647HRESULT VirtualBox::loadHardDisks (const settings::Key &aNode)
3648{
3649 using namespace settings;
3650
3651 AutoCaller autoCaller (this);
3652 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3653
3654 AssertReturn (!aNode.isNull(), E_INVALIDARG);
3655
3656 HRESULT rc = S_OK;
3657
3658 Key::List disks = aNode.keys ("HardDisk");
3659 for (Key::List::const_iterator it = disks.begin();
3660 it != disks.end(); ++ it)
3661 {
3662 Key storageNode;
3663
3664 /* detect the type of the hard disk (either one of VirtualDiskImage,
3665 * ISCSIHardDisk, VMDKImage or HCustomHardDisk) */
3666
3667 do
3668 {
3669 storageNode = (*it).findKey ("VirtualDiskImage");
3670 if (!storageNode.isNull())
3671 {
3672 ComObjPtr <HVirtualDiskImage> vdi;
3673 vdi.createObject();
3674 rc = vdi->init (this, NULL, (*it), storageNode);
3675 break;
3676 }
3677
3678 storageNode = (*it).findKey ("ISCSIHardDisk");
3679 if (!storageNode.isNull())
3680 {
3681 ComObjPtr <HISCSIHardDisk> iscsi;
3682 iscsi.createObject();
3683 rc = iscsi->init (this, (*it), storageNode);
3684 break;
3685 }
3686
3687 storageNode = (*it).findKey ("VMDKImage");
3688 if (!storageNode.isNull())
3689 {
3690 ComObjPtr <HVMDKImage> vmdk;
3691 vmdk.createObject();
3692 rc = vmdk->init (this, NULL, (*it), storageNode);
3693 break;
3694 }
3695
3696 storageNode = (*it).findKey ("CustomHardDisk");
3697 if (!storageNode.isNull())
3698 {
3699 ComObjPtr <HCustomHardDisk> custom;
3700 custom.createObject();
3701 rc = custom->init (this, NULL, (*it), storageNode);
3702 break;
3703 }
3704
3705 storageNode = (*it).findKey ("VHDImage");
3706 if (!storageNode.isNull())
3707 {
3708 ComObjPtr <HVHDImage> vhd;
3709 vhd.createObject();
3710 rc = vhd->init (this, NULL, (*it), storageNode);
3711 break;
3712 }
3713
3714 ComAssertMsgFailedBreak (("No valid hard disk storage node!\n"),
3715 rc = E_FAIL);
3716 }
3717 while (0);
3718 }
3719
3720 return rc;
3721}
3722
3723/**
3724 * Helper function to write out the configuration tree.
3725 *
3726 * @note Locks objects for reading!
3727 */
3728HRESULT VirtualBox::saveSettings()
3729{
3730 AutoCaller autoCaller (this);
3731 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3732
3733 AssertReturn (!!mData.mCfgFile.mName, E_FAIL);
3734
3735 HRESULT rc = S_OK;
3736
3737 AutoReaderLock alock (this);
3738
3739 try
3740 {
3741 using namespace settings;
3742
3743#if 0
3744 /// @todo disabled until made thread-safe by using handle duplicates
3745 File file (File::ReadWrite, mData.mCfgFile.mHandle,
3746 Utf8Str (mData.mCfgFile.mName));
3747#else
3748 File file (File::ReadWrite, Utf8Str (mData.mCfgFile.mName));
3749#endif
3750 XmlTreeBackend tree;
3751
3752 rc = VirtualBox::loadSettingsTree_ForUpdate (tree, file);
3753 CheckComRCThrowRC (rc);
3754
3755 Key global = tree.rootKey().createKey ("Global");
3756
3757 /* machines */
3758 {
3759 /* first, delete the entire machine registry */
3760 Key registryNode = global.findKey ("MachineRegistry");
3761 if (!registryNode.isNull())
3762 registryNode.zap();
3763 /* then, recreate it */
3764 registryNode = global.createKey ("MachineRegistry");
3765
3766 /* write out the machines */
3767 for (MachineList::iterator it = mData.mMachines.begin();
3768 it != mData.mMachines.end();
3769 ++ it)
3770 {
3771 Key entryNode = registryNode.appendKey ("MachineEntry");
3772 rc = (*it)->saveRegistryEntry (entryNode);
3773 CheckComRCThrowRC (rc);
3774 }
3775 }
3776
3777 /* disk images */
3778 {
3779 /* first, delete the entire disk image registr */
3780 Key registryNode = global.findKey ("DiskRegistry");
3781 if (!registryNode.isNull())
3782 registryNode.zap();
3783 /* then, recreate it */
3784 registryNode = global.createKey ("DiskRegistry");
3785
3786 /* write out the hard disks */
3787 {
3788 Key imagesNode = registryNode.createKey ("HardDisks");
3789 rc = saveHardDisks (imagesNode);
3790 CheckComRCThrowRC (rc);
3791 }
3792
3793 /* write out the CD/DVD images */
3794 {
3795 Key imagesNode = registryNode.createKey ("DVDImages");
3796
3797 for (DVDImageList::iterator it = mData.mDVDImages.begin();
3798 it != mData.mDVDImages.end();
3799 ++ it)
3800 {
3801 ComObjPtr <DVDImage> dvd = *it;
3802 /* no need to lock: fields are constant */
3803 Key imageNode = imagesNode.appendKey ("Image");
3804 imageNode.setValue <Guid> ("uuid", dvd->id());
3805 imageNode.setValue <Bstr> ("src", dvd->filePath());
3806 }
3807 }
3808
3809 /* write out the floppy images */
3810 {
3811 Key imagesNode = registryNode.createKey ("FloppyImages");
3812
3813 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3814 it != mData.mFloppyImages.end();
3815 ++ it)
3816 {
3817 ComObjPtr <FloppyImage> fd = *it;
3818 /* no need to lock: fields are constant */
3819 Key imageNode = imagesNode.appendKey ("Image");
3820 imageNode.setValue <Guid> ("uuid", fd->id());
3821 imageNode.setValue <Bstr> ("src", fd->filePath());
3822 }
3823 }
3824 }
3825
3826 /* host data (USB filters) */
3827 rc = mData.mHost->saveSettings (global);
3828 CheckComRCThrowRC (rc);
3829
3830 rc = mData.mSystemProperties->saveSettings (global);
3831 CheckComRCThrowRC (rc);
3832
3833 /* save the settings on success */
3834 rc = VirtualBox::saveSettingsTree (tree, file,
3835 mData.mSettingsFileVersion);
3836 CheckComRCThrowRC (rc);
3837 }
3838 catch (HRESULT err)
3839 {
3840 /* we assume that error info is set by the thrower */
3841 rc = err;
3842 }
3843 catch (...)
3844 {
3845 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3846 }
3847
3848 return rc;
3849}
3850
3851/**
3852 * Saves all hard disks to the given <HardDisks> node.
3853 *
3854 * @param aNode <HardDisks> node.
3855 *
3856 * @note Locks this object for reding.
3857 */
3858HRESULT VirtualBox::saveHardDisks (settings::Key &aNode)
3859{
3860 using namespace settings;
3861
3862 AssertReturn (!aNode.isNull(), E_INVALIDARG);
3863
3864 HRESULT rc = S_OK;
3865
3866 AutoReaderLock alock (this);
3867
3868 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
3869 it != mData.mHardDisks.end();
3870 ++ it)
3871 {
3872 ComObjPtr <HardDisk> hd = *it;
3873 AutoReaderLock hdLock (hd);
3874
3875 Key hdNode = aNode.appendKey ("HardDisk");
3876
3877 switch (hd->storageType())
3878 {
3879 case HardDiskStorageType_VirtualDiskImage:
3880 {
3881 Key storageNode = hdNode.createKey ("VirtualDiskImage");
3882 rc = hd->saveSettings (hdNode, storageNode);
3883 break;
3884 }
3885
3886 case HardDiskStorageType_ISCSIHardDisk:
3887 {
3888 Key storageNode = hdNode.createKey ("ISCSIHardDisk");
3889 rc = hd->saveSettings (hdNode, storageNode);
3890 break;
3891 }
3892
3893 case HardDiskStorageType_VMDKImage:
3894 {
3895 Key storageNode = hdNode.createKey ("VMDKImage");
3896 rc = hd->saveSettings (hdNode, storageNode);
3897 break;
3898 }
3899 case HardDiskStorageType_CustomHardDisk:
3900 {
3901 Key storageNode = hdNode.createKey ("CustomHardDisk");
3902 rc = hd->saveSettings (hdNode, storageNode);
3903 break;
3904 }
3905
3906 case HardDiskStorageType_VHDImage:
3907 {
3908 Key storageNode = hdNode.createKey ("VHDImage");
3909 rc = hd->saveSettings (hdNode, storageNode);
3910 break;
3911 }
3912 }
3913
3914 CheckComRCBreakRC (rc);
3915 }
3916
3917 return rc;
3918}
3919
3920/**
3921 * Helper to register the machine.
3922 *
3923 * When called during VirtualBox startup, adds the given machine to the
3924 * collection of registered machines. Otherwise tries to mark the machine
3925 * as registered, and, if succeeded, adds it to the collection and
3926 * saves global settings.
3927 *
3928 * @note The caller must have added itself as a caller of the @a aMachine
3929 * object if calls this method not on VirtualBox startup.
3930 *
3931 * @param aMachine machine to register
3932 *
3933 * @note Locks objects!
3934 */
3935HRESULT VirtualBox::registerMachine (Machine *aMachine)
3936{
3937 ComAssertRet (aMachine, E_INVALIDARG);
3938
3939 AutoCaller autoCaller (this);
3940 CheckComRCReturnRC (autoCaller.rc());
3941
3942 AutoLock alock (this);
3943
3944 HRESULT rc = S_OK;
3945
3946 {
3947 ComObjPtr <Machine> m;
3948 rc = findMachine (aMachine->uuid(), false /* aDoSetError */, &m);
3949 if (SUCCEEDED (rc))
3950 {
3951 /* sanity */
3952 AutoLimitedCaller machCaller (m);
3953 AssertComRC (machCaller.rc());
3954
3955 return setError (E_INVALIDARG,
3956 tr ("Registered machine with UUID {%Vuuid} ('%ls') already exists"),
3957 aMachine->uuid().raw(), m->settingsFileFull().raw());
3958 }
3959
3960 ComAssertRet (rc == E_INVALIDARG, rc);
3961 rc = S_OK;
3962 }
3963
3964 if (autoCaller.state() != InInit)
3965 {
3966 /* Machine::trySetRegistered() will commit and save machine settings */
3967 rc = aMachine->trySetRegistered (TRUE);
3968 CheckComRCReturnRC (rc);
3969 }
3970
3971 /* add to the collection of registered machines */
3972 mData.mMachines.push_back (aMachine);
3973
3974 if (autoCaller.state() != InInit)
3975 rc = saveSettings();
3976
3977 return rc;
3978}
3979
3980/**
3981 * Helper to register the hard disk.
3982 *
3983 * @param aHardDisk object to register
3984 * @param aFlags one of RHD_* values
3985 *
3986 * @note Locks objects!
3987 */
3988HRESULT VirtualBox::registerHardDisk (HardDisk *aHardDisk, RHD_Flags aFlags)
3989{
3990 ComAssertRet (aHardDisk, E_INVALIDARG);
3991
3992 AutoCaller autoCaller (this);
3993 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3994
3995 AutoLock alock (this);
3996
3997 HRESULT rc = checkMediaForConflicts (aHardDisk, NULL, NULL);
3998 CheckComRCReturnRC (rc);
3999
4000 /* mark the hard disk as registered only when registration is external */
4001 if (aFlags == RHD_External)
4002 {
4003 rc = aHardDisk->trySetRegistered (TRUE);
4004 CheckComRCReturnRC (rc);
4005 }
4006
4007 if (!aHardDisk->parent())
4008 {
4009 /* add to the collection of top-level images */
4010 mData.mHardDisks.push_back (aHardDisk);
4011 }
4012
4013 /* insert to the map of hard disks */
4014 mData.mHardDiskMap
4015 .insert (HardDiskMap::value_type (aHardDisk->id(), aHardDisk));
4016
4017 /* save global config file if not on startup */
4018 /// @todo (dmik) optimize later to save only the <HardDisks> node
4019 if (aFlags != RHD_OnStartUp)
4020 rc = saveSettings();
4021
4022 return rc;
4023}
4024
4025/**
4026 * Helper to unregister the hard disk.
4027 *
4028 * If the hard disk is a differencing hard disk and if the unregistration
4029 * succeeds, the hard disk image is deleted and the object is uninitialized.
4030 *
4031 * @param aHardDisk hard disk to unregister
4032 *
4033 * @note Locks objects!
4034 */
4035HRESULT VirtualBox::unregisterHardDisk (HardDisk *aHardDisk)
4036{
4037 AssertReturn (aHardDisk, E_INVALIDARG);
4038
4039 AutoCaller autoCaller (this);
4040 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4041
4042 LogFlowThisFunc (("image='%ls'\n", aHardDisk->toString().raw()));
4043
4044 AutoLock alock (this);
4045
4046 /* Lock the hard disk to ensure nobody registers it again before we delete
4047 * the differencing image (sanity check actually -- should never happen). */
4048 AutoLock hdLock (aHardDisk);
4049
4050 /* try to unregister */
4051 HRESULT rc = aHardDisk->trySetRegistered (FALSE);
4052 CheckComRCReturnRC (rc);
4053
4054 /* remove from the map of hard disks */
4055 mData.mHardDiskMap.erase (aHardDisk->id());
4056
4057 if (!aHardDisk->parent())
4058 {
4059 /* non-differencing hard disk:
4060 * remove from the collection of top-level hard disks */
4061 mData.mHardDisks.remove (aHardDisk);
4062 }
4063 else
4064 {
4065 Assert (aHardDisk->isDifferencing());
4066
4067 /* differencing hard disk: delete and uninitialize
4068 *
4069 * Note that we ignore errors because this operation may be a result
4070 * of unregistering a missing (inaccessible) differencing hard disk
4071 * in which case a failure to implicitly delete the image will not
4072 * prevent it from being unregistered and therefore should not pop up
4073 * on the caller's side. */
4074 rc = aHardDisk->asVDI()->deleteImage (true /* aIgnoreErrors*/);
4075 aHardDisk->uninit();
4076 }
4077
4078 /* save the global config file anyway (already unregistered) */
4079 /// @todo (dmik) optimize later to save only the <HardDisks> node
4080 HRESULT rc2 = saveSettings();
4081 if (SUCCEEDED (rc))
4082 rc = rc2;
4083
4084 return rc;
4085}
4086
4087/**
4088 * Helper to unregister the differencing hard disk image.
4089 * Resets machine ID of the hard disk (to let the unregistration succeed)
4090 * and then calls #unregisterHardDisk().
4091 *
4092 * @param aHardDisk differencing hard disk image to unregister
4093 *
4094 * @note Locks objects!
4095 */
4096HRESULT VirtualBox::unregisterDiffHardDisk (HardDisk *aHardDisk)
4097{
4098 AssertReturn (aHardDisk, E_INVALIDARG);
4099
4100 AutoCaller autoCaller (this);
4101 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4102
4103 AutoLock alock (this);
4104
4105 /*
4106 * Note: it's safe to lock aHardDisk here because the same object
4107 * will be locked by #unregisterHardDisk().
4108 */
4109 AutoLock hdLock (aHardDisk);
4110
4111 AssertReturn (aHardDisk->isDifferencing(), E_INVALIDARG);
4112
4113 /*
4114 * deassociate the machine from the hard disk
4115 * (otherwise trySetRegistered() will definitely fail)
4116 */
4117 aHardDisk->setMachineId (Guid());
4118
4119 return unregisterHardDisk (aHardDisk);
4120}
4121
4122
4123/**
4124 * Helper to update the global settings file when the name of some machine
4125 * changes so that file and directory renaming occurs. This method ensures
4126 * that all affected paths in the disk registry are properly updated.
4127 *
4128 * @param aOldPath old path (full)
4129 * @param aNewPath new path (full)
4130 *
4131 * @note Locks this object + DVD, Floppy and HardDisk children for writing.
4132 */
4133HRESULT VirtualBox::updateSettings (const char *aOldPath, const char *aNewPath)
4134{
4135 LogFlowThisFunc (("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
4136
4137 AssertReturn (aOldPath, E_INVALIDARG);
4138 AssertReturn (aNewPath, E_INVALIDARG);
4139
4140 AutoCaller autoCaller (this);
4141 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4142
4143 AutoLock alock (this);
4144
4145 size_t oldPathLen = strlen (aOldPath);
4146
4147 /* check DVD paths */
4148 for (DVDImageList::iterator it = mData.mDVDImages.begin();
4149 it != mData.mDVDImages.end();
4150 ++ it)
4151 {
4152 ComObjPtr <DVDImage> image = *it;
4153
4154 /* no need to lock: fields are constant */
4155 Utf8Str path = image->filePathFull();
4156 LogFlowThisFunc (("DVD.fullPath={%s}\n", path.raw()));
4157
4158 if (RTPathStartsWith (path, aOldPath))
4159 {
4160 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
4161 path.raw() + oldPathLen);
4162 path = newPath;
4163 calculateRelativePath (path, path);
4164 image->updatePath (newPath, path);
4165
4166 LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
4167 newPath.raw(), path.raw()));
4168 }
4169 }
4170
4171 /* check Floppy paths */
4172 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
4173 it != mData.mFloppyImages.end();
4174 ++ it)
4175 {
4176 ComObjPtr <FloppyImage> image = *it;
4177
4178 /* no need to lock: fields are constant */
4179 Utf8Str path = image->filePathFull();
4180 LogFlowThisFunc (("Floppy.fullPath={%s}\n", path.raw()));
4181
4182 if (RTPathStartsWith (path, aOldPath))
4183 {
4184 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
4185 path.raw() + oldPathLen);
4186 path = newPath;
4187 calculateRelativePath (path, path);
4188 image->updatePath (newPath, path);
4189
4190 LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
4191 newPath.raw(), path.raw()));
4192 }
4193 }
4194
4195 /* check HardDisk paths */
4196 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
4197 it != mData.mHardDisks.end();
4198 ++ it)
4199 {
4200 (*it)->updatePaths (aOldPath, aNewPath);
4201 }
4202
4203 HRESULT rc = saveSettings();
4204
4205 return rc;
4206}
4207
4208/**
4209 * Helper method to load the setting tree and turn expected exceptions into
4210 * COM errors, according to arguments.
4211 *
4212 * Note that this method will not catch unexpected errors so it may still
4213 * throw something.
4214 *
4215 * @param aTree Tree to load into settings.
4216 * @param aFile File to load settings from.
4217 * @param aValidate @c @true to enable tree validation.
4218 * @param aCatchLoadErrors @c true to catch exceptions caused by file
4219 * access or validation errors.
4220 * @param aAddDefaults @c true to cause the substitution of default
4221 * values for for missing attributes that have
4222 * defaults in the XML schema.
4223 * @param aFormatVersion Where to store the current format version of the
4224 * loaded settings tree (optional, may be NULL).
4225 */
4226/* static */
4227HRESULT VirtualBox::loadSettingsTree (settings::XmlTreeBackend &aTree,
4228 settings::File &aFile,
4229 bool aValidate,
4230 bool aCatchLoadErrors,
4231 bool aAddDefaults,
4232 Utf8Str *aFormatVersion /* = NULL */)
4233{
4234 using namespace settings;
4235
4236 try
4237 {
4238 SettingsTreeHelper helper = SettingsTreeHelper();
4239
4240 aTree.setInputResolver (helper);
4241 aTree.setAutoConverter (helper);
4242
4243 aTree.read (aFile, aValidate ? VBOX_XML_SCHEMA : NULL,
4244 aAddDefaults ? XmlTreeBackend::Read_AddDefaults : 0);
4245
4246 aTree.resetAutoConverter();
4247 aTree.resetInputResolver();
4248
4249 /* on success, memorize the current settings file version or set it to
4250 * the most recent version if no settings conversion took place. Note
4251 * that it's not necessary to do it every time we load the settings file
4252 * (i.e. only loadSettingsTree_FirstTime() passes a non-NULL
4253 * aFormatVersion value) because currently we keep the settings
4254 * files locked so that the only legal way to change the format version
4255 * while VirtualBox is running is saveSettingsTree(). */
4256 if (aFormatVersion != NULL)
4257 {
4258 *aFormatVersion = aTree.oldVersion();
4259 if (aFormatVersion->isNull())
4260 *aFormatVersion = VBOX_XML_VERSION_FULL;
4261 }
4262 }
4263 catch (const EIPRTFailure &err)
4264 {
4265 if (!aCatchLoadErrors)
4266 throw;
4267
4268 return setError (E_FAIL,
4269 tr ("Could not load the settings file '%s' (%Vrc)"),
4270 aFile.uri(), err.rc());
4271 }
4272 catch (const XmlTreeBackend::Error &err)
4273 {
4274 Assert (err.what() != NULL);
4275
4276 if (!aCatchLoadErrors)
4277 throw;
4278
4279 return setError (E_FAIL,
4280 tr ("Could not load the settings file '%s'.\n%s"),
4281 aFile.uri(),
4282 err.what() ? err.what() : "Unknown error");
4283 }
4284
4285 return S_OK;
4286}
4287
4288/**
4289 * Helper method to save the settings tree and turn expected exceptions to COM
4290 * errors.
4291 *
4292 * Note that this method will not catch unexpected errors so it may still
4293 * throw something.
4294 *
4295 * @param aTree Tree to save.
4296 * @param aFile File to save the tree to.
4297 * @param aFormatVersion Where to store the (recent) format version of the
4298 * saved settings tree on success.
4299 */
4300/* static */
4301HRESULT VirtualBox::saveSettingsTree (settings::TreeBackend &aTree,
4302 settings::File &aFile,
4303 Utf8Str &aFormatVersion)
4304{
4305 using namespace settings;
4306
4307 try
4308 {
4309 aTree.write (aFile);
4310
4311 /* set the current settings file version to the most recent version on
4312 * success. See also VirtualBox::loadSettingsTree(). */
4313 if (aFormatVersion != VBOX_XML_VERSION_FULL)
4314 aFormatVersion = VBOX_XML_VERSION_FULL;
4315 }
4316 catch (const EIPRTFailure &err)
4317 {
4318 /* this is the only expected exception for now */
4319 return setError (E_FAIL,
4320 tr ("Could not save the settings file '%s' (%Vrc)"),
4321 aFile.uri(), err.rc());
4322 }
4323
4324 return S_OK;
4325}
4326
4327/**
4328 * Creates a backup copy of the given settings file by suffixing it with the
4329 * supplied version format string and optionally with numbers from .0 to .9
4330 * if the backup file already exists.
4331 *
4332 * @param aFileName Orignal settings file name.
4333 * @param aOldFormat Version of the original format.
4334 * @param aBakFileName File name of the created backup copy (only on success).
4335 */
4336/* static */
4337HRESULT VirtualBox::backupSettingsFile (const Bstr &aFileName,
4338 const Utf8Str &aOldFormat,
4339 Bstr &aBakFileName)
4340{
4341 Utf8Str of = aFileName;
4342 Utf8Str nf = Utf8StrFmt ("%s.%s.bak", of.raw(), aOldFormat.raw());
4343
4344 int vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
4345 NULL, NULL);
4346
4347 /* try progressive suffix from .0 to .9 on failure */
4348 if (vrc == VERR_ALREADY_EXISTS)
4349 {
4350 Utf8Str tmp = nf;
4351 for (int i = 0; i <= 9 && RT_FAILURE (vrc); ++ i)
4352 {
4353 nf = Utf8StrFmt ("%s.%d", tmp.raw(), i);
4354 vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
4355 NULL, NULL);
4356 }
4357 }
4358
4359 if (RT_FAILURE (vrc))
4360 return setError (E_FAIL,
4361 tr ("Could not copy the settings file '%s' to '%s' (%Vrc)"),
4362 of.raw(), nf.raw(), vrc);
4363
4364 aBakFileName = nf;
4365
4366 return S_OK;
4367}
4368
4369/**
4370 * Handles unexpected exceptions by turning them into COM errors in release
4371 * builds or by hitting a breakpoint in the release builds.
4372 *
4373 * Usage pattern:
4374 * @code
4375 try
4376 {
4377 // ...
4378 }
4379 catch (LaLalA)
4380 {
4381 // ...
4382 }
4383 catch (...)
4384 {
4385 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
4386 }
4387 * @endcode
4388 *
4389 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
4390 */
4391/* static */
4392HRESULT VirtualBox::handleUnexpectedExceptions (RT_SRC_POS_DECL)
4393{
4394 try
4395 {
4396 /* rethrow the current exception */
4397 throw;
4398 }
4399 catch (const std::exception &err)
4400 {
4401 ComAssertMsgFailedPos (("Unexpected exception '%s' (%s)\n",
4402 typeid (err).name(), err.what()),
4403 pszFile, iLine, pszFunction);
4404 return E_FAIL;
4405 }
4406 catch (...)
4407 {
4408 ComAssertMsgFailedPos (("Unknown exception\n"),
4409 pszFile, iLine, pszFunction);
4410 return E_FAIL;
4411 }
4412
4413 /* should not get here */
4414 AssertFailed();
4415 return E_FAIL;
4416}
4417
4418/**
4419 * Helper to register the DVD image.
4420 *
4421 * @param aImage object to register
4422 * @param aOnStartUp whether this method called during VirtualBox init or not
4423 *
4424 * @return COM status code
4425 *
4426 * @note Locks objects!
4427 */
4428HRESULT VirtualBox::registerDVDImage (DVDImage *aImage, bool aOnStartUp)
4429{
4430 AssertReturn (aImage, E_INVALIDARG);
4431
4432 AutoCaller autoCaller (this);
4433 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4434
4435 AutoLock alock (this);
4436
4437 HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
4438 aImage->filePathFull());
4439 CheckComRCReturnRC (rc);
4440
4441 /* add to the collection */
4442 mData.mDVDImages.push_back (aImage);
4443
4444 /* save global config file if we're supposed to */
4445 if (!aOnStartUp)
4446 rc = saveSettings();
4447
4448 return rc;
4449}
4450
4451/**
4452 * Helper to register the floppy image.
4453 *
4454 * @param aImage object to register
4455 * @param aOnStartUp whether this method called during VirtualBox init or not
4456 *
4457 * @return COM status code
4458 *
4459 * @note Locks objects!
4460 */
4461HRESULT VirtualBox::registerFloppyImage (FloppyImage *aImage, bool aOnStartUp)
4462{
4463 AssertReturn (aImage, E_INVALIDARG);
4464
4465 AutoCaller autoCaller (this);
4466 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4467
4468 AutoLock alock (this);
4469
4470 HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
4471 aImage->filePathFull());
4472 CheckComRCReturnRC (rc);
4473
4474 /* add to the collection */
4475 mData.mFloppyImages.push_back (aImage);
4476
4477 /* save global config file if we're supposed to */
4478 if (!aOnStartUp)
4479 rc = saveSettings();
4480
4481 return rc;
4482}
4483
4484/**
4485 * Helper function to create the guest OS type objects and our collection
4486 *
4487 * @returns COM status code
4488 */
4489HRESULT VirtualBox::registerGuestOSTypes()
4490{
4491 AutoCaller autoCaller (this);
4492 AssertComRCReturn (autoCaller.rc(), E_FAIL);
4493 AssertReturn (autoCaller.state() == InInit, E_FAIL);
4494
4495 HRESULT rc = S_OK;
4496
4497 // this table represents our os type / string mapping
4498 static struct
4499 {
4500 const char *id; // utf-8
4501 const char *description; // utf-8
4502 const VBOXOSTYPE osType;
4503 const uint32_t recommendedRAM;
4504 const uint32_t recommendedVRAM;
4505 const uint32_t recommendedHDD;
4506 } OSTypes [SchemaDefs::OSTypeId_COUNT] =
4507 {
4508 /// @todo (dmik) get the list of OS types from the XML schema
4509 /* NOTE1: we assume that unknown is always the first entry!
4510 * NOTE2: please use powers of 2 when specifying the size of harddisks since
4511 * '2GB' looks better than '1.95GB' (= 2000MB) */
4512 { SchemaDefs_OSTypeId_unknown, tr ("Other/Unknown"), VBOXOSTYPE_Unknown, 64, 4, 2 * _1K },
4513 { SchemaDefs_OSTypeId_dos, "DOS", VBOXOSTYPE_DOS, 32, 4, 512 },
4514 { SchemaDefs_OSTypeId_win31, "Windows 3.1", VBOXOSTYPE_Win31, 32, 4, 1 * _1K },
4515 { SchemaDefs_OSTypeId_win95, "Windows 95", VBOXOSTYPE_Win95, 64, 4, 2 * _1K },
4516 { SchemaDefs_OSTypeId_win98, "Windows 98", VBOXOSTYPE_Win98, 64, 4, 2 * _1K },
4517 { SchemaDefs_OSTypeId_winme, "Windows Me", VBOXOSTYPE_WinMe, 64, 4, 4 * _1K },
4518 { SchemaDefs_OSTypeId_winnt4, "Windows NT 4", VBOXOSTYPE_WinNT4, 128, 4, 2 * _1K },
4519 { SchemaDefs_OSTypeId_win2k, "Windows 2000", VBOXOSTYPE_Win2k, 168, 12, 4 * _1K },
4520 { SchemaDefs_OSTypeId_winxp, "Windows XP", VBOXOSTYPE_WinXP, 192, 12, 10 * _1K },
4521 { SchemaDefs_OSTypeId_win2k3, "Windows Server 2003", VBOXOSTYPE_Win2k3, 256, 12, 20 * _1K },
4522 { SchemaDefs_OSTypeId_winvista, "Windows Vista", VBOXOSTYPE_WinVista, 512, 12, 20 * _1K },
4523 { SchemaDefs_OSTypeId_win2k8, "Windows Server 2008", VBOXOSTYPE_Win2k8, 256, 12, 20 * _1K },
4524 { SchemaDefs_OSTypeId_os2warp3, "OS/2 Warp 3", VBOXOSTYPE_OS2Warp3, 48, 4, 1 * _1K },
4525 { SchemaDefs_OSTypeId_os2warp4, "OS/2 Warp 4", VBOXOSTYPE_OS2Warp4, 64, 4, 2 * _1K },
4526 { SchemaDefs_OSTypeId_os2warp45, "OS/2 Warp 4.5", VBOXOSTYPE_OS2Warp45, 96, 4, 2 * _1K },
4527 { SchemaDefs_OSTypeId_ecs, "eComStation", VBOXOSTYPE_ECS, 96, 4, 2 * _1K },
4528 { SchemaDefs_OSTypeId_linux22, "Linux 2.2", VBOXOSTYPE_Linux22, 64, 4, 2 * _1K },
4529 { SchemaDefs_OSTypeId_linux24, "Linux 2.4", VBOXOSTYPE_Linux24, 128, 4, 4 * _1K },
4530 { SchemaDefs_OSTypeId_linux26, "Linux 2.6", VBOXOSTYPE_Linux26, 256, 4, 8 * _1K },
4531 { SchemaDefs_OSTypeId_archlinux, "Arch Linux", VBOXOSTYPE_ArchLinux, 256, 12, 8 * _1K },
4532 { SchemaDefs_OSTypeId_debian, "Debian", VBOXOSTYPE_Debian, 256, 12, 8 * _1K },
4533 { SchemaDefs_OSTypeId_opensuse, "openSUSE", VBOXOSTYPE_OpenSUSE, 256, 12, 8 * _1K },
4534 { SchemaDefs_OSTypeId_fedoracore,"Fedora", VBOXOSTYPE_FedoraCore,256, 12, 8 * _1K },
4535 { SchemaDefs_OSTypeId_gentoo, "Gentoo Linux", VBOXOSTYPE_Gentoo, 256, 12, 8 * _1K },
4536 { SchemaDefs_OSTypeId_mandriva, "Mandriva", VBOXOSTYPE_Mandriva, 256, 12, 8 * _1K },
4537 { SchemaDefs_OSTypeId_redhat, "Red Hat", VBOXOSTYPE_RedHat, 256, 12, 8 * _1K },
4538 { SchemaDefs_OSTypeId_ubuntu, "Ubuntu", VBOXOSTYPE_Ubuntu, 256, 12, 8 * _1K },
4539 { SchemaDefs_OSTypeId_xandros, "Xandros", VBOXOSTYPE_Xandros, 256, 12, 8 * _1K },
4540 { SchemaDefs_OSTypeId_freebsd, "FreeBSD", VBOXOSTYPE_FreeBSD, 64, 4, 2 * _1K },
4541 { SchemaDefs_OSTypeId_openbsd, "OpenBSD", VBOXOSTYPE_OpenBSD, 64, 4, 2 * _1K },
4542 { SchemaDefs_OSTypeId_netbsd, "NetBSD", VBOXOSTYPE_NetBSD, 64, 4, 2 * _1K },
4543 { SchemaDefs_OSTypeId_netware, "Netware", VBOXOSTYPE_Netware, 128, 4, 4 * _1K },
4544 { SchemaDefs_OSTypeId_solaris, "Solaris", VBOXOSTYPE_Solaris, 512, 12, 16 * _1K },
4545 { SchemaDefs_OSTypeId_l4, "L4", VBOXOSTYPE_L4, 64, 4, 2 * _1K }
4546 };
4547
4548 for (uint32_t i = 0; i < ELEMENTS (OSTypes) && SUCCEEDED (rc); i++)
4549 {
4550 ComObjPtr <GuestOSType> guestOSTypeObj;
4551 rc = guestOSTypeObj.createObject();
4552 if (SUCCEEDED (rc))
4553 {
4554 rc = guestOSTypeObj->init (OSTypes[i].id,
4555 OSTypes[i].description,
4556 OSTypes[i].osType,
4557 OSTypes[i].recommendedRAM,
4558 OSTypes[i].recommendedVRAM,
4559 OSTypes[i].recommendedHDD);
4560 if (SUCCEEDED (rc))
4561 mData.mGuestOSTypes.push_back (guestOSTypeObj);
4562 }
4563 }
4564
4565 return rc;
4566}
4567
4568/**
4569 * Helper to lock the VirtualBox configuration for write access.
4570 *
4571 * @note This method is not thread safe (must be called only from #init()
4572 * or #uninit()).
4573 *
4574 * @note If the configuration file is not found, the method returns
4575 * S_OK, but subsequent #isConfigLocked() will return FALSE. This is used
4576 * in some places to determine the (valid) situation when no config file
4577 * exists yet, and therefore a new one should be created from scatch.
4578 */
4579HRESULT VirtualBox::lockConfig()
4580{
4581 AutoCaller autoCaller (this);
4582 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4583 AssertReturn (autoCaller.state() == InInit, E_FAIL);
4584
4585 HRESULT rc = S_OK;
4586
4587 Assert (!isConfigLocked());
4588 if (!isConfigLocked())
4589 {
4590 /* open the associated config file */
4591 int vrc = RTFileOpen (&mData.mCfgFile.mHandle,
4592 Utf8Str (mData.mCfgFile.mName),
4593 RTFILE_O_READWRITE | RTFILE_O_OPEN |
4594 RTFILE_O_DENY_WRITE);
4595 if (VBOX_FAILURE (vrc))
4596 {
4597 mData.mCfgFile.mHandle = NIL_RTFILE;
4598
4599 /*
4600 * It is ok if the file is not found, it will be created by
4601 * init(). Otherwise return an error.
4602 */
4603 if (vrc != VERR_FILE_NOT_FOUND)
4604 rc = setError (E_FAIL,
4605 tr ("Could not lock the settings file '%ls' (%Vrc)"),
4606 mData.mCfgFile.mName.raw(), vrc);
4607 }
4608
4609 LogFlowThisFunc (("mCfgFile.mName='%ls', mCfgFile.mHandle=%d, rc=%08X\n",
4610 mData.mCfgFile.mName.raw(), mData.mCfgFile.mHandle, rc));
4611 }
4612
4613 return rc;
4614}
4615
4616/**
4617 * Helper to unlock the VirtualBox configuration from write access.
4618 *
4619 * @note This method is not thread safe (must be called only from #init()
4620 * or #uninit()).
4621 */
4622HRESULT VirtualBox::unlockConfig()
4623{
4624 AutoCaller autoCaller (this);
4625 AssertComRCReturn (autoCaller.rc(), E_FAIL);
4626 AssertReturn (autoCaller.state() == InUninit, E_FAIL);
4627
4628 HRESULT rc = S_OK;
4629
4630 if (isConfigLocked())
4631 {
4632 RTFileFlush (mData.mCfgFile.mHandle);
4633 RTFileClose (mData.mCfgFile.mHandle);
4634 /** @todo flush the directory too. */
4635 mData.mCfgFile.mHandle = NIL_RTFILE;
4636 LogFlowThisFunc (("\n"));
4637 }
4638
4639 return rc;
4640}
4641
4642/**
4643 * Thread function that watches the termination of all client processes
4644 * that have opened sessions using IVirtualBox::OpenSession()
4645 */
4646// static
4647DECLCALLBACK(int) VirtualBox::ClientWatcher (RTTHREAD thread, void *pvUser)
4648{
4649 LogFlowFuncEnter();
4650
4651 VirtualBox *that = (VirtualBox *) pvUser;
4652 Assert (that);
4653
4654 SessionMachineVector machines;
4655 size_t cnt = 0;
4656
4657#if defined(RT_OS_WINDOWS)
4658
4659 HRESULT hrc = CoInitializeEx (NULL,
4660 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
4661 COINIT_SPEED_OVER_MEMORY);
4662 AssertComRC (hrc);
4663
4664 /// @todo (dmik) processes reaping!
4665
4666 HANDLE *handles = new HANDLE [1];
4667 handles [0] = that->mWatcherData.mUpdateReq;
4668
4669 do
4670 {
4671 AutoCaller autoCaller (that);
4672 /* VirtualBox has been early uninitialized, terminate */
4673 if (!autoCaller.isOk())
4674 break;
4675
4676 do
4677 {
4678 /* release the caller to let uninit() ever proceed */
4679 autoCaller.release();
4680
4681 DWORD rc = ::WaitForMultipleObjects (cnt + 1, handles, FALSE, INFINITE);
4682
4683 /*
4684 * Restore the caller before using VirtualBox. If it fails, this
4685 * means VirtualBox is being uninitialized and we must terminate.
4686 */
4687 autoCaller.add();
4688 if (!autoCaller.isOk())
4689 break;
4690
4691 bool update = false;
4692 if (rc == WAIT_OBJECT_0)
4693 {
4694 /* update event is signaled */
4695 update = true;
4696 }
4697 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4698 {
4699 /* machine mutex is released */
4700 (machines [rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4701 update = true;
4702 }
4703 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4704 {
4705 /* machine mutex is abandoned due to client process termination */
4706 (machines [rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4707 update = true;
4708 }
4709 if (update)
4710 {
4711 /* obtain a new set of opened machines */
4712 that->getOpenedMachines (machines);
4713 cnt = machines.size();
4714 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4715 AssertMsg ((cnt + 1) <= MAXIMUM_WAIT_OBJECTS,
4716 ("MAXIMUM_WAIT_OBJECTS reached"));
4717 /* renew the set of event handles */
4718 delete [] handles;
4719 handles = new HANDLE [cnt + 1];
4720 handles [0] = that->mWatcherData.mUpdateReq;
4721 for (size_t i = 0; i < cnt; ++ i)
4722 handles [i + 1] = (machines [i])->ipcSem();
4723 }
4724 }
4725 while (true);
4726 }
4727 while (0);
4728
4729 /* delete the set of event handles */
4730 delete [] handles;
4731
4732 /* delete the set of opened machines if any */
4733 machines.clear();
4734
4735 ::CoUninitialize();
4736
4737#elif defined (RT_OS_OS2)
4738
4739 /// @todo (dmik) processes reaping!
4740
4741 /* according to PMREF, 64 is the maximum for the muxwait list */
4742 SEMRECORD handles [64];
4743
4744 HMUX muxSem = NULLHANDLE;
4745
4746 do
4747 {
4748 AutoCaller autoCaller (that);
4749 /* VirtualBox has been early uninitialized, terminate */
4750 if (!autoCaller.isOk())
4751 break;
4752
4753 do
4754 {
4755 /* release the caller to let uninit() ever proceed */
4756 autoCaller.release();
4757
4758 int vrc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4759
4760 /*
4761 * Restore the caller before using VirtualBox. If it fails, this
4762 * means VirtualBox is being uninitialized and we must terminate.
4763 */
4764 autoCaller.add();
4765 if (!autoCaller.isOk())
4766 break;
4767
4768 bool update = false;
4769
4770 if (VBOX_SUCCESS (vrc))
4771 {
4772 /* update event is signaled */
4773 update = true;
4774 }
4775 else
4776 {
4777 AssertMsg (vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4778 ("RTSemEventWait returned %Vrc\n", vrc));
4779
4780 /* are there any mutexes? */
4781 if (cnt > 0)
4782 {
4783 /* figure out what's going on with machines */
4784
4785 unsigned long semId = 0;
4786 APIRET arc = ::DosWaitMuxWaitSem (muxSem,
4787 SEM_IMMEDIATE_RETURN, &semId);
4788
4789 if (arc == NO_ERROR)
4790 {
4791 /* machine mutex is normally released */
4792 Assert (semId >= 0 && semId < cnt);
4793 if (semId >= 0 && semId < cnt)
4794 {
4795#ifdef DEBUG
4796 {
4797 AutoReaderLock machieLock (machines [semId]);
4798 LogFlowFunc (("released mutex: machine='%ls'\n",
4799 machines [semId]->name().raw()));
4800 }
4801#endif
4802 machines [semId]->checkForDeath();
4803 }
4804 update = true;
4805 }
4806 else if (arc == ERROR_SEM_OWNER_DIED)
4807 {
4808 /* machine mutex is abandoned due to client process
4809 * termination; find which mutex is in the Owner Died
4810 * state */
4811 for (size_t i = 0; i < cnt; ++ i)
4812 {
4813 PID pid; TID tid;
4814 unsigned long reqCnt;
4815 arc = DosQueryMutexSem ((HMTX) handles [i].hsemCur, &pid,
4816 &tid, &reqCnt);
4817 if (arc == ERROR_SEM_OWNER_DIED)
4818 {
4819 /* close the dead mutex as asked by PMREF */
4820 ::DosCloseMutexSem ((HMTX) handles [i].hsemCur);
4821
4822 Assert (i >= 0 && i < cnt);
4823 if (i >= 0 && i < cnt)
4824 {
4825#ifdef DEBUG
4826 {
4827 AutoReaderLock machieLock (machines [semId]);
4828 LogFlowFunc (("mutex owner dead: machine='%ls'\n",
4829 machines [i]->name().raw()));
4830 }
4831#endif
4832 machines [i]->checkForDeath();
4833 }
4834 }
4835 }
4836 update = true;
4837 }
4838 else
4839 AssertMsg (arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4840 ("DosWaitMuxWaitSem returned %d\n", arc));
4841 }
4842 }
4843
4844 if (update)
4845 {
4846 /* close the old muxsem */
4847 if (muxSem != NULLHANDLE)
4848 ::DosCloseMuxWaitSem (muxSem);
4849 /* obtain a new set of opened machines */
4850 that->getOpenedMachines (machines);
4851 cnt = machines.size();
4852 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4853 /// @todo use several muxwait sems if cnt is > 64
4854 AssertMsg (cnt <= 64 /* according to PMREF */,
4855 ("maximum of 64 mutex semaphores reached (%d)", cnt));
4856 if (cnt > 64)
4857 cnt = 64;
4858 if (cnt > 0)
4859 {
4860 /* renew the set of event handles */
4861 for (size_t i = 0; i < cnt; ++ i)
4862 {
4863 handles [i].hsemCur = (HSEM) machines [i]->ipcSem();
4864 handles [i].ulUser = i;
4865 }
4866 /* create a new muxsem */
4867 APIRET arc = ::DosCreateMuxWaitSem (NULL, &muxSem, cnt, handles,
4868 DCMW_WAIT_ANY);
4869 AssertMsg (arc == NO_ERROR,
4870 ("DosCreateMuxWaitSem returned %d\n", arc)); NOREF(arc);
4871 }
4872 }
4873 }
4874 while (true);
4875 }
4876 while (0);
4877
4878 /* close the muxsem */
4879 if (muxSem != NULLHANDLE)
4880 ::DosCloseMuxWaitSem (muxSem);
4881
4882 /* delete the set of opened machines if any */
4883 machines.clear();
4884
4885#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4886
4887 bool need_update = false;
4888
4889 do
4890 {
4891 AutoCaller autoCaller (that);
4892 if (!autoCaller.isOk())
4893 break;
4894
4895 do
4896 {
4897 /* release the caller to let uninit() ever proceed */
4898 autoCaller.release();
4899
4900 int rc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4901
4902 /*
4903 * Restore the caller before using VirtualBox. If it fails, this
4904 * means VirtualBox is being uninitialized and we must terminate.
4905 */
4906 autoCaller.add();
4907 if (!autoCaller.isOk())
4908 break;
4909
4910 if (VBOX_SUCCESS (rc) || need_update)
4911 {
4912 /* VBOX_SUCCESS (rc) means an update event is signaled */
4913
4914 /* obtain a new set of opened machines */
4915 that->getOpenedMachines (machines);
4916 cnt = machines.size();
4917 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4918 }
4919
4920 need_update = false;
4921 for (size_t i = 0; i < cnt; ++ i)
4922 need_update |= (machines [i])->checkForDeath();
4923
4924 /* reap child processes */
4925 {
4926 AutoLock alock (that);
4927 if (that->mWatcherData.mProcesses.size())
4928 {
4929 LogFlowFunc (("UPDATE: child process count = %d\n",
4930 that->mWatcherData.mProcesses.size()));
4931 ClientWatcherData::ProcessList::iterator it =
4932 that->mWatcherData.mProcesses.begin();
4933 while (it != that->mWatcherData.mProcesses.end())
4934 {
4935 RTPROCESS pid = *it;
4936 RTPROCSTATUS status;
4937 int vrc = ::RTProcWait (pid, RTPROCWAIT_FLAGS_NOBLOCK,
4938 &status);
4939 if (vrc == VINF_SUCCESS)
4940 {
4941 LogFlowFunc (("pid %d (%x) was reaped, "
4942 "status=%d, reason=%d\n",
4943 pid, pid, status.iStatus,
4944 status.enmReason));
4945 it = that->mWatcherData.mProcesses.erase (it);
4946 }
4947 else
4948 {
4949 LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Vrc\n",
4950 pid, pid, vrc));
4951 if (vrc != VERR_PROCESS_RUNNING)
4952 {
4953 /* remove the process if it is not already running */
4954 it = that->mWatcherData.mProcesses.erase (it);
4955 }
4956 else
4957 ++ it;
4958 }
4959 }
4960 }
4961 }
4962 }
4963 while (true);
4964 }
4965 while (0);
4966
4967 /* delete the set of opened machines if any */
4968 machines.clear();
4969
4970#else
4971# error "Port me!"
4972#endif
4973
4974 LogFlowFuncLeave();
4975 return 0;
4976}
4977
4978/**
4979 * Thread function that handles custom events posted using #postEvent().
4980 */
4981// static
4982DECLCALLBACK(int) VirtualBox::AsyncEventHandler (RTTHREAD thread, void *pvUser)
4983{
4984 LogFlowFuncEnter();
4985
4986 AssertReturn (pvUser, VERR_INVALID_POINTER);
4987
4988 // create an event queue for the current thread
4989 EventQueue *eventQ = new EventQueue();
4990 AssertReturn (eventQ, VERR_NO_MEMORY);
4991
4992 // return the queue to the one who created this thread
4993 *(static_cast <EventQueue **> (pvUser)) = eventQ;
4994 // signal that we're ready
4995 RTThreadUserSignal (thread);
4996
4997 BOOL ok = TRUE;
4998 Event *event = NULL;
4999
5000 while ((ok = eventQ->waitForEvent (&event)) && event)
5001 eventQ->handleEvent (event);
5002
5003 AssertReturn (ok, VERR_GENERAL_FAILURE);
5004
5005 delete eventQ;
5006
5007 LogFlowFuncLeave();
5008
5009 return 0;
5010}
5011
5012////////////////////////////////////////////////////////////////////////////////
5013
5014/**
5015 * Takes the current list of registered callbacks of the managed VirtualBox
5016 * instance, and calls #handleCallback() for every callback item from the
5017 * list, passing the item as an argument.
5018 *
5019 * @note Locks the managed VirtualBox object for reading but leaves the lock
5020 * before iterating over callbacks and calling their methods.
5021 */
5022void *VirtualBox::CallbackEvent::handler()
5023{
5024 if (mVirtualBox.isNull())
5025 return NULL;
5026
5027 AutoCaller autoCaller (mVirtualBox);
5028 if (!autoCaller.isOk())
5029 {
5030 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
5031 "the callback event is discarded!\n",
5032 autoCaller.state()));
5033 /* We don't need mVirtualBox any more, so release it */
5034 mVirtualBox.setNull();
5035 return NULL;
5036 }
5037
5038 CallbackVector callbacks;
5039 {
5040 /* Make a copy to release the lock before iterating */
5041 AutoReaderLock alock (mVirtualBox);
5042 callbacks = CallbackVector (mVirtualBox->mData.mCallbacks.begin(),
5043 mVirtualBox->mData.mCallbacks.end());
5044 /* We don't need mVirtualBox any more, so release it */
5045 mVirtualBox.setNull();
5046 }
5047
5048 for (VirtualBox::CallbackVector::const_iterator it = callbacks.begin();
5049 it != callbacks.end(); ++ it)
5050 handleCallback (*it);
5051
5052 return NULL;
5053}
5054
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