VirtualBox

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

Last change on this file since 19460 was 19283, checked in by vboxsync, 16 years ago

removed debugging code

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