VirtualBox

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

Last change on this file since 5272 was 5236, checked in by vboxsync, 18 years ago

Main: Differencing hard disks belonging to a snapshot could be sometimes left in the snapshot directory after discarding the snapshot instead of deleting them (in particular, when powering off and reverting to the current snapshot at once).

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette