VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp@ 36523

Last change on this file since 36523 was 36523, checked in by vboxsync, 14 years ago

Renamed iprt::Error to RTCError.

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