VirtualBox

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

Last change on this file since 3481 was 3480, checked in by vboxsync, 17 years ago

Main: Use named mutexes for watching client processes on OS/2.

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

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