VirtualBox

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

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

Main: Return an error when two machines witht he same UUID are detected in the global registry instead of asserting.

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