VirtualBox

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

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

Main: Return the correct value for IGuest::OSTypeId as reported by the guest additions when they are active (fixes #1761#c77).

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