VirtualBox

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

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

Moved the template code out of cdefs.h, partly because it didn't belong there but mostly because it was at the end of the file and would screw up any attempts made by the object cache at avoid recompiling on cdefs.h changes.

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