VirtualBox

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

Last change on this file since 6861 was 6384, checked in by vboxsync, 17 years ago

next try

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