VirtualBox

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

Last change on this file since 2603 was 2567, checked in by vboxsync, 18 years ago

Main & All Frontends: replaced the IGuestOSType IMachine::OSType property with the wstring IMachine::OSTypeId property (+ converted IGuest and IGuestOSType to VirtualBoxBaseNEXT semantics).

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