VirtualBox

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

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

Python: moved waiting on client side, removed main API for event waiting, as making no sense

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 148.2 KB
Line 
1/* $Id: VirtualBoxImpl.cpp 20630 2009-06-16 13:55:38Z 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 Ethernet 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_BSTR 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_BSTR 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_BSTR 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_BSTR 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_BSTR 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_BSTR 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_BSTR 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_BSTR 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_BSTR 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_BSTR 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 (id, SessionState_Open);
1737 }
1738
1739 return rc;
1740}
1741
1742/**
1743 * @note Locks objects!
1744 */
1745STDMETHODIMP VirtualBox::OpenRemoteSession (ISession *aSession,
1746 IN_BSTR aMachineId,
1747 IN_BSTR aType,
1748 IN_BSTR aEnvironment,
1749 IProgress **aProgress)
1750{
1751 LogRel(("remotesession=%s\n", Utf8Str(aMachineId).c_str()));
1752
1753 CheckComArgNotNull(aMachineId);
1754 CheckComArgNotNull(aSession);
1755 CheckComArgNotNull(aType);
1756 CheckComArgOutSafeArrayPointerValid(aProgress);
1757
1758 AutoCaller autoCaller (this);
1759 CheckComRCReturnRC (autoCaller.rc());
1760
1761 Guid id(aMachineId);
1762 ComObjPtr <Machine> machine;
1763
1764 HRESULT rc = findMachine (id, true /* setError */, &machine);
1765 CheckComRCReturnRC (rc);
1766
1767 /* check the session state */
1768 SessionState_T state;
1769 rc = aSession->COMGETTER(State) (&state);
1770 CheckComRCReturnRC (rc);
1771
1772 if (state != SessionState_Closed)
1773 return setError (VBOX_E_INVALID_OBJECT_STATE,
1774 tr ("The given session is already open or being opened"));
1775
1776 /* get the IInternalSessionControl interface */
1777 ComPtr <IInternalSessionControl> control = aSession;
1778 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1779 E_INVALIDARG);
1780
1781 /* create a progress object */
1782 ComObjPtr <Progress> progress;
1783 progress.createObject();
1784 progress->init (this, static_cast <IMachine *> (machine),
1785 Bstr (tr ("Spawning session")),
1786 FALSE /* aCancelable */);
1787
1788 rc = machine->openRemoteSession (control, aType, aEnvironment, progress);
1789
1790 if (SUCCEEDED (rc))
1791 {
1792 progress.queryInterfaceTo (aProgress);
1793
1794 /* signal the client watcher thread */
1795 updateClientWatcher();
1796
1797 /* fire an event */
1798 onSessionStateChange (id, SessionState_Spawning);
1799 }
1800
1801 return rc;
1802}
1803
1804/**
1805 * @note Locks objects!
1806 */
1807STDMETHODIMP VirtualBox::OpenExistingSession (ISession *aSession,
1808 IN_BSTR aMachineId)
1809{
1810 CheckComArgNotNull(aSession);
1811
1812 AutoCaller autoCaller (this);
1813 CheckComRCReturnRC (autoCaller.rc());
1814
1815 Guid id(aMachineId);
1816 ComObjPtr <Machine> machine;
1817
1818 HRESULT rc = findMachine (id, true /* setError */, &machine);
1819 CheckComRCReturnRC (rc);
1820
1821 /* check the session state */
1822 SessionState_T state;
1823 rc = aSession->COMGETTER(State) (&state);
1824 CheckComRCReturnRC (rc);
1825
1826 if (state != SessionState_Closed)
1827 return setError (VBOX_E_INVALID_OBJECT_STATE,
1828 tr ("The given session is already open or being opened"));
1829
1830 /* get the IInternalSessionControl interface */
1831 ComPtr <IInternalSessionControl> control = aSession;
1832 ComAssertMsgRet (!!control, ("No IInternalSessionControl interface"),
1833 E_INVALIDARG);
1834
1835 rc = machine->openExistingSession (control);
1836
1837 return rc;
1838}
1839
1840/**
1841 * @note Locks this object for writing.
1842 */
1843STDMETHODIMP VirtualBox::RegisterCallback (IVirtualBoxCallback *aCallback)
1844{
1845 LogFlowThisFunc (("aCallback=%p\n", aCallback));
1846
1847 CheckComArgNotNull(aCallback);
1848
1849 AutoCaller autoCaller (this);
1850 CheckComRCReturnRC (autoCaller.rc());
1851
1852#if 0 /** @todo r=bird,r=pritesh: must check that the interface id match correct or we might screw up with old code! */
1853 void *dummy;
1854 HRESULT hrc = aCallback->QueryInterface(NS_GET_IID(IVirtualBoxCallback), &dummy);
1855 if (FAILED(hrc))
1856 return hrc;
1857 aCallback->Release();
1858#endif
1859
1860 AutoWriteLock alock (this);
1861 mData.mCallbacks.push_back (CallbackList::value_type (aCallback));
1862
1863 return S_OK;
1864}
1865
1866/**
1867 * @note Locks this object for writing.
1868 */
1869STDMETHODIMP VirtualBox::UnregisterCallback (IVirtualBoxCallback *aCallback)
1870{
1871 CheckComArgNotNull(aCallback);
1872
1873 AutoCaller autoCaller (this);
1874 CheckComRCReturnRC (autoCaller.rc());
1875
1876 HRESULT rc = S_OK;
1877
1878 AutoWriteLock alock (this);
1879
1880 CallbackList::iterator it;
1881 it = std::find (mData.mCallbacks.begin(),
1882 mData.mCallbacks.end(),
1883 CallbackList::value_type (aCallback));
1884 if (it == mData.mCallbacks.end())
1885 rc = E_INVALIDARG;
1886 else
1887 mData.mCallbacks.erase (it);
1888
1889 LogFlowThisFunc (("aCallback=%p, rc=%08X\n", aCallback, rc));
1890 return rc;
1891}
1892
1893
1894STDMETHODIMP VirtualBox::WaitForPropertyChange (IN_BSTR /* aWhat */, ULONG /* aTimeout */,
1895 BSTR * /* aChanged */, BSTR * /* aValues */)
1896{
1897 ReturnComNotImplemented();
1898}
1899
1900STDMETHODIMP VirtualBox::SaveSettings()
1901{
1902 AutoCaller autoCaller (this);
1903 CheckComRCReturnRC (autoCaller.rc());
1904
1905 return saveSettings();
1906}
1907
1908STDMETHODIMP VirtualBox::SaveSettingsWithBackup (BSTR *aBakFileName)
1909{
1910 CheckComArgNotNull(aBakFileName);
1911
1912 AutoCaller autoCaller (this);
1913 CheckComRCReturnRC (autoCaller.rc());
1914
1915 /* saveSettings() needs write lock */
1916 AutoWriteLock alock (this);
1917
1918 /* perform backup only when there was auto-conversion */
1919 if (mData.mSettingsFileVersion != VBOX_XML_VERSION_FULL)
1920 {
1921 Bstr bakFileName;
1922
1923 HRESULT rc = backupSettingsFile (mData.mCfgFile.mName,
1924 mData.mSettingsFileVersion,
1925 bakFileName);
1926 CheckComRCReturnRC (rc);
1927
1928 bakFileName.cloneTo (aBakFileName);
1929 }
1930
1931 return saveSettings();
1932}
1933
1934// public methods only for internal purposes
1935/////////////////////////////////////////////////////////////////////////////
1936
1937/**
1938 * Posts an event to the event queue that is processed asynchronously
1939 * on a dedicated thread.
1940 *
1941 * Posting events to the dedicated event queue is useful to perform secondary
1942 * actions outside any object locks -- for example, to iterate over a list
1943 * of callbacks and inform them about some change caused by some object's
1944 * method call.
1945 *
1946 * @param event event to post
1947 * (must be allocated using |new|, will be deleted automatically
1948 * by the event thread after processing)
1949 *
1950 * @note Doesn't lock any object.
1951 */
1952HRESULT VirtualBox::postEvent (Event *event)
1953{
1954 AutoCaller autoCaller (this);
1955 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
1956
1957 if (autoCaller.state() != Ready)
1958 {
1959 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
1960 "the event is discarded!\n",
1961 autoCaller.state()));
1962 return S_OK;
1963 }
1964
1965 AssertReturn (event, E_FAIL);
1966 AssertReturn (mAsyncEventQ, E_FAIL);
1967
1968 if (mAsyncEventQ->postEvent (event))
1969 return S_OK;
1970
1971 return E_FAIL;
1972}
1973
1974/**
1975 * Adds a progress to the global collection of pending operations.
1976 * Usually gets called upon progress object initialization.
1977 *
1978 * @param aProgress Operation to add to the collection.
1979 *
1980 * @note Doesn't lock objects.
1981 */
1982HRESULT VirtualBox::addProgress (IProgress *aProgress)
1983{
1984 CheckComArgNotNull(aProgress);
1985
1986 AutoCaller autoCaller (this);
1987 CheckComRCReturnRC (autoCaller.rc());
1988
1989 Bstr id;
1990 HRESULT rc = aProgress->COMGETTER(Id) (id.asOutParam());
1991 AssertComRCReturnRC (rc);
1992
1993 /* protect mProgressOperations */
1994 AutoWriteLock safeLock (mSafeLock);
1995
1996 mData.mProgressOperations.insert (ProgressMap::value_type (Guid(id), aProgress));
1997 return S_OK;
1998}
1999
2000/**
2001 * Removes the progress from the global collection of pending operations.
2002 * Usually gets called upon progress completion.
2003 *
2004 * @param aId UUID of the progress operation to remove
2005 *
2006 * @note Doesn't lock objects.
2007 */
2008HRESULT VirtualBox::removeProgress (IN_GUID aId)
2009{
2010 AutoCaller autoCaller (this);
2011 CheckComRCReturnRC (autoCaller.rc());
2012
2013 ComPtr <IProgress> progress;
2014
2015 /* protect mProgressOperations */
2016 AutoWriteLock safeLock (mSafeLock);
2017
2018 size_t cnt = mData.mProgressOperations.erase (aId);
2019 Assert (cnt == 1);
2020 NOREF(cnt);
2021
2022 return S_OK;
2023}
2024
2025#ifdef RT_OS_WINDOWS
2026
2027struct StartSVCHelperClientData
2028{
2029 ComObjPtr <VirtualBox> that;
2030 ComObjPtr <Progress> progress;
2031 bool privileged;
2032 VirtualBox::SVCHelperClientFunc func;
2033 void *user;
2034};
2035
2036/**
2037 * Helper method that starts a worker thread that:
2038 * - creates a pipe communication channel using SVCHlpClient;
2039 * - starts an SVC Helper process that will inherit this channel;
2040 * - executes the supplied function by passing it the created SVCHlpClient
2041 * and opened instance to communicate to the Helper process and the given
2042 * Progress object.
2043 *
2044 * The user function is supposed to communicate to the helper process
2045 * using the \a aClient argument to do the requested job and optionally expose
2046 * the progress through the \a aProgress object. The user function should never
2047 * call notifyComplete() on it: this will be done automatically using the
2048 * result code returned by the function.
2049 *
2050 * Before the user function is started, the communication channel passed to
2051 * the \a aClient argument is fully set up, the function should start using
2052 * its write() and read() methods directly.
2053 *
2054 * The \a aVrc parameter of the user function may be used to return an error
2055 * code if it is related to communication errors (for example, returned by
2056 * the SVCHlpClient members when they fail). In this case, the correct error
2057 * message using this value will be reported to the caller. Note that the
2058 * value of \a aVrc is inspected only if the user function itself returns
2059 * success.
2060 *
2061 * If a failure happens anywhere before the user function would be normally
2062 * called, it will be called anyway in special "cleanup only" mode indicated
2063 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode,
2064 * all the function is supposed to do is to cleanup its aUser argument if
2065 * necessary (it's assumed that the ownership of this argument is passed to
2066 * the user function once #startSVCHelperClient() returns a success, thus
2067 * making it responsible for the cleanup).
2068 *
2069 * After the user function returns, the thread will send the SVCHlpMsg::Null
2070 * message to indicate a process termination.
2071 *
2072 * @param aPrivileged |true| to start the SVC Helper process as a privileged
2073 * user that can perform administrative tasks
2074 * @param aFunc user function to run
2075 * @param aUser argument to the user function
2076 * @param aProgress progress object that will track operation completion
2077 *
2078 * @note aPrivileged is currently ignored (due to some unsolved problems in
2079 * Vista) and the process will be started as a normal (unprivileged)
2080 * process.
2081 *
2082 * @note Doesn't lock anything.
2083 */
2084HRESULT VirtualBox::startSVCHelperClient (bool aPrivileged,
2085 SVCHelperClientFunc aFunc,
2086 void *aUser, Progress *aProgress)
2087{
2088 AssertReturn (aFunc, E_POINTER);
2089 AssertReturn (aProgress, E_POINTER);
2090
2091 AutoCaller autoCaller (this);
2092 CheckComRCReturnRC (autoCaller.rc());
2093
2094 /* create the SVCHelperClientThread() argument */
2095 std::auto_ptr <StartSVCHelperClientData>
2096 d (new StartSVCHelperClientData());
2097 AssertReturn (d.get(), E_OUTOFMEMORY);
2098
2099 d->that = this;
2100 d->progress = aProgress;
2101 d->privileged = aPrivileged;
2102 d->func = aFunc;
2103 d->user = aUser;
2104
2105 RTTHREAD tid = NIL_RTTHREAD;
2106 int vrc = RTThreadCreate (&tid, SVCHelperClientThread,
2107 static_cast <void *> (d.get()),
2108 0, RTTHREADTYPE_MAIN_WORKER,
2109 RTTHREADFLAGS_WAITABLE, "SVCHelper");
2110
2111 ComAssertMsgRCRet (vrc, ("Could not create SVCHelper thread (%Rrc)", vrc),
2112 E_FAIL);
2113
2114 /* d is now owned by SVCHelperClientThread(), so release it */
2115 d.release();
2116
2117 return S_OK;
2118}
2119
2120/**
2121 * Worker thread for startSVCHelperClient().
2122 */
2123/* static */
2124DECLCALLBACK(int)
2125VirtualBox::SVCHelperClientThread (RTTHREAD aThread, void *aUser)
2126{
2127 LogFlowFuncEnter();
2128
2129 std::auto_ptr <StartSVCHelperClientData>
2130 d (static_cast <StartSVCHelperClientData *> (aUser));
2131
2132 HRESULT rc = S_OK;
2133 bool userFuncCalled = false;
2134
2135 do
2136 {
2137 AssertBreakStmt (d.get(), rc = E_POINTER);
2138 AssertReturn (!d->progress.isNull(), E_POINTER);
2139
2140 /* protect VirtualBox from uninitialization */
2141 AutoCaller autoCaller (d->that);
2142 if (!autoCaller.isOk())
2143 {
2144 /* it's too late */
2145 rc = autoCaller.rc();
2146 break;
2147 }
2148
2149 int vrc = VINF_SUCCESS;
2150
2151 Guid id;
2152 id.create();
2153 SVCHlpClient client;
2154 vrc = client.create (Utf8StrFmt ("VirtualBox\\SVCHelper\\{%RTuuid}",
2155 id.raw()));
2156 if (RT_FAILURE (vrc))
2157 {
2158 rc = setError (E_FAIL,
2159 tr ("Could not create the communication channel (%Rrc)"), vrc);
2160 break;
2161 }
2162
2163 /* get the path to the executable */
2164 char exePathBuf [RTPATH_MAX];
2165 char *exePath = RTProcGetExecutableName (exePathBuf, RTPATH_MAX);
2166 ComAssertBreak (exePath, E_FAIL);
2167
2168 Utf8Str argsStr = Utf8StrFmt ("/Helper %s", client.name().raw());
2169
2170 LogFlowFunc (("Starting '\"%s\" %s'...\n", exePath, argsStr.raw()));
2171
2172 RTPROCESS pid = NIL_RTPROCESS;
2173
2174 if (d->privileged)
2175 {
2176 /* Attempt to start a privileged process using the Run As dialog */
2177
2178 Bstr file = exePath;
2179 Bstr parameters = argsStr;
2180
2181 SHELLEXECUTEINFO shExecInfo;
2182
2183 shExecInfo.cbSize = sizeof (SHELLEXECUTEINFO);
2184
2185 shExecInfo.fMask = NULL;
2186 shExecInfo.hwnd = NULL;
2187 shExecInfo.lpVerb = L"runas";
2188 shExecInfo.lpFile = file;
2189 shExecInfo.lpParameters = parameters;
2190 shExecInfo.lpDirectory = NULL;
2191 shExecInfo.nShow = SW_NORMAL;
2192 shExecInfo.hInstApp = NULL;
2193
2194 if (!ShellExecuteEx (&shExecInfo))
2195 {
2196 int vrc2 = RTErrConvertFromWin32 (GetLastError());
2197 /* hide excessive details in case of a frequent error
2198 * (pressing the Cancel button to close the Run As dialog) */
2199 if (vrc2 == VERR_CANCELLED)
2200 rc = setError (E_FAIL,
2201 tr ("Operation cancelled by the user"));
2202 else
2203 rc = setError (E_FAIL,
2204 tr ("Could not launch a privileged process '%s' (%Rrc)"),
2205 exePath, vrc2);
2206 break;
2207 }
2208 }
2209 else
2210 {
2211 const char *args[] = { exePath, "/Helper", client.name(), 0 };
2212 vrc = RTProcCreate (exePath, args, RTENV_DEFAULT, 0, &pid);
2213 if (RT_FAILURE (vrc))
2214 {
2215 rc = setError (E_FAIL,
2216 tr ("Could not launch a process '%s' (%Rrc)"), exePath, vrc);
2217 break;
2218 }
2219 }
2220
2221 /* wait for the client to connect */
2222 vrc = client.connect();
2223 if (RT_SUCCESS (vrc))
2224 {
2225 /* start the user supplied function */
2226 rc = d->func (&client, d->progress, d->user, &vrc);
2227 userFuncCalled = true;
2228 }
2229
2230 /* send the termination signal to the process anyway */
2231 {
2232 int vrc2 = client.write (SVCHlpMsg::Null);
2233 if (RT_SUCCESS (vrc))
2234 vrc = vrc2;
2235 }
2236
2237 if (SUCCEEDED (rc) && RT_FAILURE (vrc))
2238 {
2239 rc = setError (E_FAIL,
2240 tr ("Could not operate the communication channel (%Rrc)"), vrc);
2241 break;
2242 }
2243 }
2244 while (0);
2245
2246 if (FAILED (rc) && !userFuncCalled)
2247 {
2248 /* call the user function in the "cleanup only" mode
2249 * to let it free resources passed to in aUser */
2250 d->func (NULL, NULL, d->user, NULL);
2251 }
2252
2253 d->progress->notifyComplete (rc);
2254
2255 LogFlowFuncLeave();
2256 return 0;
2257}
2258
2259#endif /* RT_OS_WINDOWS */
2260
2261/**
2262 * Sends a signal to the client watcher thread to rescan the set of machines
2263 * that have open sessions.
2264 *
2265 * @note Doesn't lock anything.
2266 */
2267void VirtualBox::updateClientWatcher()
2268{
2269 AutoCaller autoCaller (this);
2270 AssertComRCReturn (autoCaller.rc(), (void) 0);
2271
2272 AssertReturn (mWatcherData.mThread != NIL_RTTHREAD, (void) 0);
2273
2274 /* sent an update request */
2275#if defined(RT_OS_WINDOWS)
2276 ::SetEvent (mWatcherData.mUpdateReq);
2277#elif defined(RT_OS_OS2)
2278 RTSemEventSignal (mWatcherData.mUpdateReq);
2279#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
2280 RTSemEventSignal (mWatcherData.mUpdateReq);
2281#else
2282# error "Port me!"
2283#endif
2284}
2285
2286/**
2287 * Adds the given child process ID to the list of processes to be reaped.
2288 * This call should be followed by #updateClientWatcher() to take the effect.
2289 */
2290void VirtualBox::addProcessToReap (RTPROCESS pid)
2291{
2292 AutoCaller autoCaller (this);
2293 AssertComRCReturn (autoCaller.rc(), (void) 0);
2294
2295 /// @todo (dmik) Win32?
2296#ifndef RT_OS_WINDOWS
2297 AutoWriteLock alock (this);
2298 mWatcherData.mProcesses.push_back (pid);
2299#endif
2300}
2301
2302/** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */
2303struct MachineEvent : public VirtualBox::CallbackEvent
2304{
2305 enum What { DataChanged, StateChanged, Registered };
2306
2307 MachineEvent (VirtualBox *aVB, const Guid &aId)
2308 : CallbackEvent (aVB), what (DataChanged), id (aId)
2309 {}
2310
2311 MachineEvent (VirtualBox *aVB, const Guid &aId, MachineState_T aState)
2312 : CallbackEvent (aVB), what (StateChanged), id (aId)
2313 , state (aState)
2314 {}
2315
2316 MachineEvent (VirtualBox *aVB, const Guid &aId, BOOL aRegistered)
2317 : CallbackEvent (aVB), what (Registered), id (aId)
2318 , registered (aRegistered)
2319 {}
2320
2321 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2322 {
2323 switch (what)
2324 {
2325 case DataChanged:
2326 LogFlow (("OnMachineDataChange: id={%RTuuid}\n", id.ptr()));
2327 aCallback->OnMachineDataChange (id.toUtf16());
2328 break;
2329
2330 case StateChanged:
2331 LogFlow (("OnMachineStateChange: id={%RTuuid}, state=%d\n",
2332 id.ptr(), state));
2333 aCallback->OnMachineStateChange (id.toUtf16(), state);
2334 break;
2335
2336 case Registered:
2337 LogFlow (("OnMachineRegistered: id={%RTuuid}, registered=%d\n",
2338 id.ptr(), registered));
2339 aCallback->OnMachineRegistered (id.toUtf16(), registered);
2340 break;
2341 }
2342 }
2343
2344 const What what;
2345
2346 Guid id;
2347 MachineState_T state;
2348 BOOL registered;
2349};
2350
2351/**
2352 * @note Doesn't lock any object.
2353 */
2354void VirtualBox::onMachineStateChange (const Guid &aId, MachineState_T aState)
2355{
2356 postEvent (new MachineEvent (this, aId, aState));
2357}
2358
2359/**
2360 * @note Doesn't lock any object.
2361 */
2362void VirtualBox::onMachineDataChange (const Guid &aId)
2363{
2364 postEvent (new MachineEvent (this, aId));
2365}
2366
2367/**
2368 * @note Locks this object for reading.
2369 */
2370BOOL VirtualBox::onExtraDataCanChange (const Guid &aId, IN_BSTR aKey, IN_BSTR aValue,
2371 Bstr &aError)
2372{
2373 LogFlowThisFunc (("machine={%s} aKey={%ls} aValue={%ls}\n",
2374 aId.toString().raw(), aKey, aValue));
2375
2376 AutoCaller autoCaller (this);
2377 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2378
2379 CallbackList list;
2380 {
2381 AutoReadLock alock (this);
2382 list = mData.mCallbacks;
2383 }
2384
2385 BOOL allowChange = TRUE;
2386 CallbackList::iterator it = list.begin();
2387 Bstr id = aId.toUtf16();
2388 while ((it != list.end()) && allowChange)
2389 {
2390 HRESULT rc = (*it++)->OnExtraDataCanChange (id, aKey, aValue,
2391 aError.asOutParam(), &allowChange);
2392 if (FAILED (rc))
2393 {
2394 /* if a call to this method fails for some reason (for ex., because
2395 * the other side is dead), we ensure allowChange stays true
2396 * (MS COM RPC implementation seems to zero all output vars before
2397 * issuing an IPC call or after a failure, so it's essential
2398 * there) */
2399 allowChange = TRUE;
2400 }
2401 }
2402
2403 LogFlowThisFunc (("allowChange=%RTbool\n", allowChange));
2404 return allowChange;
2405}
2406
2407/** Event for onExtraDataChange() */
2408struct ExtraDataEvent : public VirtualBox::CallbackEvent
2409{
2410 ExtraDataEvent (VirtualBox *aVB, const Guid &aMachineId,
2411 IN_BSTR aKey, IN_BSTR aVal)
2412 : CallbackEvent (aVB), machineId (aMachineId)
2413 , key (aKey), val (aVal)
2414 {}
2415
2416 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2417 {
2418 LogFlow (("OnExtraDataChange: machineId={%RTuuid}, key='%ls', val='%ls'\n",
2419 machineId.ptr(), key.raw(), val.raw()));
2420 aCallback->OnExtraDataChange (machineId.toUtf16(), key, val);
2421 }
2422
2423 Guid machineId;
2424 Bstr key, val;
2425};
2426
2427/**
2428 * @note Doesn't lock any object.
2429 */
2430void VirtualBox::onExtraDataChange (const Guid &aId, IN_BSTR aKey, IN_BSTR aValue)
2431{
2432 postEvent (new ExtraDataEvent (this, aId, aKey, aValue));
2433}
2434
2435/**
2436 * @note Doesn't lock any object.
2437 */
2438void VirtualBox::onMachineRegistered (const Guid &aId, BOOL aRegistered)
2439{
2440 postEvent (new MachineEvent (this, aId, aRegistered));
2441}
2442
2443/** Event for onSessionStateChange() */
2444struct SessionEvent : public VirtualBox::CallbackEvent
2445{
2446 SessionEvent (VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState)
2447 : CallbackEvent (aVB), machineId (aMachineId), sessionState (aState)
2448 {}
2449
2450 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2451 {
2452 LogFlow (("OnSessionStateChange: machineId={%RTuuid}, sessionState=%d\n",
2453 machineId.ptr(), sessionState));
2454 aCallback->OnSessionStateChange (machineId.toUtf16(), sessionState);
2455 }
2456
2457 Guid machineId;
2458 SessionState_T sessionState;
2459};
2460
2461/**
2462 * @note Doesn't lock any object.
2463 */
2464void VirtualBox::onSessionStateChange (const Guid &aId, SessionState_T aState)
2465{
2466 postEvent (new SessionEvent (this, aId, aState));
2467}
2468
2469/** Event for onSnapshotTaken(), onSnapshotRemoved() and onSnapshotChange() */
2470struct SnapshotEvent : public VirtualBox::CallbackEvent
2471{
2472 enum What { Taken, Discarded, Changed };
2473
2474 SnapshotEvent (VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId,
2475 What aWhat)
2476 : CallbackEvent (aVB)
2477 , what (aWhat)
2478 , machineId (aMachineId), snapshotId (aSnapshotId)
2479 {}
2480
2481 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2482 {
2483 Bstr mid = machineId.toUtf16();
2484 Bstr sid = snapshotId.toUtf16();
2485
2486 switch (what)
2487 {
2488 case Taken:
2489 LogFlow (("OnSnapshotTaken: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2490 machineId.ptr(), snapshotId.ptr()));
2491 aCallback->OnSnapshotTaken (mid, sid);
2492 break;
2493
2494 case Discarded:
2495 LogFlow (("OnSnapshotDiscarded: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2496 machineId.ptr(), snapshotId.ptr()));
2497 aCallback->OnSnapshotDiscarded (mid, sid);
2498 break;
2499
2500 case Changed:
2501 LogFlow (("OnSnapshotChange: machineId={%RTuuid}, snapshotId={%RTuuid}\n",
2502 machineId.ptr(), snapshotId.ptr()));
2503 aCallback->OnSnapshotChange (mid, sid);
2504 break;
2505 }
2506 }
2507
2508 const What what;
2509
2510 Guid machineId;
2511 Guid snapshotId;
2512};
2513
2514/**
2515 * @note Doesn't lock any object.
2516 */
2517void VirtualBox::onSnapshotTaken (const Guid &aMachineId, const Guid &aSnapshotId)
2518{
2519 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Taken));
2520}
2521
2522/**
2523 * @note Doesn't lock any object.
2524 */
2525void VirtualBox::onSnapshotDiscarded (const Guid &aMachineId, const Guid &aSnapshotId)
2526{
2527 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Discarded));
2528}
2529
2530/**
2531 * @note Doesn't lock any object.
2532 */
2533void VirtualBox::onSnapshotChange (const Guid &aMachineId, const Guid &aSnapshotId)
2534{
2535 postEvent (new SnapshotEvent (this, aMachineId, aSnapshotId, SnapshotEvent::Changed));
2536}
2537
2538/** Event for onGuestPropertyChange() */
2539struct GuestPropertyEvent : public VirtualBox::CallbackEvent
2540{
2541 GuestPropertyEvent (VirtualBox *aVBox, const Guid &aMachineId,
2542 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
2543 : CallbackEvent (aVBox), machineId (aMachineId)
2544 , name (aName), value (aValue), flags(aFlags)
2545 {}
2546
2547 void handleCallback (const ComPtr <IVirtualBoxCallback> &aCallback)
2548 {
2549 LogFlow (("OnGuestPropertyChange: machineId={%RTuuid}, name='%ls', value='%ls', flags='%ls'\n",
2550 machineId.ptr(), name.raw(), value.raw(), flags.raw()));
2551 aCallback->OnGuestPropertyChange (machineId.toUtf16(), name, value, flags);
2552 }
2553
2554 Guid machineId;
2555 Bstr name, value, flags;
2556};
2557
2558/**
2559 * @note Doesn't lock any object.
2560 */
2561void VirtualBox::onGuestPropertyChange (const Guid &aMachineId, IN_BSTR aName,
2562 IN_BSTR aValue, IN_BSTR aFlags)
2563{
2564 postEvent (new GuestPropertyEvent (this, aMachineId, aName, aValue, aFlags));
2565}
2566
2567/**
2568 * @note Locks this object for reading.
2569 */
2570ComObjPtr <GuestOSType> VirtualBox::getUnknownOSType()
2571{
2572 ComObjPtr <GuestOSType> type;
2573
2574 AutoCaller autoCaller (this);
2575 AssertComRCReturn (autoCaller.rc(), type);
2576
2577 AutoReadLock alock (this);
2578
2579 /* unknown type must always be the first */
2580 ComAssertRet (mData.mGuestOSTypes.size() > 0, type);
2581
2582 type = mData.mGuestOSTypes.front();
2583 return type;
2584}
2585
2586/**
2587 * Returns the list of opened machines (machines having direct sessions opened
2588 * by client processes) and optionally the list of direct session controls.
2589 *
2590 * @param aMachines Where to put opened machines (will be empty if none).
2591 * @param aControls Where to put direct session controls (optional).
2592 *
2593 * @note The returned lists contain smart pointers. So, clear it as soon as
2594 * it becomes no more necessary to release instances.
2595 *
2596 * @note It can be possible that a session machine from the list has been
2597 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence
2598 * when accessing unprotected data directly.
2599 *
2600 * @note Locks objects for reading.
2601 */
2602void VirtualBox::getOpenedMachines (SessionMachineVector &aMachines,
2603 InternalControlVector *aControls /*= NULL*/)
2604{
2605 AutoCaller autoCaller (this);
2606 AssertComRCReturnVoid (autoCaller.rc());
2607
2608 aMachines.clear();
2609 if (aControls)
2610 aControls->clear();
2611
2612 AutoReadLock alock (this);
2613
2614 for (MachineList::iterator it = mData.mMachines.begin();
2615 it != mData.mMachines.end();
2616 ++ it)
2617 {
2618 ComObjPtr <SessionMachine> sm;
2619 ComPtr <IInternalSessionControl> ctl;
2620 if ((*it)->isSessionOpen (sm, &ctl))
2621 {
2622 aMachines.push_back (sm);
2623 if (aControls)
2624 aControls->push_back (ctl);
2625 }
2626 }
2627}
2628
2629/**
2630 * Searches for a Machine object with the given ID in the collection
2631 * of registered machines.
2632 *
2633 * @param id
2634 * ID of the machine
2635 * @param doSetError
2636 * if TRUE, the appropriate error info is set in case when the machine
2637 * is not found
2638 * @param machine
2639 * where to store the found machine object (can be NULL)
2640 *
2641 * @return
2642 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
2643 *
2644 * @note Locks this object for reading.
2645 */
2646HRESULT VirtualBox::findMachine (const Guid &aId, bool aSetError,
2647 ComObjPtr <Machine> *aMachine /* = NULL */)
2648{
2649 AutoCaller autoCaller (this);
2650 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
2651
2652 bool found = false;
2653
2654 {
2655 AutoReadLock alock (this);
2656
2657 for (MachineList::iterator it = mData.mMachines.begin();
2658 !found && it != mData.mMachines.end();
2659 ++ it)
2660 {
2661 /* sanity */
2662 AutoLimitedCaller machCaller (*it);
2663 AssertComRC (machCaller.rc());
2664
2665 found = (*it)->id() == aId;
2666 if (found && aMachine)
2667 *aMachine = *it;
2668 }
2669 }
2670
2671 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2672
2673 if (aSetError && !found)
2674 {
2675 setError (VBOX_E_OBJECT_NOT_FOUND,
2676 tr ("Could not find a registered machine with UUID {%RTuuid}"),
2677 aId.raw());
2678 }
2679
2680 return rc;
2681}
2682
2683/**
2684 * Searches for a HardDisk object with the given ID or location in the list of
2685 * registered hard disks. If both ID and location are specified, the first
2686 * object that matches either of them (not necessarily both) is returned.
2687 *
2688 * @param aId ID of the hard disk (unused when NULL).
2689 * @param aLocation Full location specification (unused NULL).
2690 * @param aSetError If @c true , the appropriate error info is set in case
2691 * when the hard disk is not found.
2692 * @param aHardDisk Where to store the found hard disk object (can be NULL).
2693 *
2694 * @return S_OK when found or E_INVALIDARG when not found.
2695 *
2696 * @note Locks this object and hard disk objects for reading.
2697 */
2698HRESULT VirtualBox::
2699findHardDisk(const Guid *aId, CBSTR aLocation,
2700 bool aSetError, ComObjPtr<HardDisk> *aHardDisk /*= NULL*/)
2701{
2702 AssertReturn (aId || aLocation, E_INVALIDARG);
2703
2704 AutoReadLock alock (this);
2705
2706 /* first, look up by UUID in the map if UUID is provided */
2707 if (aId)
2708 {
2709 HardDiskMap::const_iterator it = mData.mHardDiskMap.find (*aId);
2710 if (it != mData.mHardDiskMap.end())
2711 {
2712 if (aHardDisk)
2713 *aHardDisk = (*it).second;
2714 return S_OK;
2715 }
2716 }
2717
2718 /* then iterate and search by location */
2719 int result = -1;
2720 if (aLocation)
2721 {
2722 Utf8Str location = aLocation;
2723
2724 for (HardDiskMap::const_iterator it = mData.mHardDiskMap.begin();
2725 it != mData.mHardDiskMap.end();
2726 ++ it)
2727 {
2728 const ComObjPtr<HardDisk> &hd = (*it).second;
2729
2730 HRESULT rc = hd->compareLocationTo (location, result);
2731 CheckComRCReturnRC (rc);
2732
2733 if (result == 0)
2734 {
2735 if (aHardDisk)
2736 *aHardDisk = hd;
2737 break;
2738 }
2739 }
2740 }
2741
2742 HRESULT rc = result == 0 ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2743
2744 if (aSetError && result != 0)
2745 {
2746 if (aId)
2747 setError (rc, tr ("Could not find a hard disk with UUID {%RTuuid} "
2748 "in the media registry ('%ls')"),
2749 aId->raw(), mData.mCfgFile.mName.raw());
2750 else
2751 setError (rc, tr ("Could not find a hard disk with location '%ls' "
2752 "in the media registry ('%ls')"),
2753 aLocation, mData.mCfgFile.mName.raw());
2754 }
2755
2756 return rc;
2757}
2758
2759/**
2760 * Searches for a DVDImage object with the given ID or location in the list of
2761 * registered DVD images. If both ID and file path are specified, the first
2762 * object that matches either of them (not necessarily both) is returned.
2763 *
2764 * @param aId ID of the DVD image (unused when NULL).
2765 * @param aLocation Full path to the image file (unused when NULL).
2766 * @param aSetError If @c true, the appropriate error info is set in case when
2767 * the image is not found.
2768 * @param aImage Where to store the found image object (can be NULL).
2769 *
2770 * @return S_OK when found or E_INVALIDARG when not found.
2771 *
2772 * @note Locks this object and image objects for reading.
2773 */
2774HRESULT VirtualBox::findDVDImage(const Guid *aId, CBSTR aLocation,
2775 bool aSetError,
2776 ComObjPtr<DVDImage> *aImage /* = NULL */)
2777{
2778 AssertReturn (aId || aLocation, E_INVALIDARG);
2779
2780 Utf8Str location;
2781
2782 if (aLocation != NULL)
2783 {
2784 int vrc = calculateFullPath (Utf8Str (aLocation), location);
2785 if (RT_FAILURE (vrc))
2786 return setError (VBOX_E_FILE_ERROR,
2787 tr ("Invalid image file location '%ls' (%Rrc)"),
2788 aLocation, vrc);
2789 }
2790
2791 AutoReadLock alock (this);
2792
2793 bool found = false;
2794
2795 for (DVDImageList::const_iterator it = mData.mDVDImages.begin();
2796 it != mData.mDVDImages.end();
2797 ++ it)
2798 {
2799 /* no AutoCaller, registered image life time is bound to this */
2800 AutoReadLock imageLock (*it);
2801
2802 found = (aId && (*it)->id() == *aId) ||
2803 (aLocation != NULL &&
2804 RTPathCompare (location, Utf8Str ((*it)->locationFull())) == 0);
2805 if (found)
2806 {
2807 if (aImage)
2808 *aImage = *it;
2809 break;
2810 }
2811 }
2812
2813 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2814
2815 if (aSetError && !found)
2816 {
2817 if (aId)
2818 setError (rc, tr ("Could not find a CD/DVD image with UUID {%RTuuid} "
2819 "in the media registry ('%ls')"),
2820 aId->raw(), mData.mCfgFile.mName.raw());
2821 else
2822 setError (rc, tr ("Could not find a CD/DVD image with location '%ls' "
2823 "in the media registry ('%ls')"),
2824 aLocation, mData.mCfgFile.mName.raw());
2825 }
2826
2827 return rc;
2828}
2829
2830/**
2831 * Searches for a FloppyImage object with the given ID or location in the
2832 * collection of registered DVD images. If both ID and file path are specified,
2833 * the first object that matches either of them (not necessarily both) is
2834 * returned.
2835 *
2836 * @param aId ID of the DVD image (unused when NULL).
2837 * @param aLocation Full path to the image file (unused when NULL).
2838 * @param aSetError If @c true, the appropriate error info is set in case when
2839 * the image is not found.
2840 * @param aImage Where to store the found image object (can be NULL).
2841 *
2842 * @return S_OK when found or E_INVALIDARG when not found.
2843 *
2844 * @note Locks this object and image objects for reading.
2845 */
2846HRESULT VirtualBox::findFloppyImage(const Guid *aId, CBSTR aLocation,
2847 bool aSetError,
2848 ComObjPtr<FloppyImage> *aImage /* = NULL */)
2849{
2850 AssertReturn (aId || aLocation, E_INVALIDARG);
2851
2852 Utf8Str location;
2853
2854 if (aLocation != NULL)
2855 {
2856 int vrc = calculateFullPath (Utf8Str (aLocation), location);
2857 if (RT_FAILURE (vrc))
2858 return setError (VBOX_E_FILE_ERROR,
2859 tr ("Invalid image file location '%ls' (%Rrc)"),
2860 aLocation, vrc);
2861 }
2862
2863 AutoReadLock alock (this);
2864
2865 bool found = false;
2866
2867 for (FloppyImageList::const_iterator it = mData.mFloppyImages.begin();
2868 it != mData.mFloppyImages.end();
2869 ++ it)
2870 {
2871 /* no AutoCaller, registered image life time is bound to this */
2872 AutoReadLock imageLock (*it);
2873
2874 found = (aId && (*it)->id() == *aId) ||
2875 (aLocation != NULL &&
2876 RTPathCompare (location, Utf8Str ((*it)->locationFull())) == 0);
2877 if (found)
2878 {
2879 if (aImage)
2880 *aImage = *it;
2881 break;
2882 }
2883 }
2884
2885 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
2886
2887 if (aSetError && !found)
2888 {
2889 if (aId)
2890 setError (rc, tr ("Could not find a floppy image with UUID {%RTuuid} "
2891 "in the media registry ('%ls')"),
2892 aId->raw(), mData.mCfgFile.mName.raw());
2893 else
2894 setError (rc, tr ("Could not find a floppy image with location '%ls' "
2895 "in the media registry ('%ls')"),
2896 aLocation, mData.mCfgFile.mName.raw());
2897 }
2898
2899 return rc;
2900}
2901
2902/**
2903 * Calculates the absolute path of the given path taking the VirtualBox home
2904 * directory as the current directory.
2905 *
2906 * @param aPath Path to calculate the absolute path for.
2907 * @param aResult Where to put the result (used only on success, can be the
2908 * same Utf8Str instance as passed in @a aPath).
2909 * @return IPRT result.
2910 *
2911 * @note Doesn't lock any object.
2912 */
2913int VirtualBox::calculateFullPath (const char *aPath, Utf8Str &aResult)
2914{
2915 AutoCaller autoCaller (this);
2916 AssertComRCReturn (autoCaller.rc(), VERR_GENERAL_FAILURE);
2917
2918 /* no need to lock since mHomeDir is const */
2919
2920 char folder [RTPATH_MAX];
2921 int vrc = RTPathAbsEx (mData.mHomeDir, aPath, folder, sizeof (folder));
2922 if (RT_SUCCESS (vrc))
2923 aResult = folder;
2924
2925 return vrc;
2926}
2927
2928/**
2929 * Tries to calculate the relative path of the given absolute path using the
2930 * directory of the VirtualBox settings file as the base directory.
2931 *
2932 * @param aPath Absolute path to calculate the relative path for.
2933 * @param aResult Where to put the result (used only when it's possible to
2934 * make a relative path from the given absolute path; otherwise
2935 * left untouched).
2936 *
2937 * @note Doesn't lock any object.
2938 */
2939void VirtualBox::calculateRelativePath (const char *aPath, Utf8Str &aResult)
2940{
2941 AutoCaller autoCaller (this);
2942 AssertComRCReturnVoid (autoCaller.rc());
2943
2944 /* no need to lock since mHomeDir is const */
2945
2946 Utf8Str settingsDir = mData.mHomeDir;
2947
2948 if (RTPathStartsWith (aPath, settingsDir))
2949 {
2950 /* when assigning, we create a separate Utf8Str instance because both
2951 * aPath and aResult can point to the same memory location when this
2952 * func is called (if we just do aResult = aPath, aResult will be freed
2953 * first, and since its the same as aPath, an attempt to copy garbage
2954 * will be made. */
2955 aResult = Utf8Str (aPath + settingsDir.length() + 1);
2956 }
2957}
2958
2959// private methods
2960/////////////////////////////////////////////////////////////////////////////
2961
2962/**
2963 * Checks if there is a hard disk, DVD or floppy image with the given ID or
2964 * location already registered.
2965 *
2966 * On return, sets @aConflict to the string describing the conflicting medium,
2967 * or sets it to @c Null if no conflicting media is found. Returns S_OK in
2968 * either case. A failure is unexpected.
2969 *
2970 * @param aId UUID to check.
2971 * @param aLocation Location to check.
2972 * @param aConflict Where to return parameters of the conflicting medium.
2973 *
2974 * @note Locks this object and media objects for reading.
2975 */
2976HRESULT VirtualBox::checkMediaForConflicts2 (const Guid &aId,
2977 const Bstr &aLocation,
2978 Utf8Str &aConflict)
2979{
2980 aConflict.setNull();
2981
2982 AssertReturn (!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL);
2983
2984 AutoReadLock alock (this);
2985
2986 HRESULT rc = S_OK;
2987
2988 {
2989 ComObjPtr<HardDisk> hardDisk;
2990 rc = findHardDisk(&aId, aLocation, false /* aSetError */, &hardDisk);
2991 if (SUCCEEDED (rc))
2992 {
2993 /* Note: no AutoCaller since bound to this */
2994 AutoReadLock mediaLock (hardDisk);
2995 aConflict = Utf8StrFmt (
2996 tr ("hard disk '%ls' with UUID {%RTuuid}"),
2997 hardDisk->locationFull().raw(), hardDisk->id().raw());
2998 return S_OK;
2999 }
3000 }
3001
3002 {
3003 ComObjPtr<DVDImage> image;
3004 rc = findDVDImage (&aId, aLocation, false /* aSetError */, &image);
3005 if (SUCCEEDED (rc))
3006 {
3007 /* Note: no AutoCaller since bound to this */
3008 AutoReadLock mediaLock (image);
3009 aConflict = Utf8StrFmt (
3010 tr ("CD/DVD image '%ls' with UUID {%RTuuid}"),
3011 image->locationFull().raw(), image->id().raw());
3012 return S_OK;
3013 }
3014 }
3015
3016 {
3017 ComObjPtr<FloppyImage> image;
3018 rc = findFloppyImage(&aId, aLocation, false /* aSetError */, &image);
3019 if (SUCCEEDED (rc))
3020 {
3021 /* Note: no AutoCaller since bound to this */
3022 AutoReadLock mediaLock (image);
3023 aConflict = Utf8StrFmt (
3024 tr ("floppy image '%ls' with UUID {%RTuuid}"),
3025 image->locationFull().raw(), image->id().raw());
3026 return S_OK;
3027 }
3028 }
3029
3030 return S_OK;
3031}
3032
3033/**
3034 * Reads in the machine definitions from the configuration loader
3035 * and creates the relevant objects.
3036 *
3037 * @param aGlobal <Global> node.
3038 *
3039 * @note Can be called only from #init().
3040 * @note Doesn't lock anything.
3041 */
3042HRESULT VirtualBox::loadMachines (const settings::Key &aGlobal)
3043{
3044 using namespace settings;
3045
3046 AutoCaller autoCaller (this);
3047 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3048
3049 HRESULT rc = S_OK;
3050
3051 Key::List machines = aGlobal.key ("MachineRegistry").keys ("MachineEntry");
3052 for (Key::List::const_iterator it = machines.begin();
3053 it != machines.end(); ++ it)
3054 {
3055 /* required */
3056 Guid uuid = (*it).value <Guid> ("uuid");
3057 /* required */
3058 Bstr src = (*it).stringValue ("src");
3059
3060 /* create a new machine object */
3061 ComObjPtr <Machine> machine;
3062 rc = machine.createObject();
3063 if (SUCCEEDED (rc))
3064 {
3065 /* initialize the machine object and register it */
3066 rc = machine->init (this, src, Machine::Init_Registered,
3067 NULL, NULL, FALSE, &uuid);
3068 if (SUCCEEDED (rc))
3069 rc = registerMachine (machine);
3070 }
3071 }
3072
3073 return rc;
3074}
3075
3076/**
3077 * Reads in the media registration entries from the global settings file
3078 * and creates the relevant objects.
3079 *
3080 * @param aGlobal <Global> node
3081 *
3082 * @note Can be called only from #init().
3083 * @note Doesn't lock anything.
3084 */
3085HRESULT VirtualBox::loadMedia (const settings::Key &aGlobal)
3086{
3087 using namespace settings;
3088
3089 AutoCaller autoCaller (this);
3090 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3091
3092 HRESULT rc = S_OK;
3093
3094 Key registry = aGlobal.key ("MediaRegistry");
3095
3096 const char *kMediaNodes[] = { "HardDisks", "DVDImages", "FloppyImages" };
3097
3098 for (size_t n = 0; n < RT_ELEMENTS (kMediaNodes); ++ n)
3099 {
3100 /* All three media nodes are optional */
3101 Key node = registry.findKey (kMediaNodes [n]);
3102 if (node.isNull())
3103 continue;
3104
3105 if (n == 0)
3106 {
3107 Key::List hardDisks = node.keys ("HardDisk");
3108 for (Key::List::const_iterator it = hardDisks.begin();
3109 it != hardDisks.end(); ++ it)
3110 {
3111 ComObjPtr<HardDisk> hardDisk;
3112 hardDisk.createObject();
3113 rc = hardDisk->init(this, NULL, *it);
3114 CheckComRCBreakRC (rc);
3115
3116 rc = registerHardDisk(hardDisk, false /* aSaveRegistry */);
3117 CheckComRCBreakRC (rc);
3118 }
3119
3120 continue;
3121 }
3122
3123 CheckComRCBreakRC (rc);
3124
3125 Key::List images = node.keys ("Image");
3126 for (Key::List::const_iterator it = images.begin();
3127 it != images.end(); ++ it)
3128 {
3129 switch (n)
3130 {
3131 case 1: /* DVDImages */
3132 {
3133 ComObjPtr<DVDImage> image;
3134 image.createObject();
3135 rc = image->init (this, *it);
3136 CheckComRCBreakRC (rc);
3137
3138 rc = registerDVDImage (image, false /* aSaveRegistry */);
3139 CheckComRCBreakRC (rc);
3140
3141 break;
3142 }
3143 case 2: /* FloppyImages */
3144 {
3145 ComObjPtr<FloppyImage> image;
3146 image.createObject();
3147 rc = image->init (this, *it);
3148 CheckComRCBreakRC (rc);
3149
3150 rc = registerFloppyImage (image, false /* aSaveRegistry */);
3151 CheckComRCBreakRC (rc);
3152
3153 break;
3154 }
3155 default:
3156 AssertFailed();
3157 }
3158
3159 CheckComRCBreakRC (rc);
3160 }
3161
3162 CheckComRCBreakRC (rc);
3163 }
3164
3165 return rc;
3166}
3167
3168/**
3169 * Reads in the network service registration entries from the global settings file
3170 * and creates the relevant objects.
3171 *
3172 * @param aGlobal <Global> node
3173 *
3174 * @note Can be called only from #init().
3175 * @note Doesn't lock anything.
3176 */
3177HRESULT VirtualBox::loadNetservices (const settings::Key &aGlobal)
3178{
3179 using namespace settings;
3180
3181 AutoCaller autoCaller (this);
3182 AssertReturn (autoCaller.state() == InInit, E_FAIL);
3183
3184 HRESULT rc = S_OK;
3185
3186 Key registry = aGlobal.findKey ("NetserviceRegistry");
3187 if(registry.isNull())
3188 return S_OK;
3189
3190 const char *kMediaNodes[] = { "DHCPServers" };
3191
3192 for (size_t n = 0; n < RT_ELEMENTS (kMediaNodes); ++ n)
3193 {
3194 /* All three media nodes are optional */
3195 Key node = registry.findKey (kMediaNodes [n]);
3196 if (node.isNull())
3197 continue;
3198
3199 if (n == 0)
3200 {
3201 Key::List dhcpServers = node.keys ("DHCPServer");
3202 for (Key::List::const_iterator it = dhcpServers.begin();
3203 it != dhcpServers.end(); ++ it)
3204 {
3205 ComObjPtr<DHCPServer> dhcpServer;
3206 dhcpServer.createObject();
3207 rc = dhcpServer->init (this, *it);
3208 CheckComRCBreakRC (rc);
3209
3210 rc = registerDHCPServer(dhcpServer, false /* aSaveRegistry */);
3211 CheckComRCBreakRC (rc);
3212 }
3213
3214 continue;
3215 }
3216 }
3217 return rc;
3218}
3219
3220/**
3221 * Helper function to write out the configuration tree.
3222 *
3223 * @note Locks this object for writing and child objects for reading/writing!
3224 */
3225HRESULT VirtualBox::saveSettings()
3226{
3227 AutoCaller autoCaller (this);
3228 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3229
3230 AssertReturn (!!mData.mCfgFile.mName, E_FAIL);
3231
3232 HRESULT rc = S_OK;
3233
3234 /* serialize file access (prevents concurrent reads and writes) */
3235 AutoWriteLock alock (this);
3236
3237 try
3238 {
3239 using namespace settings;
3240 using namespace xml;
3241
3242 /* load the settings file */
3243 File file (mData.mCfgFile.mHandle, Utf8Str (mData.mCfgFile.mName));
3244 XmlTreeBackend tree;
3245
3246 rc = VirtualBox::loadSettingsTree_ForUpdate (tree, file);
3247 CheckComRCThrowRC (rc);
3248
3249 Key global = tree.rootKey().createKey ("Global");
3250
3251 /* machines */
3252 {
3253 /* first, delete the entire machine registry */
3254 Key registryNode = global.findKey ("MachineRegistry");
3255 if (!registryNode.isNull())
3256 registryNode.zap();
3257 /* then, recreate it */
3258 registryNode = global.createKey ("MachineRegistry");
3259
3260 /* write out the machines */
3261 for (MachineList::iterator it = mData.mMachines.begin();
3262 it != mData.mMachines.end();
3263 ++ it)
3264 {
3265 Key entryNode = registryNode.appendKey ("MachineEntry");
3266 rc = (*it)->saveRegistryEntry (entryNode);
3267 CheckComRCThrowRC (rc);
3268 }
3269 }
3270
3271 /* media */
3272 {
3273 /* first, delete the entire media registry */
3274 Key registryNode = global.findKey ("MediaRegistry");
3275 if (!registryNode.isNull())
3276 registryNode.zap();
3277 /* then, recreate it */
3278 registryNode = global.createKey ("MediaRegistry");
3279
3280 /* hard disks */
3281 {
3282 Key hardDisksNode = registryNode.createKey ("HardDisks");
3283
3284 for (HardDiskList::const_iterator it =
3285 mData.mHardDisks.begin();
3286 it != mData.mHardDisks.end();
3287 ++ it)
3288 {
3289 rc = (*it)->saveSettings (hardDisksNode);
3290 CheckComRCThrowRC (rc);
3291 }
3292 }
3293
3294 /* CD/DVD images */
3295 {
3296 Key imagesNode = registryNode.createKey ("DVDImages");
3297
3298 for (DVDImageList::const_iterator it =
3299 mData.mDVDImages.begin();
3300 it != mData.mDVDImages.end();
3301 ++ it)
3302 {
3303 rc = (*it)->saveSettings (imagesNode);
3304 CheckComRCThrowRC (rc);
3305 }
3306 }
3307
3308 /* floppy images */
3309 {
3310 Key imagesNode = registryNode.createKey ("FloppyImages");
3311
3312 for (FloppyImageList::const_iterator it =
3313 mData.mFloppyImages.begin();
3314 it != mData.mFloppyImages.end();
3315 ++ it)
3316 {
3317 rc = (*it)->saveSettings (imagesNode);
3318 CheckComRCThrowRC (rc);
3319 }
3320 }
3321 }
3322
3323 /* netservices */
3324 {
3325 /* first, delete the entire netservice registry */
3326 Key registryNode = global.findKey ("NetserviceRegistry");
3327 if (!registryNode.isNull())
3328 registryNode.zap();
3329 /* then, recreate it */
3330 registryNode = global.createKey ("NetserviceRegistry");
3331
3332 /* hard disks */
3333 {
3334 Key dhcpServersNode = registryNode.createKey ("DHCPServers");
3335
3336 for (DHCPServerList::const_iterator it =
3337 mData.mDHCPServers.begin();
3338 it != mData.mDHCPServers.end();
3339 ++ it)
3340 {
3341 rc = (*it)->saveSettings (dhcpServersNode);
3342 CheckComRCThrowRC (rc);
3343 }
3344 }
3345 }
3346
3347 /* host data (USB filters) */
3348 rc = mData.mHost->saveSettings (global);
3349 CheckComRCThrowRC (rc);
3350
3351 rc = mData.mSystemProperties->saveSettings (global);
3352 CheckComRCThrowRC (rc);
3353
3354 /* save the settings on success */
3355 rc = VirtualBox::saveSettingsTree (tree, file,
3356 mData.mSettingsFileVersion);
3357 CheckComRCThrowRC (rc);
3358 }
3359 catch (HRESULT err)
3360 {
3361 /* we assume that error info is set by the thrower */
3362 rc = err;
3363 }
3364 catch (...)
3365 {
3366 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
3367 }
3368
3369 return rc;
3370}
3371
3372/**
3373 * Helper to register the machine.
3374 *
3375 * When called during VirtualBox startup, adds the given machine to the
3376 * collection of registered machines. Otherwise tries to mark the machine
3377 * as registered, and, if succeeded, adds it to the collection and
3378 * saves global settings.
3379 *
3380 * @note The caller must have added itself as a caller of the @a aMachine
3381 * object if calls this method not on VirtualBox startup.
3382 *
3383 * @param aMachine machine to register
3384 *
3385 * @note Locks objects!
3386 */
3387HRESULT VirtualBox::registerMachine (Machine *aMachine)
3388{
3389 ComAssertRet (aMachine, E_INVALIDARG);
3390
3391 AutoCaller autoCaller (this);
3392 CheckComRCReturnRC (autoCaller.rc());
3393
3394 AutoWriteLock alock (this);
3395
3396 HRESULT rc = S_OK;
3397
3398 {
3399 ComObjPtr <Machine> m;
3400 rc = findMachine (aMachine->id(), false /* aDoSetError */, &m);
3401 if (SUCCEEDED (rc))
3402 {
3403 /* sanity */
3404 AutoLimitedCaller machCaller (m);
3405 AssertComRC (machCaller.rc());
3406
3407 return setError (E_INVALIDARG,
3408 tr ("Registered machine with UUID {%RTuuid} ('%ls') already exists"),
3409 aMachine->id().raw(), m->settingsFileFull().raw());
3410 }
3411
3412 ComAssertRet (rc == VBOX_E_OBJECT_NOT_FOUND, rc);
3413 rc = S_OK;
3414 }
3415
3416 if (autoCaller.state() != InInit)
3417 {
3418 /* Machine::trySetRegistered() will commit and save machine settings */
3419 rc = aMachine->trySetRegistered (TRUE);
3420 CheckComRCReturnRC (rc);
3421 }
3422
3423 /* add to the collection of registered machines */
3424 mData.mMachines.push_back (aMachine);
3425
3426 if (autoCaller.state() != InInit)
3427 rc = saveSettings();
3428
3429 return rc;
3430}
3431
3432/**
3433 * Remembers the given hard disk by storing it in the hard disk registry.
3434 *
3435 * @param aHardDisk Hard disk object to remember.
3436 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3437 *
3438 * When @a aSaveRegistry is @c true, this operation may fail because of the
3439 * failed #saveSettings() method it calls. In this case, the hard disk object
3440 * will not be remembered. It is therefore the responsibility of the caller to
3441 * call this method as the last step of some action that requires registration
3442 * in order to make sure that only fully functional hard disk objects get
3443 * registered.
3444 *
3445 * @note Locks this object for writing and @a aHardDisk for reading.
3446 */
3447HRESULT VirtualBox::registerHardDisk(HardDisk *aHardDisk,
3448 bool aSaveRegistry /*= true*/)
3449{
3450 AssertReturn (aHardDisk != NULL, E_INVALIDARG);
3451
3452 AutoCaller autoCaller (this);
3453 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3454
3455 AutoWriteLock alock (this);
3456
3457 AutoCaller hardDiskCaller (aHardDisk);
3458 AssertComRCReturn (hardDiskCaller.rc(), hardDiskCaller.rc());
3459
3460 AutoReadLock hardDiskLock (aHardDisk);
3461
3462 Utf8Str conflict;
3463 HRESULT rc = checkMediaForConflicts2 (aHardDisk->id(),
3464 aHardDisk->locationFull(),
3465 conflict);
3466 CheckComRCReturnRC (rc);
3467
3468 if (!conflict.isNull())
3469 {
3470 return setError (E_INVALIDARG,
3471 tr ("Cannot register the hard disk '%ls' with UUID {%RTuuid} "
3472 "because a %s already exists in the media registry ('%ls')"),
3473 aHardDisk->locationFull().raw(), aHardDisk->id().raw(),
3474 conflict.raw(), mData.mCfgFile.mName.raw());
3475 }
3476
3477 if (aHardDisk->parent().isNull())
3478 {
3479 /* base (root) hard disk */
3480 mData.mHardDisks.push_back (aHardDisk);
3481 }
3482
3483 mData.mHardDiskMap
3484 .insert (HardDiskMap::value_type (
3485 aHardDisk->id(), HardDiskMap::mapped_type (aHardDisk)));
3486
3487 if (aSaveRegistry)
3488 {
3489 rc = saveSettings();
3490 if (FAILED (rc))
3491 unregisterHardDisk(aHardDisk, false /* aSaveRegistry */);
3492 }
3493
3494 return rc;
3495}
3496
3497/**
3498 * Removes the given hard disk from the hard disk registry.
3499 *
3500 * @param aHardDisk Hard disk object to remove.
3501 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3502 *
3503 * When @a aSaveRegistry is @c true, this operation may fail because of the
3504 * failed #saveSettings() method it calls. In this case, the hard disk object
3505 * will NOT be removed from the registry when this method returns. It is
3506 * therefore the responsibility of the caller to call this method as the first
3507 * step of some action that requires unregistration, before calling uninit() on
3508 * @a aHardDisk.
3509 *
3510 * @note Locks this object for writing and @a aHardDisk for reading.
3511 */
3512HRESULT VirtualBox::unregisterHardDisk(HardDisk *aHardDisk,
3513 bool aSaveRegistry /*= true*/)
3514{
3515 AssertReturn (aHardDisk != NULL, E_INVALIDARG);
3516
3517 AutoCaller autoCaller (this);
3518 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3519
3520 AutoWriteLock alock (this);
3521
3522 AutoCaller hardDiskCaller (aHardDisk);
3523 AssertComRCReturn (hardDiskCaller.rc(), hardDiskCaller.rc());
3524
3525 AutoReadLock hardDiskLock (aHardDisk);
3526
3527 size_t cnt = mData.mHardDiskMap.erase (aHardDisk->id());
3528 Assert (cnt == 1);
3529 NOREF(cnt);
3530
3531 if (aHardDisk->parent().isNull())
3532 {
3533 /* base (root) hard disk */
3534 mData.mHardDisks.remove (aHardDisk);
3535 }
3536
3537 HRESULT rc = S_OK;
3538
3539 if (aSaveRegistry)
3540 {
3541 rc = saveSettings();
3542 if (FAILED (rc))
3543 registerHardDisk(aHardDisk, false /* aSaveRegistry */);
3544 }
3545
3546 return rc;
3547}
3548
3549/**
3550 * Remembers the given image by storing it in the CD/DVD image registry.
3551 *
3552 * @param aImage Image object to remember.
3553 * @param aSaveRegistry @c true to save the image registry to disk (default).
3554 *
3555 * When @a aSaveRegistry is @c true, this operation may fail because of the
3556 * failed #saveSettings() method it calls. In this case, the image object
3557 * will not be remembered. It is therefore the responsibility of the caller to
3558 * call this method as the last step of some action that requires registration
3559 * in order to make sure that only fully functional image objects get
3560 * registered.
3561 *
3562 * @note Locks this object for writing and @a aImage for reading.
3563 */
3564HRESULT VirtualBox::registerDVDImage (DVDImage *aImage,
3565 bool aSaveRegistry /*= true*/)
3566{
3567 AssertReturn (aImage != NULL, E_INVALIDARG);
3568
3569 AutoCaller autoCaller (this);
3570 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3571
3572 AutoWriteLock alock (this);
3573
3574 AutoCaller imageCaller (aImage);
3575 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3576
3577 AutoReadLock imageLock (aImage);
3578
3579 Utf8Str conflict;
3580 HRESULT rc = checkMediaForConflicts2 (aImage->id(), aImage->locationFull(),
3581 conflict);
3582 CheckComRCReturnRC (rc);
3583
3584 if (!conflict.isNull())
3585 {
3586 return setError (VBOX_E_INVALID_OBJECT_STATE,
3587 tr ("Cannot register the CD/DVD image '%ls' with UUID {%RTuuid} "
3588 "because a %s already exists in the media registry ('%ls')"),
3589 aImage->locationFull().raw(), aImage->id().raw(),
3590 conflict.raw(), mData.mCfgFile.mName.raw());
3591 }
3592
3593 /* add to the collection */
3594 mData.mDVDImages.push_back (aImage);
3595
3596 if (aSaveRegistry)
3597 {
3598 rc = saveSettings();
3599 if (FAILED (rc))
3600 unregisterDVDImage (aImage, false /* aSaveRegistry */);
3601 }
3602
3603 return rc;
3604}
3605
3606/**
3607 * Removes the given image from the CD/DVD image registry registry.
3608 *
3609 * @param aImage Image object to remove.
3610 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3611 *
3612 * When @a aSaveRegistry is @c true, this operation may fail because of the
3613 * failed #saveSettings() method it calls. In this case, the image object
3614 * will NOT be removed from the registry when this method returns. It is
3615 * therefore the responsibility of the caller to call this method as the first
3616 * step of some action that requires unregistration, before calling uninit() on
3617 * @a aImage.
3618 *
3619 * @note Locks this object for writing and @a aImage for reading.
3620 */
3621HRESULT VirtualBox::unregisterDVDImage (DVDImage *aImage,
3622 bool aSaveRegistry /*= true*/)
3623{
3624 AssertReturn (aImage != NULL, E_INVALIDARG);
3625
3626 AutoCaller autoCaller (this);
3627 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3628
3629 AutoWriteLock alock (this);
3630
3631 AutoCaller imageCaller (aImage);
3632 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3633
3634 AutoReadLock imageLock (aImage);
3635
3636 mData.mDVDImages.remove (aImage);
3637
3638 HRESULT rc = S_OK;
3639
3640 if (aSaveRegistry)
3641 {
3642 rc = saveSettings();
3643 if (FAILED (rc))
3644 registerDVDImage (aImage, false /* aSaveRegistry */);
3645 }
3646
3647 return rc;
3648}
3649
3650/**
3651 * Remembers the given image by storing it in the floppy image registry.
3652 *
3653 * @param aImage Image object to remember.
3654 * @param aSaveRegistry @c true to save the image registry to disk (default).
3655 *
3656 * When @a aSaveRegistry is @c true, this operation may fail because of the
3657 * failed #saveSettings() method it calls. In this case, the image object
3658 * will not be remembered. It is therefore the responsibility of the caller to
3659 * call this method as the last step of some action that requires registration
3660 * in order to make sure that only fully functional image objects get
3661 * registered.
3662 *
3663 * @note Locks this object for writing and @a aImage for reading.
3664 */
3665HRESULT VirtualBox::registerFloppyImage(FloppyImage *aImage,
3666 bool aSaveRegistry /*= true*/)
3667{
3668 AssertReturn (aImage != NULL, E_INVALIDARG);
3669
3670 AutoCaller autoCaller (this);
3671 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3672
3673 AutoWriteLock alock (this);
3674
3675 AutoCaller imageCaller (aImage);
3676 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3677
3678 AutoReadLock imageLock (aImage);
3679
3680 Utf8Str conflict;
3681 HRESULT rc = checkMediaForConflicts2 (aImage->id(), aImage->locationFull(),
3682 conflict);
3683 CheckComRCReturnRC (rc);
3684
3685 if (!conflict.isNull())
3686 {
3687 return setError (VBOX_E_INVALID_OBJECT_STATE,
3688 tr ("Cannot register the floppy image '%ls' with UUID {%RTuuid} "
3689 "because a %s already exists in the media registry ('%ls')"),
3690 aImage->locationFull().raw(), aImage->id().raw(),
3691 conflict.raw(), mData.mCfgFile.mName.raw());
3692 }
3693
3694 /* add to the collection */
3695 mData.mFloppyImages.push_back (aImage);
3696
3697 if (aSaveRegistry)
3698 {
3699 rc = saveSettings();
3700 if (FAILED (rc))
3701 unregisterFloppyImage (aImage, false /* aSaveRegistry */);
3702 }
3703
3704 return rc;
3705}
3706
3707/**
3708 * Removes the given image from the floppy image registry registry.
3709 *
3710 * @param aImage Image object to remove.
3711 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
3712 *
3713 * When @a aSaveRegistry is @c true, this operation may fail because of the
3714 * failed #saveSettings() method it calls. In this case, the image object
3715 * will NOT be removed from the registry when this method returns. It is
3716 * therefore the responsibility of the caller to call this method as the first
3717 * step of some action that requires unregistration, before calling uninit() on
3718 * @a aImage.
3719 *
3720 * @note Locks this object for writing and @a aImage for reading.
3721 */
3722HRESULT VirtualBox::unregisterFloppyImage(FloppyImage *aImage,
3723 bool aSaveRegistry /*= true*/)
3724{
3725 AssertReturn (aImage != NULL, E_INVALIDARG);
3726
3727 AutoCaller autoCaller (this);
3728 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3729
3730 AutoWriteLock alock (this);
3731
3732 AutoCaller imageCaller (aImage);
3733 AssertComRCReturn (imageCaller.rc(), imageCaller.rc());
3734
3735 AutoReadLock imageLock (aImage);
3736
3737 mData.mFloppyImages.remove (aImage);
3738
3739 HRESULT rc = S_OK;
3740
3741 if (aSaveRegistry)
3742 {
3743 rc = saveSettings();
3744 if (FAILED (rc))
3745 registerFloppyImage (aImage, false /* aSaveRegistry */);
3746 }
3747
3748 return rc;
3749}
3750
3751/**
3752 * Attempts to cast from a raw interface pointer to an underlying object.
3753 * On success, @a aTo will contain the object reference. On failure, @a aTo will
3754 * be set to @c null and an extended error info will be returned.
3755 *
3756 * @param aFrom Interface pointer to cast from.
3757 * @param aTo Where to store a reference to the underlying object.
3758 *
3759 * @note Locks #childrenLock() for reading.
3760 */
3761HRESULT VirtualBox::cast (IHardDisk *aFrom, ComObjPtr <HardDisk> &aTo)
3762{
3763 AssertReturn (aFrom != NULL, E_INVALIDARG);
3764
3765 AutoCaller autoCaller (this);
3766 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3767
3768 /* We need the children map lock here to keep the getDependentChild() result
3769 * valid until we finish */
3770 AutoReadLock chLock (childrenLock());
3771
3772 VirtualBoxBase *child = getDependentChild (aFrom);
3773 if (!child)
3774 return setError (E_FAIL, tr ("The given hard disk object is not created "
3775 "within this VirtualBox instance"));
3776
3777 /* we can safely cast child to HardDisk * here because only HardDisk
3778 * implementations of IHardDisk can be among our children */
3779
3780 aTo = static_cast<HardDisk*>(child);
3781
3782 return S_OK;
3783}
3784
3785/**
3786 * Helper to update the global settings file when the name of some machine
3787 * changes so that file and directory renaming occurs. This method ensures that
3788 * all affected paths in the disk registry are properly updated.
3789 *
3790 * @param aOldPath Old path (full).
3791 * @param aNewPath New path (full).
3792 *
3793 * @note Locks this object + DVD, Floppy and HardDisk children for writing.
3794 */
3795HRESULT VirtualBox::updateSettings (const char *aOldPath, const char *aNewPath)
3796{
3797 LogFlowThisFunc (("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
3798
3799 AssertReturn (aOldPath, E_INVALIDARG);
3800 AssertReturn (aNewPath, E_INVALIDARG);
3801
3802 AutoCaller autoCaller (this);
3803 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
3804
3805 AutoWriteLock alock (this);
3806
3807 /* check DVD paths */
3808 for (DVDImageList::iterator it = mData.mDVDImages.begin();
3809 it != mData.mDVDImages.end();
3810 ++ it)
3811 {
3812 (*it)->updatePath (aOldPath, aNewPath);
3813 }
3814
3815 /* check Floppy paths */
3816 for (FloppyImageList::iterator it = mData.mFloppyImages.begin();
3817 it != mData.mFloppyImages .end();
3818 ++ it)
3819 {
3820 (*it)->updatePath (aOldPath, aNewPath);
3821 }
3822
3823 /* check HardDisk paths */
3824 for (HardDiskList::const_iterator it = mData.mHardDisks.begin();
3825 it != mData.mHardDisks.end();
3826 ++ it)
3827 {
3828 (*it)->updatePaths (aOldPath, aNewPath);
3829 }
3830
3831 HRESULT rc = saveSettings();
3832
3833 return rc;
3834}
3835
3836/**
3837 * Creates the path to the specified file according to the path information
3838 * present in the file name.
3839 *
3840 * Note that the given file name must contain the full path otherwise the
3841 * extracted relative path will be created based on the current working
3842 * directory which is normally unknown.
3843 *
3844 * @param aFileName Full file name which path needs to be created.
3845 *
3846 * @return Extended error information on failure to create the path.
3847 */
3848/* static */
3849HRESULT VirtualBox::ensureFilePathExists (const char *aFileName)
3850{
3851 Utf8Str dir = aFileName;
3852 RTPathStripFilename (dir.mutableRaw());
3853 if (!RTDirExists (dir))
3854 {
3855 int vrc = RTDirCreateFullPath (dir, 0777);
3856 if (RT_FAILURE (vrc))
3857 {
3858 return setError (E_FAIL,
3859 tr ("Could not create the directory '%s' (%Rrc)"),
3860 dir.raw(), vrc);
3861 }
3862 }
3863
3864 return S_OK;
3865}
3866
3867/**
3868 * Helper method to load the setting tree and turn expected exceptions into
3869 * COM errors, according to arguments.
3870 *
3871 * Note that this method will not catch unexpected errors so it may still
3872 * throw something.
3873 *
3874 * @param aTree Tree to load into settings.
3875 * @param aFile File to load settings from.
3876 * @param aValidate @c @true to enable tree validation.
3877 * @param aCatchLoadErrors @c true to catch exceptions caused by file
3878 * access or validation errors.
3879 * @param aAddDefaults @c true to cause the substitution of default
3880 * values for missing attributes that have
3881 * defaults in the XML schema.
3882 * @param aFormatVersion Where to store the current format version of the
3883 * loaded settings tree (optional, may be NULL).
3884 */
3885/* static */
3886HRESULT VirtualBox::loadSettingsTree (settings::XmlTreeBackend &aTree,
3887 xml::File &aFile,
3888 bool aValidate,
3889 bool aCatchLoadErrors,
3890 bool aAddDefaults,
3891 Utf8Str *aFormatVersion /* = NULL */)
3892{
3893 using namespace settings;
3894
3895 try
3896 {
3897 SettingsTreeHelper helper = SettingsTreeHelper();
3898
3899 aTree.setInputResolver (helper);
3900 aTree.setAutoConverter (helper);
3901
3902 aTree.read (aFile, aValidate ? VBOX_XML_SCHEMA : NULL,
3903 aAddDefaults ? XmlTreeBackend::Read_AddDefaults : 0);
3904
3905 aTree.resetAutoConverter();
3906 aTree.resetInputResolver();
3907
3908 /* on success, memorize the current settings file version or set it to
3909 * the most recent version if no settings conversion took place. Note
3910 * that it's not necessary to do it every time we load the settings file
3911 * (i.e. only loadSettingsTree_FirstTime() passes a non-NULL
3912 * aFormatVersion value) because currently we keep the settings
3913 * files locked so that the only legal way to change the format version
3914 * while VirtualBox is running is saveSettingsTree(). */
3915 if (aFormatVersion != NULL)
3916 {
3917 *aFormatVersion = aTree.oldVersion();
3918 if (aFormatVersion->isNull())
3919 *aFormatVersion = VBOX_XML_VERSION_FULL;
3920 }
3921 }
3922 catch (const xml::EIPRTFailure &err)
3923 {
3924 if (!aCatchLoadErrors)
3925 throw;
3926
3927 return setError (VBOX_E_FILE_ERROR,
3928 tr ("Could not load the settings file '%s' (%Rrc)"),
3929 aFile.uri(), err.rc());
3930 }
3931 catch (const xml::RuntimeError &err)
3932 {
3933 Assert (err.what() != NULL);
3934
3935 if (!aCatchLoadErrors)
3936 throw;
3937
3938 return setError (VBOX_E_XML_ERROR,
3939 tr ("Could not load the settings file '%s'.\n%s"),
3940 aFile.uri(),
3941 err.what() ? err.what() : "Unknown error");
3942 }
3943
3944 return S_OK;
3945}
3946
3947/**
3948 * Helper method to save the settings tree and turn expected exceptions to COM
3949 * errors.
3950 *
3951 * Note that this method will not catch unexpected errors so it may still
3952 * throw something.
3953 *
3954 * @param aTree Tree to save.
3955 * @param aFile File to save the tree to.
3956 * @param aFormatVersion Where to store the (recent) format version of the
3957 * saved settings tree on success.
3958 */
3959/* static */
3960HRESULT VirtualBox::saveSettingsTree (settings::TreeBackend &aTree,
3961 xml::File &aFile,
3962 Utf8Str &aFormatVersion)
3963{
3964 using namespace settings;
3965
3966 try
3967 {
3968 aTree.write (aFile);
3969
3970 /* set the current settings file version to the most recent version on
3971 * success. See also VirtualBox::loadSettingsTree(). */
3972 if (aFormatVersion != VBOX_XML_VERSION_FULL)
3973 aFormatVersion = VBOX_XML_VERSION_FULL;
3974 }
3975 catch (const xml::EIPRTFailure &err)
3976 {
3977 /* this is the only expected exception for now */
3978 return setError (VBOX_E_FILE_ERROR,
3979 tr ("Could not save the settings file '%s' (%Rrc)"),
3980 aFile.uri(), err.rc());
3981 }
3982
3983 return S_OK;
3984}
3985
3986/**
3987 * Creates a backup copy of the given settings file by suffixing it with the
3988 * supplied version format string and optionally with numbers from .0 to .9
3989 * if the backup file already exists.
3990 *
3991 * @param aFileName Original settings file name.
3992 * @param aOldFormat Version of the original format.
3993 * @param aBakFileName File name of the created backup copy (only on success).
3994 */
3995/* static */
3996HRESULT VirtualBox::backupSettingsFile (const Bstr &aFileName,
3997 const Utf8Str &aOldFormat,
3998 Bstr &aBakFileName)
3999{
4000 Utf8Str of = aFileName;
4001 Utf8Str nf = Utf8StrFmt ("%s.%s.bak", of.raw(), aOldFormat.raw());
4002
4003 int vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
4004 NULL, NULL);
4005
4006 /* try progressive suffix from .0 to .9 on failure */
4007 if (vrc == VERR_ALREADY_EXISTS)
4008 {
4009 Utf8Str tmp = nf;
4010 for (int i = 0; i <= 9 && RT_FAILURE (vrc); ++ i)
4011 {
4012 nf = Utf8StrFmt ("%s.%d", tmp.raw(), i);
4013 vrc = RTFileCopyEx (of, nf, RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE,
4014 NULL, NULL);
4015 }
4016 }
4017
4018 if (RT_FAILURE (vrc))
4019 return setError (VBOX_E_IPRT_ERROR,
4020 tr ("Could not copy the settings file '%s' to '%s' (%Rrc)"),
4021 of.raw(), nf.raw(), vrc);
4022
4023 aBakFileName = nf;
4024
4025 return S_OK;
4026}
4027
4028/**
4029 * Handles unexpected exceptions by turning them into COM errors in release
4030 * builds or by hitting a breakpoint in the release builds.
4031 *
4032 * Usage pattern:
4033 * @code
4034 try
4035 {
4036 // ...
4037 }
4038 catch (LaLalA)
4039 {
4040 // ...
4041 }
4042 catch (...)
4043 {
4044 rc = VirtualBox::handleUnexpectedExceptions (RT_SRC_POS);
4045 }
4046 * @endcode
4047 *
4048 * @param RT_SRC_POS_DECL "RT_SRC_POS" macro instantiation.
4049 */
4050/* static */
4051HRESULT VirtualBox::handleUnexpectedExceptions (RT_SRC_POS_DECL)
4052{
4053 try
4054 {
4055 /* re-throw the current exception */
4056 throw;
4057 }
4058 catch (const std::exception &err)
4059 {
4060 ComAssertMsgFailedPos (("Unexpected exception '%s' (%s)",
4061 typeid (err).name(), err.what()),
4062 pszFile, iLine, pszFunction);
4063 return E_FAIL;
4064 }
4065 catch (...)
4066 {
4067 ComAssertMsgFailedPos (("Unknown exception"),
4068 pszFile, iLine, pszFunction);
4069 return E_FAIL;
4070 }
4071
4072 /* should not get here */
4073 AssertFailed();
4074 return E_FAIL;
4075}
4076
4077/**
4078 * Helper to lock the VirtualBox configuration for write access.
4079 *
4080 * @note This method is not thread safe (must be called only from #init()
4081 * or #uninit()).
4082 *
4083 * @note If the configuration file is not found, the method returns
4084 * S_OK, but subsequent #isConfigLocked() will return FALSE. This is used
4085 * in some places to determine the (valid) situation when no config file
4086 * exists yet, and therefore a new one should be created from scratch.
4087 */
4088HRESULT VirtualBox::lockConfig()
4089{
4090 AutoCaller autoCaller (this);
4091 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4092 AssertReturn (autoCaller.state() == InInit, E_FAIL);
4093
4094 HRESULT rc = S_OK;
4095
4096 Assert (!isConfigLocked());
4097 if (!isConfigLocked())
4098 {
4099 /* Open the associated config file. */
4100 int vrc = RTFileOpen (&mData.mCfgFile.mHandle,
4101 Utf8Str (mData.mCfgFile.mName),
4102 RTFILE_O_READWRITE | RTFILE_O_OPEN |
4103 RTFILE_O_DENY_WRITE);
4104 if (RT_FAILURE (vrc) && (vrc != VERR_FILE_NOT_FOUND))
4105 {
4106 /* Open the associated config file only with read access. */
4107 vrc = RTFileOpen (&mData.mCfgFile.mHandle,
4108 Utf8Str (mData.mCfgFile.mName),
4109 RTFILE_O_READ | RTFILE_O_OPEN |
4110 RTFILE_O_DENY_NONE);
4111 if (RT_FAILURE (vrc))
4112 {
4113 /* We even cannot open it in read mode, so there's seriously
4114 something wrong. */
4115 rc = setError (E_FAIL,
4116 tr ("Could not even open settings file '%ls' in read mode (%Rrc)"),
4117 mData.mCfgFile.mName.raw(), vrc);
4118 }
4119 else
4120 {
4121 mData.mCfgFile.mReadonly = TRUE;
4122 }
4123 }
4124 else
4125 {
4126 mData.mCfgFile.mReadonly = FALSE;
4127 }
4128
4129 if (RT_FAILURE(vrc))
4130 {
4131 mData.mCfgFile.mHandle = NIL_RTFILE;
4132 mData.mCfgFile.mReadonly = FALSE;
4133 }
4134
4135 LogFlowThisFunc (("mCfgFile.mName='%ls', mCfgFile.mHandle=%d, rc=%08X\n",
4136 mData.mCfgFile.mName.raw(), mData.mCfgFile.mHandle, rc));
4137 }
4138 return rc;
4139}
4140
4141/**
4142 * Helper to unlock the VirtualBox configuration from write access.
4143 *
4144 * @note This method is not thread safe (must be called only from #init()
4145 * or #uninit()).
4146 */
4147HRESULT VirtualBox::unlockConfig()
4148{
4149 AutoCaller autoCaller (this);
4150 AssertComRCReturn (autoCaller.rc(), E_FAIL);
4151 AssertReturn (autoCaller.state() == InUninit, E_FAIL);
4152
4153 HRESULT rc = S_OK;
4154
4155 if (isConfigLocked())
4156 {
4157 RTFileFlush (mData.mCfgFile.mHandle);
4158 RTFileClose (mData.mCfgFile.mHandle);
4159 /** @todo flush the directory too. */
4160 mData.mCfgFile.mHandle = NIL_RTFILE;
4161 mData.mCfgFile.mReadonly = FALSE;
4162 LogFlowThisFunc (("\n"));
4163 }
4164
4165 return rc;
4166}
4167
4168/**
4169 * Thread function that watches the termination of all client processes
4170 * that have opened sessions using IVirtualBox::OpenSession()
4171 */
4172// static
4173DECLCALLBACK(int) VirtualBox::ClientWatcher (RTTHREAD /* thread */, void *pvUser)
4174{
4175 LogFlowFuncEnter();
4176
4177 VirtualBox *that = (VirtualBox *) pvUser;
4178 Assert (that);
4179
4180 SessionMachineVector machines;
4181 MachineVector spawnedMachines;
4182
4183 size_t cnt = 0;
4184 size_t cntSpawned = 0;
4185
4186#if defined(RT_OS_WINDOWS)
4187
4188 HRESULT hrc = CoInitializeEx (NULL,
4189 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
4190 COINIT_SPEED_OVER_MEMORY);
4191 AssertComRC (hrc);
4192
4193 /// @todo (dmik) processes reaping!
4194
4195 HANDLE handles [MAXIMUM_WAIT_OBJECTS];
4196 handles [0] = that->mWatcherData.mUpdateReq;
4197
4198 do
4199 {
4200 AutoCaller autoCaller (that);
4201 /* VirtualBox has been early uninitialized, terminate */
4202 if (!autoCaller.isOk())
4203 break;
4204
4205 do
4206 {
4207 /* release the caller to let uninit() ever proceed */
4208 autoCaller.release();
4209
4210 DWORD rc = ::WaitForMultipleObjects ((DWORD)(1 + cnt + cntSpawned),
4211 handles, FALSE, INFINITE);
4212
4213 /* Restore the caller before using VirtualBox. If it fails, this
4214 * means VirtualBox is being uninitialized and we must terminate. */
4215 autoCaller.add();
4216 if (!autoCaller.isOk())
4217 break;
4218
4219 bool update = false;
4220
4221 if (rc == WAIT_OBJECT_0)
4222 {
4223 /* update event is signaled */
4224 update = true;
4225 }
4226 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))
4227 {
4228 /* machine mutex is released */
4229 (machines [rc - WAIT_OBJECT_0 - 1])->checkForDeath();
4230 update = true;
4231 }
4232 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))
4233 {
4234 /* machine mutex is abandoned due to client process termination */
4235 (machines [rc - WAIT_ABANDONED_0 - 1])->checkForDeath();
4236 update = true;
4237 }
4238 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))
4239 {
4240 /* spawned VM process has terminated (normally or abnormally) */
4241 (spawnedMachines [rc - WAIT_OBJECT_0 - cnt - 1])->
4242 checkForSpawnFailure();
4243 update = true;
4244 }
4245
4246 if (update)
4247 {
4248 /* close old process handles */
4249 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4250 CloseHandle (handles [i]);
4251
4252 AutoReadLock thatLock (that);
4253
4254 /* obtain a new set of opened machines */
4255 cnt = 0;
4256 machines.clear();
4257
4258 for (MachineList::iterator it = that->mData.mMachines.begin();
4259 it != that->mData.mMachines.end(); ++ it)
4260 {
4261 /// @todo handle situations with more than 64 objects
4262 AssertMsgBreak ((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,
4263 ("MAXIMUM_WAIT_OBJECTS reached"));
4264
4265 ComObjPtr <SessionMachine> sm;
4266 HANDLE ipcSem;
4267 if ((*it)->isSessionOpenOrClosing (sm, NULL, &ipcSem))
4268 {
4269 machines.push_back (sm);
4270 handles [1 + cnt] = ipcSem;
4271 ++ cnt;
4272 }
4273 }
4274
4275 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4276
4277 /* obtain a new set of spawned machines */
4278 cntSpawned = 0;
4279 spawnedMachines.clear();
4280
4281 for (MachineList::iterator it = that->mData.mMachines.begin();
4282 it != that->mData.mMachines.end(); ++ it)
4283 {
4284 /// @todo handle situations with more than 64 objects
4285 AssertMsgBreak ((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,
4286 ("MAXIMUM_WAIT_OBJECTS reached"));
4287
4288 RTPROCESS pid;
4289 if ((*it)->isSessionSpawning (&pid))
4290 {
4291 HANDLE ph = OpenProcess (SYNCHRONIZE, FALSE, pid);
4292 AssertMsg (ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",
4293 pid, GetLastError()));
4294 if (rc == 0)
4295 {
4296 spawnedMachines.push_back (*it);
4297 handles [1 + cnt + cntSpawned] = ph;
4298 ++ cntSpawned;
4299 }
4300 }
4301 }
4302
4303 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4304 }
4305 }
4306 while (true);
4307 }
4308 while (0);
4309
4310 /* close old process handles */
4311 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)
4312 CloseHandle (handles [i]);
4313
4314 /* release sets of machines if any */
4315 machines.clear();
4316 spawnedMachines.clear();
4317
4318 ::CoUninitialize();
4319
4320#elif defined (RT_OS_OS2)
4321
4322 /// @todo (dmik) processes reaping!
4323
4324 /* according to PMREF, 64 is the maximum for the muxwait list */
4325 SEMRECORD handles [64];
4326
4327 HMUX muxSem = NULLHANDLE;
4328
4329 do
4330 {
4331 AutoCaller autoCaller (that);
4332 /* VirtualBox has been early uninitialized, terminate */
4333 if (!autoCaller.isOk())
4334 break;
4335
4336 do
4337 {
4338 /* release the caller to let uninit() ever proceed */
4339 autoCaller.release();
4340
4341 int vrc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4342
4343 /* Restore the caller before using VirtualBox. If it fails, this
4344 * means VirtualBox is being uninitialized and we must terminate. */
4345 autoCaller.add();
4346 if (!autoCaller.isOk())
4347 break;
4348
4349 bool update = false;
4350 bool updateSpawned = false;
4351
4352 if (RT_SUCCESS (vrc))
4353 {
4354 /* update event is signaled */
4355 update = true;
4356 updateSpawned = true;
4357 }
4358 else
4359 {
4360 AssertMsg (vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,
4361 ("RTSemEventWait returned %Rrc\n", vrc));
4362
4363 /* are there any mutexes? */
4364 if (cnt > 0)
4365 {
4366 /* figure out what's going on with machines */
4367
4368 unsigned long semId = 0;
4369 APIRET arc = ::DosWaitMuxWaitSem (muxSem,
4370 SEM_IMMEDIATE_RETURN, &semId);
4371
4372 if (arc == NO_ERROR)
4373 {
4374 /* machine mutex is normally released */
4375 Assert (semId >= 0 && semId < cnt);
4376 if (semId >= 0 && semId < cnt)
4377 {
4378#ifdef DEBUG
4379 {
4380 AutoReadLock machineLock (machines [semId]);
4381 LogFlowFunc (("released mutex: machine='%ls'\n",
4382 machines [semId]->name().raw()));
4383 }
4384#endif
4385 machines [semId]->checkForDeath();
4386 }
4387 update = true;
4388 }
4389 else if (arc == ERROR_SEM_OWNER_DIED)
4390 {
4391 /* machine mutex is abandoned due to client process
4392 * termination; find which mutex is in the Owner Died
4393 * state */
4394 for (size_t i = 0; i < cnt; ++ i)
4395 {
4396 PID pid; TID tid;
4397 unsigned long reqCnt;
4398 arc = DosQueryMutexSem ((HMTX) handles [i].hsemCur, &pid,
4399 &tid, &reqCnt);
4400 if (arc == ERROR_SEM_OWNER_DIED)
4401 {
4402 /* close the dead mutex as asked by PMREF */
4403 ::DosCloseMutexSem ((HMTX) handles [i].hsemCur);
4404
4405 Assert (i >= 0 && i < cnt);
4406 if (i >= 0 && i < cnt)
4407 {
4408#ifdef DEBUG
4409 {
4410 AutoReadLock machineLock (machines [semId]);
4411 LogFlowFunc (("mutex owner dead: machine='%ls'\n",
4412 machines [i]->name().raw()));
4413 }
4414#endif
4415 machines [i]->checkForDeath();
4416 }
4417 }
4418 }
4419 update = true;
4420 }
4421 else
4422 AssertMsg (arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,
4423 ("DosWaitMuxWaitSem returned %d\n", arc));
4424 }
4425
4426 /* are there any spawning sessions? */
4427 if (cntSpawned > 0)
4428 {
4429 for (size_t i = 0; i < cntSpawned; ++ i)
4430 updateSpawned |= (spawnedMachines [i])->
4431 checkForSpawnFailure();
4432 }
4433 }
4434
4435 if (update || updateSpawned)
4436 {
4437 AutoReadLock thatLock (that);
4438
4439 if (update)
4440 {
4441 /* close the old muxsem */
4442 if (muxSem != NULLHANDLE)
4443 ::DosCloseMuxWaitSem (muxSem);
4444
4445 /* obtain a new set of opened machines */
4446 cnt = 0;
4447 machines.clear();
4448
4449 for (MachineList::iterator it = that->mData.mMachines.begin();
4450 it != that->mData.mMachines.end(); ++ it)
4451 {
4452 /// @todo handle situations with more than 64 objects
4453 AssertMsg (cnt <= 64 /* according to PMREF */,
4454 ("maximum of 64 mutex semaphores reached (%d)",
4455 cnt));
4456
4457 ComObjPtr <SessionMachine> sm;
4458 HMTX ipcSem;
4459 if ((*it)->isSessionOpenOrClosing (sm, NULL, &ipcSem))
4460 {
4461 machines.push_back (sm);
4462 handles [cnt].hsemCur = (HSEM) ipcSem;
4463 handles [cnt].ulUser = cnt;
4464 ++ cnt;
4465 }
4466 }
4467
4468 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4469
4470 if (cnt > 0)
4471 {
4472 /* create a new muxsem */
4473 APIRET arc = ::DosCreateMuxWaitSem (NULL, &muxSem, cnt,
4474 handles,
4475 DCMW_WAIT_ANY);
4476 AssertMsg (arc == NO_ERROR,
4477 ("DosCreateMuxWaitSem returned %d\n", arc));
4478 NOREF(arc);
4479 }
4480 }
4481
4482 if (updateSpawned)
4483 {
4484 /* obtain a new set of spawned machines */
4485 spawnedMachines.clear();
4486
4487 for (MachineList::iterator it = that->mData.mMachines.begin();
4488 it != that->mData.mMachines.end(); ++ it)
4489 {
4490 if ((*it)->isSessionSpawning())
4491 spawnedMachines.push_back (*it);
4492 }
4493
4494 cntSpawned = spawnedMachines.size();
4495 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4496 }
4497 }
4498 }
4499 while (true);
4500 }
4501 while (0);
4502
4503 /* close the muxsem */
4504 if (muxSem != NULLHANDLE)
4505 ::DosCloseMuxWaitSem (muxSem);
4506
4507 /* release sets of machines if any */
4508 machines.clear();
4509 spawnedMachines.clear();
4510
4511#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
4512
4513 bool update = false;
4514 bool updateSpawned = false;
4515
4516 do
4517 {
4518 AutoCaller autoCaller (that);
4519 if (!autoCaller.isOk())
4520 break;
4521
4522 do
4523 {
4524 /* release the caller to let uninit() ever proceed */
4525 autoCaller.release();
4526
4527 int rc = RTSemEventWait (that->mWatcherData.mUpdateReq, 500);
4528
4529 /*
4530 * Restore the caller before using VirtualBox. If it fails, this
4531 * means VirtualBox is being uninitialized and we must terminate.
4532 */
4533 autoCaller.add();
4534 if (!autoCaller.isOk())
4535 break;
4536
4537 if (RT_SUCCESS (rc) || update || updateSpawned)
4538 {
4539 /* RT_SUCCESS (rc) means an update event is signaled */
4540
4541 AutoReadLock thatLock (that);
4542
4543 if (RT_SUCCESS (rc) || update)
4544 {
4545 /* obtain a new set of opened machines */
4546 machines.clear();
4547
4548 for (MachineList::iterator it = that->mData.mMachines.begin();
4549 it != that->mData.mMachines.end(); ++ it)
4550 {
4551 ComObjPtr <SessionMachine> sm;
4552 if ((*it)->isSessionOpenOrClosing (sm))
4553 machines.push_back (sm);
4554 }
4555
4556 cnt = machines.size();
4557 LogFlowFunc (("UPDATE: direct session count = %d\n", cnt));
4558 }
4559
4560 if (RT_SUCCESS (rc) || updateSpawned)
4561 {
4562 /* obtain a new set of spawned machines */
4563 spawnedMachines.clear();
4564
4565 for (MachineList::iterator it = that->mData.mMachines.begin();
4566 it != that->mData.mMachines.end(); ++ it)
4567 {
4568 if ((*it)->isSessionSpawning())
4569 spawnedMachines.push_back (*it);
4570 }
4571
4572 cntSpawned = spawnedMachines.size();
4573 LogFlowFunc (("UPDATE: spawned session count = %d\n", cntSpawned));
4574 }
4575 }
4576
4577 update = false;
4578 for (size_t i = 0; i < cnt; ++ i)
4579 update |= (machines [i])->checkForDeath();
4580
4581 updateSpawned = false;
4582 for (size_t i = 0; i < cntSpawned; ++ i)
4583 updateSpawned |= (spawnedMachines [i])->checkForSpawnFailure();
4584
4585 /* reap child processes */
4586 {
4587 AutoWriteLock alock (that);
4588 if (that->mWatcherData.mProcesses.size())
4589 {
4590 LogFlowFunc (("UPDATE: child process count = %d\n",
4591 that->mWatcherData.mProcesses.size()));
4592 ClientWatcherData::ProcessList::iterator it =
4593 that->mWatcherData.mProcesses.begin();
4594 while (it != that->mWatcherData.mProcesses.end())
4595 {
4596 RTPROCESS pid = *it;
4597 RTPROCSTATUS status;
4598 int vrc = ::RTProcWait (pid, RTPROCWAIT_FLAGS_NOBLOCK,
4599 &status);
4600 if (vrc == VINF_SUCCESS)
4601 {
4602 LogFlowFunc (("pid %d (%x) was reaped, "
4603 "status=%d, reason=%d\n",
4604 pid, pid, status.iStatus,
4605 status.enmReason));
4606 it = that->mWatcherData.mProcesses.erase (it);
4607 }
4608 else
4609 {
4610 LogFlowFunc (("pid %d (%x) was NOT reaped, vrc=%Rrc\n",
4611 pid, pid, vrc));
4612 if (vrc != VERR_PROCESS_RUNNING)
4613 {
4614 /* remove the process if it is not already running */
4615 it = that->mWatcherData.mProcesses.erase (it);
4616 }
4617 else
4618 ++ it;
4619 }
4620 }
4621 }
4622 }
4623 }
4624 while (true);
4625 }
4626 while (0);
4627
4628 /* release sets of machines if any */
4629 machines.clear();
4630 spawnedMachines.clear();
4631
4632#else
4633# error "Port me!"
4634#endif
4635
4636 LogFlowFuncLeave();
4637 return 0;
4638}
4639
4640/**
4641 * Thread function that handles custom events posted using #postEvent().
4642 */
4643// static
4644DECLCALLBACK(int) VirtualBox::AsyncEventHandler (RTTHREAD thread, void *pvUser)
4645{
4646 LogFlowFuncEnter();
4647
4648 AssertReturn (pvUser, VERR_INVALID_POINTER);
4649
4650 // create an event queue for the current thread
4651 EventQueue *eventQ = new EventQueue();
4652 AssertReturn (eventQ, VERR_NO_MEMORY);
4653
4654 // return the queue to the one who created this thread
4655 *(static_cast <EventQueue **> (pvUser)) = eventQ;
4656 // signal that we're ready
4657 RTThreadUserSignal (thread);
4658
4659 BOOL ok = TRUE;
4660 Event *event = NULL;
4661
4662 while ((ok = eventQ->waitForEvent (&event)) && event)
4663 eventQ->handleEvent (event);
4664
4665 AssertReturn (ok, VERR_GENERAL_FAILURE);
4666
4667 delete eventQ;
4668
4669 LogFlowFuncLeave();
4670
4671 return 0;
4672}
4673
4674////////////////////////////////////////////////////////////////////////////////
4675
4676/**
4677 * Takes the current list of registered callbacks of the managed VirtualBox
4678 * instance, and calls #handleCallback() for every callback item from the
4679 * list, passing the item as an argument.
4680 *
4681 * @note Locks the managed VirtualBox object for reading but leaves the lock
4682 * before iterating over callbacks and calling their methods.
4683 */
4684void *VirtualBox::CallbackEvent::handler()
4685{
4686 if (mVirtualBox.isNull())
4687 return NULL;
4688
4689 AutoCaller autoCaller (mVirtualBox);
4690 if (!autoCaller.isOk())
4691 {
4692 LogWarningFunc (("VirtualBox has been uninitialized (state=%d), "
4693 "the callback event is discarded!\n",
4694 autoCaller.state()));
4695 /* We don't need mVirtualBox any more, so release it */
4696 mVirtualBox.setNull();
4697 return NULL;
4698 }
4699
4700 CallbackVector callbacks;
4701 {
4702 /* Make a copy to release the lock before iterating */
4703 AutoReadLock alock (mVirtualBox);
4704 callbacks = CallbackVector (mVirtualBox->mData.mCallbacks.begin(),
4705 mVirtualBox->mData.mCallbacks.end());
4706 /* We don't need mVirtualBox any more, so release it */
4707 mVirtualBox.setNull();
4708 }
4709
4710 for (VirtualBox::CallbackVector::const_iterator it = callbacks.begin();
4711 it != callbacks.end(); ++ it)
4712 handleCallback (*it);
4713
4714 return NULL;
4715}
4716
4717//STDMETHODIMP VirtualBox::CreateDHCPServerForInterface (/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)
4718//{
4719// return E_NOTIMPL;
4720//}
4721
4722STDMETHODIMP VirtualBox::CreateDHCPServer (IN_BSTR aName, IDHCPServer ** aServer)
4723{
4724 CheckComArgNotNull(aName);
4725 CheckComArgNotNull(aServer);
4726
4727 AutoCaller autoCaller (this);
4728 CheckComRCReturnRC (autoCaller.rc());
4729
4730 ComObjPtr<DHCPServer> dhcpServer;
4731 dhcpServer.createObject();
4732 HRESULT rc = dhcpServer->init (this, aName);
4733 CheckComRCReturnRC (rc);
4734
4735 rc = registerDHCPServer(dhcpServer, true);
4736 CheckComRCReturnRC (rc);
4737
4738 dhcpServer.queryInterfaceTo(aServer);
4739
4740 return rc;
4741}
4742
4743//STDMETHODIMP VirtualBox::FindDHCPServerForInterface (IHostNetworkInterface * aIinterface, IDHCPServer ** aServer)
4744//{
4745// return E_NOTIMPL;
4746//}
4747
4748STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName (IN_BSTR aName, IDHCPServer ** aServer)
4749{
4750 CheckComArgNotNull(aName);
4751 CheckComArgNotNull(aServer);
4752
4753 AutoCaller autoCaller (this);
4754 CheckComRCReturnRC (autoCaller.rc());
4755
4756 AutoWriteLock alock (this);
4757
4758 HRESULT rc;
4759 Bstr bstr;
4760 ComPtr <DHCPServer> found;
4761
4762 for (DHCPServerList::const_iterator it =
4763 mData.mDHCPServers.begin();
4764 it != mData.mDHCPServers.end();
4765 ++ it)
4766 {
4767 rc = (*it)->COMGETTER(NetworkName) (bstr.asOutParam());
4768 CheckComRCThrowRC (rc);
4769
4770 if(bstr == aName)
4771 {
4772 found = *it;
4773 break;
4774 }
4775 }
4776
4777 if (!found)
4778 return E_INVALIDARG;
4779
4780 return found.queryInterfaceTo (aServer);
4781}
4782
4783STDMETHODIMP VirtualBox::RemoveDHCPServer (IDHCPServer * aServer)
4784{
4785 CheckComArgNotNull(aServer);
4786
4787 AutoCaller autoCaller (this);
4788 CheckComRCReturnRC (autoCaller.rc());
4789
4790 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);
4791
4792 return rc;
4793}
4794
4795/**
4796 * Remembers the given dhcp server by storing it in the hard disk registry.
4797 *
4798 * @param aDHCPServer Dhcp Server object to remember.
4799 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4800 *
4801 * When @a aSaveRegistry is @c true, this operation may fail because of the
4802 * failed #saveSettings() method it calls. In this case, the dhcp server object
4803 * will not be remembered. It is therefore the responsibility of the caller to
4804 * call this method as the last step of some action that requires registration
4805 * in order to make sure that only fully functional dhcp server objects get
4806 * registered.
4807 *
4808 * @note Locks this object for writing and @a aDHCPServer for reading.
4809 */
4810HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,
4811 bool aSaveRegistry /*= true*/)
4812{
4813 AssertReturn (aDHCPServer != NULL, E_INVALIDARG);
4814
4815 AutoCaller autoCaller (this);
4816 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4817
4818 AutoWriteLock alock (this);
4819
4820 AutoCaller dhcpServerCaller (aDHCPServer);
4821 AssertComRCReturn (dhcpServerCaller.rc(), dhcpServerCaller.rc());
4822
4823 AutoReadLock dhcpServerLock (aDHCPServer);
4824
4825 Bstr name;
4826 HRESULT rc;
4827 rc = aDHCPServer->COMGETTER(NetworkName) (name.asOutParam());
4828 CheckComRCReturnRC (rc);
4829
4830 ComPtr<IDHCPServer> existing;
4831 rc = FindDHCPServerByNetworkName(name.mutableRaw(), existing.asOutParam());
4832 if(SUCCEEDED(rc))
4833 {
4834 return E_INVALIDARG;
4835 }
4836 rc = S_OK;
4837
4838 mData.mDHCPServers.push_back (aDHCPServer);
4839
4840 if (aSaveRegistry)
4841 {
4842 rc = saveSettings();
4843 if (FAILED (rc))
4844 unregisterDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4845 }
4846
4847 return rc;
4848}
4849
4850/**
4851 * Removes the given hard disk from the hard disk registry.
4852 *
4853 * @param aHardDisk Hard disk object to remove.
4854 * @param aSaveRegistry @c true to save hard disk registry to disk (default).
4855 *
4856 * When @a aSaveRegistry is @c true, this operation may fail because of the
4857 * failed #saveSettings() method it calls. In this case, the hard disk object
4858 * will NOT be removed from the registry when this method returns. It is
4859 * therefore the responsibility of the caller to call this method as the first
4860 * step of some action that requires unregistration, before calling uninit() on
4861 * @a aHardDisk.
4862 *
4863 * @note Locks this object for writing and @a aHardDisk for reading.
4864 */
4865HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,
4866 bool aSaveRegistry /*= true*/)
4867{
4868 AssertReturn (aDHCPServer != NULL, E_INVALIDARG);
4869
4870 AutoCaller autoCaller (this);
4871 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
4872
4873 AutoWriteLock alock (this);
4874
4875 AutoCaller dhcpServerCaller (aDHCPServer);
4876 AssertComRCReturn (dhcpServerCaller.rc(), dhcpServerCaller.rc());
4877
4878 AutoReadLock dhcpServerLock (aDHCPServer);
4879
4880 mData.mDHCPServers.remove (aDHCPServer);
4881
4882 HRESULT rc = S_OK;
4883
4884 if (aSaveRegistry)
4885 {
4886 rc = saveSettings();
4887 if (FAILED (rc))
4888 registerDHCPServer(aDHCPServer, false /* aSaveRegistry */);
4889 }
4890
4891 return rc;
4892}
4893
4894/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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