VirtualBox

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

Last change on this file since 3411 was 3387, checked in by vboxsync, 17 years ago

Main: com::GetVBoxUserHomeDirectory() now takes char * instead of Utf8Str because on XPCOM platforms, this function may be called before XPCOM has been initialized, when Utf8Str functionality is not yet available (because it uses XPCOM memory management).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use