VirtualBox

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

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

Changed the configuration directory to Library/VirtualBox on darwin.

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