VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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