VirtualBox

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

Last change on this file since 18499 was 18484, checked in by vboxsync, 15 years ago

VirtualBoxImpl.cpp: size_t/DWORD warning.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use