VirtualBox

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

Last change on this file since 2042 was 2029, checked in by vboxsync, 18 years ago

Main: Added IVirtualBox::findHardDisk().
VBoxSDL: Fixed so that .vmdk images can be passed using the -hda switch.

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