VirtualBox

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

Last change on this file since 5135 was 5101, checked in by vboxsync, 18 years ago

Introduce a new harddisk object which supports virtual harddisk through plugins

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette