VirtualBox

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

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

increased default disk size of solaris to 8GB (#2025)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 137.3 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22#include "VirtualBoxImpl.h"
23#include "MachineImpl.h"
24#include "HardDiskImpl.h"
25#include "DVDImageImpl.h"
26#include "FloppyImageImpl.h"
27#include "SharedFolderImpl.h"
28#include "ProgressImpl.h"
29#include "HostImpl.h"
30#include "USBControllerImpl.h"
31#include "SystemPropertiesImpl.h"
32#include "Logging.h"
33
34#include "GuestOSTypeImpl.h"
35
36#ifdef __WIN__
37#include "win32/svchlp.h"
38#endif
39
40#include <stdio.h>
41#include <stdlib.h>
42
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 id
3167 * ID of the DVD image (unused when NULL)
3168 * @param filePathFull
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 dvdImage
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(__WIN__)
3331 /// @todo (dmik) stupid BSTR declaration lacks the BCSTR counterpart
3332 const_cast <BSTR> (aFilePathFull) = aHardDisk->asVDI()->filePathFull();
3333#else
3334 aFilePathFull = aHardDisk->asVDI()->filePathFull();
3335#endif
3336 }
3337
3338 bool found = false;
3339
3340 if (aId || aFilePathFull) do
3341 {
3342 if (!aHardDisk)
3343 {
3344 rc = findHardDisk (aId, aFilePathFull, false /* aSetError */);
3345 found = SUCCEEDED (rc);
3346 if (found)
3347 break;
3348 }
3349
3350 rc = findDVDImage (aId, aFilePathFull, false /* aSetError */);
3351 found = SUCCEEDED (rc);
3352 if (found)
3353 break;
3354
3355 rc = findFloppyImage (aId, aFilePathFull, false /* aSetError */);
3356 found = SUCCEEDED (rc);
3357 if (found)
3358 break;
3359 }
3360 while (0);
3361
3362 if (found)
3363 {
3364 if (aId && !aFilePathFull)
3365 rc = setError (E_INVALIDARG,
3366 tr ("A disk image with UUID {%Vuuid} is already registered"),
3367 aId->raw());
3368 else if (aFilePathFull && !aId)
3369 rc = setError (E_INVALIDARG,
3370 tr ("A disk image with file path '%ls' is already registered"),
3371 aFilePathFull);
3372 else
3373 rc = setError (E_INVALIDARG,
3374 tr ("A disk image with UUID {%Vuuid} or file path '%ls' "
3375 "is already registered"), aId->raw(), aFilePathFull);
3376 }
3377 else
3378 rc = S_OK;
3379
3380 return rc;
3381}
3382
3383/**
3384 * Reads in the machine definitions from the configuration loader
3385 * and creates the relevant objects.
3386 *
3387 * @note Can be called only from #init().
3388 * @note Doesn't lock anything.
3389 */
3390HRESULT VirtualBox::loadMachines (CFGNODE aGlobal)
3391{
3392 AutoCaller autoCaller (this);
3393 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3394
3395 HRESULT rc = S_OK;
3396 CFGNODE machineRegistry = 0;
3397 unsigned count = 0;
3398
3399 CFGLDRGetChildNode (aGlobal, "MachineRegistry", 0, &machineRegistry);
3400 Assert (machineRegistry);
3401
3402 CFGLDRCountChildren(machineRegistry, "MachineEntry", &count);
3403 for (unsigned i = 0; i < count && SUCCEEDED (rc); i++)
3404 {
3405 CFGNODE vm = 0;
3406 CFGLDRGetChildNode(machineRegistry, "MachineEntry", i, &vm);
3407 /* get the UUID */
3408 Guid uuid;
3409 CFGLDRQueryUUID(vm, "uuid", uuid.ptr());
3410 /* get the machine configuration file name */
3411 Bstr src;
3412 CFGLDRQueryBSTR(vm, "src", src.asOutParam());
3413
3414 /* create a new object */
3415 ComObjPtr <Machine> machine;
3416 rc = machine.createObject();
3417 if (SUCCEEDED (rc))
3418 {
3419 /* initialize the machine object and register it */
3420 rc = machine->init (this, src, Machine::Init_Registered,
3421 NULL, FALSE, &uuid);
3422 if (SUCCEEDED (rc))
3423 rc = registerMachine (machine);
3424 }
3425
3426 CFGLDRReleaseNode(vm);
3427 }
3428
3429 CFGLDRReleaseNode(machineRegistry);
3430
3431 return rc;
3432}
3433
3434/**
3435 * Reads in the disk registration entries from the global settings file
3436 * and creates the relevant objects
3437 *
3438 * @param aGlobal <Global> node
3439 *
3440 * @note Can be called only from #init().
3441 * @note Doesn't lock anything.
3442 */
3443HRESULT VirtualBox::loadDisks (CFGNODE aGlobal)
3444{
3445 AutoCaller autoCaller (this);
3446 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3447
3448 HRESULT rc = S_OK;
3449 CFGNODE registryNode = 0;
3450
3451 CFGLDRGetChildNode (aGlobal, "DiskRegistry", 0, &registryNode);
3452 ComAssertRet (registryNode, E_FAIL);
3453
3454 const char *ImagesNodes[] = { "HardDisks", "DVDImages", "FloppyImages" };
3455
3456 for (size_t node = 0; node < ELEMENTS (ImagesNodes) && SUCCEEDED (rc); node ++)
3457 {
3458 CFGNODE imagesNode = 0;
3459 CFGLDRGetChildNode (registryNode, ImagesNodes [node], 0, &imagesNode);
3460
3461 // all three nodes are optional
3462 if (!imagesNode)
3463 continue;
3464
3465 if (node == 0) // HardDisks node
3466 rc = loadHardDisks (imagesNode);
3467 else
3468 {
3469 unsigned count = 0;
3470 CFGLDRCountChildren (imagesNode, "Image", &count);
3471 for (unsigned i = 0; i < count && SUCCEEDED (rc); ++ i)
3472 {
3473 CFGNODE imageNode = 0;
3474 CFGLDRGetChildNode (imagesNode, "Image", i, &imageNode);
3475 ComAssertBreak (imageNode, rc = E_FAIL);
3476
3477 Guid uuid; // uuid (required)
3478 CFGLDRQueryUUID (imageNode, "uuid", uuid.ptr());
3479 Bstr src; // source (required)
3480 CFGLDRQueryBSTR (imageNode, "src", src.asOutParam());
3481
3482 switch (node)
3483 {
3484 case 1: // DVDImages
3485 {
3486 ComObjPtr <DVDImage> dvdImage;
3487 dvdImage.createObject();
3488 rc = dvdImage->init (this, src, TRUE /* isRegistered */, uuid);
3489 if (SUCCEEDED (rc))
3490 rc = registerDVDImage (dvdImage, TRUE /* aOnStartUp */);
3491
3492 break;
3493 }
3494 case 2: // FloppyImages
3495 {
3496 ComObjPtr <FloppyImage> floppyImage;
3497 floppyImage.createObject();
3498 rc = floppyImage->init (this, src, TRUE /* isRegistered */, uuid);
3499 if (SUCCEEDED (rc))
3500 rc = registerFloppyImage (floppyImage, TRUE /* aOnStartUp */);
3501
3502 break;
3503 }
3504 default:
3505 AssertFailed();
3506 }
3507
3508 CFGLDRReleaseNode (imageNode);
3509 }
3510 }
3511
3512 CFGLDRReleaseNode (imagesNode);
3513 }
3514
3515 CFGLDRReleaseNode (registryNode);
3516
3517 return rc;
3518}
3519
3520/**
3521 * Loads all hard disks from the given <HardDisks> node.
3522 * Note that all loaded hard disks register themselves within this VirtualBox.
3523 *
3524 * @param aNode <HardDisks> node
3525 *
3526 * @note Can be called only from #init().
3527 * @note Doesn't lock anything.
3528 */
3529HRESULT VirtualBox::loadHardDisks (CFGNODE aNode)
3530{
3531 AutoCaller autoCaller (this);
3532 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3533
3534 AssertReturn (aNode, E_INVALIDARG);
3535
3536 HRESULT rc = S_OK;
3537
3538 unsigned count = 0;
3539 CFGLDRCountChildren (aNode, "HardDisk", &count);
3540 for (unsigned i = 0; i < count && SUCCEEDED (rc); ++ i)
3541 {
3542 CFGNODE hdNode = 0;
3543
3544 CFGLDRGetChildNode (aNode, "HardDisk", i, &hdNode);
3545 ComAssertBreak (hdNode, rc = E_FAIL);
3546
3547 {
3548 CFGNODE storageNode = 0;
3549
3550 // detect the type of the hard disk
3551 // (either one of HVirtualDiskImage, HISCSIHardDisk or HPhysicalVolume
3552 do
3553 {
3554 CFGLDRGetChildNode (hdNode, "VirtualDiskImage", 0, &storageNode);
3555 if (storageNode)
3556 {
3557 ComObjPtr <HVirtualDiskImage> vdi;
3558 vdi.createObject();
3559 rc = vdi->init (this, NULL, hdNode, storageNode);
3560 break;
3561 }
3562
3563 CFGLDRGetChildNode (hdNode, "ISCSIHardDisk", 0, &storageNode);
3564 if (storageNode)
3565 {
3566 ComObjPtr <HISCSIHardDisk> iscsi;
3567 iscsi.createObject();
3568 rc = iscsi->init (this, hdNode, storageNode);
3569 break;
3570 }
3571
3572 CFGLDRGetChildNode (hdNode, "VMDKImage", 0, &storageNode);
3573 if (storageNode)
3574 {
3575 ComObjPtr <HVMDKImage> vmdk;
3576 vmdk.createObject();
3577 rc = vmdk->init (this, NULL, hdNode, storageNode);
3578 break;
3579 }
3580
3581 /// @todo (dmik) later
3582// CFGLDRGetChildNode (hdNode, "PhysicalVolume", 0, &storageNode);
3583// if (storageNode)
3584// {
3585// break;
3586// }
3587
3588 ComAssertMsgFailedBreak (("No valid hard disk storage node!\n"),
3589 rc = E_FAIL);
3590 }
3591 while (0);
3592
3593 if (storageNode)
3594 CFGLDRReleaseNode (storageNode);
3595 }
3596
3597 CFGLDRReleaseNode (hdNode);
3598 }
3599
3600 return rc;
3601}
3602
3603/**
3604 * Helper function to write out the configuration to XML.
3605 *
3606 * @note Locks objects!
3607 */
3608HRESULT VirtualBox::saveConfig()
3609{
3610 AutoCaller autoCaller (this);
3611 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3612
3613 ComAssertRet (!!mData.mCfgFile.mName, E_FAIL);
3614
3615 HRESULT rc = S_OK;
3616
3617 AutoLock alock (this);
3618
3619 CFGHANDLE configLoader;
3620
3621 /* load the config file */
3622 int vrc = CFGLDRLoad (&configLoader, Utf8Str (mData.mCfgFile.mName),
3623 mData.mCfgFile.mHandle,
3624 XmlSchemaNS, true, cfgLdrEntityResolver, NULL);
3625 ComAssertRCRet (vrc, E_FAIL);
3626
3627 const char * Global = "VirtualBox/Global";
3628 CFGNODE global;
3629
3630 vrc = CFGLDRGetNode(configLoader, Global, 0, &global);
3631 if (VBOX_FAILURE (vrc))
3632 CFGLDRCreateNode (configLoader, Global, &global);
3633
3634 do
3635 {
3636 ComAssertBreak (global, rc = E_FAIL);
3637
3638 /* machines */
3639 do
3640 {
3641 const char *Registry = "MachineRegistry";
3642 const char *Entry = "MachineEntry";
3643 CFGNODE registryNode = NULL;
3644
3645 /* first, delete the entire machine registry */
3646 if (VBOX_SUCCESS (CFGLDRGetChildNode (global, Registry, 0, &registryNode)))
3647 CFGLDRDeleteNode (registryNode);
3648
3649 /* then, recreate it */
3650 CFGLDRCreateChildNode (global, Registry, &registryNode);
3651
3652 /* write out the machines */
3653 for (MachineList::iterator it = mData.mMachines.begin();
3654 it != mData.mMachines.end();
3655 ++ it)
3656 {
3657 /// @todo (dmik) move this part to Machine for better incapsulation
3658
3659 ComObjPtr <Machine> m = *it;
3660 AutoReaderLock machineLock (m);
3661
3662 AssertMsg (m->data(), ("Machine data must not be NULL"));
3663 CFGNODE entryNode;
3664 CFGLDRAppendChildNode (registryNode, Entry, &entryNode);
3665 /* UUID + curly brackets */
3666 CFGLDRSetUUID (entryNode, "uuid", unconst (m->data()->mUuid).ptr());
3667 /* source */
3668 CFGLDRSetBSTR (entryNode, "src", m->data()->mConfigFile);
3669 /* done */
3670 CFGLDRReleaseNode (entryNode);
3671 }
3672
3673 CFGLDRReleaseNode (registryNode);
3674 }
3675 while (0);
3676 if (FAILED (rc))
3677 break;
3678
3679 /* disk images */
3680 do
3681 {
3682 CFGNODE registryNode = 0;
3683 CFGLDRGetChildNode (global, "DiskRegistry", 0, &registryNode);
3684 /* first, delete the entire disk image registr */
3685 if (registryNode)
3686 CFGLDRDeleteNode (registryNode);
3687 /* then, recreate it */
3688 CFGLDRCreateChildNode (global, "DiskRegistry", &registryNode);
3689 ComAssertBreak (registryNode, rc = E_FAIL);
3690
3691 /* write out the hard disks */
3692 {
3693 CFGNODE imagesNode = 0;
3694 CFGLDRCreateChildNode (registryNode, "HardDisks", &imagesNode);
3695 rc = saveHardDisks (imagesNode);
3696 CFGLDRReleaseNode (imagesNode);
3697 if (FAILED (rc))
3698 break;
3699 }
3700
3701 /* write out the CD/DVD images */
3702 {
3703 CFGNODE imagesNode = 0;
3704 CFGLDRCreateChildNode (registryNode, "DVDImages", &imagesNode);
3705
3706 for (DVDImageList::iterator it = mData.mDVDImages.begin();
3707 it != mData.mDVDImages.end();
3708 ++ it)
3709 {
3710 ComObjPtr <DVDImage> dvd = *it;
3711 /* no need to lock: fields are constant */
3712 CFGNODE imageNode = 0;
3713 CFGLDRAppendChildNode (imagesNode, "Image", &imageNode);
3714 CFGLDRSetUUID (imageNode, "uuid", dvd->id());
3715 CFGLDRSetBSTR (imageNode, "src", dvd->filePath());
3716 CFGLDRReleaseNode (imageNode);
3717 }
3718
3719 CFGLDRReleaseNode (imagesNode);
3720 }
3721
3722 /* write out the floppy images */
3723 {
3724 CFGNODE imagesNode = 0;
3725 CFGLDRCreateChildNode (registryNode, "FloppyImages", &imagesNode);
3726
3727 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3728 it != mData.mFloppyImages.end();
3729 ++ it)
3730 {
3731 ComObjPtr <FloppyImage> fd = *it;
3732 /* no need to lock: fields are constant */
3733 CFGNODE imageNode = 0;
3734 CFGLDRAppendChildNode (imagesNode, "Image", &imageNode);
3735 CFGLDRSetUUID (imageNode, "uuid", fd->id());
3736 CFGLDRSetBSTR (imageNode, "src", fd->filePath());
3737 CFGLDRReleaseNode (imageNode);
3738 }
3739
3740 CFGLDRReleaseNode (imagesNode);
3741 }
3742
3743 CFGLDRReleaseNode (registryNode);
3744 }
3745 while (0);
3746 if (FAILED (rc))
3747 break;
3748
3749 do
3750 {
3751 /* host data (USB filters) */
3752 rc = mData.mHost->saveSettings (global);
3753 if (FAILED (rc))
3754 break;
3755
3756 rc = mData.mSystemProperties->saveSettings (global);
3757 if (FAILED (rc))
3758 break;
3759 }
3760 while (0);
3761 }
3762 while (0);
3763
3764 if (global)
3765 CFGLDRReleaseNode (global);
3766
3767 if (SUCCEEDED (rc))
3768 {
3769 char *loaderError = NULL;
3770 vrc = CFGLDRSave (configLoader, &loaderError);
3771 if (VBOX_FAILURE (vrc))
3772 {
3773 rc = setError (E_FAIL,
3774 tr ("Could not save the settings file '%ls' (%Vrc)%s%s"),
3775 mData.mCfgFile.mName.raw(), vrc,
3776 loaderError ? ".\n" : "", loaderError ? loaderError : "");
3777 if (loaderError)
3778 RTMemTmpFree (loaderError);
3779 }
3780 }
3781
3782 CFGLDRFree(configLoader);
3783
3784 return rc;
3785}
3786
3787/**
3788 * Saves all hard disks to the given <HardDisks> node.
3789 *
3790 * @param aNode <HardDisks> node
3791 *
3792 * @note Locks this object for reding.
3793 */
3794HRESULT VirtualBox::saveHardDisks (CFGNODE aNode)
3795{
3796 AssertReturn (aNode, E_INVALIDARG);
3797
3798 HRESULT rc = S_OK;
3799
3800 AutoReaderLock alock (this);
3801
3802 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
3803 it != mData.mHardDisks.end() && SUCCEEDED (rc);
3804 ++ it)
3805 {
3806 ComObjPtr <HardDisk> hd = *it;
3807 AutoReaderLock hdLock (hd);
3808
3809 CFGNODE hdNode = 0;
3810 CFGLDRAppendChildNode (aNode, "HardDisk", &hdNode);
3811 ComAssertBreak (hdNode, rc = E_FAIL);
3812
3813 CFGNODE storageNode = 0;
3814
3815 switch (hd->storageType())
3816 {
3817 case HardDiskStorageType_VirtualDiskImage:
3818 {
3819 CFGLDRAppendChildNode (hdNode, "VirtualDiskImage", &storageNode);
3820 ComAssertBreak (storageNode, rc = E_FAIL);
3821 rc = hd->saveSettings (hdNode, storageNode);
3822 break;
3823 }
3824
3825 case HardDiskStorageType_ISCSIHardDisk:
3826 {
3827 CFGLDRAppendChildNode (hdNode, "ISCSIHardDisk", &storageNode);
3828 ComAssertBreak (storageNode, rc = E_FAIL);
3829 rc = hd->saveSettings (hdNode, storageNode);
3830 break;
3831 }
3832
3833 case HardDiskStorageType_VMDKImage:
3834 {
3835 CFGLDRAppendChildNode (hdNode, "VMDKImage", &storageNode);
3836 ComAssertBreak (storageNode, rc = E_FAIL);
3837 rc = hd->saveSettings (hdNode, storageNode);
3838 break;
3839 }
3840
3841 /// @todo (dmik) later
3842// case HardDiskStorageType_PhysicalVolume:
3843// {
3844// break;
3845// }
3846 }
3847
3848 if (storageNode)
3849 CFGLDRReleaseNode (storageNode);
3850
3851 CFGLDRReleaseNode (hdNode);
3852 }
3853
3854 return rc;
3855}
3856
3857/**
3858 * Helper to register the machine.
3859 *
3860 * When called during VirtualBox startup, adds the given machine to the
3861 * collection of registered machines. Otherwise tries to mark the machine
3862 * as registered, and, if succeeded, adds it to the collection and
3863 * saves global settings.
3864 *
3865 * @param aMachine machine to register
3866 *
3867 * @note Locks objects!
3868 */
3869HRESULT VirtualBox::registerMachine (Machine *aMachine)
3870{
3871 ComAssertRet (aMachine, E_INVALIDARG);
3872
3873 AutoCaller autoCaller (this);
3874 CheckComRCReturnRC (autoCaller.rc());
3875
3876 AutoLock alock (this);
3877
3878 ComAssertRet (findMachine (aMachine->data()->mUuid,
3879 false /* aDoSetError */, NULL) == E_INVALIDARG,
3880 E_FAIL);
3881
3882 HRESULT rc = S_OK;
3883
3884 if (autoCaller.state() != InInit)
3885 {
3886 /* Machine::trySetRegistered() will commit and save machine settings */
3887 rc = aMachine->trySetRegistered (TRUE);
3888 CheckComRCReturnRC (rc);
3889 }
3890
3891 /* add to the collection of registered machines */
3892 mData.mMachines.push_back (aMachine);
3893
3894 if (autoCaller.state() != InInit)
3895 rc = saveConfig();
3896
3897 return rc;
3898}
3899
3900/**
3901 * Helper to register the hard disk.
3902 *
3903 * @param aHardDisk object to register
3904 * @param aFlags one of RHD_* values
3905 *
3906 * @note Locks objects!
3907 */
3908HRESULT VirtualBox::registerHardDisk (HardDisk *aHardDisk, RHD_Flags aFlags)
3909{
3910 ComAssertRet (aHardDisk, E_INVALIDARG);
3911
3912 AutoCaller autoCaller (this);
3913 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3914
3915 AutoLock alock (this);
3916
3917 HRESULT rc = checkMediaForConflicts (aHardDisk, NULL, NULL);
3918 CheckComRCReturnRC (rc);
3919
3920 /* mark the hard disk as registered only when registration is external */
3921 if (aFlags == RHD_External)
3922 {
3923 rc = aHardDisk->trySetRegistered (TRUE);
3924 CheckComRCReturnRC (rc);
3925 }
3926
3927 if (!aHardDisk->parent())
3928 {
3929 /* add to the collection of top-level images */
3930 mData.mHardDisks.push_back (aHardDisk);
3931 }
3932
3933 /* insert to the map of hard disks */
3934 mData.mHardDiskMap
3935 .insert (HardDiskMap::value_type (aHardDisk->id(), aHardDisk));
3936
3937 /* save global config file if not on startup */
3938 /// @todo (dmik) optimize later to save only the <HardDisks> node
3939 if (aFlags != RHD_OnStartUp)
3940 rc = saveConfig();
3941
3942 return rc;
3943}
3944
3945/**
3946 * Helper to unregister the hard disk.
3947 *
3948 * If the hard disk is a differencing hard disk and if the unregistration
3949 * succeeds, the hard disk image is deleted and the object is uninitialized.
3950 *
3951 * @param aHardDisk hard disk to unregister
3952 *
3953 * @note Locks objects!
3954 */
3955HRESULT VirtualBox::unregisterHardDisk (HardDisk *aHardDisk)
3956{
3957 AssertReturn (aHardDisk, E_INVALIDARG);
3958
3959 AutoCaller autoCaller (this);
3960 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3961
3962 LogFlowThisFunc (("image='%ls'\n", aHardDisk->toString().raw()));
3963
3964 AutoLock alock (this);
3965
3966 /* Lock the hard disk to ensure nobody registers it again before we delete
3967 * the differencing image (sanity check actually -- should never happen). */
3968 AutoLock hdLock (aHardDisk);
3969
3970 /* try to unregister */
3971 HRESULT rc = aHardDisk->trySetRegistered (FALSE);
3972 CheckComRCReturnRC (rc);
3973
3974 /* remove from the map of hard disks */
3975 mData.mHardDiskMap.erase (aHardDisk->id());
3976
3977 if (!aHardDisk->parent())
3978 {
3979 /* non-differencing hard disk:
3980 * remove from the collection of top-level hard disks */
3981 mData.mHardDisks.remove (aHardDisk);
3982 }
3983 else
3984 {
3985 Assert (aHardDisk->isDifferencing());
3986
3987 /* differencing hard disk: delete (only if the last access check
3988 * succeeded) and uninitialize */
3989 if (aHardDisk->asVDI()->lastAccessError().isNull())
3990 rc = aHardDisk->asVDI()->DeleteImage();
3991 aHardDisk->uninit();
3992 }
3993
3994 /* save the global config file anyway (already unregistered) */
3995 /// @todo (dmik) optimize later to save only the <HardDisks> node
3996 HRESULT rc2 = saveConfig();
3997 if (SUCCEEDED (rc))
3998 rc = rc2;
3999
4000 return rc;
4001}
4002
4003/**
4004 * Helper to unregister the differencing hard disk image.
4005 * Resets machine ID of the hard disk (to let the unregistration succeed)
4006 * and then calls #unregisterHardDisk().
4007 *
4008 * @param aHardDisk differencing hard disk image to unregister
4009 *
4010 * @note Locks objects!
4011 */
4012HRESULT VirtualBox::unregisterDiffHardDisk (HardDisk *aHardDisk)
4013{
4014 AssertReturn (aHardDisk, E_INVALIDARG);
4015
4016 AutoCaller autoCaller (this);
4017 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4018
4019 AutoLock alock (this);
4020
4021 /*
4022 * Note: it's safe to lock aHardDisk here because the same object
4023 * will be locked by #unregisterHardDisk().
4024 */
4025 AutoLock hdLock (aHardDisk);
4026
4027 AssertReturn (aHardDisk->isDifferencing(), E_INVALIDARG);
4028
4029 /*
4030 * deassociate the machine from the hard disk
4031 * (otherwise trySetRegistered() will definitely fail)
4032 */
4033 aHardDisk->setMachineId (Guid());
4034
4035 return unregisterHardDisk (aHardDisk);
4036}
4037
4038
4039/**
4040 * Helper to update the global settings file when the name of some machine
4041 * changes so that file and directory renaming occurs. This method ensures
4042 * that all affected paths in the disk registry are properly updated.
4043 *
4044 * @param aOldPath old path (full)
4045 * @param aNewPath new path (full)
4046 *
4047 * @note Locks this object + DVD, Floppy and HardDisk children for writing.
4048 */
4049HRESULT VirtualBox::updateSettings (const char *aOldPath, const char *aNewPath)
4050{
4051 LogFlowThisFunc (("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
4052
4053 AssertReturn (aOldPath, E_INVALIDARG);
4054 AssertReturn (aNewPath, E_INVALIDARG);
4055
4056 AutoCaller autoCaller (this);
4057 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4058
4059 AutoLock alock (this);
4060
4061 size_t oldPathLen = strlen (aOldPath);
4062
4063 /* check DVD paths */
4064 for (DVDImageList::iterator it = mData.mDVDImages.begin();
4065 it != mData.mDVDImages.end();
4066 ++ it)
4067 {
4068 ComObjPtr <DVDImage> image = *it;
4069
4070 /* no need to lock: fields are constant */
4071 Utf8Str path = image->filePathFull();
4072 LogFlowThisFunc (("DVD.fullPath={%s}\n", path.raw()));
4073
4074 if (RTPathStartsWith (path, aOldPath))
4075 {
4076 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
4077 path.raw() + oldPathLen);
4078 path = newPath;
4079 calculateRelativePath (path, path);
4080 image->updatePath (newPath, path);
4081
4082 LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
4083 newPath.raw(), path.raw()));
4084 }
4085 }
4086
4087 /* check Floppy paths */
4088 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
4089 it != mData.mFloppyImages.end();
4090 ++ it)
4091 {
4092 ComObjPtr <FloppyImage> image = *it;
4093
4094 /* no need to lock: fields are constant */
4095 Utf8Str path = image->filePathFull();
4096 LogFlowThisFunc (("Floppy.fullPath={%s}\n", path.raw()));
4097
4098 if (RTPathStartsWith (path, aOldPath))
4099 {
4100 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
4101 path.raw() + oldPathLen);
4102 path = newPath;
4103 calculateRelativePath (path, path);
4104 image->updatePath (newPath, path);
4105
4106 LogFlowThisFunc (("-> updated: full={%s} rel={%s}\n",
4107 newPath.raw(), path.raw()));
4108 }
4109 }
4110
4111 /* check HardDisk paths */
4112 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
4113 it != mData.mHardDisks.end();
4114 ++ it)
4115 {
4116 (*it)->updatePaths (aOldPath, aNewPath);
4117 }
4118
4119 HRESULT rc = saveConfig();
4120
4121 return rc;
4122}
4123
4124/**
4125 * Helper to register the DVD image.
4126 *
4127 * @param aImage object to register
4128 * @param aOnStartUp whether this method called during VirtualBox init or not
4129 *
4130 * @return COM status code
4131 *
4132 * @note Locks objects!
4133 */
4134HRESULT VirtualBox::registerDVDImage (DVDImage *aImage, bool aOnStartUp)
4135{
4136 AssertReturn (aImage, E_INVALIDARG);
4137
4138 AutoCaller autoCaller (this);
4139 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4140
4141 AutoLock alock (this);
4142
4143 HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
4144 aImage->filePathFull());
4145 CheckComRCReturnRC (rc);
4146
4147 /* add to the collection */
4148 mData.mDVDImages.push_back (aImage);
4149
4150 /* save global config file if we're supposed to */
4151 if (!aOnStartUp)
4152 rc = saveConfig();
4153
4154 return rc;
4155}
4156
4157/**
4158 * Helper to register the floppy image.
4159 *
4160 * @param aImage object to register
4161 * @param aOnStartUp whether this method called during VirtualBox init or not
4162 *
4163 * @return COM status code
4164 *
4165 * @note Locks objects!
4166 */
4167HRESULT VirtualBox::registerFloppyImage (FloppyImage *aImage, bool aOnStartUp)
4168{
4169 AssertReturn (aImage, E_INVALIDARG);
4170
4171 AutoCaller autoCaller (this);
4172 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4173
4174 AutoLock alock (this);
4175
4176 HRESULT rc = checkMediaForConflicts (NULL, &aImage->id(),
4177 aImage->filePathFull());
4178 CheckComRCReturnRC (rc);
4179
4180 /* add to the collection */
4181 mData.mFloppyImages.push_back (aImage);
4182
4183 /* save global config file if we're supposed to */
4184 if (!aOnStartUp)
4185 rc = saveConfig();
4186
4187 return rc;
4188}
4189
4190/**
4191 * Helper function to create the guest OS type objects and our collection
4192 *
4193 * @returns COM status code
4194 */
4195HRESULT VirtualBox::registerGuestOSTypes()
4196{
4197 AutoCaller autoCaller (this);
4198 AssertComRCReturn (autoCaller.rc(), E_FAIL);
4199 AssertReturn (autoCaller.state() == InInit, E_FAIL);
4200
4201 HRESULT rc = S_OK;
4202
4203 // this table represents our os type / string mapping
4204 static struct
4205 {
4206 const char *id; // utf-8
4207 const char *description; // utf-8
4208 const OSType osType;
4209 const uint32_t recommendedRAM;
4210 const uint32_t recommendedVRAM;
4211 const uint32_t recommendedHDD;
4212 } OSTypes[] =
4213 {
4214 /// @todo (dmik) get the list of OS types from the XML schema
4215 /* NOTE: we assume that unknown is always the first entry! */
4216 { "unknown", tr ("Other/Unknown"), OSTypeUnknown, 64, 4, 2000 },
4217 { "dos", "DOS", OSTypeDOS, 32, 4, 500 },
4218 { "win31", "Windows 3.1", OSTypeWin31, 32, 4, 1000 },
4219 { "win95", "Windows 95", OSTypeWin95, 64, 4, 2000 },
4220 { "win98", "Windows 98", OSTypeWin98, 64, 4, 2000 },
4221 { "winme", "Windows Me", OSTypeWinMe, 64, 4, 4000 },
4222 { "winnt4", "Windows NT 4", OSTypeWinNT4, 128, 4, 2000 },
4223 { "win2k", "Windows 2000", OSTypeWin2k, 168, 4, 4000 },
4224 { "winxp", "Windows XP", OSTypeWinXP, 192, 4, 10000 },
4225 { "win2k3", "Windows Server 2003", OSTypeWin2k3, 256, 4, 20000 },
4226 { "winvista", "Windows Vista", OSTypeWinVista, 512, 4, 20000 },
4227 { "os2warp3", "OS/2 Warp 3", OSTypeOS2Warp3, 48, 4, 1000 },
4228 { "os2warp4", "OS/2 Warp 4", OSTypeOS2Warp4, 64, 4, 2000 },
4229 { "os2warp45", "OS/2 Warp 4.5", OSTypeOS2Warp45, 96, 4, 2000 },
4230 { "linux22", "Linux 2.2", OSTypeLinux22, 64, 4, 2000 },
4231 { "linux24", "Linux 2.4", OSTypeLinux24, 128, 4, 4000 },
4232 { "linux26", "Linux 2.6", OSTypeLinux26, 128, 4, 8000 },
4233 { "freebsd", "FreeBSD", OSTypeFreeBSD, 64, 4, 2000 },
4234 { "openbsd", "OpenBSD", OSTypeOpenBSD, 64, 4, 2000 },
4235 { "netbsd", "NetBSD", OSTypeNetBSD, 64, 4, 2000 },
4236 { "netware", "Netware", OSTypeNetware, 128, 4, 4000 },
4237 { "solaris", "Solaris", OSTypeSolaris, 128, 4, 8000 },
4238 { "l4", "L4", OSTypeL4, 64, 4, 2000 }
4239 };
4240
4241 for (uint32_t i = 0; i < ELEMENTS (OSTypes) && SUCCEEDED (rc); i++)
4242 {
4243 ComObjPtr <GuestOSType> guestOSTypeObj;
4244 rc = guestOSTypeObj.createObject();
4245 if (SUCCEEDED (rc))
4246 {
4247 rc = guestOSTypeObj->init (OSTypes[i].id,
4248 OSTypes[i].description,
4249 OSTypes[i].osType,
4250 OSTypes[i].recommendedRAM,
4251 OSTypes[i].recommendedVRAM,
4252 OSTypes[i].recommendedHDD);
4253 if (SUCCEEDED (rc))
4254 mData.mGuestOSTypes.push_back (guestOSTypeObj);
4255 }
4256 }
4257
4258 return rc;
4259}
4260
4261/**
4262 * Helper to lock the VirtualBox configuration for write access.
4263 *
4264 * @note This method is not thread safe (must be called only from #init()
4265 * or #uninit()).
4266 *
4267 * @note If the configuration file is not found, the method returns
4268 * S_OK, but subsequent #isConfigLocked() will return FALSE. This is used
4269 * in some places to determine the (valid) situation when no config file
4270 * exists yet, and therefore a new one should be created from scatch.
4271 */
4272HRESULT VirtualBox::lockConfig()
4273{
4274 AutoCaller autoCaller (this);
4275 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4276 AssertReturn (autoCaller.state() == InInit, E_FAIL);
4277
4278 HRESULT rc = S_OK;
4279
4280 Assert (!isConfigLocked());
4281 if (!isConfigLocked())
4282 {
4283 /* open the associated config file */
4284 int vrc = RTFileOpen (&mData.mCfgFile.mHandle,
4285 Utf8Str (mData.mCfgFile.mName),
4286 RTFILE_O_READWRITE | RTFILE_O_OPEN |
4287 RTFILE_O_DENY_WRITE | RTFILE_O_WRITE_THROUGH);
4288 if (VBOX_FAILURE (vrc))
4289 {
4290 mData.mCfgFile.mHandle = NIL_RTFILE;
4291
4292 /*
4293 * It is ok if the file is not found, it will be created by
4294 * init(). Otherwise return an error.
4295 */
4296 if (vrc != VERR_FILE_NOT_FOUND)
4297 rc = setError (E_FAIL,
4298 tr ("Could not lock the settings file '%ls' (%Vrc)"),
4299 mData.mCfgFile.mName.raw(), vrc);
4300 }
4301
4302 LogFlowThisFunc (("mCfgFile.mName='%ls', mCfgFile.mHandle=%d, rc=%08X\n",
4303 mData.mCfgFile.mName.raw(), mData.mCfgFile.mHandle, rc));
4304 }
4305
4306 return rc;
4307}
4308
4309/**
4310 * Helper to unlock the VirtualBox configuration from write access.
4311 *
4312 * @note This method is not thread safe (must be called only from #init()
4313 * or #uninit()).
4314 */
4315HRESULT VirtualBox::unlockConfig()
4316{
4317 AutoCaller autoCaller (this);
4318 AssertComRCReturn (autoCaller.rc(), E_FAIL);
4319 AssertReturn (autoCaller.state() == InUninit, E_FAIL);
4320
4321 HRESULT rc = S_OK;
4322
4323 if (isConfigLocked())
4324 {
4325 RTFileFlush (mData.mCfgFile.mHandle);
4326 RTFileClose (mData.mCfgFile.mHandle);
4327 /** @todo flush the directory too. */
4328 mData.mCfgFile.mHandle = NIL_RTFILE;
4329 LogFlowThisFunc (("\n"));
4330 }
4331
4332 return rc;
4333}
4334
4335/**
4336 * Thread function that watches the termination of all client processes
4337 * that have opened sessions using IVirtualBox::OpenSession()
4338 */
4339// static
4340DECLCALLBACK(int) VirtualBox::clientWatcher (RTTHREAD thread, void *pvUser)
4341{
4342 LogFlowFuncEnter();
4343
4344 VirtualBox *that = (VirtualBox *) pvUser;
4345 Assert (that);
4346
4347 SessionMachineVector machines;
4348 int cnt = 0;
4349
4350#if defined(__WIN__)
4351
4352 HRESULT hrc = CoInitializeEx (NULL,
4353 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
4354 COINIT_SPEED_OVER_MEMORY);
4355 AssertComRC (hrc);
4356
4357 /// @todo (dmik) processes reaping!
4358
4359 HANDLE *handles = new HANDLE [1];
4360 handles [0] = that->mWatcherData.mUpdateReq;
4361
4362 do
4363 {
4364 AutoCaller autoCaller (that);
4365 /* VirtualBox has been early uninitialized, terminate */
4366 if (!autoCaller.isOk())
4367 break;
4368
4369 do
4370 {
4371 /* release the caller to let uninit() ever proceed */
4372 autoCaller.release();
4373
4374 DWORD rc = ::WaitForMultipleObjects (cnt + 1, handles, FALSE, INFINITE);
4375
4376 /*
4377 * Restore the caller before using VirtualBox. If it fails, this
4378 * means VirtualBox is being uninitialized and we must terminate.
4379 */
4380 autoCaller.add();
4381 if (!autoCaller.isOk())
4382 break;
4383
4384 bool update = false;
4385 if (rc == WAIT_OBJECT_0)
4386 {
4387 /* update event is signaled */
4388 update = true;
4389 }
4390 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4391 {
4392 /* machine mutex is released */
4393 (machines [rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4394 update = true;
4395 }
4396 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4397 {
4398 /* machine mutex is abandoned due to client process termination */
4399 (machines [rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4400 update = true;
4401 }
4402 if (update)
4403 {
4404 /* obtain a new set of opened machines */
4405 that->getOpenedMachines (machines);
4406 cnt = machines.size();
4407 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4408 AssertMsg ((cnt + 1) <= MAXIMUM_WAIT_OBJECTS,
4409 ("MAXIMUM_WAIT_OBJECTS reached"));
4410 /* renew the set of event handles */
4411 delete [] handles;
4412 handles = new HANDLE [cnt + 1];
4413 handles [0] = that->mWatcherData.mUpdateReq;
4414 for (int i = 0; i < cnt; i++)
4415 handles [i + 1] = (machines [i])->ipcSem();
4416 }
4417 }
4418 while (true);
4419 }
4420 while (0);
4421
4422 /* delete the set of event handles */
4423 delete [] handles;
4424
4425 /* delete the set of opened machines if any */
4426 machines.clear();
4427
4428 ::CoUninitialize();
4429
4430#else
4431
4432 bool need_update = false;
4433
4434 do
4435 {
4436 AutoCaller autoCaller (that);
4437 if (!autoCaller.isOk())
4438 break;
4439
4440 do
4441 {
4442 /* release the caller to let uninit() ever proceed */
4443 autoCaller.release();
4444
4445 int rc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4446
4447 /*
4448 * Restore the caller before using VirtualBox. If it fails, this
4449 * means VirtualBox is being uninitialized and we must terminate.
4450 */
4451 autoCaller.add();
4452 if (!autoCaller.isOk())
4453 break;
4454
4455 if (VBOX_SUCCESS (rc) || need_update)
4456 {
4457 /* VBOX_SUCCESS (rc) means an update event is signaled */
4458
4459 /* obtain a new set of opened machines */
4460 that->getOpenedMachines (machines);
4461 cnt = machines.size();
4462 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4463 }
4464
4465 need_update = false;
4466 for (int i = 0; i < cnt; i++)
4467 need_update |= (machines [i])->checkForDeath();
4468
4469 /* reap child processes */
4470 {
4471 AutoLock alock (that);
4472 if (that->mWatcherData.mProcesses.size())
4473 {
4474 LogFlowFunc (("UPDATE: child process count = %d\n",
4475 that->mWatcherData.mProcesses.size()));
4476 ClientWatcherData::ProcessList::iterator it =
4477 that->mWatcherData.mProcesses.begin();
4478 while (it != that->mWatcherData.mProcesses.end())
4479 {
4480 RTPROCESS pid = *it;
4481 RTPROCSTATUS status;
4482 int vrc = ::RTProcWait (pid, RTPROCWAIT_FLAGS_NOBLOCK,
4483 &status);
4484 if (vrc == VINF_SUCCESS)
4485 {
4486 LogFlowFunc (("pid %d (%x) was reaped, "
4487 "status=%d, reason=%d\n",
4488 pid, pid, status.iStatus,
4489 status.enmReason));
4490 it = that->mWatcherData.mProcesses.erase (it);
4491 }
4492 else
4493 {
4494 LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Vrc\n",
4495 pid, pid, vrc));
4496 if (vrc != VERR_PROCESS_RUNNING)
4497 {
4498 /* remove the process if it is not already running */
4499 it = that->mWatcherData.mProcesses.erase (it);
4500 }
4501 else
4502 ++ it;
4503 }
4504 }
4505 }
4506 }
4507 }
4508 while (true);
4509 }
4510 while (0);
4511
4512 /* delete the set of opened machines if any */
4513 machines.clear();
4514
4515#endif
4516
4517 LogFlowFuncLeave();
4518 return 0;
4519}
4520
4521/**
4522 * Thread function that handles custom events posted using #postEvent().
4523 */
4524// static
4525DECLCALLBACK(int) VirtualBox::asyncEventHandler (RTTHREAD thread, void *pvUser)
4526{
4527 LogFlowFuncEnter();
4528
4529 AssertReturn (pvUser, VERR_INVALID_POINTER);
4530
4531 // create an event queue for the current thread
4532 EventQueue *eventQ = new EventQueue();
4533 AssertReturn (eventQ, VERR_NO_MEMORY);
4534
4535 // return the queue to the one who created this thread
4536 *(static_cast <EventQueue **> (pvUser)) = eventQ;
4537 // signal that we're ready
4538 RTThreadUserSignal (thread);
4539
4540 BOOL ok = TRUE;
4541 Event *event = NULL;
4542
4543 while ((ok = eventQ->waitForEvent (&event)) && event)
4544 eventQ->handleEvent (event);
4545
4546 AssertReturn (ok, VERR_GENERAL_FAILURE);
4547
4548 delete eventQ;
4549
4550 LogFlowFuncLeave();
4551
4552 return 0;
4553}
4554
4555////////////////////////////////////////////////////////////////////////////////
4556
4557/**
4558 * Takes the current list of registered callbacks of the managed VirtualBox
4559 * instance, and calls #handleCallback() for every callback item from the
4560 * list, passing the item as an argument.
4561 *
4562 * @note Locks the managed VirtualBox object for reading but leaves the lock
4563 * before iterating over callbacks and calling their methods.
4564 */
4565void *VirtualBox::CallbackEvent::handler()
4566{
4567 if (mVirtualBox.isNull())
4568 return NULL;
4569
4570 AutoCaller autoCaller (mVirtualBox);
4571 if (!autoCaller.isOk())
4572 {
4573 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
4574 "the callback event is discarded!\n",
4575 autoCaller.state()));
4576 /* We don't need mVirtualBox any more, so release it */
4577 mVirtualBox.setNull();
4578 return NULL;
4579 }
4580
4581 CallbackVector callbacks;
4582 {
4583 /* Make a copy to release the lock before iterating */
4584 AutoReaderLock alock (mVirtualBox);
4585 callbacks = CallbackVector (mVirtualBox->mData.mCallbacks.begin(),
4586 mVirtualBox->mData.mCallbacks.end());
4587 /* We don't need mVirtualBox any more, so release it */
4588 mVirtualBox.setNull();
4589 }
4590
4591 for (VirtualBox::CallbackVector::const_iterator it = callbacks.begin();
4592 it != callbacks.end(); ++ it)
4593 handleCallback (*it);
4594
4595 return NULL;
4596}
4597
Note: See TracBrowser for help on using the repository browser.

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