VirtualBox

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

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

12MB VRAM default for guests capable of seamless mode, Fedora Core is now called Fedora

  • 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, 12, 4 * _1K },
4495 { SchemaDefs_OSTypeId_winxp, "Windows XP", VBOXOSTYPE_WinXP, 192, 12, 10 * _1K },
4496 { SchemaDefs_OSTypeId_win2k3, "Windows Server 2003", VBOXOSTYPE_Win2k3, 256, 12, 20 * _1K },
4497 { SchemaDefs_OSTypeId_winvista, "Windows Vista", VBOXOSTYPE_WinVista, 512, 12, 20 * _1K },
4498 { SchemaDefs_OSTypeId_win2k8, "Windows Server 2008", VBOXOSTYPE_Win2k8, 256, 12, 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, 12, 8 * _1K },
4507 { SchemaDefs_OSTypeId_debian, "Debian", VBOXOSTYPE_Debian, 256, 12, 8 * _1K },
4508 { SchemaDefs_OSTypeId_opensuse, "openSUSE", VBOXOSTYPE_OpenSUSE, 256, 12, 8 * _1K },
4509 { SchemaDefs_OSTypeId_fedoracore,"Fedora", VBOXOSTYPE_FedoraCore,256, 12, 8 * _1K },
4510 { SchemaDefs_OSTypeId_gentoo, "Gentoo Linux", VBOXOSTYPE_Gentoo, 256, 12, 8 * _1K },
4511 { SchemaDefs_OSTypeId_mandriva, "Mandriva", VBOXOSTYPE_Mandriva, 256, 12, 8 * _1K },
4512 { SchemaDefs_OSTypeId_redhat, "Red Hat", VBOXOSTYPE_RedHat, 256, 12, 8 * _1K },
4513 { SchemaDefs_OSTypeId_ubuntu, "Ubuntu", VBOXOSTYPE_Ubuntu, 256, 12, 8 * _1K },
4514 { SchemaDefs_OSTypeId_xandros, "Xandros", VBOXOSTYPE_Xandros, 256, 12, 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, 512, 12, 16 * _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