VirtualBox

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

Last change on this file since 1713 was 1491, checked in by vboxsync, 18 years ago

Flush the config file before closing it to try prevent data loss on panic/fault/whatever. TODO: flush the directory too.

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