VirtualBox

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

Last change on this file since 18761 was 18520, checked in by vboxsync, 16 years ago

Main: #3548 VBoxSVC holds vm xml definition file open after VM is unregistered
Make Machine::unlockConfig public and call it from VirtualBox::UnregisterMachine.

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