VirtualBox

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

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

Main: Added more OS Type IDs.

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