VirtualBox

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

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

Main/Frontends: Pass the value of the DISPLAY variable of the starting application to the VM process (#2101).

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