VirtualBox

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

Last change on this file since 1158 was 1015, checked in by vboxsync, 18 years ago

Storage/Main/GUI: Implemented preliminary support of VMDK images version 3 and 4 (no separate Disk DescriptionFile support).

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

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