VirtualBox

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

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

Main/Settings: Implemented support for settings file auto-conversion at VBoxSVC startup (#2705).

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