VirtualBox

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

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

Main: Implemented IConsole::AdoptSavedState(); Prototyped IVirtualBox::WaitForPropertyChange().

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

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