VirtualBox

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

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

Main: Converted DVDDrive and FloppyDrive to the new locking scheme.

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

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