VirtualBox

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

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

Main:

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