VirtualBox

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

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

API change to allow VM to be created with predefined UUID.

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