ConsoleImpl.cpp revision 525cfe310305abf78e4fd4cb98bd0fa603694a65
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder/* $Id$ */
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder/** @file
e6d40133bc9f858308654afb1262b8b483ec5922Till Mossakowski * VBox Console COM Class implementation
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder */
97018cf5fa25b494adffd7e9b4e87320dae6bf47Christian Maeder
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder/*
b4fbc96e05117839ca409f5f20f97b3ac872d1edTill Mossakowski * Copyright (C) 2006-2010 Oracle Corporation
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder *
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder * This file is part of VirtualBox Open Source Edition (OSE), as
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder * available from http://www.virtualbox.org. This file is free software;
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder * you can redistribute it and/or modify it under the terms of the GNU
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder * General Public License (GPL) as published by the Free Software
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder * Foundation, in version 2 as it comes in the "COPYING" file of the
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder */
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder
5a635ded758223aace5fd93489167f03cc336502Christian Maeder/** @todo Move the TAP mess back into the driver! */
5a635ded758223aace5fd93489167f03cc336502Christian Maeder#if defined(RT_OS_WINDOWS)
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder#elif defined(RT_OS_LINUX)
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder# include <errno.h>
2eb84fc82d3ffa9116bc471fda3742bd9e5a24bbChristian Maeder# include <sys/ioctl.h>
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder# include <sys/poll.h>
6ab1767d594a99e063bb698c123823411c05700eChristian Maeder# include <sys/fcntl.h>
6ab1767d594a99e063bb698c123823411c05700eChristian Maeder# include <sys/types.h>
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder# include <sys/wait.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder# include <net/if.h>
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder# include <linux/if_tun.h>
efcb75d747dcdecc53bc5a584b6b4d98d1ae2755Christian Maeder# include <stdio.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder# include <stdlib.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder# include <string.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#elif defined(RT_OS_FREEBSD)
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder# include <errno.h>
efcb75d747dcdecc53bc5a584b6b4d98d1ae2755Christian Maeder# include <sys/ioctl.h>
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski# include <sys/poll.h>
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski# include <sys/fcntl.h>
efcb75d747dcdecc53bc5a584b6b4d98d1ae2755Christian Maeder# include <sys/types.h>
efcb75d747dcdecc53bc5a584b6b4d98d1ae2755Christian Maeder# include <sys/wait.h>
efcb75d747dcdecc53bc5a584b6b4d98d1ae2755Christian Maeder# include <stdio.h>
efcb75d747dcdecc53bc5a584b6b4d98d1ae2755Christian Maeder# include <stdlib.h>
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder# include <string.h>
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#elif defined(RT_OS_SOLARIS)
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski# include <iprt/coredumper.h>
efcb75d747dcdecc53bc5a584b6b4d98d1ae2755Christian Maeder#endif
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "ConsoleImpl.h"
efcb75d747dcdecc53bc5a584b6b4d98d1ae2755Christian Maeder
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "Global.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "VirtualBoxErrorInfoImpl.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "GuestImpl.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "KeyboardImpl.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "MouseImpl.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "DisplayImpl.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "MachineDebuggerImpl.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "USBDeviceImpl.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "RemoteUSBDeviceImpl.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "SharedFolderImpl.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "AudioSnifferInterface.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "ProgressCombinedImpl.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "ConsoleVRDPServer.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "VMMDev.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "package-generated.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski// generated header
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include "SchemaDefs.h"
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski
b645cf3dc1e449038ed291bbd11fcc6e02b2fc7fChristian Maeder#include "AutoCaller.h"
efcb75d747dcdecc53bc5a584b6b4d98d1ae2755Christian Maeder#include "Logging.h"
b645cf3dc1e449038ed291bbd11fcc6e02b2fc7fChristian Maeder
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#include <VBox/com/array.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#include "VBox/com/ErrorInfo.h"
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#include <iprt/asm.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#include <iprt/buildconfig.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#include <iprt/cpp/utils.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#include <iprt/dir.h>
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder#include <iprt/file.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#include <iprt/ldr.h>
5516c28f6575b3550029d1d82c6a14f8402ac3a2Christian Maeder#include <iprt/path.h>
5516c28f6575b3550029d1d82c6a14f8402ac3a2Christian Maeder#include <iprt/process.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#include <iprt/string.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#include <iprt/system.h>
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder#include <VBox/vmapi.h>
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#include <VBox/vmm.h>
f8c976764f99f18f986cfbb75da7150741d2e883Klaus Luettich#include <VBox/err.h>
5a635ded758223aace5fd93489167f03cc336502Christian Maeder#include <VBox/param.h>
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder#include <VBox/pdmnetifs.h>
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder#include <VBox/vusb.h>
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder#include <VBox/mm.h>
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski#include <VBox/ftm.h>
efcb75d747dcdecc53bc5a584b6b4d98d1ae2755Christian Maeder#include <VBox/ssm.h>
781d04c5e02635caed8b98f0adcf559f9426a39cTill Mossakowski#include <VBox/version.h>
781d04c5e02635caed8b98f0adcf559f9426a39cTill Mossakowski#ifdef VBOX_WITH_USB
5516c28f6575b3550029d1d82c6a14f8402ac3a2Christian Maeder# include <VBox/pdmusb.h>
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder#endif
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder
5a635ded758223aace5fd93489167f03cc336502Christian Maeder#include <VBox/VMMDev.h>
5a635ded758223aace5fd93489167f03cc336502Christian Maeder
efcb75d747dcdecc53bc5a584b6b4d98d1ae2755Christian Maeder#include <VBox/HostServices/VBoxClipboardSvc.h>
5a635ded758223aace5fd93489167f03cc336502Christian Maeder#ifdef VBOX_WITH_GUEST_PROPS
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski# include <VBox/HostServices/GuestPropertySvc.h>
5a635ded758223aace5fd93489167f03cc336502Christian Maeder# include <VBox/com/array.h>
db7143998eee23e3d781f1f1e97e953bb831df1fTill Mossakowski#endif
db7143998eee23e3d781f1f1e97e953bb831df1fTill Mossakowski
db7143998eee23e3d781f1f1e97e953bb831df1fTill Mossakowski#include <set>
6ab1767d594a99e063bb698c123823411c05700eChristian Maeder#include <algorithm>
6ab1767d594a99e063bb698c123823411c05700eChristian Maeder#include <memory> // for auto_ptr
6ab1767d594a99e063bb698c123823411c05700eChristian Maeder#include <vector>
6ab1767d594a99e063bb698c123823411c05700eChristian Maeder#include <typeinfo>
6ab1767d594a99e063bb698c123823411c05700eChristian Maeder
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder// VMTask and friends
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder////////////////////////////////////////////////////////////////////////////////
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder
b645cf3dc1e449038ed291bbd11fcc6e02b2fc7fChristian Maeder/**
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski * Task structure for asynchronous VM operations.
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski *
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski * Once created, the task structure adds itself as a Console caller. This means:
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski *
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski * 1. The user must check for #rc() before using the created structure
b645cf3dc1e449038ed291bbd11fcc6e02b2fc7fChristian Maeder * (e.g. passing it as a thread function argument). If #rc() returns a
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder * failure, the Console object may not be used by the task (see
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski * Console::addCaller() for more details).
b645cf3dc1e449038ed291bbd11fcc6e02b2fc7fChristian Maeder * 2. On successful initialization, the structure keeps the Console caller
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder * until destruction (to ensure Console remains in the Ready state and won't
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski * be accidentally uninitialized). Forgetting to delete the created task
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski * will lead to Console::uninit() stuck waiting for releasing all added
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski * callers.
5a635ded758223aace5fd93489167f03cc336502Christian Maeder *
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder * If \a aUsesVMPtr parameter is true, the task structure will also add itself
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder * as a Console::mpVM caller with the same meaning as above. See
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder * Console::addVMCaller() for more info.
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder */
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maederstruct VMTask
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder{
1df7e6a9e5273f3eeb770d9f2fbbee40fb1ba51dChristian Maeder VMTask(Console *aConsole, bool aUsesVMPtr)
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder : mConsole(aConsole),
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder mConsoleCaller(aConsole),
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder mVMCallerAdded(false)
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder {
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder AssertReturnVoid(aConsole);
277f12a72ee6876b9c48f01ef34d7cde65a423c8Christian Maeder mRC = mConsoleCaller.rc();
277f12a72ee6876b9c48f01ef34d7cde65a423c8Christian Maeder if (FAILED(mRC))
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder return;
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder if (aUsesVMPtr)
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder {
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder mRC = aConsole->addVMCaller();
277f12a72ee6876b9c48f01ef34d7cde65a423c8Christian Maeder if (SUCCEEDED(mRC))
277f12a72ee6876b9c48f01ef34d7cde65a423c8Christian Maeder mVMCallerAdded = true;
277f12a72ee6876b9c48f01ef34d7cde65a423c8Christian Maeder }
277f12a72ee6876b9c48f01ef34d7cde65a423c8Christian Maeder }
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder ~VMTask()
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder {
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder if (mVMCallerAdded)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder mConsole->releaseVMCaller();
5a635ded758223aace5fd93489167f03cc336502Christian Maeder }
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder HRESULT rc() const { return mRC; }
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder bool isOk() const { return SUCCEEDED(rc()); }
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder
781d04c5e02635caed8b98f0adcf559f9426a39cTill Mossakowski /** Releases the VM caller before destruction. Not normally necessary. */
781d04c5e02635caed8b98f0adcf559f9426a39cTill Mossakowski void releaseVMCaller()
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder {
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder AssertReturnVoid(mVMCallerAdded);
94fa5b6f4ce199b15c9e3c530d96a8c403e20193Christian Maeder mConsole->releaseVMCaller();
94fa5b6f4ce199b15c9e3c530d96a8c403e20193Christian Maeder mVMCallerAdded = false;
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski }
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder const ComObjPtr<Console> mConsole;
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder AutoCaller mConsoleCaller;
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder
5a635ded758223aace5fd93489167f03cc336502Christian Maederprivate:
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder
eca18e4fda019d7e57e1fe1cc16ed21592c9e11bChristian Maeder HRESULT mRC;
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder bool mVMCallerAdded : 1;
5a635ded758223aace5fd93489167f03cc336502Christian Maeder};
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder
eca18e4fda019d7e57e1fe1cc16ed21592c9e11bChristian Maederstruct VMProgressTask : public VMTask
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder{
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder VMProgressTask(Console *aConsole,
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder Progress *aProgress,
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder bool aUsesVMPtr)
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder : VMTask(aConsole, aUsesVMPtr),
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder mProgress(aProgress)
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder {}
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder const ComObjPtr<Progress> mProgress;
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder Utf8Str mErrorMsg;
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder};
4561227a776bdf0ab679b19fb92f1eaaed8786f7Christian Maeder
277f12a72ee6876b9c48f01ef34d7cde65a423c8Christian Maederstruct VMTakeSnapshotTask : public VMProgressTask
277f12a72ee6876b9c48f01ef34d7cde65a423c8Christian Maeder{
277f12a72ee6876b9c48f01ef34d7cde65a423c8Christian Maeder VMTakeSnapshotTask(Console *aConsole,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder Progress *aProgress,
5a635ded758223aace5fd93489167f03cc336502Christian Maeder IN_BSTR aName,
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder IN_BSTR aDescription)
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder : VMProgressTask(aConsole, aProgress, false /* aUsesVMPtr */),
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder bstrName(aName),
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder bstrDescription(aDescription),
5a635ded758223aace5fd93489167f03cc336502Christian Maeder lastMachineState(MachineState_Null)
5a635ded758223aace5fd93489167f03cc336502Christian Maeder {}
5a635ded758223aace5fd93489167f03cc336502Christian Maeder
5a635ded758223aace5fd93489167f03cc336502Christian Maeder Bstr bstrName,
5a635ded758223aace5fd93489167f03cc336502Christian Maeder bstrDescription;
5a635ded758223aace5fd93489167f03cc336502Christian Maeder Bstr bstrSavedStateFile; // received from BeginTakeSnapshot()
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder MachineState_T lastMachineState;
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder bool fTakingSnapshotOnline;
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder ULONG ulMemSize;
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder};
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maederstruct VMPowerUpTask : public VMProgressTask
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder{
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder VMPowerUpTask(Console *aConsole,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder Progress *aProgress)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder : VMProgressTask(aConsole, aProgress, false /* aUsesVMPtr */),
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder mConfigConstructor(NULL),
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder mStartPaused(false),
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder mTeleporterEnabled(FALSE),
5a635ded758223aace5fd93489167f03cc336502Christian Maeder mEnmFaultToleranceState(FaultToleranceState_Inactive)
5a635ded758223aace5fd93489167f03cc336502Christian Maeder {}
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder PFNCFGMCONSTRUCTOR mConfigConstructor;
dd776ca56af505d39e16e9eecf69948beddfb279Christian Maeder Utf8Str mSavedStateFile;
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder Console::SharedFolderDataMap mSharedFolders;
dd776ca56af505d39e16e9eecf69948beddfb279Christian Maeder bool mStartPaused;
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder BOOL mTeleporterEnabled;
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder FaultToleranceState_T mEnmFaultToleranceState;
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder /* array of progress objects for hard disk reset operations */
27273a2967ca2f12b5e6ff0ec5e3f66e2ea8fcb2Christian Maeder typedef std::list< ComPtr<IProgress> > ProgressList;
25a0b76bc87e80c0f697951d9817862755a71d33Christian Maeder ProgressList hardDiskProgresses;
27273a2967ca2f12b5e6ff0ec5e3f66e2ea8fcb2Christian Maeder};
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder
27273a2967ca2f12b5e6ff0ec5e3f66e2ea8fcb2Christian Maederstruct VMSaveTask : public VMProgressTask
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder{
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder VMSaveTask(Console *aConsole, Progress *aProgress)
27273a2967ca2f12b5e6ff0ec5e3f66e2ea8fcb2Christian Maeder : VMProgressTask(aConsole, aProgress, true /* aUsesVMPtr */),
27273a2967ca2f12b5e6ff0ec5e3f66e2ea8fcb2Christian Maeder mLastMachineState(MachineState_Null)
5a635ded758223aace5fd93489167f03cc336502Christian Maeder {}
5a635ded758223aace5fd93489167f03cc336502Christian Maeder
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder Utf8Str mSavedStateFile;
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder MachineState_T mLastMachineState;
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder ComPtr<IProgress> mServerProgress;
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski};
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder
6e6ba3ab90205840b9c0ea408befaed7d1d7b80bChristian Maeder// ConsoleCallbackRegistration
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski////////////////////////////////////////////////////////////////////////////////
6e6ba3ab90205840b9c0ea408befaed7d1d7b80bChristian Maeder
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski/**
6e6ba3ab90205840b9c0ea408befaed7d1d7b80bChristian Maeder * Registered IConsoleCallback, used by Console::CallbackList and
6e6ba3ab90205840b9c0ea408befaed7d1d7b80bChristian Maeder * Console::mCallbacks.
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder *
b645cf3dc1e449038ed291bbd11fcc6e02b2fc7fChristian Maeder * In addition to keeping the interface pointer this also keeps track of the
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder * methods that asked to not be called again. The latter is for reducing
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski * unnecessary IPC.
b645cf3dc1e449038ed291bbd11fcc6e02b2fc7fChristian Maeder */
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maederclass ConsoleCallbackRegistration
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder{
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maederpublic:
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder /** Callback bit indexes (for bmDisabled). */
25d484b7e8a319494046201857a80ed4dd2cea86Christian Maeder typedef enum
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder {
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder kOnMousePointerShapeChanged = 0,
25d484b7e8a319494046201857a80ed4dd2cea86Christian Maeder kOnMouseCapabilityChanged,
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder kOnKeyboardLedsChanged,
25d484b7e8a319494046201857a80ed4dd2cea86Christian Maeder kOnStateChanged,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder kOnAdditionsStateChanged,
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski kOnNetworkAdapterChanged,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder kOnSerialPortChanged,
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder kOnParallelPortChanged,
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder kOnStorageControllerChanged,
25d484b7e8a319494046201857a80ed4dd2cea86Christian Maeder kOnMediumChanged,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder kOnCPUChanged,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder kOnCPUPriorityChanged,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder kOnVRDPServerChanged,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder kOnRemoteDisplayInfoChanged,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder kOnUSBControllerChanged,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder kOnUSBDeviceStateChanged,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder kOnSharedFolderChanged,
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder kOnRuntimeError,
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder kOnCanShowWindow,
5a635ded758223aace5fd93489167f03cc336502Christian Maeder kOnShowWindow
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder } CallbackBit;
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder ConsoleCallbackRegistration()
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski {
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder /* nothing */
1f726c6d4aea88acb19f73a1471257dbde301156Till Mossakowski }
1f726c6d4aea88acb19f73a1471257dbde301156Till Mossakowski
1f726c6d4aea88acb19f73a1471257dbde301156Till Mossakowski ~ConsoleCallbackRegistration()
1f726c6d4aea88acb19f73a1471257dbde301156Till Mossakowski {
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder /* nothing */
0ed43197461f040473d13c45f85c384c3e83144dChristian Maeder }
0ed43197461f040473d13c45f85c384c3e83144dChristian Maeder};
0ed43197461f040473d13c45f85c384c3e83144dChristian Maeder
0ed43197461f040473d13c45f85c384c3e83144dChristian Maeder
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder#define PREP_ARGS0()
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder#define PREP_ARGS1(a1) a1
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder#define PREP_ARGS2(a1,a2) a2, a2
6ab1767d594a99e063bb698c123823411c05700eChristian Maeder#define PREP_ARGS3(a1,a2,a3) a1, a2, a3
6ab1767d594a99e063bb698c123823411c05700eChristian Maeder#define PREP_ARGS4(a1,a2,a3,a4) a1, a2, a3, a4
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder#define PREP_ARGS5(a1,a2,a3,a4,a5) a1, a2, a3, a4, a5
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder#define PREP_ARGS6(a1,a2,a3,a4,a5,a6) a1, a2, a3, a4, a5, a6
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder#define PREP_ARGS7(a1,a2,a3,a4,a5,a6,a7) a1, a2, a3, a4, a5, a6, a7
90f31c67ccb0973bf6daf66ea7e541e6151dd982Christian Maeder#define PREP_ARGS8(a1,a2,a3,a4,a5,a6,a7,a8) a1, a2, a3, a4, a5, a6, a7, a8
90f31c67ccb0973bf6daf66ea7e541e6151dd982Christian Maeder
90f31c67ccb0973bf6daf66ea7e541e6151dd982Christian Maeder/**
90f31c67ccb0973bf6daf66ea7e541e6151dd982Christian Maeder * Macro for firing appropriate event.
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder *
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder * @param Event Event, like OnKeyboardLedsChanged.
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder * @param InvokeCb Callbacks invocation code
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder * @param PreprEvent Event preparation code
90f31c67ccb0973bf6daf66ea7e541e6151dd982Christian Maeder * @param Args Number of callback arguments
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder */
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder#define CONSOLE_DO_CALLBACKS_GEN(Event, Args, MaybeComma) \
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder do \
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder { \
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder VBoxEventDesc evDesc; \
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder evDesc.init(mEventSource, VBoxEventType_##Event MaybeComma Args); \
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder evDesc.fire(/* don't wait for delivery */ 0); \
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder } while (0)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder#define COMMA ,
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder#define NO_COMMA
90f31c67ccb0973bf6daf66ea7e541e6151dd982Christian Maeder/* Actual invocation macroses for different number of parameters */
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder#define CONSOLE_DO_CALLBACKS0(CallbackMethod) \
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder CONSOLE_DO_CALLBACKS_GEN(CallbackMethod, PREP_ARGS0(), NO_COMMA)
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder#define CONSOLE_DO_CALLBACKS1(CallbackMethod,Arg1) \
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder CONSOLE_DO_CALLBACKS_GEN(CallbackMethod, PREP_ARGS1(Arg1), COMMA)
90f31c67ccb0973bf6daf66ea7e541e6151dd982Christian Maeder#define CONSOLE_DO_CALLBACKS2(CallbackMethod,Arg1,Arg2) \
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder CONSOLE_DO_CALLBACKS_GEN(CallbackMethod, PREP_ARGS2(Arg1,Arg2),COMMA)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder#define CONSOLE_DO_CALLBACKS3(CallbackMethod,Arg1,Arg2,Arg3) \
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder CONSOLE_DO_CALLBACKS_GEN(CallbackMethod, PREP_ARGS3(Arg1,Arg2,Arg3), COMMA)
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder#define CONSOLE_DO_CALLBACKS4(CallbackMethod,Arg1,Arg2,Arg3,Arg4) \
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder CONSOLE_DO_CALLBACKS_GEN(CallbackMethod, PREP_ARGS4(Arg1,Arg2,Arg3,Arg4), COMMA)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder#define CONSOLE_DO_CALLBACKS7(CallbackMethod,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7) \
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder CONSOLE_DO_CALLBACKS_GEN(CallbackMethod, PREP_ARGS7(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7), COMMA)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder#define CONSOLE_DO_CALLBACKS8(CallbackMethod,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7,Arg8) \
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder CONSOLE_DO_CALLBACKS_GEN(CallbackMethod, PREP_ARGS8(Arg1,Arg2,Arg3,Arg4,Arg5,Arg6,Arg7,Arg8), COMMA)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder// constructor / destructor
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder/////////////////////////////////////////////////////////////////////////////
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder
90f31c67ccb0973bf6daf66ea7e541e6151dd982Christian MaederConsole::Console()
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder : mSavedStateDataLoaded(false)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder , mConsoleVRDPServer(NULL)
0cb5f9c8582ad87ceef1c16b5d92347ae0878019Christian Maeder , mpVM(NULL)
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder , mVMCallers(0)
90f31c67ccb0973bf6daf66ea7e541e6151dd982Christian Maeder , mVMZeroCallersSem(NIL_RTSEMEVENT)
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder , mVMDestroying(false)
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder , mVMPoweredOff(false)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder , mVMIsAlreadyPoweringOff(false)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder , mfSnapshotFolderSizeWarningShown(false)
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder , mfSnapshotFolderExt4WarningShown(false)
5a635ded758223aace5fd93489167f03cc336502Christian Maeder , mpVmm2UserMethods(NULL)
aefb25a4f42b9078ea2b04fe2b599da4ca20cd34Christian Maeder , m_pVMMDev(NULL)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder , mAudioSniffer(NULL)
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski , mVMStateChangeCallbackDisabled(false)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder , mMachineState(MachineState_PoweredOff)
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder{
add1aac0ed564b88495edb5491efd8c511607ea9Christian Maeder for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; ++slot)
b645cf3dc1e449038ed291bbd11fcc6e02b2fc7fChristian Maeder meAttachmentType[slot] = NetworkAttachmentType_Null;
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski}
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski
56e7a33c7dabf252c78da3fa2bc601436294cc48Christian MaederConsole::~Console()
56e7a33c7dabf252c78da3fa2bc601436294cc48Christian Maeder{}
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill MossakowskiHRESULT Console::FinalConstruct()
b645cf3dc1e449038ed291bbd11fcc6e02b2fc7fChristian Maeder{
56e7a33c7dabf252c78da3fa2bc601436294cc48Christian Maeder LogFlowThisFunc(("\n"));
56e7a33c7dabf252c78da3fa2bc601436294cc48Christian Maeder
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski memset(mapStorageLeds, 0, sizeof(mapStorageLeds));
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski memset(mapNetworkLeds, 0, sizeof(mapNetworkLeds));
56e7a33c7dabf252c78da3fa2bc601436294cc48Christian Maeder memset(&mapUSBLed, 0, sizeof(mapUSBLed));
5a635ded758223aace5fd93489167f03cc336502Christian Maeder memset(&mapSharedFolderLed, 0, sizeof(mapSharedFolderLed));
5a635ded758223aace5fd93489167f03cc336502Christian Maeder
5a635ded758223aace5fd93489167f03cc336502Christian Maeder for (unsigned i = 0; i < RT_ELEMENTS(maStorageDevType); ++ i)
5a635ded758223aace5fd93489167f03cc336502Christian Maeder maStorageDevType[i] = DeviceType_Null;
5a635ded758223aace5fd93489167f03cc336502Christian Maeder
5a635ded758223aace5fd93489167f03cc336502Christian Maeder VMM2USERMETHODS *pVmm2UserMethods = (VMM2USERMETHODS *)RTMemAlloc(sizeof(*mpVmm2UserMethods) + sizeof(Console *));
5a635ded758223aace5fd93489167f03cc336502Christian Maeder if (!pVmm2UserMethods)
5a635ded758223aace5fd93489167f03cc336502Christian Maeder return E_OUTOFMEMORY;
5a635ded758223aace5fd93489167f03cc336502Christian Maeder pVmm2UserMethods->u32Magic = VMM2USERMETHODS_MAGIC;
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski pVmm2UserMethods->u32Version = VMM2USERMETHODS_VERSION;
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski pVmm2UserMethods->pfnSaveState = Console::vmm2User_SaveState;
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski pVmm2UserMethods->u32EndMagic = VMM2USERMETHODS_MAGIC;
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski *(Console **)(pVmm2UserMethods + 1) = this; /* lazy bird. */
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski mpVmm2UserMethods = pVmm2UserMethods;
5a635ded758223aace5fd93489167f03cc336502Christian Maeder
5a635ded758223aace5fd93489167f03cc336502Christian Maeder return S_OK;
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder}
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maedervoid Console::FinalRelease()
5a635ded758223aace5fd93489167f03cc336502Christian Maeder{
9929f81562adecc8aafaefb14a0159afcf4a3351Christian Maeder LogFlowThisFunc(("\n"));
42c01284bba8d7c8d995c8dfb96ace57d28ed1bcTill Mossakowski
2b4130336e941b7d01c78a6da55449a4c6eca609Till Mossakowski uninit();
2b4130336e941b7d01c78a6da55449a4c6eca609Till Mossakowski}
c64d33a7fbeae730cbe65193fe3cc24e7aa1ddd6Christian Maeder
c64d33a7fbeae730cbe65193fe3cc24e7aa1ddd6Christian Maeder// public initializer/uninitializer for internal purposes only
/////////////////////////////////////////////////////////////////////////////
HRESULT Console::init(IMachine *aMachine, IInternalMachineControl *aControl)
{
AssertReturn(aMachine && aControl, E_INVALIDARG);
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
AssertReturn(autoInitSpan.isOk(), E_FAIL);
LogFlowThisFuncEnter();
LogFlowThisFunc(("aMachine=%p, aControl=%p\n", aMachine, aControl));
HRESULT rc = E_FAIL;
unconst(mMachine) = aMachine;
unconst(mControl) = aControl;
/* Cache essential properties and objects */
rc = mMachine->COMGETTER(State)(&mMachineState);
AssertComRCReturnRC(rc);
#ifdef VBOX_WITH_VRDP
rc = mMachine->COMGETTER(VRDPServer)(unconst(mVRDPServer).asOutParam());
AssertComRCReturnRC(rc);
#endif
/* Create associated child COM objects */
// Event source may be needed by other children
unconst(mEventSource).createObject();
rc = mEventSource->init(static_cast<IConsole*>(this));
AssertComRCReturnRC(rc);
unconst(mGuest).createObject();
rc = mGuest->init(this);
AssertComRCReturnRC(rc);
unconst(mKeyboard).createObject();
rc = mKeyboard->init(this);
AssertComRCReturnRC(rc);
unconst(mMouse).createObject();
rc = mMouse->init(this);
AssertComRCReturnRC(rc);
unconst(mDisplay).createObject();
rc = mDisplay->init(this);
AssertComRCReturnRC(rc);
unconst(mRemoteDisplayInfo).createObject();
rc = mRemoteDisplayInfo->init(this);
AssertComRCReturnRC(rc);
/* Grab global and machine shared folder lists */
rc = fetchSharedFolders(true /* aGlobal */);
AssertComRCReturnRC(rc);
rc = fetchSharedFolders(false /* aGlobal */);
AssertComRCReturnRC(rc);
/* Create other child objects */
unconst(mConsoleVRDPServer) = new ConsoleVRDPServer(this);
AssertReturn(mConsoleVRDPServer, E_FAIL);
mcAudioRefs = 0;
mcVRDPClients = 0;
mu32SingleRDPClientId = 0;
// VirtualBox 4.0: We no longer initialize the VMMDev instance here,
// which starts the HGCM thread. Instead, this is now done in the
// power-up thread when a VM is actually being powered up to avoid
// having HGCM threads all over the place every time a session is
// opened, even if that session will not run a VM.
// unconst(m_pVMMDev) = new VMMDev(this);
// AssertReturn(mVMMDev, E_FAIL);
unconst(mAudioSniffer) = new AudioSniffer(this);
AssertReturn(mAudioSniffer, E_FAIL);
/* Confirm a successful initialization when it's the case */
autoInitSpan.setSucceeded();
LogFlowThisFuncLeave();
return S_OK;
}
/**
* Uninitializes the Console object.
*/
void Console::uninit()
{
LogFlowThisFuncEnter();
/* Enclose the state transition Ready->InUninit->NotReady */
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
{
LogFlowThisFunc(("Already uninitialized.\n"));
LogFlowThisFuncLeave();
return;
}
LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
/*
* Uninit all children that use addDependentChild()/removeDependentChild()
* in their init()/uninit() methods.
*/
uninitDependentChildren();
/* power down the VM if necessary */
if (mpVM)
{
powerDown();
Assert(mpVM == NULL);
}
if (mVMZeroCallersSem != NIL_RTSEMEVENT)
{
RTSemEventDestroy(mVMZeroCallersSem);
mVMZeroCallersSem = NIL_RTSEMEVENT;
}
if (mpVmm2UserMethods)
{
RTMemFree((void *)mpVmm2UserMethods);
mpVmm2UserMethods = NULL;
}
if (mAudioSniffer)
{
delete mAudioSniffer;
unconst(mAudioSniffer) = NULL;
}
// if the VM had a VMMDev with an HGCM thread, then remove that here
if (m_pVMMDev)
{
delete m_pVMMDev;
unconst(m_pVMMDev) = NULL;
}
mGlobalSharedFolders.clear();
mMachineSharedFolders.clear();
mSharedFolders.clear();
mRemoteUSBDevices.clear();
mUSBDevices.clear();
if (mRemoteDisplayInfo)
{
mRemoteDisplayInfo->uninit();
unconst(mRemoteDisplayInfo).setNull();;
}
if (mDebugger)
{
mDebugger->uninit();
unconst(mDebugger).setNull();
}
if (mDisplay)
{
mDisplay->uninit();
unconst(mDisplay).setNull();
}
if (mMouse)
{
mMouse->uninit();
unconst(mMouse).setNull();
}
if (mKeyboard)
{
mKeyboard->uninit();
unconst(mKeyboard).setNull();;
}
if (mGuest)
{
mGuest->uninit();
unconst(mGuest).setNull();;
}
if (mConsoleVRDPServer)
{
delete mConsoleVRDPServer;
unconst(mConsoleVRDPServer) = NULL;
}
#ifdef VBOX_WITH_VRDP
unconst(mVRDPServer).setNull();
#endif
unconst(mControl).setNull();
unconst(mMachine).setNull();
// we don't perform uninit() as it's possible that some pending event refers to this source
unconst(mEventSource).setNull();
mCallbackData.clear();
LogFlowThisFuncLeave();
}
#ifdef VBOX_WITH_GUEST_PROPS
bool Console::enabledGuestPropertiesVRDP(void)
{
Bstr value;
HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/EnableGuestPropertiesVRDP").raw(),
value.asOutParam());
if (hrc == S_OK)
{
if (value == "1")
{
return true;
}
}
return false;
}
void Console::updateGuestPropertiesVRDPLogon(uint32_t u32ClientId, const char *pszUser, const char *pszDomain)
{
if (!enabledGuestPropertiesVRDP())
{
return;
}
int rc;
char *pszPropertyName;
Bstr bstrReadOnlyGuest(L"RDONLYGUEST");
rc = RTStrAPrintf(&pszPropertyName, "/VirtualBox/HostInfo/VRDP/Client/%u/Name", u32ClientId);
if (RT_SUCCESS(rc))
{
Bstr clientName;
mRemoteDisplayInfo->COMGETTER(ClientName)(clientName.asOutParam());
mMachine->SetGuestProperty(Bstr(pszPropertyName).raw(),
clientName.raw(),
bstrReadOnlyGuest.raw());
RTStrFree(pszPropertyName);
}
rc = RTStrAPrintf(&pszPropertyName, "/VirtualBox/HostInfo/VRDP/Client/%u/User", u32ClientId);
if (RT_SUCCESS(rc))
{
mMachine->SetGuestProperty(Bstr(pszPropertyName).raw(),
Bstr(pszUser).raw(),
bstrReadOnlyGuest.raw());
RTStrFree(pszPropertyName);
}
rc = RTStrAPrintf(&pszPropertyName, "/VirtualBox/HostInfo/VRDP/Client/%u/Domain", u32ClientId);
if (RT_SUCCESS(rc))
{
mMachine->SetGuestProperty(Bstr(pszPropertyName).raw(),
Bstr(pszDomain).raw(),
bstrReadOnlyGuest.raw());
RTStrFree(pszPropertyName);
}
char *pszClientId;
rc = RTStrAPrintf(&pszClientId, "%d", u32ClientId);
if (RT_SUCCESS(rc))
{
mMachine->SetGuestProperty(Bstr("/VirtualBox/HostInfo/VRDP/LastConnectedClient").raw(),
Bstr(pszClientId).raw(),
bstrReadOnlyGuest.raw());
RTStrFree(pszClientId);
}
return;
}
void Console::updateGuestPropertiesVRDPDisconnect(uint32_t u32ClientId)
{
if (!enabledGuestPropertiesVRDP())
return;
Bstr bstrReadOnlyGuest(L"RDONLYGUEST");
int rc;
char *pszPropertyName;
rc = RTStrAPrintf(&pszPropertyName, "/VirtualBox/HostInfo/VRDP/Client/%u/Name", u32ClientId);
if (RT_SUCCESS(rc))
{
mMachine->SetGuestProperty(Bstr(pszPropertyName).raw(), Bstr("").raw(),
bstrReadOnlyGuest.raw());
RTStrFree(pszPropertyName);
}
rc = RTStrAPrintf(&pszPropertyName, "/VirtualBox/HostInfo/VRDP/Client/%u/User", u32ClientId);
if (RT_SUCCESS(rc))
{
mMachine->SetGuestProperty(Bstr(pszPropertyName).raw(), Bstr("").raw(),
bstrReadOnlyGuest.raw());
RTStrFree(pszPropertyName);
}
rc = RTStrAPrintf(&pszPropertyName, "/VirtualBox/HostInfo/VRDP/Client/%u/Domain", u32ClientId);
if (RT_SUCCESS(rc))
{
mMachine->SetGuestProperty(Bstr(pszPropertyName).raw(), Bstr("").raw(),
bstrReadOnlyGuest.raw());
RTStrFree(pszPropertyName);
}
char *pszClientId;
rc = RTStrAPrintf(&pszClientId, "%d", u32ClientId);
if (RT_SUCCESS(rc))
{
mMachine->SetGuestProperty(Bstr("/VirtualBox/HostInfo/VRDP/LastDisconnectedClient").raw(),
Bstr(pszClientId).raw(),
bstrReadOnlyGuest.raw());
RTStrFree(pszClientId);
}
return;
}
#endif /* VBOX_WITH_GUEST_PROPS */
int Console::VRDPClientLogon(uint32_t u32ClientId, const char *pszUser, const char *pszPassword, const char *pszDomain)
{
LogFlowFuncEnter();
LogFlowFunc(("%d, %s, %s, %s\n", u32ClientId, pszUser, pszPassword, pszDomain));
AutoCaller autoCaller(this);
if (!autoCaller.isOk())
{
/* Console has been already uninitialized, deny request */
LogRel(("VRDPAUTH: Access denied (Console uninitialized).\n"));
LogFlowFuncLeave();
return VERR_ACCESS_DENIED;
}
Bstr id;
HRESULT hrc = mMachine->COMGETTER(Id)(id.asOutParam());
Guid uuid = Guid(id);
AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
VRDPAuthType_T authType = VRDPAuthType_Null;
hrc = mVRDPServer->COMGETTER(AuthType)(&authType);
AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
ULONG authTimeout = 0;
hrc = mVRDPServer->COMGETTER(AuthTimeout)(&authTimeout);
AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
VRDPAuthResult result = VRDPAuthAccessDenied;
VRDPAuthGuestJudgement guestJudgement = VRDPAuthGuestNotAsked;
LogFlowFunc(("Auth type %d\n", authType));
LogRel(("VRDPAUTH: User: [%s]. Domain: [%s]. Authentication type: [%s]\n",
pszUser, pszDomain,
authType == VRDPAuthType_Null?
"Null":
(authType == VRDPAuthType_External?
"External":
(authType == VRDPAuthType_Guest?
"Guest":
"INVALID"
)
)
));
switch (authType)
{
case VRDPAuthType_Null:
{
result = VRDPAuthAccessGranted;
break;
}
case VRDPAuthType_External:
{
/* Call the external library. */
result = mConsoleVRDPServer->Authenticate(uuid, guestJudgement, pszUser, pszPassword, pszDomain, u32ClientId);
if (result != VRDPAuthDelegateToGuest)
{
break;
}
LogRel(("VRDPAUTH: Delegated to guest.\n"));
LogFlowFunc(("External auth asked for guest judgement\n"));
} /* pass through */
case VRDPAuthType_Guest:
{
guestJudgement = VRDPAuthGuestNotReacted;
// @todo r=dj locking required here for m_pVMMDev?
PPDMIVMMDEVPORT pDevPort;
if ( (m_pVMMDev)
&& ((pDevPort = m_pVMMDev->getVMMDevPort()))
)
{
/* Issue the request to guest. Assume that the call does not require EMT. It should not. */
/* Ask the guest to judge these credentials. */
uint32_t u32GuestFlags = VMMDEV_SETCREDENTIALS_JUDGE;
int rc = pDevPort->pfnSetCredentials(pDevPort, pszUser, pszPassword, pszDomain, u32GuestFlags);
if (RT_SUCCESS(rc))
{
/* Wait for guest. */
rc = m_pVMMDev->WaitCredentialsJudgement(authTimeout, &u32GuestFlags);
if (RT_SUCCESS(rc))
{
switch (u32GuestFlags & (VMMDEV_CREDENTIALS_JUDGE_OK | VMMDEV_CREDENTIALS_JUDGE_DENY | VMMDEV_CREDENTIALS_JUDGE_NOJUDGEMENT))
{
case VMMDEV_CREDENTIALS_JUDGE_DENY: guestJudgement = VRDPAuthGuestAccessDenied; break;
case VMMDEV_CREDENTIALS_JUDGE_NOJUDGEMENT: guestJudgement = VRDPAuthGuestNoJudgement; break;
case VMMDEV_CREDENTIALS_JUDGE_OK: guestJudgement = VRDPAuthGuestAccessGranted; break;
default:
LogFlowFunc(("Invalid guest flags %08X!!!\n", u32GuestFlags)); break;
}
}
else
{
LogFlowFunc(("Wait for credentials judgement rc = %Rrc!!!\n", rc));
}
LogFlowFunc(("Guest judgement %d\n", guestJudgement));
}
else
{
LogFlowFunc(("Could not set credentials rc = %Rrc!!!\n", rc));
}
}
if (authType == VRDPAuthType_External)
{
LogRel(("VRDPAUTH: Guest judgement %d.\n", guestJudgement));
LogFlowFunc(("External auth called again with guest judgement = %d\n", guestJudgement));
result = mConsoleVRDPServer->Authenticate(uuid, guestJudgement, pszUser, pszPassword, pszDomain, u32ClientId);
}
else
{
switch (guestJudgement)
{
case VRDPAuthGuestAccessGranted:
result = VRDPAuthAccessGranted;
break;
default:
result = VRDPAuthAccessDenied;
break;
}
}
} break;
default:
AssertFailed();
}
LogFlowFunc(("Result = %d\n", result));
LogFlowFuncLeave();
if (result != VRDPAuthAccessGranted)
{
/* Reject. */
LogRel(("VRDPAUTH: Access denied.\n"));
return VERR_ACCESS_DENIED;
}
LogRel(("VRDPAUTH: Access granted.\n"));
/* Multiconnection check must be made after authentication, so bad clients would not interfere with a good one. */
BOOL allowMultiConnection = FALSE;
hrc = mVRDPServer->COMGETTER(AllowMultiConnection)(&allowMultiConnection);
AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
BOOL reuseSingleConnection = FALSE;
hrc = mVRDPServer->COMGETTER(ReuseSingleConnection)(&reuseSingleConnection);
AssertComRCReturn(hrc, VERR_ACCESS_DENIED);
LogFlowFunc(("allowMultiConnection %d, reuseSingleConnection = %d, mcVRDPClients = %d, mu32SingleRDPClientId = %d\n", allowMultiConnection, reuseSingleConnection, mcVRDPClients, mu32SingleRDPClientId));
if (allowMultiConnection == FALSE)
{
/* Note: the 'mcVRDPClients' variable is incremented in ClientConnect callback, which is called when the client
* is successfully connected, that is after the ClientLogon callback. Therefore the mcVRDPClients
* value is 0 for first client.
*/
if (mcVRDPClients != 0)
{
Assert(mcVRDPClients == 1);
/* There is a client already.
* If required drop the existing client connection and let the connecting one in.
*/
if (reuseSingleConnection)
{
LogRel(("VRDPAUTH: Multiple connections are not enabled. Disconnecting existing client.\n"));
mConsoleVRDPServer->DisconnectClient(mu32SingleRDPClientId, false);
}
else
{
/* Reject. */
LogRel(("VRDPAUTH: Multiple connections are not enabled. Access denied.\n"));
return VERR_ACCESS_DENIED;
}
}
/* Save the connected client id. From now on it will be necessary to disconnect this one. */
mu32SingleRDPClientId = u32ClientId;
}
#ifdef VBOX_WITH_GUEST_PROPS
updateGuestPropertiesVRDPLogon(u32ClientId, pszUser, pszDomain);
#endif /* VBOX_WITH_GUEST_PROPS */
/* Check if the successfully verified credentials are to be sent to the guest. */
BOOL fProvideGuestCredentials = FALSE;
Bstr value;
hrc = mMachine->GetExtraData(Bstr("VRDP/ProvideGuestCredentials").raw(),
value.asOutParam());
if (SUCCEEDED(hrc) && value == "1")
{
/* Provide credentials only if there are no logged in users. */
Bstr noLoggedInUsersValue;
LONG64 ul64Timestamp = 0;
Bstr flags;
hrc = getGuestProperty(Bstr("/VirtualBox/GuestInfo/OS/NoLoggedInUsers").raw(),
noLoggedInUsersValue.asOutParam(), &ul64Timestamp, flags.asOutParam());
if (SUCCEEDED(hrc) && noLoggedInUsersValue != Bstr("false"))
{
fProvideGuestCredentials = TRUE;
}
}
// @todo r=dj locking required here for m_pVMMDev?
if ( fProvideGuestCredentials
&& m_pVMMDev)
{
uint32_t u32GuestFlags = VMMDEV_SETCREDENTIALS_GUESTLOGON;
int rc = m_pVMMDev->getVMMDevPort()->pfnSetCredentials(m_pVMMDev->getVMMDevPort(),
pszUser, pszPassword, pszDomain, u32GuestFlags);
AssertRC(rc);
}
return VINF_SUCCESS;
}
void Console::VRDPClientConnect(uint32_t u32ClientId)
{
LogFlowFuncEnter();
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
#ifdef VBOX_WITH_VRDP
uint32_t u32Clients = ASMAtomicIncU32(&mcVRDPClients);
VMMDev *pDev;
PPDMIVMMDEVPORT pPort;
if ( (u32Clients == 1)
&& ((pDev = getVMMDev()))
&& ((pPort = pDev->getVMMDevPort()))
)
{
pPort->pfnVRDPChange(pPort,
true,
VRDP_EXPERIENCE_LEVEL_FULL); // @todo configurable
}
NOREF(u32ClientId);
mDisplay->VideoAccelVRDP(true);
#endif /* VBOX_WITH_VRDP */
LogFlowFuncLeave();
return;
}
void Console::VRDPClientDisconnect(uint32_t u32ClientId,
uint32_t fu32Intercepted)
{
LogFlowFuncEnter();
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
AssertReturnVoid(mConsoleVRDPServer);
#ifdef VBOX_WITH_VRDP
uint32_t u32Clients = ASMAtomicDecU32(&mcVRDPClients);
VMMDev *pDev;
PPDMIVMMDEVPORT pPort;
if ( (u32Clients == 0)
&& ((pDev = getVMMDev()))
&& ((pPort = pDev->getVMMDevPort()))
)
{
pPort->pfnVRDPChange(pPort,
false,
0);
}
mDisplay->VideoAccelVRDP(false);
#endif /* VBOX_WITH_VRDP */
if (fu32Intercepted & VRDP_CLIENT_INTERCEPT_USB)
{
mConsoleVRDPServer->USBBackendDelete(u32ClientId);
}
#ifdef VBOX_WITH_VRDP
if (fu32Intercepted & VRDP_CLIENT_INTERCEPT_CLIPBOARD)
{
mConsoleVRDPServer->ClipboardDelete(u32ClientId);
}
if (fu32Intercepted & VRDP_CLIENT_INTERCEPT_AUDIO)
{
mcAudioRefs--;
if (mcAudioRefs <= 0)
{
if (mAudioSniffer)
{
PPDMIAUDIOSNIFFERPORT port = mAudioSniffer->getAudioSnifferPort();
if (port)
{
port->pfnSetup(port, false, false);
}
}
}
}
#endif /* VBOX_WITH_VRDP */
Bstr uuid;
HRESULT hrc = mMachine->COMGETTER(Id)(uuid.asOutParam());
AssertComRC(hrc);
VRDPAuthType_T authType = VRDPAuthType_Null;
hrc = mVRDPServer->COMGETTER(AuthType)(&authType);
AssertComRC(hrc);
if (authType == VRDPAuthType_External)
mConsoleVRDPServer->AuthDisconnect(uuid, u32ClientId);
#ifdef VBOX_WITH_GUEST_PROPS
updateGuestPropertiesVRDPDisconnect(u32ClientId);
#endif /* VBOX_WITH_GUEST_PROPS */
#ifdef VBOX_WITH_VRDP_MEMLEAK_DETECTOR
MLDMemDump();
#endif /* !VBOX_WITH_VRDP_MEMLEAK_DETECTOR */
LogFlowFuncLeave();
return;
}
void Console::VRDPInterceptAudio(uint32_t u32ClientId)
{
LogFlowFuncEnter();
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
LogFlowFunc(("mAudioSniffer %p, u32ClientId %d.\n",
mAudioSniffer, u32ClientId));
NOREF(u32ClientId);
#ifdef VBOX_WITH_VRDP
++mcAudioRefs;
if (mcAudioRefs == 1)
{
if (mAudioSniffer)
{
PPDMIAUDIOSNIFFERPORT port = mAudioSniffer->getAudioSnifferPort();
if (port)
{
port->pfnSetup(port, true, true);
}
}
}
#endif
LogFlowFuncLeave();
return;
}
void Console::VRDPInterceptUSB(uint32_t u32ClientId, void **ppvIntercept)
{
LogFlowFuncEnter();
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
AssertReturnVoid(mConsoleVRDPServer);
mConsoleVRDPServer->USBBackendCreate(u32ClientId, ppvIntercept);
LogFlowFuncLeave();
return;
}
void Console::VRDPInterceptClipboard(uint32_t u32ClientId)
{
LogFlowFuncEnter();
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
AssertReturnVoid(mConsoleVRDPServer);
#ifdef VBOX_WITH_VRDP
mConsoleVRDPServer->ClipboardCreate(u32ClientId);
#endif /* VBOX_WITH_VRDP */
LogFlowFuncLeave();
return;
}
//static
const char *Console::sSSMConsoleUnit = "ConsoleData";
//static
uint32_t Console::sSSMConsoleVer = 0x00010001;
/**
* Loads various console data stored in the saved state file.
* This method does validation of the state file and returns an error info
* when appropriate.
*
* The method does nothing if the machine is not in the Saved file or if
* console data from it has already been loaded.
*
* @note The caller must lock this object for writing.
*/
HRESULT Console::loadDataFromSavedState()
{
if (mMachineState != MachineState_Saved || mSavedStateDataLoaded)
return S_OK;
Bstr savedStateFile;
HRESULT rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
if (FAILED(rc))
return rc;
PSSMHANDLE ssm;
int vrc = SSMR3Open(Utf8Str(savedStateFile).c_str(), 0, &ssm);
if (RT_SUCCESS(vrc))
{
uint32_t version = 0;
vrc = SSMR3Seek(ssm, sSSMConsoleUnit, 0 /* iInstance */, &version);
if (SSM_VERSION_MAJOR(version) == SSM_VERSION_MAJOR(sSSMConsoleVer))
{
if (RT_SUCCESS(vrc))
vrc = loadStateFileExecInternal(ssm, version);
else if (vrc == VERR_SSM_UNIT_NOT_FOUND)
vrc = VINF_SUCCESS;
}
else
vrc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
SSMR3Close(ssm);
}
if (RT_FAILURE(vrc))
rc = setError(VBOX_E_FILE_ERROR,
tr("The saved state file '%ls' is invalid (%Rrc). Delete the saved state and try again"),
savedStateFile.raw(), vrc);
mSavedStateDataLoaded = true;
return rc;
}
/**
* Callback handler to save various console data to the state file,
* called when the user saves the VM state.
*
* @param pvUser pointer to Console
*
* @note Locks the Console object for reading.
*/
//static
DECLCALLBACK(void)
Console::saveStateFileExec(PSSMHANDLE pSSM, void *pvUser)
{
LogFlowFunc(("\n"));
Console *that = static_cast<Console *>(pvUser);
AssertReturnVoid(that);
AutoCaller autoCaller(that);
AssertComRCReturnVoid(autoCaller.rc());
AutoReadLock alock(that COMMA_LOCKVAL_SRC_POS);
int vrc = SSMR3PutU32(pSSM, (uint32_t)that->mSharedFolders.size());
AssertRC(vrc);
for (SharedFolderMap::const_iterator it = that->mSharedFolders.begin();
it != that->mSharedFolders.end();
++ it)
{
ComObjPtr<SharedFolder> folder = (*it).second;
// don't lock the folder because methods we access are const
Utf8Str name = folder->getName();
vrc = SSMR3PutU32(pSSM, (uint32_t)name.length() + 1 /* term. 0 */);
AssertRC(vrc);
vrc = SSMR3PutStrZ(pSSM, name.c_str());
AssertRC(vrc);
Utf8Str hostPath = folder->getHostPath();
vrc = SSMR3PutU32(pSSM, (uint32_t)hostPath.length() + 1 /* term. 0 */);
AssertRC(vrc);
vrc = SSMR3PutStrZ(pSSM, hostPath.c_str());
AssertRC(vrc);
vrc = SSMR3PutBool(pSSM, !!folder->isWritable());
AssertRC(vrc);
vrc = SSMR3PutBool(pSSM, !!folder->isAutoMounted());
AssertRC(vrc);
}
return;
}
/**
* Callback handler to load various console data from the state file.
* Called when the VM is being restored from the saved state.
*
* @param pvUser pointer to Console
* @param uVersion Console unit version.
* Should match sSSMConsoleVer.
* @param uPass The data pass.
*
* @note Should locks the Console object for writing, if necessary.
*/
//static
DECLCALLBACK(int)
Console::loadStateFileExec(PSSMHANDLE pSSM, void *pvUser, uint32_t uVersion, uint32_t uPass)
{
LogFlowFunc(("\n"));
if (SSM_VERSION_MAJOR_CHANGED(uVersion, sSSMConsoleVer))
return VERR_VERSION_MISMATCH;
Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
Console *that = static_cast<Console *>(pvUser);
AssertReturn(that, VERR_INVALID_PARAMETER);
/* Currently, nothing to do when we've been called from VMR3Load*. */
return SSMR3SkipToEndOfUnit(pSSM);
}
/**
* Method to load various console data from the state file.
* Called from #loadDataFromSavedState.
*
* @param pvUser pointer to Console
* @param u32Version Console unit version.
* Should match sSSMConsoleVer.
*
* @note Locks the Console object for writing.
*/
int
Console::loadStateFileExecInternal(PSSMHANDLE pSSM, uint32_t u32Version)
{
AutoCaller autoCaller(this);
AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(mSharedFolders.size() == 0, VERR_INTERNAL_ERROR);
uint32_t size = 0;
int vrc = SSMR3GetU32(pSSM, &size);
AssertRCReturn(vrc, vrc);
for (uint32_t i = 0; i < size; ++ i)
{
Bstr name;
Bstr hostPath;
bool writable = true;
bool autoMount = false;
uint32_t szBuf = 0;
char *buf = NULL;
vrc = SSMR3GetU32(pSSM, &szBuf);
AssertRCReturn(vrc, vrc);
buf = new char[szBuf];
vrc = SSMR3GetStrZ(pSSM, buf, szBuf);
AssertRC(vrc);
name = buf;
delete[] buf;
vrc = SSMR3GetU32(pSSM, &szBuf);
AssertRCReturn(vrc, vrc);
buf = new char[szBuf];
vrc = SSMR3GetStrZ(pSSM, buf, szBuf);
AssertRC(vrc);
hostPath = buf;
delete[] buf;
if (u32Version > 0x00010000)
SSMR3GetBool(pSSM, &writable);
if (u32Version > 0x00010000) // ???
SSMR3GetBool(pSSM, &autoMount);
ComObjPtr<SharedFolder> sharedFolder;
sharedFolder.createObject();
HRESULT rc = sharedFolder->init(this, name.raw(), hostPath.raw(),
writable, autoMount);
AssertComRCReturn(rc, VERR_INTERNAL_ERROR);
mSharedFolders.insert(std::make_pair(name, sharedFolder));
}
return VINF_SUCCESS;
}
#ifdef VBOX_WITH_GUEST_PROPS
// static
DECLCALLBACK(int) Console::doGuestPropNotification(void *pvExtension,
uint32_t u32Function,
void *pvParms,
uint32_t cbParms)
{
using namespace guestProp;
Assert(u32Function == 0); NOREF(u32Function);
/*
* No locking, as this is purely a notification which does not make any
* changes to the object state.
*/
PHOSTCALLBACKDATA pCBData = reinterpret_cast<PHOSTCALLBACKDATA>(pvParms);
AssertReturn(sizeof(HOSTCALLBACKDATA) == cbParms, VERR_INVALID_PARAMETER);
AssertReturn(HOSTCALLBACKMAGIC == pCBData->u32Magic, VERR_INVALID_PARAMETER);
Log5(("Console::doGuestPropNotification: pCBData={.pcszName=%s, .pcszValue=%s, .pcszFlags=%s}\n",
pCBData->pcszName, pCBData->pcszValue, pCBData->pcszFlags));
int rc;
Bstr name(pCBData->pcszName);
Bstr value(pCBData->pcszValue);
Bstr flags(pCBData->pcszFlags);
ComObjPtr<Console> ptrConsole = reinterpret_cast<Console *>(pvExtension);
HRESULT hrc = ptrConsole->mControl->PushGuestProperty(name.raw(),
value.raw(),
pCBData->u64Timestamp,
flags.raw());
if (SUCCEEDED(hrc))
rc = VINF_SUCCESS;
else
{
LogFunc(("Console::doGuestPropNotification: hrc=%Rhrc pCBData={.pcszName=%s, .pcszValue=%s, .pcszFlags=%s}\n",
pCBData->pcszName, pCBData->pcszValue, pCBData->pcszFlags));
rc = Global::vboxStatusCodeFromCOM(hrc);
}
return rc;
}
HRESULT Console::doEnumerateGuestProperties(CBSTR aPatterns,
ComSafeArrayOut(BSTR, aNames),
ComSafeArrayOut(BSTR, aValues),
ComSafeArrayOut(LONG64, aTimestamps),
ComSafeArrayOut(BSTR, aFlags))
{
AssertReturn(m_pVMMDev, E_FAIL);
using namespace guestProp;
VBOXHGCMSVCPARM parm[3];
Utf8Str utf8Patterns(aPatterns);
parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
parm[0].u.pointer.addr = (void*)utf8Patterns.c_str();
parm[0].u.pointer.size = (uint32_t)utf8Patterns.length() + 1;
/*
* Now things get slightly complicated. Due to a race with the guest adding
* properties, there is no good way to know how much to enlarge a buffer for
* the service to enumerate into. We choose a decent starting size and loop a
* few times, each time retrying with the size suggested by the service plus
* one Kb.
*/
size_t cchBuf = 4096;
Utf8Str Utf8Buf;
int vrc = VERR_BUFFER_OVERFLOW;
for (unsigned i = 0; i < 10 && (VERR_BUFFER_OVERFLOW == vrc); ++i)
{
try
{
Utf8Buf.reserve(cchBuf + 1024);
}
catch(...)
{
return E_OUTOFMEMORY;
}
parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
parm[1].u.pointer.addr = Utf8Buf.mutableRaw();
parm[1].u.pointer.size = (uint32_t)cchBuf + 1024;
vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", ENUM_PROPS_HOST, 3,
&parm[0]);
Utf8Buf.jolt();
if (parm[2].type != VBOX_HGCM_SVC_PARM_32BIT)
return setError(E_FAIL, tr("Internal application error"));
cchBuf = parm[2].u.uint32;
}
if (VERR_BUFFER_OVERFLOW == vrc)
return setError(E_UNEXPECTED,
tr("Temporary failure due to guest activity, please retry"));
/*
* Finally we have to unpack the data returned by the service into the safe
* arrays supplied by the caller. We start by counting the number of entries.
*/
const char *pszBuf
= reinterpret_cast<const char *>(parm[1].u.pointer.addr);
unsigned cEntries = 0;
/* The list is terminated by a zero-length string at the end of a set
* of four strings. */
for (size_t i = 0; strlen(pszBuf + i) != 0; )
{
/* We are counting sets of four strings. */
for (unsigned j = 0; j < 4; ++j)
i += strlen(pszBuf + i) + 1;
++cEntries;
}
/*
* And now we create the COM safe arrays and fill them in.
*/
com::SafeArray<BSTR> names(cEntries);
com::SafeArray<BSTR> values(cEntries);
com::SafeArray<LONG64> timestamps(cEntries);
com::SafeArray<BSTR> flags(cEntries);
size_t iBuf = 0;
/* Rely on the service to have formated the data correctly. */
for (unsigned i = 0; i < cEntries; ++i)
{
size_t cchName = strlen(pszBuf + iBuf);
Bstr(pszBuf + iBuf).detachTo(&names[i]);
iBuf += cchName + 1;
size_t cchValue = strlen(pszBuf + iBuf);
Bstr(pszBuf + iBuf).detachTo(&values[i]);
iBuf += cchValue + 1;
size_t cchTimestamp = strlen(pszBuf + iBuf);
timestamps[i] = RTStrToUInt64(pszBuf + iBuf);
iBuf += cchTimestamp + 1;
size_t cchFlags = strlen(pszBuf + iBuf);
Bstr(pszBuf + iBuf).detachTo(&flags[i]);
iBuf += cchFlags + 1;
}
names.detachTo(ComSafeArrayOutArg(aNames));
values.detachTo(ComSafeArrayOutArg(aValues));
timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
flags.detachTo(ComSafeArrayOutArg(aFlags));
return S_OK;
}
#endif /* VBOX_WITH_GUEST_PROPS */
// IConsole properties
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP Console::COMGETTER(Machine)(IMachine **aMachine)
{
CheckComArgOutPointerValid(aMachine);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/* mMachine is constant during life time, no need to lock */
mMachine.queryInterfaceTo(aMachine);
/* callers expect to get a valid reference, better fail than crash them */
if (mMachine.isNull())
return E_FAIL;
return S_OK;
}
STDMETHODIMP Console::COMGETTER(State)(MachineState_T *aMachineState)
{
CheckComArgOutPointerValid(aMachineState);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
/* we return our local state (since it's always the same as on the server) */
*aMachineState = mMachineState;
return S_OK;
}
STDMETHODIMP Console::COMGETTER(Guest)(IGuest **aGuest)
{
CheckComArgOutPointerValid(aGuest);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/* mGuest is constant during life time, no need to lock */
mGuest.queryInterfaceTo(aGuest);
return S_OK;
}
STDMETHODIMP Console::COMGETTER(Keyboard)(IKeyboard **aKeyboard)
{
CheckComArgOutPointerValid(aKeyboard);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/* mKeyboard is constant during life time, no need to lock */
mKeyboard.queryInterfaceTo(aKeyboard);
return S_OK;
}
STDMETHODIMP Console::COMGETTER(Mouse)(IMouse **aMouse)
{
CheckComArgOutPointerValid(aMouse);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/* mMouse is constant during life time, no need to lock */
mMouse.queryInterfaceTo(aMouse);
return S_OK;
}
STDMETHODIMP Console::COMGETTER(Display)(IDisplay **aDisplay)
{
CheckComArgOutPointerValid(aDisplay);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/* mDisplay is constant during life time, no need to lock */
mDisplay.queryInterfaceTo(aDisplay);
return S_OK;
}
STDMETHODIMP Console::COMGETTER(Debugger)(IMachineDebugger **aDebugger)
{
CheckComArgOutPointerValid(aDebugger);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/* we need a write lock because of the lazy mDebugger initialization*/
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/* check if we have to create the debugger object */
if (!mDebugger)
{
unconst(mDebugger).createObject();
mDebugger->init(this);
}
mDebugger.queryInterfaceTo(aDebugger);
return S_OK;
}
STDMETHODIMP Console::COMGETTER(USBDevices)(ComSafeArrayOut(IUSBDevice *, aUSBDevices))
{
CheckComArgOutSafeArrayPointerValid(aUSBDevices);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
SafeIfaceArray<IUSBDevice> collection(mUSBDevices);
collection.detachTo(ComSafeArrayOutArg(aUSBDevices));
return S_OK;
}
STDMETHODIMP Console::COMGETTER(RemoteUSBDevices)(ComSafeArrayOut(IHostUSBDevice *, aRemoteUSBDevices))
{
CheckComArgOutSafeArrayPointerValid(aRemoteUSBDevices);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
SafeIfaceArray<IHostUSBDevice> collection(mRemoteUSBDevices);
collection.detachTo(ComSafeArrayOutArg(aRemoteUSBDevices));
return S_OK;
}
STDMETHODIMP Console::COMGETTER(RemoteDisplayInfo)(IRemoteDisplayInfo **aRemoteDisplayInfo)
{
CheckComArgOutPointerValid(aRemoteDisplayInfo);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/* mDisplay is constant during life time, no need to lock */
mRemoteDisplayInfo.queryInterfaceTo(aRemoteDisplayInfo);
return S_OK;
}
STDMETHODIMP
Console::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
{
CheckComArgOutSafeArrayPointerValid(aSharedFolders);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/* loadDataFromSavedState() needs a write lock */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/* Read console data stored in the saved state file (if not yet done) */
HRESULT rc = loadDataFromSavedState();
if (FAILED(rc)) return rc;
SafeIfaceArray<ISharedFolder> sf(mSharedFolders);
sf.detachTo(ComSafeArrayOutArg(aSharedFolders));
return S_OK;
}
STDMETHODIMP Console::COMGETTER(EventSource)(IEventSource ** aEventSource)
{
CheckComArgOutPointerValid(aEventSource);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
// no need to lock - lifetime constant
mEventSource.queryInterfaceTo(aEventSource);
return S_OK;
}
// IConsole methods
/////////////////////////////////////////////////////////////////////////////
STDMETHODIMP Console::PowerUp(IProgress **aProgress)
{
return powerUp(aProgress, false /* aPaused */);
}
STDMETHODIMP Console::PowerUpPaused(IProgress **aProgress)
{
return powerUp(aProgress, true /* aPaused */);
}
STDMETHODIMP Console::PowerDown(IProgress **aProgress)
{
if (aProgress == NULL)
return E_POINTER;
LogFlowThisFuncEnter();
LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
switch (mMachineState)
{
case MachineState_Running:
case MachineState_Paused:
case MachineState_Stuck:
break;
/* Try cancel the teleportation. */
case MachineState_Teleporting:
case MachineState_TeleportingPausedVM:
if (!mptrCancelableProgress.isNull())
{
HRESULT hrc = mptrCancelableProgress->Cancel();
if (SUCCEEDED(hrc))
break;
}
return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point in a teleportation"));
/* Try cancel the live snapshot. */
case MachineState_LiveSnapshotting:
if (!mptrCancelableProgress.isNull())
{
HRESULT hrc = mptrCancelableProgress->Cancel();
if (SUCCEEDED(hrc))
break;
}
return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point in a live snapshot"));
/* Try cancel the FT sync. */
case MachineState_FaultTolerantSyncing:
if (!mptrCancelableProgress.isNull())
{
HRESULT hrc = mptrCancelableProgress->Cancel();
if (SUCCEEDED(hrc))
break;
}
return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point in a fault tolerant sync"));
/* extra nice error message for a common case */
case MachineState_Saved:
return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down a saved virtual machine"));
case MachineState_Stopping:
return setError(VBOX_E_INVALID_VM_STATE, tr("The virtual machine is being powered down"));
default:
return setError(VBOX_E_INVALID_VM_STATE,
tr("Invalid machine state: %s (must be Running, Paused or Stuck)"),
Global::stringifyMachineState(mMachineState));
}
LogFlowThisFunc(("Initiating SHUTDOWN request...\n"));
/* create an IProgress object to track progress of this operation */
ComObjPtr<Progress> progress;
progress.createObject();
progress->init(static_cast<IConsole *>(this),
Bstr(tr("Stopping virtual machine")).raw(),
FALSE /* aCancelable */);
/* setup task object and thread to carry out the operation asynchronously */
std::auto_ptr<VMProgressTask> task(new VMProgressTask(this, progress, true /* aUsesVMPtr */));
AssertReturn(task->isOk(), E_FAIL);
int vrc = RTThreadCreate(NULL, Console::powerDownThread,
(void *) task.get(), 0,
RTTHREADTYPE_MAIN_WORKER, 0,
"VMPowerDown");
if (RT_FAILURE(vrc))
return setError(E_FAIL, "Could not create VMPowerDown thread (%Rrc)", vrc);
/* task is now owned by powerDownThread(), so release it */
task.release();
/* go to Stopping state to forbid state-dependant operations */
setMachineState(MachineState_Stopping);
/* pass the progress to the caller */
progress.queryInterfaceTo(aProgress);
LogFlowThisFuncLeave();
return S_OK;
}
STDMETHODIMP Console::Reset()
{
LogFlowThisFuncEnter();
LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Teleporting
&& mMachineState != MachineState_LiveSnapshotting
/** @todo r=bird: This should be allowed on paused VMs as well. Later. */
)
return setInvalidMachineStateError();
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* leave the lock before a VMR3* call (EMT will call us back)! */
alock.leave();
int vrc = VMR3Reset(mpVM);
HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
setError(VBOX_E_VM_ERROR,
tr("Could not reset the machine (%Rrc)"),
vrc);
LogFlowThisFunc(("mMachineState=%d, rc=%08X\n", mMachineState, rc));
LogFlowThisFuncLeave();
return rc;
}
DECLCALLBACK(int) Console::unplugCpu(Console *pThis, unsigned uCpu)
{
LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, uCpu));
AssertReturn(pThis, VERR_INVALID_PARAMETER);
int vrc = PDMR3DeviceDetach(pThis->mpVM, "acpi", 0, uCpu, 0);
Log(("UnplugCpu: rc=%Rrc\n", vrc));
return vrc;
}
HRESULT Console::doCPURemove(ULONG aCpu)
{
HRESULT rc = S_OK;
LogFlowThisFuncEnter();
LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/** @todo r=klaus holding the lock while triggering VMMDev/EMT activity is
* asking for deadlocks. Code MUST drop any lock before touching VMMDev. */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn(m_pVMMDev, E_FAIL);
PPDMIVMMDEVPORT pDevPort = m_pVMMDev->getVMMDevPort();
AssertReturn(pDevPort, E_FAIL);
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Teleporting
&& mMachineState != MachineState_LiveSnapshotting
)
return setInvalidMachineStateError();
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* Check if the CPU is present */
BOOL fCpuAttached;
rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached);
if (FAILED(rc)) return rc;
if (!fCpuAttached)
return setError(E_FAIL,
tr("CPU %d is not attached"), aCpu);
/* Check if the CPU is unlocked */
PPDMIBASE pBase;
int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, aCpu, &pBase);
bool fLocked = true;
if (RT_SUCCESS(vrc))
{
uint32_t idCpuCore, idCpuPackage;
/* Notify the guest if possible. */
vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(mpVM, aCpu, &idCpuCore, &idCpuPackage);
AssertRC(vrc);
Assert(pBase);
PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
vrc = pDevPort->pfnCpuHotUnplug(pDevPort, idCpuCore, idCpuPackage);
if (RT_SUCCESS(vrc))
{
unsigned cTries = 100;
do
{
/* It will take some time until the event is processed in the guest. Wait */
vrc = pPort ? pPort->pfnGetCpuStatus(pPort, aCpu, &fLocked) : VERR_INVALID_POINTER;
if (RT_SUCCESS(vrc) && !fLocked)
break;
/* Sleep a bit */
RTThreadSleep(100);
} while (cTries-- > 0);
}
else if (vrc == VERR_CPU_HOTPLUG_NOT_MONITORED_BY_GUEST)
{
/* Query one time. It is possible that the user ejected the CPU. */
vrc = pPort ? pPort->pfnGetCpuStatus(pPort, aCpu, &fLocked) : VERR_INVALID_POINTER;
}
}
/* If the CPU was unlocked we can detach it now. */
if (RT_SUCCESS(vrc) && !fLocked)
{
/*
* Call worker in EMT, that's faster and safer than doing everything
* using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
* here to make requests from under the lock in order to serialize them.
*/
PVMREQ pReq;
vrc = VMR3ReqCall(mpVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
(PFNRT)Console::unplugCpu, 2,
this, aCpu);
/* leave the lock before a VMR3* call (EMT will call us back)! */
alock.leave();
if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
{
vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
AssertRC(vrc);
if (RT_SUCCESS(vrc))
vrc = pReq->iStatus;
}
VMR3ReqFree(pReq);
if (RT_SUCCESS(vrc))
{
/* Detach it from the VM */
vrc = VMR3HotUnplugCpu(mpVM, aCpu);
AssertRC(vrc);
}
else
rc = setError(VBOX_E_VM_ERROR,
tr("Hot-Remove failed (rc=%Rrc)"), vrc);
}
else
rc = setError(VBOX_E_VM_ERROR,
tr("Hot-Remove was aborted because the CPU may still be used by the guest"), VERR_RESOURCE_BUSY);
LogFlowThisFunc(("mMachineState=%d, rc=%08X\n", mMachineState, rc));
LogFlowThisFuncLeave();
return rc;
}
DECLCALLBACK(int) Console::plugCpu(Console *pThis, unsigned uCpu)
{
LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, uCpu));
AssertReturn(pThis, VERR_INVALID_PARAMETER);
int rc = VMR3HotPlugCpu(pThis->mpVM, uCpu);
AssertRC(rc);
PCFGMNODE pInst = CFGMR3GetChild(CFGMR3GetRoot(pThis->mpVM), "Devices/acpi/0/");
AssertRelease(pInst);
/* nuke anything which might have been left behind. */
CFGMR3RemoveNode(CFGMR3GetChildF(pInst, "LUN#%d", uCpu));
#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; } } while (0)
PCFGMNODE pLunL0;
PCFGMNODE pCfg;
rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%d", uCpu); RC_CHECK();
rc = CFGMR3InsertString(pLunL0, "Driver", "ACPICpu"); RC_CHECK();
rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();
/*
* Attach the driver.
*/
PPDMIBASE pBase;
rc = PDMR3DeviceAttach(pThis->mpVM, "acpi", 0, uCpu, 0, &pBase); RC_CHECK();
Log(("PlugCpu: rc=%Rrc\n", rc));
CFGMR3Dump(pInst);
#undef RC_CHECK
return VINF_SUCCESS;
}
HRESULT Console::doCPUAdd(ULONG aCpu)
{
HRESULT rc = S_OK;
LogFlowThisFuncEnter();
LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/** @todo r=klaus holding the lock while triggering VMMDev/EMT activity is
* asking for deadlocks. Code MUST drop any lock before touching VMMDev. */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Teleporting
&& mMachineState != MachineState_LiveSnapshotting
/** @todo r=bird: This should be allowed on paused VMs as well. Later. */
)
return setInvalidMachineStateError();
AssertReturn(m_pVMMDev, E_FAIL);
PPDMIVMMDEVPORT pDevPort = m_pVMMDev->getVMMDevPort();
AssertReturn(pDevPort, E_FAIL);
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* Check if the CPU is present */
BOOL fCpuAttached;
rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached);
if (FAILED(rc)) return rc;
if (fCpuAttached)
return setError(E_FAIL,
tr("CPU %d is already attached"), aCpu);
/*
* Call worker in EMT, that's faster and safer than doing everything
* using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
* here to make requests from under the lock in order to serialize them.
*/
PVMREQ pReq;
int vrc = VMR3ReqCall(mpVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
(PFNRT)Console::plugCpu, 2,
this, aCpu);
/* leave the lock before a VMR3* call (EMT will call us back)! */
alock.leave();
if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
{
vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
AssertRC(vrc);
if (RT_SUCCESS(vrc))
vrc = pReq->iStatus;
}
VMR3ReqFree(pReq);
rc = RT_SUCCESS(vrc) ? S_OK :
setError(VBOX_E_VM_ERROR,
tr("Could not add CPU to the machine (%Rrc)"),
vrc);
if (RT_SUCCESS(vrc))
{
uint32_t idCpuCore, idCpuPackage;
/* Notify the guest if possible. */
vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(mpVM, aCpu, &idCpuCore, &idCpuPackage);
AssertRC(vrc);
vrc = pDevPort->pfnCpuHotPlug(pDevPort, idCpuCore, idCpuPackage);
/** @todo warning if the guest doesn't support it */
}
LogFlowThisFunc(("mMachineState=%d, rc=%08X\n", mMachineState, rc));
LogFlowThisFuncLeave();
return rc;
}
STDMETHODIMP Console::Pause()
{
LogFlowThisFuncEnter();
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
switch (mMachineState)
{
case MachineState_Running:
case MachineState_Teleporting:
case MachineState_LiveSnapshotting:
break;
case MachineState_Paused:
case MachineState_TeleportingPausedVM:
case MachineState_Saving:
return setError(VBOX_E_INVALID_VM_STATE, tr("Already paused"));
default:
return setInvalidMachineStateError();
}
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
LogFlowThisFunc(("Sending PAUSE request...\n"));
/* leave the lock before a VMR3* call (EMT will call us back)! */
alock.leave();
int vrc = VMR3Suspend(mpVM);
HRESULT hrc = S_OK;
if (RT_FAILURE(vrc))
hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
LogFlowThisFunc(("hrc=%Rhrc\n", hrc));
LogFlowThisFuncLeave();
return hrc;
}
STDMETHODIMP Console::Resume()
{
LogFlowThisFuncEnter();
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (mMachineState != MachineState_Paused)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot resume the machine as it is not paused (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
LogFlowThisFunc(("Sending RESUME request...\n"));
/* leave the lock before a VMR3* call (EMT will call us back)! */
alock.leave();
int vrc;
if (VMR3GetState(mpVM) == VMSTATE_CREATED)
vrc = VMR3PowerOn(mpVM); /* (PowerUpPaused) */
else
vrc = VMR3Resume(mpVM);
HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
setError(VBOX_E_VM_ERROR,
tr("Could not resume the machine execution (%Rrc)"),
vrc);
LogFlowThisFunc(("rc=%08X\n", rc));
LogFlowThisFuncLeave();
return rc;
}
STDMETHODIMP Console::PowerButton()
{
LogFlowThisFuncEnter();
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Teleporting
&& mMachineState != MachineState_LiveSnapshotting
)
return setInvalidMachineStateError();
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
PPDMIBASE pBase;
int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase);
if (RT_SUCCESS(vrc))
{
Assert(pBase);
PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
vrc = pPort ? pPort->pfnPowerButtonPress(pPort) : VERR_INVALID_POINTER;
}
HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
setError(VBOX_E_PDM_ERROR,
tr("Controlled power off failed (%Rrc)"),
vrc);
LogFlowThisFunc(("rc=%08X\n", rc));
LogFlowThisFuncLeave();
return rc;
}
STDMETHODIMP Console::GetPowerButtonHandled(BOOL *aHandled)
{
LogFlowThisFuncEnter();
CheckComArgOutPointerValid(aHandled);
*aHandled = FALSE;
AutoCaller autoCaller(this);
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Teleporting
&& mMachineState != MachineState_LiveSnapshotting
)
return setInvalidMachineStateError();
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
PPDMIBASE pBase;
int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase);
bool handled = false;
if (RT_SUCCESS(vrc))
{
Assert(pBase);
PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
vrc = pPort ? pPort->pfnGetPowerButtonHandled(pPort, &handled) : VERR_INVALID_POINTER;
}
HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
setError(VBOX_E_PDM_ERROR,
tr("Checking if the ACPI Power Button event was handled by the guest OS failed (%Rrc)"),
vrc);
*aHandled = handled;
LogFlowThisFunc(("rc=%08X\n", rc));
LogFlowThisFuncLeave();
return rc;
}
STDMETHODIMP Console::GetGuestEnteredACPIMode(BOOL *aEntered)
{
LogFlowThisFuncEnter();
CheckComArgOutPointerValid(aEntered);
*aEntered = FALSE;
AutoCaller autoCaller(this);
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Teleporting
&& mMachineState != MachineState_LiveSnapshotting
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Invalid machine state %s when checking if the guest entered the ACPI mode)"),
Global::stringifyMachineState(mMachineState));
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
PPDMIBASE pBase;
int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase);
bool entered = false;
if (RT_SUCCESS(vrc))
{
Assert(pBase);
PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
vrc = pPort ? pPort->pfnGetGuestEnteredACPIMode(pPort, &entered) : VERR_INVALID_POINTER;
}
*aEntered = RT_SUCCESS(vrc) ? entered : false;
LogFlowThisFuncLeave();
return S_OK;
}
STDMETHODIMP Console::SleepButton()
{
LogFlowThisFuncEnter();
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (mMachineState != MachineState_Running) /** @todo Live Migration: ??? */
return setInvalidMachineStateError();
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
PPDMIBASE pBase;
int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase);
if (RT_SUCCESS(vrc))
{
Assert(pBase);
PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT);
vrc = pPort ? pPort->pfnSleepButtonPress(pPort) : VERR_INVALID_POINTER;
}
HRESULT rc = RT_SUCCESS(vrc) ? S_OK :
setError(VBOX_E_PDM_ERROR,
tr("Sending sleep button event failed (%Rrc)"),
vrc);
LogFlowThisFunc(("rc=%08X\n", rc));
LogFlowThisFuncLeave();
return rc;
}
STDMETHODIMP Console::SaveState(IProgress **aProgress)
{
LogFlowThisFuncEnter();
LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
CheckComArgOutPointerValid(aProgress);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Paused)
{
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
}
/* memorize the current machine state */
MachineState_T lastMachineState = mMachineState;
if (mMachineState == MachineState_Running)
{
HRESULT rc = Pause();
if (FAILED(rc)) return rc;
}
HRESULT rc = S_OK;
/* create a progress object to track operation completion */
ComObjPtr<Progress> progress;
progress.createObject();
progress->init(static_cast<IConsole *>(this),
Bstr(tr("Saving the execution state of the virtual machine")).raw(),
FALSE /* aCancelable */);
bool fBeganSavingState = false;
bool fTaskCreationFailed = false;
do
{
/* create a task object early to ensure mpVM protection is successful */
std::auto_ptr <VMSaveTask> task(new VMSaveTask(this, progress));
rc = task->rc();
/*
* If we fail here it means a PowerDown() call happened on another
* thread while we were doing Pause() (which leaves the Console lock).
* We assign PowerDown() a higher precedence than SaveState(),
* therefore just return the error to the caller.
*/
if (FAILED(rc))
{
fTaskCreationFailed = true;
break;
}
Bstr stateFilePath;
/*
* request a saved state file path from the server
* (this will set the machine state to Saving on the server to block
* others from accessing this machine)
*/
rc = mControl->BeginSavingState(progress, stateFilePath.asOutParam());
if (FAILED(rc)) break;
fBeganSavingState = true;
/* sync the state with the server */
setMachineStateLocally(MachineState_Saving);
/* ensure the directory for the saved state file exists */
{
Utf8Str dir = stateFilePath;
dir.stripFilename();
if (!RTDirExists(dir.c_str()))
{
int vrc = RTDirCreateFullPath(dir.c_str(), 0777);
if (RT_FAILURE(vrc))
{
rc = setError(VBOX_E_FILE_ERROR,
tr("Could not create a directory '%s' to save the state to (%Rrc)"),
dir.c_str(), vrc);
break;
}
}
}
/* setup task object and thread to carry out the operation asynchronously */
task->mSavedStateFile = stateFilePath;
/* set the state the operation thread will restore when it is finished */
task->mLastMachineState = lastMachineState;
/* create a thread to wait until the VM state is saved */
int vrc = RTThreadCreate(NULL, Console::saveStateThread, (void *) task.get(),
0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave");
if (RT_FAILURE(vrc))
{
rc = setError(E_FAIL, "Could not create VMSave thread (%Rrc)", vrc);
break;
}
/* task is now owned by saveStateThread(), so release it */
task.release();
/* return the progress to the caller */
progress.queryInterfaceTo(aProgress);
}
while (0);
if (FAILED(rc) && !fTaskCreationFailed)
{
/* preserve existing error info */
ErrorInfoKeeper eik;
if (fBeganSavingState)
{
/*
* cancel the requested save state procedure.
* This will reset the machine state to the state it had right
* before calling mControl->BeginSavingState().
*/
mControl->EndSavingState(FALSE);
}
if (lastMachineState == MachineState_Running)
{
/* restore the paused state if appropriate */
setMachineStateLocally(MachineState_Paused);
/* restore the running state if appropriate */
Resume();
}
else
setMachineStateLocally(lastMachineState);
}
LogFlowThisFunc(("rc=%08X\n", rc));
LogFlowThisFuncLeave();
return rc;
}
STDMETHODIMP Console::AdoptSavedState(IN_BSTR aSavedStateFile)
{
CheckComArgStrNotEmptyOrNull(aSavedStateFile);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if ( mMachineState != MachineState_PoweredOff
&& mMachineState != MachineState_Teleported
&& mMachineState != MachineState_Aborted
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
return mControl->AdoptSavedState(aSavedStateFile);
}
STDMETHODIMP Console::DiscardSavedState(BOOL aRemoveFile)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (mMachineState != MachineState_Saved)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
HRESULT rc = mControl->SetRemoveSavedStateFile(aRemoveFile);
if (FAILED(rc)) return rc;
/*
* Saved -> PoweredOff transition will be detected in the SessionMachine
* and properly handled.
*/
rc = setMachineState(MachineState_PoweredOff);
return rc;
}
/** read the value of a LEd. */
inline uint32_t readAndClearLed(PPDMLED pLed)
{
if (!pLed)
return 0;
uint32_t u32 = pLed->Actual.u32 | pLed->Asserted.u32;
pLed->Asserted.u32 = 0;
return u32;
}
STDMETHODIMP Console::GetDeviceActivity(DeviceType_T aDeviceType,
DeviceActivity_T *aDeviceActivity)
{
CheckComArgNotNull(aDeviceActivity);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/*
* Note: we don't lock the console object here because
* readAndClearLed() should be thread safe.
*/
/* Get LED array to read */
PDMLEDCORE SumLed = {0};
switch (aDeviceType)
{
case DeviceType_Floppy:
case DeviceType_DVD:
case DeviceType_HardDisk:
{
for (unsigned i = 0; i < RT_ELEMENTS(mapStorageLeds); ++i)
if (maStorageDevType[i] == aDeviceType)
SumLed.u32 |= readAndClearLed(mapStorageLeds[i]);
break;
}
case DeviceType_Network:
{
for (unsigned i = 0; i < RT_ELEMENTS(mapNetworkLeds); ++i)
SumLed.u32 |= readAndClearLed(mapNetworkLeds[i]);
break;
}
case DeviceType_USB:
{
for (unsigned i = 0; i < RT_ELEMENTS(mapUSBLed); ++i)
SumLed.u32 |= readAndClearLed(mapUSBLed[i]);
break;
}
case DeviceType_SharedFolder:
{
SumLed.u32 |= readAndClearLed(mapSharedFolderLed);
break;
}
default:
return setError(E_INVALIDARG,
tr("Invalid device type: %d"),
aDeviceType);
}
/* Compose the result */
switch (SumLed.u32 & (PDMLED_READING | PDMLED_WRITING))
{
case 0:
*aDeviceActivity = DeviceActivity_Idle;
break;
case PDMLED_READING:
*aDeviceActivity = DeviceActivity_Reading;
break;
case PDMLED_WRITING:
case PDMLED_READING | PDMLED_WRITING:
*aDeviceActivity = DeviceActivity_Writing;
break;
}
return S_OK;
}
STDMETHODIMP Console::AttachUSBDevice(IN_BSTR aId)
{
#ifdef VBOX_WITH_USB
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if ( mMachineState != MachineState_Running
&& mMachineState != MachineState_Paused)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot attach a USB device to the machine which is not running or paused (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* Don't proceed unless we've found the usb controller. */
PPDMIBASE pBase = NULL;
int vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
if (RT_FAILURE(vrc))
return setError(VBOX_E_PDM_ERROR,
tr("The virtual machine does not have a USB controller"));
/* leave the lock because the USB Proxy service may call us back
* (via onUSBDeviceAttach()) */
alock.leave();
/* Request the device capture */
HRESULT rc = mControl->CaptureUSBDevice(aId);
if (FAILED(rc)) return rc;
return rc;
#else /* !VBOX_WITH_USB */
return setError(VBOX_E_PDM_ERROR,
tr("The virtual machine does not have a USB controller"));
#endif /* !VBOX_WITH_USB */
}
STDMETHODIMP Console::DetachUSBDevice(IN_BSTR aId, IUSBDevice **aDevice)
{
#ifdef VBOX_WITH_USB
CheckComArgOutPointerValid(aDevice);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/* Find it. */
ComObjPtr<OUSBDevice> device;
USBDeviceList::iterator it = mUSBDevices.begin();
Guid uuid(aId);
while (it != mUSBDevices.end())
{
if ((*it)->id() == uuid)
{
device = *it;
break;
}
++ it;
}
if (!device)
return setError(E_INVALIDARG,
tr("USB device with UUID {%RTuuid} is not attached to this machine"),
Guid(aId).raw());
/*
* Inform the USB device and USB proxy about what's cooking.
*/
alock.leave();
HRESULT rc2 = mControl->DetachUSBDevice(aId, false /* aDone */);
if (FAILED(rc2))
return rc2;
alock.enter();
/* Request the PDM to detach the USB device. */
HRESULT rc = detachUSBDevice(it);
if (SUCCEEDED(rc))
{
/* leave the lock since we don't need it any more (note though that
* the USB Proxy service must not call us back here) */
alock.leave();
/* Request the device release. Even if it fails, the device will
* remain as held by proxy, which is OK for us (the VM process). */
rc = mControl->DetachUSBDevice(aId, true /* aDone */);
}
return rc;
#else /* !VBOX_WITH_USB */
return setError(VBOX_E_PDM_ERROR,
tr("The virtual machine does not have a USB controller"));
#endif /* !VBOX_WITH_USB */
}
STDMETHODIMP Console::FindUSBDeviceByAddress(IN_BSTR aAddress, IUSBDevice **aDevice)
{
#ifdef VBOX_WITH_USB
CheckComArgStrNotEmptyOrNull(aAddress);
CheckComArgOutPointerValid(aDevice);
*aDevice = NULL;
SafeIfaceArray<IUSBDevice> devsvec;
HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
if (FAILED(rc)) return rc;
for (size_t i = 0; i < devsvec.size(); ++i)
{
Bstr address;
rc = devsvec[i]->COMGETTER(Address)(address.asOutParam());
if (FAILED(rc)) return rc;
if (address == aAddress)
{
ComObjPtr<OUSBDevice> found;
found.createObject();
found->init(devsvec[i]);
return found.queryInterfaceTo(aDevice);
}
}
return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
tr("Could not find a USB device with address '%ls'"),
aAddress);
#else /* !VBOX_WITH_USB */
return E_NOTIMPL;
#endif /* !VBOX_WITH_USB */
}
STDMETHODIMP Console::FindUSBDeviceById(IN_BSTR aId, IUSBDevice **aDevice)
{
#ifdef VBOX_WITH_USB
CheckComArgExpr(aId, Guid(aId).isEmpty() == false);
CheckComArgOutPointerValid(aDevice);
*aDevice = NULL;
SafeIfaceArray<IUSBDevice> devsvec;
HRESULT rc = COMGETTER(USBDevices)(ComSafeArrayAsOutParam(devsvec));
if (FAILED(rc)) return rc;
for (size_t i = 0; i < devsvec.size(); ++i)
{
Bstr id;
rc = devsvec[i]->COMGETTER(Id)(id.asOutParam());
if (FAILED(rc)) return rc;
if (id == aId)
{
ComObjPtr<OUSBDevice> found;
found.createObject();
found->init(devsvec[i]);
return found.queryInterfaceTo(aDevice);
}
}
return setErrorNoLog(VBOX_E_OBJECT_NOT_FOUND,
tr("Could not find a USB device with uuid {%RTuuid}"),
Guid(aId).raw());
#else /* !VBOX_WITH_USB */
return E_NOTIMPL;
#endif /* !VBOX_WITH_USB */
}
STDMETHODIMP
Console::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
{
CheckComArgStrNotEmptyOrNull(aName);
CheckComArgStrNotEmptyOrNull(aHostPath);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/// @todo see @todo in AttachUSBDevice() about the Paused state
if (mMachineState == MachineState_Saved)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot create a transient shared folder on the machine in the saved state"));
if ( mMachineState != MachineState_PoweredOff
&& mMachineState != MachineState_Teleported
&& mMachineState != MachineState_Aborted
&& mMachineState != MachineState_Running
&& mMachineState != MachineState_Paused
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot create a transient shared folder on the machine while it is changing the state (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
ComObjPtr<SharedFolder> sharedFolder;
HRESULT rc = findSharedFolder(aName, sharedFolder, false /* aSetError */);
if (SUCCEEDED(rc))
return setError(VBOX_E_FILE_ERROR,
tr("Shared folder named '%ls' already exists"),
aName);
sharedFolder.createObject();
rc = sharedFolder->init(this, aName, aHostPath, aWritable, aAutoMount);
if (FAILED(rc)) return rc;
/* protect mpVM (if not NULL) */
AutoVMCallerQuietWeak autoVMCaller(this);
if ( mpVM
&& autoVMCaller.isOk()
&& m_pVMMDev
&& m_pVMMDev->isShFlActive()
)
{
/* If the VM is online and supports shared folders, share this folder
* under the specified name. */
/* first, remove the machine or the global folder if there is any */
SharedFolderDataMap::const_iterator it;
if (findOtherSharedFolder(aName, it))
{
rc = removeSharedFolder(aName);
if (FAILED(rc)) return rc;
}
/* second, create the given folder */
rc = createSharedFolder(aName, SharedFolderData(aHostPath, aWritable, aAutoMount));
if (FAILED(rc)) return rc;
}
mSharedFolders.insert(std::make_pair(aName, sharedFolder));
/* notify console callbacks after the folder is added to the list */
CONSOLE_DO_CALLBACKS1(OnSharedFolderChanged, Scope_Session);
return rc;
}
STDMETHODIMP Console::RemoveSharedFolder(IN_BSTR aName)
{
CheckComArgStrNotEmptyOrNull(aName);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/// @todo see @todo in AttachUSBDevice() about the Paused state
if (mMachineState == MachineState_Saved)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot remove a transient shared folder from the machine in the saved state"));
if ( mMachineState != MachineState_PoweredOff
&& mMachineState != MachineState_Teleported
&& mMachineState != MachineState_Aborted
&& mMachineState != MachineState_Running
&& mMachineState != MachineState_Paused
)
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot remove a transient shared folder from the machine while it is changing the state (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
ComObjPtr<SharedFolder> sharedFolder;
HRESULT rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
if (FAILED(rc)) return rc;
/* protect mpVM (if not NULL) */
AutoVMCallerQuietWeak autoVMCaller(this);
if ( mpVM
&& autoVMCaller.isOk()
&& m_pVMMDev
&& m_pVMMDev->isShFlActive()
)
{
/* if the VM is online and supports shared folders, UNshare this
* folder. */
/* first, remove the given folder */
rc = removeSharedFolder(aName);
if (FAILED(rc)) return rc;
/* first, remove the machine or the global folder if there is any */
SharedFolderDataMap::const_iterator it;
if (findOtherSharedFolder(aName, it))
{
rc = createSharedFolder(aName, it->second);
/* don't check rc here because we need to remove the console
* folder from the collection even on failure */
}
}
mSharedFolders.erase(aName);
/* notify console callbacks after the folder is removed to the list */
CONSOLE_DO_CALLBACKS1(OnSharedFolderChanged, Scope_Session);
return rc;
}
STDMETHODIMP Console::TakeSnapshot(IN_BSTR aName,
IN_BSTR aDescription,
IProgress **aProgress)
{
LogFlowThisFuncEnter();
LogFlowThisFunc(("aName='%ls' mMachineState=%08X\n", aName, mMachineState));
CheckComArgStrNotEmptyOrNull(aName);
CheckComArgOutPointerValid(aProgress);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (Global::IsTransient(mMachineState))
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot take a snapshot of the machine while it is changing the state (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
HRESULT rc = S_OK;
/* prepare the progress object:
a) count the no. of hard disk attachments to get a matching no. of progress sub-operations */
ULONG cOperations = 2; // always at least setting up + finishing up
ULONG ulTotalOperationsWeight = 2; // one each for setting up + finishing up
SafeIfaceArray<IMediumAttachment> aMediumAttachments;
rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aMediumAttachments));
if (FAILED(rc))
return setError(rc, tr("Cannot get medium attachments of the machine"));
ULONG ulMemSize;
rc = mMachine->COMGETTER(MemorySize)(&ulMemSize);
if (FAILED(rc))
return rc;
for (size_t i = 0;
i < aMediumAttachments.size();
++i)
{
DeviceType_T type;
rc = aMediumAttachments[i]->COMGETTER(Type)(&type);
if (FAILED(rc))
return rc;
if (type == DeviceType_HardDisk)
{
++cOperations;
// assume that creating a diff image takes as long as saving a 1MB state
// (note, the same value must be used in SessionMachine::BeginTakingSnapshot() on the server!)
ulTotalOperationsWeight += 1;
}
}
// b) one extra sub-operations for online snapshots OR offline snapshots that have a saved state (needs to be copied)
bool fTakingSnapshotOnline = ((mMachineState == MachineState_Running) || (mMachineState == MachineState_Paused));
LogFlowFunc(("fTakingSnapshotOnline = %d, mMachineState = %d\n", fTakingSnapshotOnline, mMachineState));
if ( fTakingSnapshotOnline
|| mMachineState == MachineState_Saved
)
{
++cOperations;
ulTotalOperationsWeight += ulMemSize;
}
// finally, create the progress object
ComObjPtr<Progress> pProgress;
pProgress.createObject();
rc = pProgress->init(static_cast<IConsole*>(this),
Bstr(tr("Taking a snapshot of the virtual machine")).raw(),
mMachineState == MachineState_Running /* aCancelable */,
cOperations,
ulTotalOperationsWeight,
Bstr(tr("Setting up snapshot operation")).raw(), // first sub-op description
1); // ulFirstOperationWeight
if (FAILED(rc))
return rc;
VMTakeSnapshotTask *pTask;
if (!(pTask = new VMTakeSnapshotTask(this, pProgress, aName, aDescription)))
return E_OUTOFMEMORY;
Assert(pTask->mProgress);
try
{
mptrCancelableProgress = pProgress;
/*
* If we fail here it means a PowerDown() call happened on another
* thread while we were doing Pause() (which leaves the Console lock).
* We assign PowerDown() a higher precedence than TakeSnapshot(),
* therefore just return the error to the caller.
*/
rc = pTask->rc();
if (FAILED(rc)) throw rc;
pTask->ulMemSize = ulMemSize;
/* memorize the current machine state */
pTask->lastMachineState = mMachineState;
pTask->fTakingSnapshotOnline = fTakingSnapshotOnline;
int vrc = RTThreadCreate(NULL,
Console::fntTakeSnapshotWorker,
(void*)pTask,
0,
RTTHREADTYPE_MAIN_WORKER,
0,
"ConsoleTakeSnap");
if (FAILED(vrc))
throw setError(E_FAIL,
tr("Could not create VMTakeSnap thread (%Rrc)"),
vrc);
pTask->mProgress.queryInterfaceTo(aProgress);
}
catch (HRESULT erc)
{
delete pTask;
NOREF(erc);
mptrCancelableProgress.setNull();
}
LogFlowThisFunc(("rc=%Rhrc\n", rc));
LogFlowThisFuncLeave();
return rc;
}
STDMETHODIMP Console::DeleteSnapshot(IN_BSTR aId, IProgress **aProgress)
{
CheckComArgExpr(aId, Guid(aId).isEmpty() == false);
CheckComArgOutPointerValid(aProgress);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (Global::IsTransient(mMachineState))
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
MachineState_T machineState = MachineState_Null;
HRESULT rc = mControl->DeleteSnapshot(this, aId, &machineState, aProgress);
if (FAILED(rc)) return rc;
setMachineStateLocally(machineState);
return S_OK;
}
STDMETHODIMP Console::RestoreSnapshot(ISnapshot *aSnapshot, IProgress **aProgress)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (Global::IsOnlineOrTransient(mMachineState))
return setError(VBOX_E_INVALID_VM_STATE,
tr("Cannot delete the current state of the running machine (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
MachineState_T machineState = MachineState_Null;
HRESULT rc = mControl->RestoreSnapshot(this, aSnapshot, &machineState, aProgress);
if (FAILED(rc)) return rc;
setMachineStateLocally(machineState);
return S_OK;
}
// Non-interface public methods
/////////////////////////////////////////////////////////////////////////////
/*static*/
HRESULT Console::setErrorStatic(HRESULT aResultCode, const char *pcsz, ...)
{
va_list args;
va_start(args, pcsz);
HRESULT rc = setErrorInternal(aResultCode,
getStaticClassIID(),
getStaticComponentName(),
Utf8StrFmtVA(pcsz, args),
false /* aWarning */,
true /* aLogIt */);
va_end(args);
return rc;
}
HRESULT Console::setInvalidMachineStateError()
{
return setError(VBOX_E_INVALID_VM_STATE,
tr("Invalid machine state: %s"),
Global::stringifyMachineState(mMachineState));
}
/**
* @copydoc VirtualBox::handleUnexpectedExceptions
*/
/* static */
HRESULT Console::handleUnexpectedExceptions(RT_SRC_POS_DECL)
{
try
{
/* re-throw the current exception */
throw;
}
catch (const std::exception &err)
{
return setErrorStatic(E_FAIL,
tr("Unexpected exception: %s [%s]\n%s[%d] (%s)"),
err.what(), typeid(err).name(),
pszFile, iLine, pszFunction);
}
catch (...)
{
return setErrorStatic(E_FAIL,
tr("Unknown exception\n%s[%d] (%s)"),
pszFile, iLine, pszFunction);
}
/* should not get here */
AssertFailed();
return E_FAIL;
}
/* static */
const char *Console::convertControllerTypeToDev(StorageControllerType_T enmCtrlType)
{
switch (enmCtrlType)
{
case StorageControllerType_LsiLogic:
return "lsilogicscsi";
case StorageControllerType_BusLogic:
return "buslogic";
case StorageControllerType_LsiLogicSas:
return "lsilogicsas";
case StorageControllerType_IntelAhci:
return "ahci";
case StorageControllerType_PIIX3:
case StorageControllerType_PIIX4:
case StorageControllerType_ICH6:
return "piix3ide";
case StorageControllerType_I82078:
return "i82078";
default:
return NULL;
}
}
HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG device, unsigned &uLun)
{
switch (enmBus)
{
case StorageBus_IDE:
case StorageBus_Floppy:
{
AssertMsgReturn(port < 2 && port >= 0, ("%d\n", port), E_INVALIDARG);
AssertMsgReturn(device < 2 && device >= 0, ("%d\n", device), E_INVALIDARG);
uLun = 2 * port + device;
return S_OK;
}
case StorageBus_SATA:
case StorageBus_SCSI:
case StorageBus_SAS:
{
uLun = port;
return S_OK;
}
default:
uLun = 0;
AssertMsgFailedReturn(("%d\n", enmBus), E_INVALIDARG);
}
}
// private methods
/////////////////////////////////////////////////////////////////////////////
/**
* Process a medium change.
*
* @param aMediumAttachment The medium attachment with the new medium state.
* @param fForce Force medium chance, if it is locked or not.
*
* @note Locks this object for writing.
*/
HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
/* We will need to release the write lock before calling EMT */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
const char *pszDevice = NULL;
SafeIfaceArray<IStorageController> ctrls;
rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
AssertComRC(rc);
IMedium *pMedium;
rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
AssertComRC(rc);
Bstr mediumLocation;
if (pMedium)
{
rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
AssertComRC(rc);
}
Bstr attCtrlName;
rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
AssertComRC(rc);
ComPtr<IStorageController> ctrl;
for (size_t i = 0; i < ctrls.size(); ++i)
{
Bstr ctrlName;
rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
AssertComRC(rc);
if (attCtrlName == ctrlName)
{
ctrl = ctrls[i];
break;
}
}
if (ctrl.isNull())
return setError(E_FAIL,
tr("Could not find storage controller '%ls'"), attCtrlName.raw());
StorageControllerType_T enmCtrlType;
rc = ctrl->COMGETTER(ControllerType)(&enmCtrlType);
AssertComRC(rc);
pszDevice = convertControllerTypeToDev(enmCtrlType);
StorageBus_T enmBus;
rc = ctrl->COMGETTER(Bus)(&enmBus);
AssertComRC(rc);
ULONG uInstance;
rc = ctrl->COMGETTER(Instance)(&uInstance);
AssertComRC(rc);
BOOL fUseHostIOCache;
rc = ctrl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
AssertComRC(rc);
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
AssertComRCReturnRC(autoVMCaller.rc());
/*
* Call worker in EMT, that's faster and safer than doing everything
* using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
* here to make requests from under the lock in order to serialize them.
*/
PVMREQ pReq;
int vrc = VMR3ReqCall(mpVM,
VMCPUID_ANY,
&pReq,
0 /* no wait! */,
VMREQFLAGS_VBOX_STATUS,
(PFNRT)Console::changeRemovableMedium,
7,
this,
pszDevice,
uInstance,
enmBus,
fUseHostIOCache,
aMediumAttachment,
fForce);
/* leave the lock before waiting for a result (EMT will call us back!) */
alock.leave();
if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
{
vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
AssertRC(vrc);
if (RT_SUCCESS(vrc))
vrc = pReq->iStatus;
}
VMR3ReqFree(pReq);
if (RT_SUCCESS(vrc))
{
LogFlowThisFunc(("Returns S_OK\n"));
return S_OK;
}
if (!pMedium)
return setError(E_FAIL,
tr("Could not mount the media/drive '%ls' (%Rrc)"),
mediumLocation.raw(), vrc);
return setError(E_FAIL,
tr("Could not unmount the currently mounted media/drive (%Rrc)"),
vrc);
}
/**
* Performs the medium change in EMT.
*
* @returns VBox status code.
*
* @param pThis Pointer to the Console object.
* @param pcszDevice The PDM device name.
* @param uInstance The PDM device instance.
* @param uLun The PDM LUN number of the drive.
* @param fHostDrive True if this is a host drive attachment.
* @param pszPath The path to the media / drive which is now being mounted / captured.
* If NULL no media or drive is attached and the LUN will be configured with
* the default block driver with no media. This will also be the state if
* mounting / capturing the specified media / drive fails.
* @param pszFormat Medium format string, usually "RAW".
* @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.
*
* @thread EMT
*/
DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole,
const char *pcszDevice,
unsigned uInstance,
StorageBus_T enmBus,
bool fUseHostIOCache,
IMediumAttachment *aMediumAtt,
bool fForce)
{
LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p, fForce=%d\n",
pConsole, uInstance, pcszDevice, enmBus, fForce));
AssertReturn(pConsole, VERR_INVALID_PARAMETER);
AutoCaller autoCaller(pConsole);
AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
PVM pVM = pConsole->mpVM;
/*
* Suspend the VM first.
*
* The VM must not be running since it might have pending I/O to
* the drive which is being changed.
*/
bool fResume;
VMSTATE enmVMState = VMR3GetState(pVM);
switch (enmVMState)
{
case VMSTATE_RESETTING:
case VMSTATE_RUNNING:
{
LogFlowFunc(("Suspending the VM...\n"));
/* disable the callback to prevent Console-level state change */
pConsole->mVMStateChangeCallbackDisabled = true;
int rc = VMR3Suspend(pVM);
pConsole->mVMStateChangeCallbackDisabled = false;
AssertRCReturn(rc, rc);
fResume = true;
break;
}
case VMSTATE_SUSPENDED:
case VMSTATE_CREATED:
case VMSTATE_OFF:
fResume = false;
break;
case VMSTATE_RUNNING_LS:
return setErrorInternal(VBOX_E_INVALID_VM_STATE,
COM_IIDOF(IConsole),
getStaticComponentName(),
Utf8Str(tr("Cannot change drive during live migration")),
false /*aWarning*/,
true /*aLogIt*/);
default:
AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
}
/* Determine the base path for the device instance. */
PCFGMNODE pCtlInst;
pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice,
uInstance);
AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
int rc = VINF_SUCCESS;
int rcRet = VINF_SUCCESS;
rcRet = pConsole->configMediumAttachment(pCtlInst,
pcszDevice,
uInstance,
enmBus,
fUseHostIOCache,
false /* fSetupMerge */,
0 /* uMergeSource */,
0 /* uMergeTarget */,
aMediumAtt,
pConsole->mMachineState,
NULL /* phrc */,
true /* fAttachDetach */,
fForce /* fForceUnmount */,
pVM,
NULL /* paLedDevType */);
/** @todo this dumps everything attached to this device instance, which
* is more than necessary. Dumping the changed LUN would be enough. */
CFGMR3Dump(pCtlInst);
/*
* Resume the VM if necessary.
*/
if (fResume)
{
LogFlowFunc(("Resuming the VM...\n"));
/* disable the callback to prevent Console-level state change */
pConsole->mVMStateChangeCallbackDisabled = true;
rc = VMR3Resume(pVM);
pConsole->mVMStateChangeCallbackDisabled = false;
AssertRC(rc);
if (RT_FAILURE(rc))
{
/* too bad, we failed. try to sync the console state with the VMM state */
vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole);
}
/// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
// error (if any) will be hidden from the caller. For proper reporting
// of such multiple errors to the caller we need to enhance the
// IVirtualBoxError interface. For now, give the first error the higher
// priority.
if (RT_SUCCESS(rcRet))
rcRet = rc;
}
LogFlowFunc(("Returning %Rrc\n", rcRet));
return rcRet;
}
/**
* Called by IInternalSessionControl::OnNetworkAdapterChange().
*
* @note Locks this object for writing.
*/
HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL changeAdapter)
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
/* don't trigger network change if the VM isn't running */
if (mpVM)
{
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* Get the properties we need from the adapter */
BOOL fCableConnected, fTraceEnabled;
rc = aNetworkAdapter->COMGETTER(CableConnected)(&fCableConnected);
AssertComRC(rc);
if (SUCCEEDED(rc))
{
rc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fTraceEnabled);
AssertComRC(rc);
}
if (SUCCEEDED(rc))
{
ULONG ulInstance;
rc = aNetworkAdapter->COMGETTER(Slot)(&ulInstance);
AssertComRC(rc);
if (SUCCEEDED(rc))
{
/*
* Find the pcnet instance, get the config interface and update
* the link state.
*/
NetworkAdapterType_T adapterType;
rc = aNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
AssertComRC(rc);
const char *pszAdapterName = NULL;
switch (adapterType)
{
case NetworkAdapterType_Am79C970A:
case NetworkAdapterType_Am79C973:
pszAdapterName = "pcnet";
break;
#ifdef VBOX_WITH_E1000
case NetworkAdapterType_I82540EM:
case NetworkAdapterType_I82543GC:
case NetworkAdapterType_I82545EM:
pszAdapterName = "e1000";
break;
#endif
#ifdef VBOX_WITH_VIRTIO
case NetworkAdapterType_Virtio:
pszAdapterName = "virtio-net";
break;
#endif
default:
AssertFailed();
pszAdapterName = "unknown";
break;
}
PPDMIBASE pBase;
int vrc = PDMR3QueryDeviceLun(mpVM, pszAdapterName, ulInstance, 0, &pBase);
ComAssertRC(vrc);
if (RT_SUCCESS(vrc))
{
Assert(pBase);
PPDMINETWORKCONFIG pINetCfg;
pINetCfg = PDMIBASE_QUERY_INTERFACE(pBase, PDMINETWORKCONFIG);
if (pINetCfg)
{
Log(("Console::onNetworkAdapterChange: setting link state to %d\n",
fCableConnected));
vrc = pINetCfg->pfnSetLinkState(pINetCfg,
fCableConnected ? PDMNETWORKLINKSTATE_UP
: PDMNETWORKLINKSTATE_DOWN);
ComAssertRC(vrc);
}
#ifdef VBOX_DYNAMIC_NET_ATTACH
if (RT_SUCCESS(vrc) && changeAdapter)
{
VMSTATE enmVMState = VMR3GetState(mpVM);
if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */
|| enmVMState == VMSTATE_SUSPENDED)
{
if (fTraceEnabled && fCableConnected && pINetCfg)
{
vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_DOWN);
ComAssertRC(vrc);
}
rc = doNetworkAdapterChange(pszAdapterName, ulInstance, 0, aNetworkAdapter);
if (fTraceEnabled && fCableConnected && pINetCfg)
{
vrc = pINetCfg->pfnSetLinkState(pINetCfg, PDMNETWORKLINKSTATE_UP);
ComAssertRC(vrc);
}
}
}
#endif /* VBOX_DYNAMIC_NET_ATTACH */
}
if (RT_FAILURE(vrc))
rc = E_FAIL;
}
}
}
/* notify console callbacks on success */
if (SUCCEEDED(rc))
CONSOLE_DO_CALLBACKS1(OnNetworkAdapterChanged, aNetworkAdapter);
LogFlowThisFunc(("Leaving rc=%#x\n", rc));
return rc;
}
#ifdef VBOX_DYNAMIC_NET_ATTACH
/**
* Process a network adaptor change.
*
* @returns COM status code.
*
* @param pszDevice The PDM device name.
* @param uInstance The PDM device instance.
* @param uLun The PDM LUN number of the drive.
* @param aNetworkAdapter The network adapter whose attachment needs to be changed
*
* @note Locks this object for writing.
*/
HRESULT Console::doNetworkAdapterChange(const char *pszDevice,
unsigned uInstance,
unsigned uLun,
INetworkAdapter *aNetworkAdapter)
{
LogFlowThisFunc(("pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
/* We will need to release the write lock before calling EMT */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/*
* Call worker in EMT, that's faster and safer than doing everything
* using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
* here to make requests from under the lock in order to serialize them.
*/
PVMREQ pReq;
int vrc = VMR3ReqCall(mpVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
(PFNRT) Console::changeNetworkAttachment, 5,
this, pszDevice, uInstance, uLun, aNetworkAdapter);
/* leave the lock before waiting for a result (EMT will call us back!) */
alock.leave();
if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
{
vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
AssertRC(vrc);
if (RT_SUCCESS(vrc))
vrc = pReq->iStatus;
}
VMR3ReqFree(pReq);
if (RT_SUCCESS(vrc))
{
LogFlowThisFunc(("Returns S_OK\n"));
return S_OK;
}
return setError(E_FAIL,
tr("Could not change the network adaptor attachement type (%Rrc)"),
vrc);
}
/**
* Performs the Network Adaptor change in EMT.
*
* @returns VBox status code.
*
* @param pThis Pointer to the Console object.
* @param pszDevice The PDM device name.
* @param uInstance The PDM device instance.
* @param uLun The PDM LUN number of the drive.
* @param aNetworkAdapter The network adapter whose attachment needs to be changed
*
* @thread EMT
* @note Locks the Console object for writing.
*/
DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis,
const char *pszDevice,
unsigned uInstance,
unsigned uLun,
INetworkAdapter *aNetworkAdapter)
{
LogFlowFunc(("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u aNetworkAdapter=%p\n",
pThis, pszDevice, pszDevice, uInstance, uLun, aNetworkAdapter));
AssertReturn(pThis, VERR_INVALID_PARAMETER);
AssertMsg( ( !strcmp(pszDevice, "pcnet")
|| !strcmp(pszDevice, "e1000")
|| !strcmp(pszDevice, "virtio-net"))
&& (uLun == 0)
&& (uInstance < SchemaDefs::NetworkAdapterCount),
("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));
AutoCaller autoCaller(pThis);
AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED);
/* protect mpVM */
AutoVMCaller autoVMCaller(pThis);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
PVM pVM = pThis->mpVM;
/*
* Suspend the VM first.
*
* The VM must not be running since it might have pending I/O to
* the drive which is being changed.
*/
bool fResume;
VMSTATE enmVMState = VMR3GetState(pVM);
switch (enmVMState)
{
case VMSTATE_RESETTING:
case VMSTATE_RUNNING:
{
LogFlowFunc(("Suspending the VM...\n"));
/* disable the callback to prevent Console-level state change */
pThis->mVMStateChangeCallbackDisabled = true;
int rc = VMR3Suspend(pVM);
pThis->mVMStateChangeCallbackDisabled = false;
AssertRCReturn(rc, rc);
fResume = true;
break;
}
case VMSTATE_SUSPENDED:
case VMSTATE_CREATED:
case VMSTATE_OFF:
fResume = false;
break;
default:
AssertLogRelMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);
}
int rc = VINF_SUCCESS;
int rcRet = VINF_SUCCESS;
PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */
PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
PCFGMNODE pInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%d/", pszDevice, uInstance);
AssertRelease(pInst);
rcRet = pThis->configNetwork(pszDevice, uInstance, uLun, aNetworkAdapter, pCfg, pLunL0, pInst, true);
/*
* Resume the VM if necessary.
*/
if (fResume)
{
LogFlowFunc(("Resuming the VM...\n"));
/* disable the callback to prevent Console-level state change */
pThis->mVMStateChangeCallbackDisabled = true;
rc = VMR3Resume(pVM);
pThis->mVMStateChangeCallbackDisabled = false;
AssertRC(rc);
if (RT_FAILURE(rc))
{
/* too bad, we failed. try to sync the console state with the VMM state */
vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pThis);
}
/// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume
// error (if any) will be hidden from the caller. For proper reporting
// of such multiple errors to the caller we need to enhance the
// IVirtualBoxError interface. For now, give the first error the higher
// priority.
if (RT_SUCCESS(rcRet))
rcRet = rc;
}
LogFlowFunc(("Returning %Rrc\n", rcRet));
return rcRet;
}
#endif /* VBOX_DYNAMIC_NET_ATTACH */
/**
* Called by IInternalSessionControl::OnSerialPortChange().
*
* @note Locks this object for writing.
*/
HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort)
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
/* don't trigger serial port change if the VM isn't running */
if (mpVM)
{
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* nothing to do so far */
}
/* notify console callbacks on success */
if (SUCCEEDED(rc))
CONSOLE_DO_CALLBACKS1(OnSerialPortChanged, aSerialPort);
LogFlowThisFunc(("Leaving rc=%#x\n", rc));
return rc;
}
/**
* Called by IInternalSessionControl::OnParallelPortChange().
*
* @note Locks this object for writing.
*/
HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort)
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
/* don't trigger parallel port change if the VM isn't running */
if (mpVM)
{
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* nothing to do so far */
}
/* notify console callbacks on success */
if (SUCCEEDED(rc))
CONSOLE_DO_CALLBACKS1(OnParallelPortChanged, aParallelPort);
LogFlowThisFunc(("Leaving rc=%#x\n", rc));
return rc;
}
/**
* Called by IInternalSessionControl::OnStorageControllerChange().
*
* @note Locks this object for writing.
*/
HRESULT Console::onStorageControllerChange()
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
/* don't trigger storage controller change if the VM isn't running */
if (mpVM)
{
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* nothing to do so far */
}
/* notify console callbacks on success */
if (SUCCEEDED(rc))
CONSOLE_DO_CALLBACKS0(OnStorageControllerChanged);
LogFlowThisFunc(("Leaving rc=%#x\n", rc));
return rc;
}
/**
* Called by IInternalSessionControl::OnMediumChange().
*
* @note Locks this object for writing.
*/
HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce)
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
/* don't trigger medium change if the VM isn't running */
if (mpVM)
{
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
rc = doMediumChange(aMediumAttachment, !!aForce);
}
/* notify console callbacks on success */
if (SUCCEEDED(rc))
CONSOLE_DO_CALLBACKS1(OnMediumChanged, aMediumAttachment);
LogFlowThisFunc(("Leaving rc=%#x\n", rc));
return rc;
}
/**
* Called by IInternalSessionControl::OnCPUChange().
*
* @note Locks this object for writing.
*/
HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
/* don't trigger CPU change if the VM isn't running */
if (mpVM)
{
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
if (aRemove)
rc = doCPURemove(aCPU);
else
rc = doCPUAdd(aCPU);
}
/* notify console callbacks on success */
if (SUCCEEDED(rc))
CONSOLE_DO_CALLBACKS2(OnCPUChanged, aCPU, aRemove);
LogFlowThisFunc(("Leaving rc=%#x\n", rc));
return rc;
}
/**
* Called by IInternalSessionControl::OnCPUPriorityChange().
*
* @note Locks this object for writing.
*/
HRESULT Console::onCPUPriorityChange(ULONG aCpuPriority)
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
/* don't trigger the CPU priority change if the VM isn't running */
if (mpVM)
{
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
if ( mMachineState == MachineState_Running
|| mMachineState == MachineState_Teleporting
|| mMachineState == MachineState_LiveSnapshotting
)
{
/* No need to call in the EMT thread. */
rc = VMR3SetCpuPriority(mpVM, aCpuPriority);
}
else
rc = setInvalidMachineStateError();
}
/* notify console callbacks on success */
if (SUCCEEDED(rc))
CONSOLE_DO_CALLBACKS1(OnCPUPriorityChanged, aCpuPriority);
LogFlowThisFunc(("Leaving rc=%#x\n", rc));
return rc;
}
/**
* Called by IInternalSessionControl::OnVRDPServerChange().
*
* @note Locks this object for writing.
*/
HRESULT Console::onVRDPServerChange(BOOL aRestart)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
if ( mVRDPServer
&& ( mMachineState == MachineState_Running
|| mMachineState == MachineState_Teleporting
|| mMachineState == MachineState_LiveSnapshotting
)
)
{
BOOL vrdpEnabled = FALSE;
rc = mVRDPServer->COMGETTER(Enabled)(&vrdpEnabled);
ComAssertComRCRetRC(rc);
if (aRestart)
{
/* VRDP server may call this Console object back from other threads (VRDP INPUT or OUTPUT). */
alock.leave();
if (vrdpEnabled)
{
// If there was no VRDP server started the 'stop' will do nothing.
// However if a server was started and this notification was called,
// we have to restart the server.
mConsoleVRDPServer->Stop();
if (RT_FAILURE(mConsoleVRDPServer->Launch()))
rc = E_FAIL;
else
mConsoleVRDPServer->EnableConnections();
}
else
{
mConsoleVRDPServer->Stop();
}
alock.enter();
}
}
/* notify console callbacks on success */
if (SUCCEEDED(rc))
CONSOLE_DO_CALLBACKS0(OnVRDPServerChanged);
return rc;
}
/**
* @note Locks this object for reading.
*/
void Console::onRemoteDisplayInfoChange()
{
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
CONSOLE_DO_CALLBACKS0(OnRemoteDisplayInfoChanged);
}
/**
* Called by IInternalSessionControl::OnUSBControllerChange().
*
* @note Locks this object for writing.
*/
HRESULT Console::onUSBControllerChange()
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
/* don't trigger USB controller change if the VM isn't running */
if (mpVM)
{
/// @todo implement one day.
// Anyway, if we want to query the machine's USB Controller we need
// to cache it to mUSBController in #init() (similar to mDVDDrive).
//
// bird: While the VM supports hot-plugging, I doubt any guest can
// handle it at this time... :-)
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* nothing to do so far */
}
/* notify console callbacks on success */
if (SUCCEEDED(rc))
CONSOLE_DO_CALLBACKS0(OnUSBControllerChanged);
return rc;
}
/**
* Called by IInternalSessionControl::OnSharedFolderChange().
*
* @note Locks this object for writing.
*/
HRESULT Console::onSharedFolderChange(BOOL aGlobal)
{
LogFlowThisFunc(("aGlobal=%RTbool\n", aGlobal));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = fetchSharedFolders(aGlobal);
/* notify console callbacks on success */
if (SUCCEEDED(rc))
{
if (aGlobal)
CONSOLE_DO_CALLBACKS1(OnSharedFolderChanged, Scope_Global);
else
CONSOLE_DO_CALLBACKS1(OnSharedFolderChanged, Scope_Machine);
}
return rc;
}
/**
* Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by
* processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()
* returns TRUE for a given remote USB device.
*
* @return S_OK if the device was attached to the VM.
* @return failure if not attached.
*
* @param aDevice
* The device in question.
* @param aMaskedIfs
* The interfaces to hide from the guest.
*
* @note Locks this object for writing.
*/
HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError, ULONG aMaskedIfs)
{
#ifdef VBOX_WITH_USB
LogFlowThisFunc(("aDevice=%p aError=%p\n", aDevice, aError));
AutoCaller autoCaller(this);
ComAssertComRCRetRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/* protect mpVM (we don't need error info, since it's a callback) */
AutoVMCallerQuiet autoVMCaller(this);
if (FAILED(autoVMCaller.rc()))
{
/* The VM may be no more operational when this message arrives
* (e.g. it may be Saving or Stopping or just PoweredOff) --
* autoVMCaller.rc() will return a failure in this case. */
LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n",
mMachineState));
return autoVMCaller.rc();
}
if (aError != NULL)
{
/* notify callbacks about the error */
onUSBDeviceStateChange(aDevice, true /* aAttached */, aError);
return S_OK;
}
/* Don't proceed unless there's at least one USB hub. */
if (!PDMR3USBHasHub(mpVM))
{
LogFlowThisFunc(("Attach request ignored (no USB controller).\n"));
return E_FAIL;
}
HRESULT rc = attachUSBDevice(aDevice, aMaskedIfs);
if (FAILED(rc))
{
/* take the current error info */
com::ErrorInfoKeeper eik;
/* the error must be a VirtualBoxErrorInfo instance */
ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
Assert(!error.isNull());
if (!error.isNull())
{
/* notify callbacks about the error */
onUSBDeviceStateChange(aDevice, true /* aAttached */, error);
}
}
return rc;
#else /* !VBOX_WITH_USB */
return E_FAIL;
#endif /* !VBOX_WITH_USB */
}
/**
* Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by
* processRemoteUSBDevices().
*
* @note Locks this object for writing.
*/
HRESULT Console::onUSBDeviceDetach(IN_BSTR aId,
IVirtualBoxErrorInfo *aError)
{
#ifdef VBOX_WITH_USB
Guid Uuid(aId);
LogFlowThisFunc(("aId={%RTuuid} aError=%p\n", Uuid.raw(), aError));
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/* Find the device. */
ComObjPtr<OUSBDevice> device;
USBDeviceList::iterator it = mUSBDevices.begin();
while (it != mUSBDevices.end())
{
LogFlowThisFunc(("it={%RTuuid}\n", (*it)->id().raw()));
if ((*it)->id() == Uuid)
{
device = *it;
break;
}
++ it;
}
if (device.isNull())
{
LogFlowThisFunc(("USB device not found.\n"));
/* The VM may be no more operational when this message arrives
* (e.g. it may be Saving or Stopping or just PoweredOff). Use
* AutoVMCaller to detect it -- AutoVMCaller::rc() will return a
* failure in this case. */
AutoVMCallerQuiet autoVMCaller(this);
if (FAILED(autoVMCaller.rc()))
{
LogFlowThisFunc(("Detach request ignored (mMachineState=%d).\n",
mMachineState));
return autoVMCaller.rc();
}
/* the device must be in the list otherwise */
AssertFailedReturn(E_FAIL);
}
if (aError != NULL)
{
/* notify callback about an error */
onUSBDeviceStateChange(device, false /* aAttached */, aError);
return S_OK;
}
HRESULT rc = detachUSBDevice(it);
if (FAILED(rc))
{
/* take the current error info */
com::ErrorInfoKeeper eik;
/* the error must be a VirtualBoxErrorInfo instance */
ComPtr<IVirtualBoxErrorInfo> error = eik.takeError();
Assert(!error.isNull());
if (!error.isNull())
{
/* notify callbacks about the error */
onUSBDeviceStateChange(device, false /* aAttached */, error);
}
}
return rc;
#else /* !VBOX_WITH_USB */
return E_FAIL;
#endif /* !VBOX_WITH_USB */
}
/**
* @note Temporarily locks this object for writing.
*/
HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue,
LONG64 *aTimestamp, BSTR *aFlags)
{
#ifndef VBOX_WITH_GUEST_PROPS
ReturnComNotImplemented();
#else /* VBOX_WITH_GUEST_PROPS */
if (!VALID_PTR(aName))
return E_INVALIDARG;
if (!VALID_PTR(aValue))
return E_POINTER;
if ((aTimestamp != NULL) && !VALID_PTR(aTimestamp))
return E_POINTER;
if ((aFlags != NULL) && !VALID_PTR(aFlags))
return E_POINTER;
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
/* protect mpVM (if not NULL) */
AutoVMCallerWeak autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
* autoVMCaller, so there is no need to hold a lock of this */
HRESULT rc = E_UNEXPECTED;
using namespace guestProp;
try
{
VBOXHGCMSVCPARM parm[4];
Utf8Str Utf8Name = aName;
char pszBuffer[MAX_VALUE_LEN + MAX_FLAGS_LEN];
parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
/* The + 1 is the null terminator */
parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
parm[1].u.pointer.addr = pszBuffer;
parm[1].u.pointer.size = sizeof(pszBuffer);
int vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", GET_PROP_HOST,
4, &parm[0]);
/* The returned string should never be able to be greater than our buffer */
AssertLogRel(vrc != VERR_BUFFER_OVERFLOW);
AssertLogRel(RT_FAILURE(vrc) || VBOX_HGCM_SVC_PARM_64BIT == parm[2].type);
if (RT_SUCCESS(vrc) || (VERR_NOT_FOUND == vrc))
{
rc = S_OK;
if (vrc != VERR_NOT_FOUND)
{
Utf8Str strBuffer(pszBuffer);
strBuffer.cloneTo(aValue);
*aTimestamp = parm[2].u.uint64;
size_t iFlags = strBuffer.length() + 1;
Utf8Str(pszBuffer + iFlags).cloneTo(aFlags);
}
else
aValue = NULL;
}
else
rc = setError(E_UNEXPECTED,
tr("The service call failed with the error %Rrc"),
vrc);
}
catch(std::bad_alloc & /*e*/)
{
rc = E_OUTOFMEMORY;
}
return rc;
#endif /* VBOX_WITH_GUEST_PROPS */
}
/**
* @note Temporarily locks this object for writing.
*/
HRESULT Console::setGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags)
{
#ifndef VBOX_WITH_GUEST_PROPS
ReturnComNotImplemented();
#else /* VBOX_WITH_GUEST_PROPS */
if (!VALID_PTR(aName))
return E_INVALIDARG;
if ((aValue != NULL) && !VALID_PTR(aValue))
return E_INVALIDARG;
if ((aFlags != NULL) && !VALID_PTR(aFlags))
return E_INVALIDARG;
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
/* protect mpVM (if not NULL) */
AutoVMCallerWeak autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
* autoVMCaller, so there is no need to hold a lock of this */
HRESULT rc = E_UNEXPECTED;
using namespace guestProp;
VBOXHGCMSVCPARM parm[3];
Utf8Str Utf8Name = aName;
int vrc = VINF_SUCCESS;
parm[0].type = VBOX_HGCM_SVC_PARM_PTR;
parm[0].u.pointer.addr = (void*)Utf8Name.c_str();
/* The + 1 is the null terminator */
parm[0].u.pointer.size = (uint32_t)Utf8Name.length() + 1;
Utf8Str Utf8Value = aValue;
if (aValue != NULL)
{
parm[1].type = VBOX_HGCM_SVC_PARM_PTR;
parm[1].u.pointer.addr = (void*)Utf8Value.c_str();
/* The + 1 is the null terminator */
parm[1].u.pointer.size = (uint32_t)Utf8Value.length() + 1;
}
Utf8Str Utf8Flags = aFlags;
if (aFlags != NULL)
{
parm[2].type = VBOX_HGCM_SVC_PARM_PTR;
parm[2].u.pointer.addr = (void*)Utf8Flags.c_str();
/* The + 1 is the null terminator */
parm[2].u.pointer.size = (uint32_t)Utf8Flags.length() + 1;
}
if ((aValue != NULL) && (aFlags != NULL))
vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_HOST,
3, &parm[0]);
else if (aValue != NULL)
vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", SET_PROP_VALUE_HOST,
2, &parm[0]);
else
vrc = m_pVMMDev->hgcmHostCall("VBoxGuestPropSvc", DEL_PROP_HOST,
1, &parm[0]);
if (RT_SUCCESS(vrc))
rc = S_OK;
else
rc = setError(E_UNEXPECTED,
tr("The service call failed with the error %Rrc"),
vrc);
return rc;
#endif /* VBOX_WITH_GUEST_PROPS */
}
/**
* @note Temporarily locks this object for writing.
*/
HRESULT Console::enumerateGuestProperties(IN_BSTR aPatterns,
ComSafeArrayOut(BSTR, aNames),
ComSafeArrayOut(BSTR, aValues),
ComSafeArrayOut(LONG64, aTimestamps),
ComSafeArrayOut(BSTR, aFlags))
{
#ifndef VBOX_WITH_GUEST_PROPS
ReturnComNotImplemented();
#else /* VBOX_WITH_GUEST_PROPS */
if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
return E_POINTER;
if (ComSafeArrayOutIsNull(aNames))
return E_POINTER;
if (ComSafeArrayOutIsNull(aValues))
return E_POINTER;
if (ComSafeArrayOutIsNull(aTimestamps))
return E_POINTER;
if (ComSafeArrayOutIsNull(aFlags))
return E_POINTER;
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
/* protect mpVM (if not NULL) */
AutoVMCallerWeak autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* Note: validity of mVMMDev which is bound to uninit() is guaranteed by
* autoVMCaller, so there is no need to hold a lock of this */
return doEnumerateGuestProperties(aPatterns, ComSafeArrayOutArg(aNames),
ComSafeArrayOutArg(aValues),
ComSafeArrayOutArg(aTimestamps),
ComSafeArrayOutArg(aFlags));
#endif /* VBOX_WITH_GUEST_PROPS */
}
/*
* Internal: helper function for connecting progress reporting
*/
static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage)
{
HRESULT rc = S_OK;
IProgress *pProgress = static_cast<IProgress *>(pvUser);
if (pProgress)
rc = pProgress->SetCurrentOperationProgress(uPercentage);
return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
}
/**
* @note Temporarily locks this object for writing.
*/
HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
ULONG aSourceIdx, ULONG aTargetIdx,
IMedium *aSource, IMedium *aTarget,
BOOL aMergeForward,
IMedium *aParentForTarget,
ComSafeArrayIn(IMedium *, aChildrenToReparent),
IProgress *aProgress)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
HRESULT rc = S_OK;
int vrc = VINF_SUCCESS;
PVM pVM = mpVM;
/* We will need to release the lock before doing the actual merge */
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
/* paranoia - we don't want merges to happen while teleporting etc. */
switch (mMachineState)
{
case MachineState_DeletingSnapshotOnline:
case MachineState_DeletingSnapshotPaused:
break;
default:
return setInvalidMachineStateError();
}
SafeIfaceArray<IStorageController> ctrls;
rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls));
AssertComRC(rc);
LONG lDev;
rc = aMediumAttachment->COMGETTER(Device)(&lDev);
AssertComRC(rc);
LONG lPort;
rc = aMediumAttachment->COMGETTER(Port)(&lPort);
AssertComRC(rc);
IMedium *pMedium;
rc = aMediumAttachment->COMGETTER(Medium)(&pMedium);
AssertComRC(rc);
Bstr mediumLocation;
if (pMedium)
{
rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam());
AssertComRC(rc);
}
Bstr attCtrlName;
rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam());
AssertComRC(rc);
ComPtr<IStorageController> ctrl;
for (size_t i = 0; i < ctrls.size(); ++i)
{
Bstr ctrlName;
rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam());
AssertComRC(rc);
if (attCtrlName == ctrlName)
{
ctrl = ctrls[i];
break;
}
}
if (ctrl.isNull())
return setError(E_FAIL,
tr("Could not find storage controller '%ls'"),
attCtrlName.raw());
StorageControllerType_T enmCtrlType;
rc = ctrl->COMGETTER(ControllerType)(&enmCtrlType);
AssertComRC(rc);
const char *pcszDevice = convertControllerTypeToDev(enmCtrlType);
StorageBus_T enmBus;
rc = ctrl->COMGETTER(Bus)(&enmBus);
AssertComRC(rc);
ULONG uInstance;
rc = ctrl->COMGETTER(Instance)(&uInstance);
AssertComRC(rc);
BOOL fUseHostIOCache;
rc = ctrl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
AssertComRC(rc);
unsigned uLUN;
rc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN);
AssertComRCReturnRC(rc);
alock.release();
/* Pause the VM, as it might have pending IO on this drive */
VMSTATE enmVMState = VMR3GetState(pVM);
if (mMachineState == MachineState_DeletingSnapshotOnline)
{
LogFlowFunc(("Suspending the VM...\n"));
/* disable the callback to prevent Console-level state change */
mVMStateChangeCallbackDisabled = true;
int vrc2 = VMR3Suspend(pVM);
mVMStateChangeCallbackDisabled = false;
AssertRCReturn(vrc2, E_FAIL);
}
vrc = VMR3ReqCallWait(pVM,
VMCPUID_ANY,
(PFNRT)reconfigureMediumAttachment,
12,
this,
pVM,
pcszDevice,
uInstance,
enmBus,
fUseHostIOCache,
true /* fSetupMerge */,
aSourceIdx,
aTargetIdx,
aMediumAttachment,
mMachineState,
&rc);
/* error handling is after resuming the VM */
if (mMachineState == MachineState_DeletingSnapshotOnline)
{
LogFlowFunc(("Resuming the VM...\n"));
/* disable the callback to prevent Console-level state change */
mVMStateChangeCallbackDisabled = true;
int vrc2 = VMR3Resume(pVM);
mVMStateChangeCallbackDisabled = false;
AssertRC(vrc2);
if (RT_FAILURE(vrc2))
{
/* too bad, we failed. try to sync the console state with the VMM state */
vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this);
}
}
if (RT_FAILURE(vrc))
return setError(E_FAIL, tr("%Rrc"), vrc);
if (FAILED(rc))
return rc;
PPDMIBASE pIBase = NULL;
PPDMIMEDIA pIMedium = NULL;
vrc = PDMR3QueryDriverOnLun(pVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
if (RT_SUCCESS(vrc))
{
if (pIBase)
{
pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
if (!pIMedium)
return setError(E_FAIL, tr("could not query medium interface of controller"));
}
else
return setError(E_FAIL, tr("could not query base interface of controller"));
}
/* Finally trigger the merge. */
vrc = pIMedium->pfnMerge(pIMedium, onlineMergeMediumProgress, aProgress);
if (RT_FAILURE(vrc))
return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
/* Pause the VM, as it might have pending IO on this drive */
enmVMState = VMR3GetState(pVM);
if (mMachineState == MachineState_DeletingSnapshotOnline)
{
LogFlowFunc(("Suspending the VM...\n"));
/* disable the callback to prevent Console-level state change */
mVMStateChangeCallbackDisabled = true;
int vrc2 = VMR3Suspend(pVM);
mVMStateChangeCallbackDisabled = false;
AssertRCReturn(vrc2, E_FAIL);
}
/* Update medium chain and state now, so that the VM can continue. */
rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
aMergeForward, aParentForTarget,
ComSafeArrayInArg(aChildrenToReparent));
vrc = VMR3ReqCallWait(pVM,
VMCPUID_ANY,
(PFNRT)reconfigureMediumAttachment,
12,
this,
pVM,
pcszDevice,
uInstance,
enmBus,
fUseHostIOCache,
false /* fSetupMerge */,
0 /* uMergeSource */,
0 /* uMergeTarget */,
aMediumAttachment,
mMachineState,
&rc);
/* error handling is after resuming the VM */
if (mMachineState == MachineState_DeletingSnapshotOnline)
{
LogFlowFunc(("Resuming the VM...\n"));
/* disable the callback to prevent Console-level state change */
mVMStateChangeCallbackDisabled = true;
int vrc2 = VMR3Resume(pVM);
mVMStateChangeCallbackDisabled = false;
AssertRC(vrc2);
if (RT_FAILURE(vrc2))
{
/* too bad, we failed. try to sync the console state with the VMM state */
vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this);
}
}
if (RT_FAILURE(vrc))
return setError(E_FAIL, tr("%Rrc"), vrc);
if (FAILED(rc))
return rc;
return rc;
}
/**
* Gets called by Session::UpdateMachineState()
* (IInternalSessionControl::updateMachineState()).
*
* Must be called only in certain cases (see the implementation).
*
* @note Locks this object for writing.
*/
HRESULT Console::updateMachineState(MachineState_T aMachineState)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturn( mMachineState == MachineState_Saving
|| mMachineState == MachineState_LiveSnapshotting
|| mMachineState == MachineState_RestoringSnapshot
|| mMachineState == MachineState_DeletingSnapshot
|| mMachineState == MachineState_DeletingSnapshotOnline
|| mMachineState == MachineState_DeletingSnapshotPaused
, E_FAIL);
return setMachineStateLocally(aMachineState);
}
/**
* @note Locks this object for writing.
*/
void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,
uint32_t xHot, uint32_t yHot,
uint32_t width, uint32_t height,
ComSafeArrayIn(BYTE,pShape))
{
#if 0
LogFlowThisFuncEnter();
LogFlowThisFunc(("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, height=%d, shape=%p\n",
fVisible, fAlpha, xHot, yHot, width, height, pShape));
#endif
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
/* We need a write lock because we alter the cached callback data */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/* Save the callback arguments */
mCallbackData.mpsc.visible = fVisible;
mCallbackData.mpsc.alpha = fAlpha;
mCallbackData.mpsc.xHot = xHot;
mCallbackData.mpsc.yHot = yHot;
mCallbackData.mpsc.width = width;
mCallbackData.mpsc.height = height;
/* start with not valid */
bool wasValid = mCallbackData.mpsc.valid;
mCallbackData.mpsc.valid = false;
com::SafeArray <BYTE> aShape(ComSafeArrayInArg (pShape));
if (aShape.size() != 0)
mCallbackData.mpsc.shape.initFrom(aShape);
else
mCallbackData.mpsc.shape.resize(0);
mCallbackData.mpsc.valid = true;
/**
* Although looks stupid, this is result of fact that safearrays params in XPCOM
* passed as separate pointer and length arguments.
* @todo: better solution
*/
#ifdef RT_OS_WINDOWS
CONSOLE_DO_CALLBACKS7(OnMousePointerShapeChanged, fVisible, fAlpha, xHot, yHot, width, height, pShape);
#else
CONSOLE_DO_CALLBACKS8(OnMousePointerShapeChanged, fVisible, fAlpha, xHot, yHot, width, height, pShapeSize, pShape);
#endif
#if 0
LogFlowThisFuncLeave();
#endif
}
/**
* @note Locks this object for writing.
*/
void Console::onMouseCapabilityChange(BOOL supportsAbsolute, BOOL supportsRelative, BOOL needsHostCursor)
{
LogFlowThisFunc(("supportsAbsolute=%d supportsRelative=%d needsHostCursor=%d\n",
supportsAbsolute, supportsRelative, needsHostCursor));
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
/* We need a write lock because we alter the cached callback data */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/* save the callback arguments */
mCallbackData.mcc.supportsAbsolute = supportsAbsolute;
mCallbackData.mcc.supportsRelative = supportsRelative;
mCallbackData.mcc.needsHostCursor = needsHostCursor;
mCallbackData.mcc.valid = true;
CONSOLE_DO_CALLBACKS3(OnMouseCapabilityChanged, supportsAbsolute, supportsRelative, needsHostCursor);
}
/**
* @note Locks this object for reading.
*/
void Console::onStateChange(MachineState_T machineState)
{
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
CONSOLE_DO_CALLBACKS1(OnStateChanged, machineState);
}
/**
* @note Locks this object for reading.
*/
void Console::onAdditionsStateChange()
{
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
CONSOLE_DO_CALLBACKS0(OnAdditionsStateChanged);
}
/**
* @note Locks this object for reading.
* This notification only is for reporting an incompatible
* Guest Additions interface, *not* the Guest Additions version!
*
* The user will be notified inside the guest if new Guest
* Additions are available (via VBoxTray/VBoxClient).
*/
void Console::onAdditionsOutdated()
{
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
}
/**
* @note Locks this object for writing.
*/
void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)
{
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
/* We need a write lock because we alter the cached callback data */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/* save the callback arguments */
mCallbackData.klc.numLock = fNumLock;
mCallbackData.klc.capsLock = fCapsLock;
mCallbackData.klc.scrollLock = fScrollLock;
mCallbackData.klc.valid = true;
CONSOLE_DO_CALLBACKS3(OnKeyboardLedsChanged, fNumLock, fCapsLock, fScrollLock);
}
/**
* @note Locks this object for reading.
*/
void Console::onUSBDeviceStateChange(IUSBDevice *aDevice, bool aAttached,
IVirtualBoxErrorInfo *aError)
{
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
CONSOLE_DO_CALLBACKS3(OnUSBDeviceStateChanged, aDevice, aAttached, aError);
}
/**
* @note Locks this object for reading.
*/
void Console::onRuntimeError(BOOL aFatal, IN_BSTR aErrorID, IN_BSTR aMessage)
{
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
CONSOLE_DO_CALLBACKS3(OnRuntimeError, aFatal, aErrorID, aMessage);
}
/**
* @note Locks this object for reading.
*/
HRESULT Console::onShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId)
{
AssertReturn(aCanShow, E_POINTER);
AssertReturn(aWinId, E_POINTER);
*aCanShow = FALSE;
*aWinId = 0;
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
VBoxEventDesc evDesc;
if (aCheck)
{
evDesc.init(mEventSource, VBoxEventType_OnCanShowWindow);
BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
//Assert(fDelivered);
if (fDelivered)
{
ComPtr<IEvent> aEvent;
evDesc.getEvent(aEvent.asOutParam());
// bit clumsy
ComPtr<ICanShowWindowEvent> aCanShowEvent = aEvent;
if (aCanShowEvent)
{
BOOL fVetoed = FALSE;
aCanShowEvent->IsVetoed(&fVetoed);
*aCanShow = !fVetoed;
}
else
{
Assert(FALSE);
*aCanShow = TRUE;
}
}
else
*aCanShow = TRUE;
}
else
{
evDesc.init(mEventSource, VBoxEventType_OnShowWindow, INT64_C(0));
BOOL fDelivered = evDesc.fire(5000); /* Wait up to 5 secs for delivery */
//Assert(fDelivered);
if (fDelivered)
{
ComPtr<IEvent> aEvent;
evDesc.getEvent(aEvent.asOutParam());
ComPtr<IShowWindowEvent> aShowEvent = aEvent;
LONG64 aEvWinId = 0;
if (aShowEvent)
{
aShowEvent->COMGETTER(WinId)(&aEvWinId);
if ((aEvWinId != 0) && (*aWinId == 0))
*aWinId = aEvWinId;
}
else
Assert(FALSE);
}
}
return S_OK;
}
// private methods
////////////////////////////////////////////////////////////////////////////////
/**
* Increases the usage counter of the mpVM pointer. Guarantees that
* VMR3Destroy() will not be called on it at least until releaseVMCaller()
* is called.
*
* If this method returns a failure, the caller is not allowed to use mpVM
* and may return the failed result code to the upper level. This method sets
* the extended error info on failure if \a aQuiet is false.
*
* Setting \a aQuiet to true is useful for methods that don't want to return
* the failed result code to the caller when this method fails (e.g. need to
* silently check for the mpVM availability).
*
* When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be
* returned instead of asserting. Having it false is intended as a sanity check
* for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.
*
* @param aQuiet true to suppress setting error info
* @param aAllowNullVM true to accept mpVM being NULL and return a failure
* (otherwise this method will assert if mpVM is NULL)
*
* @note Locks this object for writing.
*/
HRESULT Console::addVMCaller(bool aQuiet /* = false */,
bool aAllowNullVM /* = false */)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (mVMDestroying)
{
/* powerDown() is waiting for all callers to finish */
return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
tr("The virtual machine is being powered down"));
}
if (mpVM == NULL)
{
Assert(aAllowNullVM == true);
/* The machine is not powered up */
return aQuiet ? E_ACCESSDENIED : setError(E_ACCESSDENIED,
tr("The virtual machine is not powered up"));
}
++mVMCallers;
return S_OK;
}
/**
* Decreases the usage counter of the mpVM pointer. Must always complete
* the addVMCaller() call after the mpVM pointer is no more necessary.
*
* @note Locks this object for writing.
*/
void Console::releaseVMCaller()
{
AutoCaller autoCaller(this);
AssertComRCReturnVoid(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
AssertReturnVoid(mpVM != NULL);
Assert(mVMCallers > 0);
--mVMCallers;
if (mVMCallers == 0 && mVMDestroying)
{
/* inform powerDown() there are no more callers */
RTSemEventSignal(mVMZeroCallersSem);
}
}
/**
* Initialize the release logging facility. In case something
* goes wrong, there will be no release logging. Maybe in the future
* we can add some logic to use different file names in this case.
* Note that the logic must be in sync with Machine::DeleteSettings().
*/
HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine)
{
HRESULT hrc = S_OK;
Bstr logFolder;
hrc = aMachine->COMGETTER(LogFolder)(logFolder.asOutParam());
if (FAILED(hrc)) return hrc;
Utf8Str logDir = logFolder;
/* make sure the Logs folder exists */
Assert(logDir.length());
if (!RTDirExists(logDir.c_str()))
RTDirCreateFullPath(logDir.c_str(), 0777);
Utf8Str logFile = Utf8StrFmt("%s%cVBox.log",
logDir.c_str(), RTPATH_DELIMITER);
Utf8Str pngFile = Utf8StrFmt("%s%cVBox.png",
logDir.c_str(), RTPATH_DELIMITER);
/*
* Age the old log files
* Rename .(n-1) to .(n), .(n-2) to .(n-1), ..., and the last log file to .1
* Overwrite target files in case they exist.
*/
ComPtr<IVirtualBox> virtualBox;
aMachine->COMGETTER(Parent)(virtualBox.asOutParam());
ComPtr<ISystemProperties> systemProperties;
virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
ULONG cHistoryFiles = 3;
systemProperties->COMGETTER(LogHistoryCount)(&cHistoryFiles);
if (cHistoryFiles)
{
for (int i = cHistoryFiles-1; i >= 0; i--)
{
Utf8Str *files[] = { &logFile, &pngFile };
Utf8Str oldName, newName;
for (unsigned int j = 0; j < RT_ELEMENTS(files); ++ j)
{
if (i > 0)
oldName = Utf8StrFmt("%s.%d", files[j]->c_str(), i);
else
oldName = *files[j];
newName = Utf8StrFmt("%s.%d", files[j]->c_str(), i + 1);
/* If the old file doesn't exist, delete the new file (if it
* exists) to provide correct rotation even if the sequence is
* broken */
if ( RTFileRename(oldName.c_str(), newName.c_str(), RTFILEMOVE_FLAGS_REPLACE)
== VERR_FILE_NOT_FOUND)
RTFileDelete(newName.c_str());
}
}
}
PRTLOGGER loggerRelease;
static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG;
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
fFlags |= RTLOGFLAGS_USECRLF;
#endif
char szError[RTPATH_MAX + 128] = "";
int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all",
"VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
RTLOGDEST_FILE, szError, sizeof(szError), logFile.c_str());
if (RT_SUCCESS(vrc))
{
/* some introductory information */
RTTIMESPEC timeSpec;
char szTmp[256];
RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp));
RTLogRelLogger(loggerRelease, 0, ~0U,
"VirtualBox %s r%u %s (%s %s) release log\n"
#ifdef VBOX_BLEEDING_EDGE
"EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
#endif
"Log opened %s\n",
VBOX_VERSION_STRING, RTBldCfgRevision(), VBOX_BUILD_TARGET,
__DATE__, __TIME__, szTmp);
vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp);
vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp);
vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp);
vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp);
vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szTmp, sizeof(szTmp));
if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Name: %s\n", szTmp);
vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION, szTmp, sizeof(szTmp));
if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Version: %s\n", szTmp);
ComPtr<IHost> host;
virtualBox->COMGETTER(Host)(host.asOutParam());
ULONG cMbHostRam = 0;
ULONG cMbHostRamAvail = 0;
host->COMGETTER(MemorySize)(&cMbHostRam);
host->COMGETTER(MemoryAvailable)(&cMbHostRamAvail);
RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n",
cMbHostRam, cMbHostRamAvail);
/* the package type is interesting for Linux distributions */
char szExecName[RTPATH_MAX];
char *pszExecName = RTProcGetExecutableName(szExecName, sizeof(szExecName));
RTLogRelLogger(loggerRelease, 0, ~0U,
"Executable: %s\n"
"Process ID: %u\n"
"Package type: %s"
#ifdef VBOX_OSE
" (OSE)"
#endif
"\n",
pszExecName ? pszExecName : "unknown",
RTProcSelf(),
VBOX_PACKAGE_STRING);
/* register this logger as the release logger */
RTLogRelSetDefaultInstance(loggerRelease);
hrc = S_OK;
/* Explicitly flush the log in case of VBOX_RELEASE_LOG=buffered. */
RTLogFlush(loggerRelease);
}
else
hrc = setError(E_FAIL,
tr("Failed to open release log (%s, %Rrc)"),
szError, vrc);
/* If we've made any directory changes, flush the directory to increase
the likelyhood that the log file will be usable after a system panic.
Tip: Try 'export VBOX_RELEASE_LOG_FLAGS=flush' if the last bits of the log
is missing. Just don't have too high hopes for this to help. */
if (SUCCEEDED(hrc) || cHistoryFiles)
RTDirFlush(logDir.c_str());
return hrc;
}
/**
* Common worker for PowerUp and PowerUpPaused.
*
* @returns COM status code.
*
* @param aProgress Where to return the progress object.
* @param aPaused true if PowerUpPaused called.
*
* @todo move down to powerDown();
*/
HRESULT Console::powerUp(IProgress **aProgress, bool aPaused)
{
if (aProgress == NULL)
return E_POINTER;
LogFlowThisFuncEnter();
LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (Global::IsOnlineOrTransient(mMachineState))
return setError(VBOX_E_INVALID_VM_STATE,
tr("The virtual machine is already running or busy (machine state: %s)"),
Global::stringifyMachineState(mMachineState));
HRESULT rc = S_OK;
/* the network cards will undergo a quick consistency check */
for (ULONG slot = 0;
slot < SchemaDefs::NetworkAdapterCount;
++slot)
{
ComPtr<INetworkAdapter> adapter;
mMachine->GetNetworkAdapter(slot, adapter.asOutParam());
BOOL enabled = FALSE;
adapter->COMGETTER(Enabled)(&enabled);
if (!enabled)
continue;
NetworkAttachmentType_T netattach;
adapter->COMGETTER(AttachmentType)(&netattach);
switch (netattach)
{
case NetworkAttachmentType_Bridged:
{
#ifdef RT_OS_WINDOWS
/* a valid host interface must have been set */
Bstr hostif;
adapter->COMGETTER(HostInterface)(hostif.asOutParam());
if (hostif.isEmpty())
{
return setError(VBOX_E_HOST_ERROR,
tr("VM cannot start because host interface networking requires a host interface name to be set"));
}
ComPtr<IVirtualBox> virtualBox;
mMachine->COMGETTER(Parent)(virtualBox.asOutParam());
ComPtr<IHost> host;
virtualBox->COMGETTER(Host)(host.asOutParam());
ComPtr<IHostNetworkInterface> hostInterface;
if (!SUCCEEDED(host->FindHostNetworkInterfaceByName(hostif.raw(),
hostInterface.asOutParam())))
{
return setError(VBOX_E_HOST_ERROR,
tr("VM cannot start because the host interface '%ls' does not exist"),
hostif.raw());
}
#endif /* RT_OS_WINDOWS */
break;
}
default:
break;
}
}
/* Read console data stored in the saved state file (if not yet done) */
rc = loadDataFromSavedState();
if (FAILED(rc)) return rc;
/* Check all types of shared folders and compose a single list */
SharedFolderDataMap sharedFolders;
{
/* first, insert global folders */
for (SharedFolderDataMap::const_iterator it = mGlobalSharedFolders.begin();
it != mGlobalSharedFolders.end(); ++ it)
sharedFolders[it->first] = it->second;
/* second, insert machine folders */
for (SharedFolderDataMap::const_iterator it = mMachineSharedFolders.begin();
it != mMachineSharedFolders.end(); ++ it)
sharedFolders[it->first] = it->second;
/* third, insert console folders */
for (SharedFolderMap::const_iterator it = mSharedFolders.begin();
it != mSharedFolders.end(); ++ it)
sharedFolders[it->first] = SharedFolderData(it->second->getHostPath(),
it->second->isWritable(),
it->second->isAutoMounted());
}
Bstr savedStateFile;
/*
* Saved VMs will have to prove that their saved states seem kosher.
*/
if (mMachineState == MachineState_Saved)
{
rc = mMachine->COMGETTER(StateFilePath)(savedStateFile.asOutParam());
if (FAILED(rc)) return rc;
ComAssertRet(!savedStateFile.isEmpty(), E_FAIL);
int vrc = SSMR3ValidateFile(Utf8Str(savedStateFile).c_str(), false /* fChecksumIt */);
if (RT_FAILURE(vrc))
return setError(VBOX_E_FILE_ERROR,
tr("VM cannot start because the saved state file '%ls' is invalid (%Rrc). Delete the saved state prior to starting the VM"),
savedStateFile.raw(), vrc);
}
/* test and clear the TeleporterEnabled property */
BOOL fTeleporterEnabled;
rc = mMachine->COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
if (FAILED(rc)) return rc;
#if 0 /** @todo we should save it afterwards, but that isn't necessarily a good idea. Find a better place for this (VBoxSVC). */
if (fTeleporterEnabled)
{
rc = mMachine->COMSETTER(TeleporterEnabled)(FALSE);
if (FAILED(rc)) return rc;
}
#endif
/* test the FaultToleranceState property */
FaultToleranceState_T enmFaultToleranceState;
rc = mMachine->COMGETTER(FaultToleranceState)(&enmFaultToleranceState);
if (FAILED(rc)) return rc;
BOOL fFaultToleranceSyncEnabled = (enmFaultToleranceState == FaultToleranceState_Standby);
/* create a progress object to track progress of this operation */
ComObjPtr<Progress> powerupProgress;
powerupProgress.createObject();
Bstr progressDesc;
if (mMachineState == MachineState_Saved)
progressDesc = tr("Restoring virtual machine");
else if (fTeleporterEnabled)
progressDesc = tr("Teleporting virtual machine");
else if (fFaultToleranceSyncEnabled)
progressDesc = tr("Fault Tolerance syncing of remote virtual machine");
else
progressDesc = tr("Starting virtual machine");
if ( mMachineState == MachineState_Saved
|| (!fTeleporterEnabled && !fFaultToleranceSyncEnabled))
rc = powerupProgress->init(static_cast<IConsole *>(this),
progressDesc.raw(),
FALSE /* aCancelable */);
else
if (fTeleporterEnabled)
rc = powerupProgress->init(static_cast<IConsole *>(this),
progressDesc.raw(),
TRUE /* aCancelable */,
3 /* cOperations */,
10 /* ulTotalOperationsWeight */,
Bstr(tr("Teleporting virtual machine")).raw(),
1 /* ulFirstOperationWeight */,
NULL);
else
if (fFaultToleranceSyncEnabled)
rc = powerupProgress->init(static_cast<IConsole *>(this),
progressDesc.raw(),
TRUE /* aCancelable */,
3 /* cOperations */,
10 /* ulTotalOperationsWeight */,
Bstr(tr("Fault Tolerance syncing of remote virtual machine")).raw(),
1 /* ulFirstOperationWeight */,
NULL);
if (FAILED(rc))
return rc;
/* Tell VBoxSVC and Machine about the progress object so they can combine
proxy it to any openRemoteSession caller. */
LogFlowThisFunc(("Calling BeginPowerUp...\n"));
rc = mControl->BeginPowerUp(powerupProgress);
if (FAILED(rc))
{
LogFlowThisFunc(("BeginPowerUp failed\n"));
return rc;
}
LogFlowThisFunc(("Checking if canceled...\n"));
BOOL fCanceled;
rc = powerupProgress->COMGETTER(Canceled)(&fCanceled);
if (FAILED(rc))
return rc;
if (fCanceled)
{
LogFlowThisFunc(("Canceled in BeginPowerUp\n"));
return setError(E_FAIL, tr("Powerup was canceled"));
}
LogFlowThisFunc(("Not canceled yet.\n"));
/* setup task object and thread to carry out the operation
* asynchronously */
std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, powerupProgress));
ComAssertComRCRetRC(task->rc());
task->mConfigConstructor = configConstructor;
task->mSharedFolders = sharedFolders;
task->mStartPaused = aPaused;
if (mMachineState == MachineState_Saved)
task->mSavedStateFile = savedStateFile;
task->mTeleporterEnabled = fTeleporterEnabled;
task->mEnmFaultToleranceState = enmFaultToleranceState;
/* Reset differencing hard disks for which autoReset is true,
* but only if the machine has no snapshots OR the current snapshot
* is an OFFLINE snapshot; otherwise we would reset the current differencing
* image of an ONLINE snapshot which contains the disk state of the machine
* while it was previously running, but without the corresponding machine
* state, which is equivalent to powering off a running machine and not
* good idea
*/
ComPtr<ISnapshot> pCurrentSnapshot;
rc = mMachine->COMGETTER(CurrentSnapshot)(pCurrentSnapshot.asOutParam());
if (FAILED(rc)) return rc;
BOOL fCurrentSnapshotIsOnline = false;
if (pCurrentSnapshot)
{
rc = pCurrentSnapshot->COMGETTER(Online)(&fCurrentSnapshotIsOnline);
if (FAILED(rc)) return rc;
}
if (!fCurrentSnapshotIsOnline)
{
LogFlowThisFunc(("Looking for immutable images to reset\n"));
com::SafeIfaceArray<IMediumAttachment> atts;
rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
if (FAILED(rc)) return rc;
for (size_t i = 0;
i < atts.size();
++i)
{
DeviceType_T devType;
rc = atts[i]->COMGETTER(Type)(&devType);
/** @todo later applies to floppies as well */
if (devType == DeviceType_HardDisk)
{
ComPtr<IMedium> medium;
rc = atts[i]->COMGETTER(Medium)(medium.asOutParam());
if (FAILED(rc)) return rc;
/* needs autoreset? */
BOOL autoReset = FALSE;
rc = medium->COMGETTER(AutoReset)(&autoReset);
if (FAILED(rc)) return rc;
if (autoReset)
{
ComPtr<IProgress> resetProgress;
rc = medium->Reset(resetProgress.asOutParam());
if (FAILED(rc)) return rc;
/* save for later use on the powerup thread */
task->hardDiskProgresses.push_back(resetProgress);
}
}
}
}
else
LogFlowThisFunc(("Machine has a current snapshot which is online, skipping immutable images reset\n"));
rc = consoleInitReleaseLog(mMachine);
if (FAILED(rc)) return rc;
#ifdef RT_OS_SOLARIS
/* setup host core dumper for the VM */
Bstr value;
HRESULT hrc = mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpEnabled").raw(), value.asOutParam());
if (SUCCEEDED(hrc) && value == "1")
{
Bstr coreDumpDir, coreDumpReplaceSys, coreDumpLive;
mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpDir").raw(), coreDumpDir.asOutParam());
mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpReplaceSystemDump").raw(), coreDumpReplaceSys.asOutParam());
mMachine->GetExtraData(Bstr("VBoxInternal2/CoreDumpLive").raw(), coreDumpLive.asOutParam());
uint32_t fCoreFlags = 0;
if ( coreDumpReplaceSys.isEmpty() == false
&& Utf8Str(coreDumpReplaceSys).toUInt32() == 1)
{
fCoreFlags |= RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP;
}
if ( coreDumpLive.isEmpty() == false
&& Utf8Str(coreDumpLive).toUInt32() == 1)
{
fCoreFlags |= RTCOREDUMPER_FLAGS_LIVE_CORE;
}
const char *pszDumpDir = Utf8Str(coreDumpDir).c_str();
if ( pszDumpDir
&& *pszDumpDir == '\0')
pszDumpDir = NULL;
int vrc;
if ( pszDumpDir
&& !RTDirExists(pszDumpDir))
{
/*
* Try create the directory.
*/
vrc = RTDirCreateFullPath(pszDumpDir, 0777);
if (RT_FAILURE(vrc))
return setError(E_FAIL, "Failed to setup CoreDumper. Couldn't create dump directory '%s' (%Rrc)\n", pszDumpDir, vrc);
}
vrc = RTCoreDumperSetup(pszDumpDir, fCoreFlags);
if (RT_FAILURE(vrc))
return setError(E_FAIL, "Failed to setup CoreDumper (%Rrc)", vrc);
else
LogRel(("CoreDumper setup successful. pszDumpDir=%s fFlags=%#x\n", pszDumpDir ? pszDumpDir : ".", fCoreFlags));
}
#endif
/* pass the progress object to the caller if requested */
if (aProgress)
{
if (task->hardDiskProgresses.size() == 0)
{
/* there are no other operations to track, return the powerup
* progress only */
powerupProgress.queryInterfaceTo(aProgress);
}
else
{
/* create a combined progress object */
ComObjPtr<CombinedProgress> progress;
progress.createObject();
VMPowerUpTask::ProgressList progresses(task->hardDiskProgresses);
progresses.push_back(ComPtr<IProgress> (powerupProgress));
rc = progress->init(static_cast<IConsole *>(this),
progressDesc.raw(), progresses.begin(),
progresses.end());
AssertComRCReturnRC(rc);
progress.queryInterfaceTo(aProgress);
}
}
int vrc = RTThreadCreate(NULL, Console::powerUpThread, (void *) task.get(),
0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");
if (RT_FAILURE(vrc))
return setError(E_FAIL, "Could not create VMPowerUp thread (%Rrc)", vrc);
/* task is now owned by powerUpThread(), so release it */
task.release();
/* finally, set the state: no right to fail in this method afterwards
* since we've already started the thread and it is now responsible for
* any error reporting and appropriate state change! */
if (mMachineState == MachineState_Saved)
setMachineState(MachineState_Restoring);
else if (fTeleporterEnabled)
setMachineState(MachineState_TeleportingIn);
else if (enmFaultToleranceState == FaultToleranceState_Standby)
setMachineState(MachineState_FaultTolerantSyncing);
else
setMachineState(MachineState_Starting);
LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
LogFlowThisFuncLeave();
return S_OK;
}
/**
* Internal power off worker routine.
*
* This method may be called only at certain places with the following meaning
* as shown below:
*
* - if the machine state is either Running or Paused, a normal
* Console-initiated powerdown takes place (e.g. PowerDown());
* - if the machine state is Saving, saveStateThread() has successfully done its
* job;
* - if the machine state is Starting or Restoring, powerUpThread() has failed
* to start/load the VM;
* - if the machine state is Stopping, the VM has powered itself off (i.e. not
* as a result of the powerDown() call).
*
* Calling it in situations other than the above will cause unexpected behavior.
*
* Note that this method should be the only one that destroys mpVM and sets it
* to NULL.
*
* @param aProgress Progress object to run (may be NULL).
*
* @note Locks this object for writing.
*
* @note Never call this method from a thread that called addVMCaller() or
* instantiated an AutoVMCaller object; first call releaseVMCaller() or
* release(). Otherwise it will deadlock.
*/
HRESULT Console::powerDown(Progress *aProgress /*= NULL*/)
{
LogFlowThisFuncEnter();
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/* Total # of steps for the progress object. Must correspond to the
* number of "advance percent count" comments in this method! */
enum { StepCount = 7 };
/* current step */
ULONG step = 0;
HRESULT rc = S_OK;
int vrc = VINF_SUCCESS;
/* sanity */
Assert(mVMDestroying == false);
Assert(mpVM != NULL);
AssertMsg( mMachineState == MachineState_Running
|| mMachineState == MachineState_Paused
|| mMachineState == MachineState_Stuck
|| mMachineState == MachineState_Starting
|| mMachineState == MachineState_Stopping
|| mMachineState == MachineState_Saving
|| mMachineState == MachineState_Restoring
|| mMachineState == MachineState_TeleportingPausedVM
|| mMachineState == MachineState_FaultTolerantSyncing
|| mMachineState == MachineState_TeleportingIn
, ("Invalid machine state: %s\n", Global::stringifyMachineState(mMachineState)));
LogRel(("Console::powerDown(): A request to power off the VM has been issued (mMachineState=%s, InUninit=%d)\n",
Global::stringifyMachineState(mMachineState), autoCaller.state() == InUninit));
/* Check if we need to power off the VM. In case of mVMPoweredOff=true, the
* VM has already powered itself off in vmstateChangeCallback() and is just
* notifying Console about that. In case of Starting or Restoring,
* powerUpThread() is calling us on failure, so the VM is already off at
* that point. */
if ( !mVMPoweredOff
&& ( mMachineState == MachineState_Starting
|| mMachineState == MachineState_Restoring
|| mMachineState == MachineState_FaultTolerantSyncing
|| mMachineState == MachineState_TeleportingIn)
)
mVMPoweredOff = true;
/*
* Go to Stopping state if not already there.
*
* Note that we don't go from Saving/Restoring to Stopping because
* vmstateChangeCallback() needs it to set the state to Saved on
* VMSTATE_TERMINATED. In terms of protecting from inappropriate operations
* while leaving the lock below, Saving or Restoring should be fine too.
* Ditto for TeleportingPausedVM -> Teleported.
*/
if ( mMachineState != MachineState_Saving
&& mMachineState != MachineState_Restoring
&& mMachineState != MachineState_Stopping
&& mMachineState != MachineState_TeleportingIn
&& mMachineState != MachineState_TeleportingPausedVM
&& mMachineState != MachineState_FaultTolerantSyncing
)
setMachineState(MachineState_Stopping);
/* ----------------------------------------------------------------------
* DONE with necessary state changes, perform the power down actions (it's
* safe to leave the object lock now if needed)
* ---------------------------------------------------------------------- */
/* Stop the VRDP server to prevent new clients connection while VM is being
* powered off. */
if (mConsoleVRDPServer)
{
LogFlowThisFunc(("Stopping VRDP server...\n"));
/* Leave the lock since EMT will call us back as addVMCaller()
* in updateDisplayData(). */
alock.leave();
mConsoleVRDPServer->Stop();
alock.enter();
}
/* advance percent count */
if (aProgress)
aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
/* ----------------------------------------------------------------------
* Now, wait for all mpVM callers to finish their work if there are still
* some on other threads. NO methods that need mpVM (or initiate other calls
* that need it) may be called after this point
* ---------------------------------------------------------------------- */
/* go to the destroying state to prevent from adding new callers */
mVMDestroying = true;
if (mVMCallers > 0)
{
/* lazy creation */
if (mVMZeroCallersSem == NIL_RTSEMEVENT)
RTSemEventCreate(&mVMZeroCallersSem);
LogFlowThisFunc(("Waiting for mpVM callers (%d) to drop to zero...\n",
mVMCallers));
alock.leave();
RTSemEventWait(mVMZeroCallersSem, RT_INDEFINITE_WAIT);
alock.enter();
}
/* advance percent count */
if (aProgress)
aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
vrc = VINF_SUCCESS;
/*
* Power off the VM if not already done that.
* Leave the lock since EMT will call vmstateChangeCallback.
*
* Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the
* VM-(guest-)initiated power off happened in parallel a ms before this
* call. So far, we let this error pop up on the user's side.
*/
if (!mVMPoweredOff)
{
LogFlowThisFunc(("Powering off the VM...\n"));
alock.leave();
vrc = VMR3PowerOff(mpVM);
alock.enter();
}
else
{
/** @todo r=bird: Doesn't make sense. Please remove after 3.1 has been branched
* off. */
/* reset the flag for future re-use */
mVMPoweredOff = false;
}
/* advance percent count */
if (aProgress)
aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
#ifdef VBOX_WITH_HGCM
/* Shutdown HGCM services before destroying the VM. */
if (m_pVMMDev)
{
LogFlowThisFunc(("Shutdown HGCM...\n"));
/* Leave the lock since EMT will call us back as addVMCaller() */
alock.leave();
m_pVMMDev->hgcmShutdown();
alock.enter();
}
/* advance percent count */
if (aProgress)
aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
#endif /* VBOX_WITH_HGCM */
LogFlowThisFunc(("Ready for VM destruction.\n"));
/* If we are called from Console::uninit(), then try to destroy the VM even
* on failure (this will most likely fail too, but what to do?..) */
if (RT_SUCCESS(vrc) || autoCaller.state() == InUninit)
{
/* If the machine has an USB controller, release all USB devices
* (symmetric to the code in captureUSBDevices()) */
bool fHasUSBController = false;
{
PPDMIBASE pBase;
vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase);
if (RT_SUCCESS(vrc))
{
fHasUSBController = true;
detachAllUSBDevices(false /* aDone */);
}
}
/* Now we've got to destroy the VM as well. (mpVM is not valid beyond
* this point). We leave the lock before calling VMR3Destroy() because
* it will result into calling destructors of drivers associated with
* Console children which may in turn try to lock Console (e.g. by
* instantiating SafeVMPtr to access mpVM). It's safe here because
* mVMDestroying is set which should prevent any activity. */
/* Set mpVM to NULL early just in case if some old code is not using
* addVMCaller()/releaseVMCaller(). */
PVM pVM = mpVM;
mpVM = NULL;
LogFlowThisFunc(("Destroying the VM...\n"));
alock.leave();
vrc = VMR3Destroy(pVM);
/* take the lock again */
alock.enter();
/* advance percent count */
if (aProgress)
aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
if (RT_SUCCESS(vrc))
{
LogFlowThisFunc(("Machine has been destroyed (mMachineState=%d)\n",
mMachineState));
/* Note: the Console-level machine state change happens on the
* VMSTATE_TERMINATE state change in vmstateChangeCallback(). If
* powerDown() is called from EMT (i.e. from vmstateChangeCallback()
* on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't
* occurred yet. This is okay, because mMachineState is already
* Stopping in this case, so any other attempt to call PowerDown()
* will be rejected. */
}
else
{
/* bad bad bad, but what to do? */
mpVM = pVM;
rc = setError(VBOX_E_VM_ERROR,
tr("Could not destroy the machine. (Error: %Rrc)"),
vrc);
}
/* Complete the detaching of the USB devices. */
if (fHasUSBController)
detachAllUSBDevices(true /* aDone */);
/* advance percent count */
if (aProgress)
aProgress->SetCurrentOperationProgress(99 * (++ step) / StepCount );
}
else
{
rc = setError(VBOX_E_VM_ERROR,
tr("Could not power off the machine. (Error: %Rrc)"),
vrc);
}
/* Finished with destruction. Note that if something impossible happened and
* we've failed to destroy the VM, mVMDestroying will remain true and
* mMachineState will be something like Stopping, so most Console methods
* will return an error to the caller. */
if (mpVM == NULL)
mVMDestroying = false;
if (SUCCEEDED(rc))
mCallbackData.clear();
/* complete the progress */
if (aProgress)
aProgress->notifyComplete(rc);
LogFlowThisFuncLeave();
return rc;
}
/**
* @note Locks this object for writing.
*/
HRESULT Console::setMachineState(MachineState_T aMachineState,
bool aUpdateServer /* = true */)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
if (mMachineState != aMachineState)
{
LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
mMachineState = aMachineState;
/// @todo (dmik)
// possibly, we need to redo onStateChange() using the dedicated
// Event thread, like it is done in VirtualBox. This will make it
// much safer (no deadlocks possible if someone tries to use the
// console from the callback), however, listeners will lose the
// ability to synchronously react to state changes (is it really
// necessary??)
LogFlowThisFunc(("Doing onStateChange()...\n"));
onStateChange(aMachineState);
LogFlowThisFunc(("Done onStateChange()\n"));
if (aUpdateServer)
{
/* Server notification MUST be done from under the lock; otherwise
* the machine state here and on the server might go out of sync
* which can lead to various unexpected results (like the machine
* state being >= MachineState_Running on the server, while the
* session state is already SessionState_Unlocked at the same time
* there).
*
* Cross-lock conditions should be carefully watched out: calling
* UpdateState we will require Machine and SessionMachine locks
* (remember that here we're holding the Console lock here, and also
* all locks that have been entered by the thread before calling
* this method).
*/
LogFlowThisFunc(("Doing mControl->UpdateState()...\n"));
rc = mControl->UpdateState(aMachineState);
LogFlowThisFunc(("mControl->UpdateState()=%08X\n", rc));
}
}
return rc;
}
/**
* Searches for a shared folder with the given logical name
* in the collection of shared folders.
*
* @param aName logical name of the shared folder
* @param aSharedFolder where to return the found object
* @param aSetError whether to set the error info if the folder is
* not found
* @return
* S_OK when found or E_INVALIDARG when not found
*
* @note The caller must lock this object for writing.
*/
HRESULT Console::findSharedFolder(CBSTR aName,
ComObjPtr<SharedFolder> &aSharedFolder,
bool aSetError /* = false */)
{
/* sanity check */
AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
SharedFolderMap::const_iterator it = mSharedFolders.find(aName);
if (it != mSharedFolders.end())
{
aSharedFolder = it->second;
return S_OK;
}
if (aSetError)
setError(VBOX_E_FILE_ERROR,
tr("Could not find a shared folder named '%ls'."),
aName);
return VBOX_E_FILE_ERROR;
}
/**
* Fetches the list of global or machine shared folders from the server.
*
* @param aGlobal true to fetch global folders.
*
* @note The caller must lock this object for writing.
*/
HRESULT Console::fetchSharedFolders(BOOL aGlobal)
{
/* sanity check */
AssertReturn(AutoCaller(this).state() == InInit ||
isWriteLockOnCurrentThread(), E_FAIL);
/* protect mpVM (if not NULL) */
AutoVMCallerQuietWeak autoVMCaller(this);
HRESULT rc = S_OK;
bool online = mpVM
&& autoVMCaller.isOk()
&& m_pVMMDev
&& m_pVMMDev->isShFlActive();
if (aGlobal)
{
/// @todo grab & process global folders when they are done
}
else
{
SharedFolderDataMap oldFolders;
if (online)
oldFolders = mMachineSharedFolders;
mMachineSharedFolders.clear();
SafeIfaceArray<ISharedFolder> folders;
rc = mMachine->COMGETTER(SharedFolders)(ComSafeArrayAsOutParam(folders));
AssertComRCReturnRC(rc);
for (size_t i = 0; i < folders.size(); ++i)
{
ComPtr<ISharedFolder> folder = folders[i];
Bstr name;
Bstr hostPath;
BOOL writable;
BOOL autoMount;
rc = folder->COMGETTER(Name)(name.asOutParam());
if (FAILED(rc)) break;
rc = folder->COMGETTER(HostPath)(hostPath.asOutParam());
if (FAILED(rc)) break;
rc = folder->COMGETTER(Writable)(&writable);
if (FAILED(rc)) break;
rc = folder->COMGETTER(AutoMount)(&autoMount);
if (FAILED(rc)) break;
mMachineSharedFolders.insert(std::make_pair(name, SharedFolderData(hostPath, writable, autoMount)));
/* send changes to HGCM if the VM is running */
/// @todo report errors as runtime warnings through VMSetError
if (online)
{
SharedFolderDataMap::iterator it = oldFolders.find(name);
if (it == oldFolders.end() || it->second.mHostPath != hostPath)
{
/* a new machine folder is added or
* the existing machine folder is changed */
if (mSharedFolders.find(name) != mSharedFolders.end())
; /* the console folder exists, nothing to do */
else
{
/* remove the old machine folder (when changed)
* or the global folder if any (when new) */
if (it != oldFolders.end() ||
mGlobalSharedFolders.find(name) !=
mGlobalSharedFolders.end())
rc = removeSharedFolder(name.raw());
/* create the new machine folder */
rc = createSharedFolder(name.raw(),
SharedFolderData(hostPath,
writable,
autoMount));
}
}
/* forget the processed (or identical) folder */
if (it != oldFolders.end())
oldFolders.erase(it);
rc = S_OK;
}
}
AssertComRCReturnRC(rc);
/* process outdated (removed) folders */
/// @todo report errors as runtime warnings through VMSetError
if (online)
{
for (SharedFolderDataMap::const_iterator it = oldFolders.begin();
it != oldFolders.end(); ++ it)
{
if (mSharedFolders.find(it->first) != mSharedFolders.end())
; /* the console folder exists, nothing to do */
else
{
/* remove the outdated machine folder */
rc = removeSharedFolder(it->first.raw());
/* create the global folder if there is any */
SharedFolderDataMap::const_iterator git =
mGlobalSharedFolders.find(it->first);
if (git != mGlobalSharedFolders.end())
rc = createSharedFolder(git->first.raw(), git->second);
}
}
rc = S_OK;
}
}
return rc;
}
/**
* Searches for a shared folder with the given name in the list of machine
* shared folders and then in the list of the global shared folders.
*
* @param aName Name of the folder to search for.
* @param aIt Where to store the pointer to the found folder.
* @return @c true if the folder was found and @c false otherwise.
*
* @note The caller must lock this object for reading.
*/
bool Console::findOtherSharedFolder(IN_BSTR aName,
SharedFolderDataMap::const_iterator &aIt)
{
/* sanity check */
AssertReturn(isWriteLockOnCurrentThread(), false);
/* first, search machine folders */
aIt = mMachineSharedFolders.find(aName);
if (aIt != mMachineSharedFolders.end())
return true;
/* second, search machine folders */
aIt = mGlobalSharedFolders.find(aName);
if (aIt != mGlobalSharedFolders.end())
return true;
return false;
}
/**
* Calls the HGCM service to add a shared folder definition.
*
* @param aName Shared folder name.
* @param aHostPath Shared folder path.
*
* @note Must be called from under AutoVMCaller and when mpVM != NULL!
* @note Doesn't lock anything.
*/
HRESULT Console::createSharedFolder(CBSTR aName, SharedFolderData aData)
{
ComAssertRet(aName && *aName, E_FAIL);
ComAssertRet(!aData.mHostPath.isEmpty(), E_FAIL);
/* sanity checks */
AssertReturn(mpVM, E_FAIL);
AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING2];
SHFLSTRING *pFolderName, *pMapName;
size_t cbString;
Log(("Adding shared folder '%ls' -> '%ls'\n", aName, aData.mHostPath.raw()));
cbString = (RTUtf16Len(aData.mHostPath.raw()) + 1) * sizeof(RTUTF16);
if (cbString >= UINT16_MAX)
return setError(E_INVALIDARG, tr("The name is too long"));
pFolderName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
Assert(pFolderName);
memcpy(pFolderName->String.ucs2, aData.mHostPath.raw(), cbString);
pFolderName->u16Size = (uint16_t)cbString;
pFolderName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
parms[0].type = VBOX_HGCM_SVC_PARM_PTR;
parms[0].u.pointer.addr = pFolderName;
parms[0].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
if (cbString >= UINT16_MAX)
{
RTMemFree(pFolderName);
return setError(E_INVALIDARG, tr("The host path is too long"));
}
pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
Assert(pMapName);
memcpy(pMapName->String.ucs2, aName, cbString);
pMapName->u16Size = (uint16_t)cbString;
pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
parms[1].type = VBOX_HGCM_SVC_PARM_PTR;
parms[1].u.pointer.addr = pMapName;
parms[1].u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
parms[2].type = VBOX_HGCM_SVC_PARM_32BIT;
parms[2].u.uint32 = aData.mWritable;
/*
* Auto-mount flag; is indicated by using the SHFL_CPARMS_ADD_MAPPING2
* define below. This shows the host service that we have supplied
* an additional parameter (auto-mount) and keeps the actual command
* backwards compatible.
*/
parms[3].type = VBOX_HGCM_SVC_PARM_32BIT;
parms[3].u.uint32 = aData.mAutoMount;
int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
SHFL_FN_ADD_MAPPING,
SHFL_CPARMS_ADD_MAPPING2, &parms[0]);
RTMemFree(pFolderName);
RTMemFree(pMapName);
if (RT_FAILURE(vrc))
return setError(E_FAIL,
tr("Could not create a shared folder '%ls' mapped to '%ls' (%Rrc)"),
aName, aData.mHostPath.raw(), vrc);
return S_OK;
}
/**
* Calls the HGCM service to remove the shared folder definition.
*
* @param aName Shared folder name.
*
* @note Must be called from under AutoVMCaller and when mpVM != NULL!
* @note Doesn't lock anything.
*/
HRESULT Console::removeSharedFolder(CBSTR aName)
{
ComAssertRet(aName && *aName, E_FAIL);
/* sanity checks */
AssertReturn(mpVM, E_FAIL);
AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL);
VBOXHGCMSVCPARM parms;
SHFLSTRING *pMapName;
size_t cbString;
Log(("Removing shared folder '%ls'\n", aName));
cbString = (RTUtf16Len(aName) + 1) * sizeof(RTUTF16);
if (cbString >= UINT16_MAX)
return setError(E_INVALIDARG, tr("The name is too long"));
pMapName = (SHFLSTRING *) RTMemAllocZ(sizeof(SHFLSTRING) + cbString);
Assert(pMapName);
memcpy(pMapName->String.ucs2, aName, cbString);
pMapName->u16Size = (uint16_t)cbString;
pMapName->u16Length = (uint16_t)cbString - sizeof(RTUTF16);
parms.type = VBOX_HGCM_SVC_PARM_PTR;
parms.u.pointer.addr = pMapName;
parms.u.pointer.size = sizeof(SHFLSTRING) + (uint16_t)cbString;
int vrc = m_pVMMDev->hgcmHostCall("VBoxSharedFolders",
SHFL_FN_REMOVE_MAPPING,
1, &parms);
RTMemFree(pMapName);
if (RT_FAILURE(vrc))
return setError(E_FAIL,
tr("Could not remove the shared folder '%ls' (%Rrc)"),
aName, vrc);
return S_OK;
}
/**
* VM state callback function. Called by the VMM
* using its state machine states.
*
* Primarily used to handle VM initiated power off, suspend and state saving,
* but also for doing termination completed work (VMSTATE_TERMINATE).
*
* In general this function is called in the context of the EMT.
*
* @param aVM The VM handle.
* @param aState The new state.
* @param aOldState The old state.
* @param aUser The user argument (pointer to the Console object).
*
* @note Locks the Console object for writing.
*/
DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM,
VMSTATE aState,
VMSTATE aOldState,
void *aUser)
{
LogFlowFunc(("Changing state from %s to %s (aVM=%p)\n",
VMR3GetStateName(aOldState), VMR3GetStateName(aState), aVM));
Console *that = static_cast<Console *>(aUser);
AssertReturnVoid(that);
AutoCaller autoCaller(that);
/* Note that we must let this method proceed even if Console::uninit() has
* been already called. In such case this VMSTATE change is a result of:
* 1) powerDown() called from uninit() itself, or
* 2) VM-(guest-)initiated power off. */
AssertReturnVoid( autoCaller.isOk()
|| autoCaller.state() == InUninit);
switch (aState)
{
/*
* The VM has terminated
*/
case VMSTATE_OFF:
{
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
if (that->mVMStateChangeCallbackDisabled)
break;
/* Do we still think that it is running? It may happen if this is a
* VM-(guest-)initiated shutdown/poweroff.
*/
if ( that->mMachineState != MachineState_Stopping
&& that->mMachineState != MachineState_Saving
&& that->mMachineState != MachineState_Restoring
&& that->mMachineState != MachineState_TeleportingIn
&& that->mMachineState != MachineState_FaultTolerantSyncing
&& that->mMachineState != MachineState_TeleportingPausedVM
&& !that->mVMIsAlreadyPoweringOff
)
{
LogFlowFunc(("VM has powered itself off but Console still thinks it is running. Notifying.\n"));
/* prevent powerDown() from calling VMR3PowerOff() again */
Assert(that->mVMPoweredOff == false);
that->mVMPoweredOff = true;
/* we are stopping now */
that->setMachineState(MachineState_Stopping);
/* Setup task object and thread to carry out the operation
* asynchronously (if we call powerDown() right here but there
* is one or more mpVM callers (added with addVMCaller()) we'll
* deadlock).
*/
std::auto_ptr<VMProgressTask> task(new VMProgressTask(that, NULL /* aProgress */,
true /* aUsesVMPtr */));
/* If creating a task is falied, this can currently mean one of
* two: either Console::uninit() has been called just a ms
* before (so a powerDown() call is already on the way), or
* powerDown() itself is being already executed. Just do
* nothing.
*/
if (!task->isOk())
{
LogFlowFunc(("Console is already being uninitialized.\n"));
break;
}
int vrc = RTThreadCreate(NULL, Console::powerDownThread,
(void *) task.get(), 0,
RTTHREADTYPE_MAIN_WORKER, 0,
"VMPowerDown");
AssertMsgRCBreak(vrc, ("Could not create VMPowerDown thread (%Rrc)\n", vrc));
/* task is now owned by powerDownThread(), so release it */
task.release();
}
break;
}
/* The VM has been completely destroyed.
*
* Note: This state change can happen at two points:
* 1) At the end of VMR3Destroy() if it was not called from EMT.
* 2) At the end of vmR3EmulationThread if VMR3Destroy() was
* called by EMT.
*/
case VMSTATE_TERMINATED:
{
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
if (that->mVMStateChangeCallbackDisabled)
break;
/* Terminate host interface networking. If aVM is NULL, we've been
* manually called from powerUpThread() either before calling
* VMR3Create() or after VMR3Create() failed, so no need to touch
* networking.
*/
if (aVM)
that->powerDownHostInterfaces();
/* From now on the machine is officially powered down or remains in
* the Saved state.
*/
switch (that->mMachineState)
{
default:
AssertFailed();
/* fall through */
case MachineState_Stopping:
/* successfully powered down */
that->setMachineState(MachineState_PoweredOff);
break;
case MachineState_Saving:
/* successfully saved (note that the machine is already in
* the Saved state on the server due to EndSavingState()
* called from saveStateThread(), so only change the local
* state) */
that->setMachineStateLocally(MachineState_Saved);
break;
case MachineState_Starting:
/* failed to start, but be patient: set back to PoweredOff
* (for similarity with the below) */
that->setMachineState(MachineState_PoweredOff);
break;
case MachineState_Restoring:
/* failed to load the saved state file, but be patient: set
* back to Saved (to preserve the saved state file) */
that->setMachineState(MachineState_Saved);
break;
case MachineState_TeleportingIn:
/* Teleportation failed or was canceled. Back to powered off. */
that->setMachineState(MachineState_PoweredOff);
break;
case MachineState_TeleportingPausedVM:
/* Successfully teleported the VM. */
that->setMachineState(MachineState_Teleported);
break;
case MachineState_FaultTolerantSyncing:
/* Fault tolerant sync failed or was canceled. Back to powered off. */
that->setMachineState(MachineState_PoweredOff);
break;
}
break;
}
case VMSTATE_SUSPENDED:
{
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
if (that->mVMStateChangeCallbackDisabled)
break;
switch (that->mMachineState)
{
case MachineState_Teleporting:
that->setMachineState(MachineState_TeleportingPausedVM);
break;
case MachineState_LiveSnapshotting:
that->setMachineState(MachineState_Saving);
break;
case MachineState_TeleportingPausedVM:
case MachineState_Saving:
case MachineState_Restoring:
case MachineState_Stopping:
case MachineState_TeleportingIn:
case MachineState_FaultTolerantSyncing:
/* The worker thread handles the transition. */
break;
default:
AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
case MachineState_Running:
that->setMachineState(MachineState_Paused);
break;
case MachineState_Paused:
/* Nothing to do. */
break;
}
break;
}
case VMSTATE_SUSPENDED_LS:
case VMSTATE_SUSPENDED_EXT_LS:
{
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
if (that->mVMStateChangeCallbackDisabled)
break;
switch (that->mMachineState)
{
case MachineState_Teleporting:
that->setMachineState(MachineState_TeleportingPausedVM);
break;
case MachineState_LiveSnapshotting:
that->setMachineState(MachineState_Saving);
break;
case MachineState_TeleportingPausedVM:
case MachineState_Saving:
/* ignore */
break;
default:
AssertMsgFailed(("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
that->setMachineState(MachineState_Paused);
break;
}
break;
}
case VMSTATE_RUNNING:
{
if ( aOldState == VMSTATE_POWERING_ON
|| aOldState == VMSTATE_RESUMING
|| aOldState == VMSTATE_RUNNING_FT)
{
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
if (that->mVMStateChangeCallbackDisabled)
break;
Assert( ( ( that->mMachineState == MachineState_Starting
|| that->mMachineState == MachineState_Paused)
&& aOldState == VMSTATE_POWERING_ON)
|| ( ( that->mMachineState == MachineState_Restoring
|| that->mMachineState == MachineState_TeleportingIn
|| that->mMachineState == MachineState_Paused
|| that->mMachineState == MachineState_Saving
)
&& aOldState == VMSTATE_RESUMING)
|| ( that->mMachineState == MachineState_FaultTolerantSyncing
&& aOldState == VMSTATE_RUNNING_FT));
that->setMachineState(MachineState_Running);
}
break;
}
case VMSTATE_RUNNING_LS:
AssertMsg( that->mMachineState == MachineState_LiveSnapshotting
|| that->mMachineState == MachineState_Teleporting,
("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
break;
case VMSTATE_RUNNING_FT:
AssertMsg(that->mMachineState == MachineState_FaultTolerantSyncing,
("%s/%s -> %s\n", Global::stringifyMachineState(that->mMachineState), VMR3GetStateName(aOldState), VMR3GetStateName(aState) ));
break;
case VMSTATE_FATAL_ERROR:
{
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
if (that->mVMStateChangeCallbackDisabled)
break;
/* Fatal errors are only for running VMs. */
Assert(Global::IsOnline(that->mMachineState));
/* Note! 'Pause' is used here in want of something better. There
* are currently only two places where fatal errors might be
* raised, so it is not worth adding a new externally
* visible state for this yet. */
that->setMachineState(MachineState_Paused);
break;
}
case VMSTATE_GURU_MEDITATION:
{
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
if (that->mVMStateChangeCallbackDisabled)
break;
/* Guru are only for running VMs */
Assert(Global::IsOnline(that->mMachineState));
that->setMachineState(MachineState_Stuck);
break;
}
default: /* shut up gcc */
break;
}
}
#ifdef VBOX_WITH_USB
/**
* Sends a request to VMM to attach the given host device.
* After this method succeeds, the attached device will appear in the
* mUSBDevices collection.
*
* @param aHostDevice device to attach
*
* @note Synchronously calls EMT.
* @note Must be called from under this object's lock.
*/
HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs)
{
AssertReturn(aHostDevice, E_FAIL);
AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
/* still want a lock object because we need to leave it */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT hrc;
/*
* Get the address and the Uuid, and call the pfnCreateProxyDevice roothub
* method in EMT (using usbAttachCallback()).
*/
Bstr BstrAddress;
hrc = aHostDevice->COMGETTER(Address)(BstrAddress.asOutParam());
ComAssertComRCRetRC(hrc);
Utf8Str Address(BstrAddress);
Bstr id;
hrc = aHostDevice->COMGETTER(Id)(id.asOutParam());
ComAssertComRCRetRC(hrc);
Guid uuid(id);
BOOL fRemote = FALSE;
hrc = aHostDevice->COMGETTER(Remote)(&fRemote);
ComAssertComRCRetRC(hrc);
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n",
Address.c_str(), uuid.raw()));
/* leave the lock before a VMR3* call (EMT will call us back)! */
alock.leave();
/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
(PFNRT)usbAttachCallback, 6, this, aHostDevice, uuid.raw(), fRemote, Address.c_str(), aMaskedIfs);
/* restore the lock */
alock.enter();
/* hrc is S_OK here */
if (RT_FAILURE(vrc))
{
LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n",
Address.c_str(), uuid.raw(), vrc));
switch (vrc)
{
case VERR_VUSB_NO_PORTS:
hrc = setError(E_FAIL,
tr("Failed to attach the USB device. (No available ports on the USB controller)."));
break;
case VERR_VUSB_USBFS_PERMISSION:
hrc = setError(E_FAIL,
tr("Not permitted to open the USB device, check usbfs options"));
break;
default:
hrc = setError(E_FAIL,
tr("Failed to create a proxy device for the USB device. (Error: %Rrc)"),
vrc);
break;
}
}
return hrc;
}
/**
* USB device attach callback used by AttachUSBDevice().
* Note that AttachUSBDevice() doesn't return until this callback is executed,
* so we don't use AutoCaller and don't care about reference counters of
* interface pointers passed in.
*
* @thread EMT
* @note Locks the console object for writing.
*/
//static
DECLCALLBACK(int)
Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs)
{
LogFlowFuncEnter();
LogFlowFunc(("that={%p}\n", that));
AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
void *pvRemoteBackend = NULL;
if (aRemote)
{
RemoteUSBDevice *pRemoteUSBDevice = static_cast<RemoteUSBDevice *>(aHostDevice);
Guid guid(*aUuid);
pvRemoteBackend = that->consoleVRDPServer()->USBBackendRequestPointer(pRemoteUSBDevice->clientId(), &guid);
if (!pvRemoteBackend)
return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */
}
USHORT portVersion = 1;
HRESULT hrc = aHostDevice->COMGETTER(PortVersion)(&portVersion);
AssertComRCReturn(hrc, VERR_GENERAL_FAILURE);
Assert(portVersion == 1 || portVersion == 2);
int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend,
portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs);
if (RT_SUCCESS(vrc))
{
/* Create a OUSBDevice and add it to the device list */
ComObjPtr<OUSBDevice> device;
device.createObject();
hrc = device->init(aHostDevice);
AssertComRC(hrc);
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
that->mUSBDevices.push_back(device);
LogFlowFunc(("Attached device {%RTuuid}\n", device->id().raw()));
/* notify callbacks */
that->onUSBDeviceStateChange(device, true /* aAttached */, NULL);
}
LogFlowFunc(("vrc=%Rrc\n", vrc));
LogFlowFuncLeave();
return vrc;
}
/**
* Sends a request to VMM to detach the given host device. After this method
* succeeds, the detached device will disappear from the mUSBDevices
* collection.
*
* @param aIt Iterator pointing to the device to detach.
*
* @note Synchronously calls EMT.
* @note Must be called from under this object's lock.
*/
HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt)
{
AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
/* still want a lock object because we need to leave it */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/* protect mpVM */
AutoVMCaller autoVMCaller(this);
if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
/* if the device is attached, then there must at least one USB hub. */
AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL);
LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n",
(*aIt)->id().raw()));
/* leave the lock before a VMR3* call (EMT will call us back)! */
alock.leave();
/** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */
int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY,
(PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw());
ComAssertRCRet(vrc, E_FAIL);
return S_OK;
}
/**
* USB device detach callback used by DetachUSBDevice().
* Note that DetachUSBDevice() doesn't return until this callback is executed,
* so we don't use AutoCaller and don't care about reference counters of
* interface pointers passed in.
*
* @thread EMT
* @note Locks the console object for writing.
*/
//static
DECLCALLBACK(int)
Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid)
{
LogFlowFuncEnter();
LogFlowFunc(("that={%p}\n", that));
AssertReturn(that && aUuid, VERR_INVALID_PARAMETER);
ComObjPtr<OUSBDevice> device = **aIt;
/*
* If that was a remote device, release the backend pointer.
* The pointer was requested in usbAttachCallback.
*/
BOOL fRemote = FALSE;
HRESULT hrc2 = (**aIt)->COMGETTER(Remote)(&fRemote);
if (FAILED(hrc2))
setErrorStatic(hrc2, "GetRemote() failed");
if (fRemote)
{
Guid guid(*aUuid);
that->consoleVRDPServer()->USBBackendReleasePointer(&guid);
}
int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid);
if (RT_SUCCESS(vrc))
{
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
/* Remove the device from the collection */
that->mUSBDevices.erase(*aIt);
LogFlowFunc(("Detached device {%RTuuid}\n", device->id().raw()));
/* notify callbacks */
that->onUSBDeviceStateChange(device, false /* aAttached */, NULL);
}
LogFlowFunc(("vrc=%Rrc\n", vrc));
LogFlowFuncLeave();
return vrc;
}
#endif /* VBOX_WITH_USB */
#if ((defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)) || defined(RT_OS_FREEBSD))
/**
* Helper function to handle host interface device creation and attachment.
*
* @param networkAdapter the network adapter which attachment should be reset
* @return COM status code
*
* @note The caller must lock this object for writing.
*
* @todo Move this back into the driver!
*/
HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter)
{
LogFlowThisFunc(("\n"));
/* sanity check */
AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
# ifdef VBOX_STRICT
/* paranoia */
NetworkAttachmentType_T attachment;
networkAdapter->COMGETTER(AttachmentType)(&attachment);
Assert(attachment == NetworkAttachmentType_Bridged);
# endif /* VBOX_STRICT */
HRESULT rc = S_OK;
ULONG slot = 0;
rc = networkAdapter->COMGETTER(Slot)(&slot);
AssertComRC(rc);
# ifdef RT_OS_LINUX
/*
* Allocate a host interface device
*/
int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun",
RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT);
if (RT_SUCCESS(rcVBox))
{
/*
* Set/obtain the tap interface.
*/
struct ifreq IfReq;
memset(&IfReq, 0, sizeof(IfReq));
/* The name of the TAP interface we are using */
Bstr tapDeviceName;
rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
if (FAILED(rc))
tapDeviceName.setNull(); /* Is this necessary? */
if (tapDeviceName.isEmpty())
{
LogRel(("No TAP device name was supplied.\n"));
rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
}
if (SUCCEEDED(rc))
{
/* If we are using a static TAP device then try to open it. */
Utf8Str str(tapDeviceName);
if (str.length() <= sizeof(IfReq.ifr_name))
strcpy(IfReq.ifr_name, str.c_str());
else
memcpy(IfReq.ifr_name, str.c_str(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */
IfReq.ifr_flags = IFF_TAP | IFF_NO_PI;
rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq);
if (rcVBox != 0)
{
LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw()));
rc = setError(E_FAIL,
tr("Failed to open the host network interface %ls"),
tapDeviceName.raw());
}
}
if (SUCCEEDED(rc))
{
/*
* Make it pollable.
*/
if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1)
{
Log(("attachToTapInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw()));
/*
* Here is the right place to communicate the TAP file descriptor and
* the host interface name to the server if/when it becomes really
* necessary.
*/
maTAPDeviceName[slot] = tapDeviceName;
rcVBox = VINF_SUCCESS;
}
else
{
int iErr = errno;
LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr)));
rcVBox = VERR_HOSTIF_BLOCKING;
rc = setError(E_FAIL,
tr("could not set up the host networking device for non blocking access: %s"),
strerror(errno));
}
}
}
else
{
LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Rrc\n", rcVBox));
switch (rcVBox)
{
case VERR_ACCESS_DENIED:
/* will be handled by our caller */
rc = rcVBox;
break;
default:
rc = setError(E_FAIL,
tr("Could not set up the host networking device: %Rrc"),
rcVBox);
break;
}
}
# elif defined(RT_OS_FREEBSD)
/*
* Set/obtain the tap interface.
*/
/* The name of the TAP interface we are using */
Bstr tapDeviceName;
rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
if (FAILED(rc))
tapDeviceName.setNull(); /* Is this necessary? */
if (tapDeviceName.isEmpty())
{
LogRel(("No TAP device name was supplied.\n"));
rc = setError(E_FAIL, tr("No TAP device name was supplied for the host networking interface"));
}
char szTapdev[1024] = "/dev/";
/* If we are using a static TAP device then try to open it. */
Utf8Str str(tapDeviceName);
if (str.length() + strlen(szTapdev) <= sizeof(szTapdev))
strcat(szTapdev, str.c_str());
else
memcpy(szTapdev + strlen(szTapdev), str.c_str(),
sizeof(szTapdev) - strlen(szTapdev) - 1); /** @todo bitch about names which are too long... */
int rcVBox = RTFileOpen(&maTapFD[slot], szTapdev,
RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT | RTFILE_O_NON_BLOCK);
if (RT_SUCCESS(rcVBox))
maTAPDeviceName[slot] = tapDeviceName;
else
{
switch (rcVBox)
{
case VERR_ACCESS_DENIED:
/* will be handled by our caller */
rc = rcVBox;
break;
default:
rc = setError(E_FAIL,
tr("Failed to open the host network interface %ls"),
tapDeviceName.raw());
break;
}
}
# else
# error "huh?"
# endif
/* in case of failure, cleanup. */
if (RT_FAILURE(rcVBox) && SUCCEEDED(rc))
{
LogRel(("General failure attaching to host interface\n"));
rc = setError(E_FAIL,
tr("General failure attaching to host interface"));
}
LogFlowThisFunc(("rc=%d\n", rc));
return rc;
}
/**
* Helper function to handle detachment from a host interface
*
* @param networkAdapter the network adapter which attachment should be reset
* @return COM status code
*
* @note The caller must lock this object for writing.
*
* @todo Move this back into the driver!
*/
HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter)
{
/* sanity check */
LogFlowThisFunc(("\n"));
AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
HRESULT rc = S_OK;
# ifdef VBOX_STRICT
/* paranoia */
NetworkAttachmentType_T attachment;
networkAdapter->COMGETTER(AttachmentType)(&attachment);
Assert(attachment == NetworkAttachmentType_Bridged);
# endif /* VBOX_STRICT */
ULONG slot = 0;
rc = networkAdapter->COMGETTER(Slot)(&slot);
AssertComRC(rc);
/* is there an open TAP device? */
if (maTapFD[slot] != NIL_RTFILE)
{
/*
* Close the file handle.
*/
Bstr tapDeviceName, tapTerminateApplication;
bool isStatic = true;
rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam());
if (FAILED(rc) || tapDeviceName.isEmpty())
{
/* If the name is empty, this is a dynamic TAP device, so close it now,
so that the termination script can remove the interface. Otherwise we still
need the FD to pass to the termination script. */
isStatic = false;
int rcVBox = RTFileClose(maTapFD[slot]);
AssertRC(rcVBox);
maTapFD[slot] = NIL_RTFILE;
}
if (isStatic)
{
/* If we are using a static TAP device, we close it now, after having called the
termination script. */
int rcVBox = RTFileClose(maTapFD[slot]);
AssertRC(rcVBox);
}
/* the TAP device name and handle are no longer valid */
maTapFD[slot] = NIL_RTFILE;
maTAPDeviceName[slot] = "";
}
LogFlowThisFunc(("returning %d\n", rc));
return rc;
}
#endif /* (RT_OS_LINUX || RT_OS_FREEBSD) && !VBOX_WITH_NETFLT */
/**
* Called at power down to terminate host interface networking.
*
* @note The caller must lock this object for writing.
*/
HRESULT Console::powerDownHostInterfaces()
{
LogFlowThisFunc(("\n"));
/* sanity check */
AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
/*
* host interface termination handling
*/
HRESULT rc;
for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)
{
ComPtr<INetworkAdapter> networkAdapter;
rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam());
if (FAILED(rc)) break;
BOOL enabled = FALSE;
networkAdapter->COMGETTER(Enabled)(&enabled);
if (!enabled)
continue;
NetworkAttachmentType_T attachment;
networkAdapter->COMGETTER(AttachmentType)(&attachment);
if (attachment == NetworkAttachmentType_Bridged)
{
#if defined(RT_OS_LINUX) && !defined(VBOX_WITH_NETFLT)
HRESULT rc2 = detachFromTapInterface(networkAdapter);
if (FAILED(rc2) && SUCCEEDED(rc))
rc = rc2;
#endif
}
}
return rc;
}
/**
* Process callback handler for VMR3LoadFromFile, VMR3LoadFromStream, VMR3Save
* and VMR3Teleport.
*
* @param pVM The VM handle.
* @param uPercent Completetion precentage (0-100).
* @param pvUser Pointer to the VMProgressTask structure.
* @return VINF_SUCCESS.
*/
/*static*/
DECLCALLBACK(int) Console::stateProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
{
VMProgressTask *task = static_cast<VMProgressTask *>(pvUser);
AssertReturn(task, VERR_INVALID_PARAMETER);
/* update the progress object */
if (task->mProgress)
task->mProgress->SetCurrentOperationProgress(uPercent);
return VINF_SUCCESS;
}
/**
* @copydoc FNVMATERROR
*
* @remarks Might be some tiny serialization concerns with access to the string
* object here...
*/
/*static*/ DECLCALLBACK(void)
Console::genericVMSetErrorCallback(PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL,
const char *pszErrorFmt, va_list va)
{
Utf8Str *pErrorText = (Utf8Str *)pvUser;
AssertPtr(pErrorText);
/* We ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users. */
va_list va2;
va_copy(va2, va);
/* Append to any the existing error message. */
if (pErrorText->length())
*pErrorText = Utf8StrFmt("%s.\n%N (%Rrc)", pErrorText->c_str(),
pszErrorFmt, &va2, rc, rc);
else
*pErrorText = Utf8StrFmt("%N (%Rrc)", pszErrorFmt, &va2, rc, rc);
va_end(va2);
}
/**
* VM runtime error callback function.
* See VMSetRuntimeError for the detailed description of parameters.
*
* @param pVM The VM handle.
* @param pvUser The user argument.
* @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
* @param pszErrorId Error ID string.
* @param pszFormat Error message format string.
* @param va Error message arguments.
* @thread EMT.
*/
/* static */ DECLCALLBACK(void)
Console::setVMRuntimeErrorCallback(PVM pVM, void *pvUser, uint32_t fFlags,
const char *pszErrorId,
const char *pszFormat, va_list va)
{
bool const fFatal = !!(fFlags & VMSETRTERR_FLAGS_FATAL);
LogFlowFuncEnter();
Console *that = static_cast<Console *>(pvUser);
AssertReturnVoid(that);
Utf8Str message = Utf8StrFmtVA(pszFormat, va);
LogRel(("Console: VM runtime error: fatal=%RTbool, errorID=%s message=\"%s\"\n",
fFatal, pszErrorId, message.c_str()));
that->onRuntimeError(BOOL(fFatal), Bstr(pszErrorId).raw(),
Bstr(message).raw());
LogFlowFuncLeave();
}
/**
* Captures USB devices that match filters of the VM.
* Called at VM startup.
*
* @param pVM The VM handle.
*
* @note The caller must lock this object for writing.
*/
HRESULT Console::captureUSBDevices(PVM pVM)
{
LogFlowThisFunc(("\n"));
/* sanity check */
ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
/* If the machine has an USB controller, ask the USB proxy service to
* capture devices */
PPDMIBASE pBase;
int vrc = PDMR3QueryLun(pVM, "usb-ohci", 0, 0, &pBase);
if (RT_SUCCESS(vrc))
{
/* leave the lock before calling Host in VBoxSVC since Host may call
* us back from under its lock (e.g. onUSBDeviceAttach()) which would
* produce an inter-process dead-lock otherwise. */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
alock.leave();
HRESULT hrc = mControl->AutoCaptureUSBDevices();
ComAssertComRCRetRC(hrc);
}
else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND
|| vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND)
vrc = VINF_SUCCESS;
else
AssertRC(vrc);
return RT_SUCCESS(vrc) ? S_OK : E_FAIL;
}
/**
* Detach all USB device which are attached to the VM for the
* purpose of clean up and such like.
*
* @note The caller must lock this object for writing.
*/
void Console::detachAllUSBDevices(bool aDone)
{
LogFlowThisFunc(("aDone=%RTbool\n", aDone));
/* sanity check */
AssertReturnVoid(isWriteLockOnCurrentThread());
mUSBDevices.clear();
/* leave the lock before calling Host in VBoxSVC since Host may call
* us back from under its lock (e.g. onUSBDeviceAttach()) which would
* produce an inter-process dead-lock otherwise. */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
alock.leave();
mControl->DetachAllUSBDevices(aDone);
}
/**
* @note Locks this object for writing.
*/
void Console::processRemoteUSBDevices(uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList)
{
LogFlowThisFuncEnter();
LogFlowThisFunc(("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList));
AutoCaller autoCaller(this);
if (!autoCaller.isOk())
{
/* Console has been already uninitialized, deny request */
AssertMsgFailed(("Console is already uninitialized\n"));
LogFlowThisFunc(("Console is already uninitialized\n"));
LogFlowThisFuncLeave();
return;
}
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
/*
* Mark all existing remote USB devices as dirty.
*/
for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
it != mRemoteUSBDevices.end();
++it)
{
(*it)->dirty(true);
}
/*
* Process the pDevList and add devices those are not already in the mRemoteUSBDevices list.
*/
/** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */
VRDPUSBDEVICEDESC *e = pDevList;
/* The cbDevList condition must be checked first, because the function can
* receive pDevList = NULL and cbDevList = 0 on client disconnect.
*/
while (cbDevList >= 2 && e->oNext)
{
LogFlowThisFunc(("vendor %04X, product %04X, name = %s\n",
e->idVendor, e->idProduct,
e->oProduct? (char *)e + e->oProduct: ""));
bool fNewDevice = true;
for (RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
it != mRemoteUSBDevices.end();
++it)
{
if ((*it)->devId() == e->id
&& (*it)->clientId() == u32ClientId)
{
/* The device is already in the list. */
(*it)->dirty(false);
fNewDevice = false;
break;
}
}
if (fNewDevice)
{
LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n",
e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: ""));
/* Create the device object and add the new device to list. */
ComObjPtr<RemoteUSBDevice> device;
device.createObject();
device->init(u32ClientId, e);
mRemoteUSBDevices.push_back(device);
/* Check if the device is ok for current USB filters. */
BOOL fMatched = FALSE;
ULONG fMaskedIfs = 0;
HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched, &fMaskedIfs);
AssertComRC(hrc);
LogFlowThisFunc(("USB filters return %d %#x\n", fMatched, fMaskedIfs));
if (fMatched)
{
hrc = onUSBDeviceAttach(device, NULL, fMaskedIfs);
/// @todo (r=dmik) warning reporting subsystem
if (hrc == S_OK)
{
LogFlowThisFunc(("Device attached\n"));
device->captured(true);
}
}
}
if (cbDevList < e->oNext)
{
LogWarningThisFunc(("cbDevList %d > oNext %d\n",
cbDevList, e->oNext));
break;
}
cbDevList -= e->oNext;
e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext);
}
/*
* Remove dirty devices, that is those which are not reported by the server anymore.
*/
for (;;)
{
ComObjPtr<RemoteUSBDevice> device;
RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin();
while (it != mRemoteUSBDevices.end())
{
if ((*it)->dirty())
{
device = *it;
break;
}
++ it;
}
if (!device)
{
break;
}
USHORT vendorId = 0;
device->COMGETTER(VendorId)(&vendorId);
USHORT productId = 0;
device->COMGETTER(ProductId)(&productId);
Bstr product;
device->COMGETTER(Product)(product.asOutParam());
LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n",
vendorId, productId, product.raw()));
/* Detach the device from VM. */
if (device->captured())
{
Bstr uuid;
device->COMGETTER(Id)(uuid.asOutParam());
onUSBDeviceDetach(uuid.raw(), NULL);
}
/* And remove it from the list. */
mRemoteUSBDevices.erase(it);
}
LogFlowThisFuncLeave();
}
/**
* Progress cancelation callback for fault tolerance VM poweron
*/
static void faultToleranceProgressCancelCallback(void *pvUser)
{
PVM pVM = (PVM)pvUser;
if (pVM)
FTMR3CancelStandby(pVM);
}
/**
* Thread function which starts the VM (also from saved state) and
* track progress.
*
* @param Thread The thread id.
* @param pvUser Pointer to a VMPowerUpTask structure.
* @return VINF_SUCCESS (ignored).
*
* @note Locks the Console object for writing.
*/
/*static*/
DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser)
{
LogFlowFuncEnter();
std::auto_ptr<VMPowerUpTask> task(static_cast<VMPowerUpTask *>(pvUser));
AssertReturn(task.get(), VERR_INVALID_PARAMETER);
AssertReturn(!task->mConsole.isNull(), VERR_INVALID_PARAMETER);
AssertReturn(!task->mProgress.isNull(), VERR_INVALID_PARAMETER);
#if defined(RT_OS_WINDOWS)
{
/* initialize COM */
HRESULT hrc = CoInitializeEx(NULL,
COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |
COINIT_SPEED_OVER_MEMORY);
LogFlowFunc(("CoInitializeEx()=%08X\n", hrc));
}
#endif
HRESULT rc = S_OK;
int vrc = VINF_SUCCESS;
/* Set up a build identifier so that it can be seen from core dumps what
* exact build was used to produce the core. */
static char saBuildID[40];
RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
"BU", "IL", "DI", "D", VBOX_VERSION_STRING, RTBldCfgRevision(), "BU", "IL", "DI", "D");
ComObjPtr<Console> console = task->mConsole;
/* Note: no need to use addCaller() because VMPowerUpTask does that */
/* The lock is also used as a signal from the task initiator (which
* releases it only after RTThreadCreate()) that we can start the job */
AutoWriteLock alock(console COMMA_LOCKVAL_SRC_POS);
/* sanity */
Assert(console->mpVM == NULL);
try
{
// Create the VMM device object, which starts the HGCM thread; do this only
// once for the console, for the pathologican case that the same console
// object is used to power up a VM twice. VirtualBox 4.0: we now do that
// here instead of the Console constructor (see Console::init())
if (!console->m_pVMMDev)
{
console->m_pVMMDev = new VMMDev(console);
AssertReturn(console->m_pVMMDev, E_FAIL);
}
/* wait for auto reset ops to complete so that we can successfully lock
* the attached hard disks by calling LockMedia() below */
for (VMPowerUpTask::ProgressList::const_iterator
it = task->hardDiskProgresses.begin();
it != task->hardDiskProgresses.end(); ++ it)
{
HRESULT rc2 = (*it)->WaitForCompletion(-1);
AssertComRC(rc2);
}
/*
* Lock attached media. This method will also check their accessibility.
* If we're a teleporter, we'll have to postpone this action so we can
* migrate between local processes.
*
* Note! The media will be unlocked automatically by
* SessionMachine::setMachineState() when the VM is powered down.
*/
if ( !task->mTeleporterEnabled
&& task->mEnmFaultToleranceState != FaultToleranceState_Standby)
{
rc = console->mControl->LockMedia();
if (FAILED(rc)) throw rc;
}
#ifdef VBOX_WITH_VRDP
/* Create the VRDP server. In case of headless operation, this will
* also create the framebuffer, required at VM creation.
*/
ConsoleVRDPServer *server = console->consoleVRDPServer();
Assert(server);
/* Does VRDP server call Console from the other thread?
* Not sure (and can change), so leave the lock just in case.
*/
alock.leave();
vrc = server->Launch();
alock.enter();
if (vrc == VERR_NET_ADDRESS_IN_USE)
{
Utf8Str errMsg;
Bstr bstr;
console->mVRDPServer->COMGETTER(Ports)(bstr.asOutParam());
Utf8Str ports = bstr;
errMsg = Utf8StrFmt(tr("VRDP server can't bind to a port: %s"),
ports.c_str());
LogRel(("Warning: failed to launch VRDP server (%Rrc): '%s'\n",
vrc, errMsg.c_str()));
}
else if (RT_FAILURE(vrc))
{
Utf8Str errMsg;
switch (vrc)
{
case VERR_FILE_NOT_FOUND:
{
errMsg = Utf8StrFmt(tr("Could not load the VRDP library"));
break;
}
default:
errMsg = Utf8StrFmt(tr("Failed to launch VRDP server (%Rrc)"),
vrc);
}
LogRel(("Failed to launch VRDP server (%Rrc), error message: '%s'\n",
vrc, errMsg.c_str()));
throw setErrorStatic(E_FAIL, errMsg.c_str());
}
#endif /* VBOX_WITH_VRDP */
ComPtr<IMachine> pMachine = console->machine();
ULONG cCpus = 1;
pMachine->COMGETTER(CPUCount)(&cCpus);
/*
* Create the VM
*/
PVM pVM;
/*
* leave the lock since EMT will call Console. It's safe because
* mMachineState is either Starting or Restoring state here.
*/
alock.leave();
vrc = VMR3Create(cCpus,
console->mpVmm2UserMethods,
Console::genericVMSetErrorCallback,
&task->mErrorMsg,
task->mConfigConstructor,
static_cast<Console *>(console),
&pVM);
alock.enter();
#ifdef VBOX_WITH_VRDP
/* Enable client connections to the server. */
console->consoleVRDPServer()->EnableConnections();
#endif /* VBOX_WITH_VRDP */
if (RT_SUCCESS(vrc))
{
do
{
/*
* Register our load/save state file handlers
*/
vrc = SSMR3RegisterExternal(pVM, sSSMConsoleUnit, 0 /*iInstance*/, sSSMConsoleVer, 0 /* cbGuess */,
NULL, NULL, NULL,
NULL, saveStateFileExec, NULL,
NULL, loadStateFileExec, NULL,
static_cast<Console *>(console));
AssertRCBreak(vrc);
vrc = static_cast<Console *>(console)->getDisplay()->registerSSM(pVM);
AssertRC(vrc);
if (RT_FAILURE(vrc))
break;
/*
* Synchronize debugger settings
*/
MachineDebugger *machineDebugger = console->getMachineDebugger();
if (machineDebugger)
machineDebugger->flushQueuedSettings();
/*
* Shared Folders
*/
if (console->m_pVMMDev->isShFlActive())
{
/* Does the code below call Console from the other thread?
* Not sure, so leave the lock just in case. */
alock.leave();
for (SharedFolderDataMap::const_iterator it = task->mSharedFolders.begin();
it != task->mSharedFolders.end();
++it)
{
rc = console->createSharedFolder((*it).first.raw(),
(*it).second);
if (FAILED(rc)) break;
}
if (FAILED(rc)) break;
/* enter the lock again */
alock.enter();
}
/*
* Capture USB devices.
*/
rc = console->captureUSBDevices(pVM);
if (FAILED(rc)) break;
/* leave the lock before a lengthy operation */
alock.leave();
/* Load saved state? */
if (task->mSavedStateFile.length())
{
LogFlowFunc(("Restoring saved state from '%s'...\n",
task->mSavedStateFile.c_str()));
vrc = VMR3LoadFromFile(pVM,
task->mSavedStateFile.c_str(),
Console::stateProgressCallback,
static_cast<VMProgressTask*>(task.get()));
if (RT_SUCCESS(vrc))
{
if (task->mStartPaused)
/* done */
console->setMachineState(MachineState_Paused);
else
{
/* Start/Resume the VM execution */
vrc = VMR3Resume(pVM);
AssertRC(vrc);
}
}
/* Power off in case we failed loading or resuming the VM */
if (RT_FAILURE(vrc))
{
int vrc2 = VMR3PowerOff(pVM);
AssertRC(vrc2);
}
}
else if (task->mTeleporterEnabled)
{
/* -> ConsoleImplTeleporter.cpp */
bool fPowerOffOnFailure;
rc = console->teleporterTrg(pVM, pMachine, &task->mErrorMsg, task->mStartPaused,
task->mProgress, &fPowerOffOnFailure);
if (FAILED(rc) && fPowerOffOnFailure)
{
ErrorInfoKeeper eik;
int vrc2 = VMR3PowerOff(pVM);
AssertRC(vrc2);
}
}
else if (task->mEnmFaultToleranceState != FaultToleranceState_Inactive)
{
/*
* Get the config.
*/
ULONG uPort;
ULONG uInterval;
Bstr bstrAddress, bstrPassword;
rc = pMachine->COMGETTER(FaultTolerancePort)(&uPort);
if (SUCCEEDED(rc))
{
rc = pMachine->COMGETTER(FaultToleranceSyncInterval)(&uInterval);
if (SUCCEEDED(rc))
rc = pMachine->COMGETTER(FaultToleranceAddress)(bstrAddress.asOutParam());
if (SUCCEEDED(rc))
rc = pMachine->COMGETTER(FaultTolerancePassword)(bstrPassword.asOutParam());
}
if (task->mProgress->setCancelCallback(faultToleranceProgressCancelCallback, pVM))
{
if (SUCCEEDED(rc))
{
Utf8Str strAddress(bstrAddress);
const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
Utf8Str strPassword(bstrPassword);
const char *pszPassword = strPassword.isEmpty() ? NULL : strPassword.c_str();
/* Power on the FT enabled VM. */
vrc = FTMR3PowerOn(pVM, (task->mEnmFaultToleranceState == FaultToleranceState_Master) /* fMaster */, uInterval, pszAddress, uPort, pszPassword);
AssertRC(vrc);
}
task->mProgress->setCancelCallback(NULL, NULL);
}
else
rc = E_FAIL;
}
else if (task->mStartPaused)
/* done */
console->setMachineState(MachineState_Paused);
else
{
/* Power on the VM (i.e. start executing) */
vrc = VMR3PowerOn(pVM);
AssertRC(vrc);
}
/* enter the lock again */
alock.enter();
}
while (0);
/* On failure, destroy the VM */
if (FAILED(rc) || RT_FAILURE(vrc))
{
/* preserve existing error info */
ErrorInfoKeeper eik;
/* powerDown() will call VMR3Destroy() and do all necessary
* cleanup (VRDP, USB devices) */
HRESULT rc2 = console->powerDown();
AssertComRC(rc2);
}
else
{
/*
* Deregister the VMSetError callback. This is necessary as the
* pfnVMAtError() function passed to VMR3Create() is supposed to
* be sticky but our error callback isn't.
*/
alock.leave();
VMR3AtErrorDeregister(pVM, Console::genericVMSetErrorCallback, &task->mErrorMsg);
/** @todo register another VMSetError callback? */
alock.enter();
}
}
else
{
/*
* If VMR3Create() failed it has released the VM memory.
*/
console->mpVM = NULL;
}
if (SUCCEEDED(rc) && RT_FAILURE(vrc))
{
/* If VMR3Create() or one of the other calls in this function fail,
* an appropriate error message has been set in task->mErrorMsg.
* However since that happens via a callback, the rc status code in
* this function is not updated.
*/
if (!task->mErrorMsg.length())
{
/* If the error message is not set but we've got a failure,
* convert the VBox status code into a meaningful error message.
* This becomes unused once all the sources of errors set the
* appropriate error message themselves.
*/
AssertMsgFailed(("Missing error message during powerup for status code %Rrc\n", vrc));
task->mErrorMsg = Utf8StrFmt(tr("Failed to start VM execution (%Rrc)"),
vrc);
}
/* Set the error message as the COM error.
* Progress::notifyComplete() will pick it up later. */
throw setErrorStatic(E_FAIL, task->mErrorMsg.c_str());
}
}
catch (HRESULT aRC) { rc = aRC; }
if ( console->mMachineState == MachineState_Starting
|| console->mMachineState == MachineState_Restoring
|| console->mMachineState == MachineState_TeleportingIn
)
{
/* We are still in the Starting/Restoring state. This means one of:
*
* 1) we failed before VMR3Create() was called;
* 2) VMR3Create() failed.
*
* In both cases, there is no need to call powerDown(), but we still
* need to go back to the PoweredOff/Saved state. Reuse
* vmstateChangeCallback() for that purpose.
*/
/* preserve existing error info */
ErrorInfoKeeper eik;
Assert(console->mpVM == NULL);
vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING,
console);
}
/*
* Evaluate the final result. Note that the appropriate mMachineState value
* is already set by vmstateChangeCallback() in all cases.
*/
/* leave the lock, don't need it any more */
alock.leave();
if (SUCCEEDED(rc))
{
/* Notify the progress object of the success */
task->mProgress->notifyComplete(S_OK);
}
else
{
/* The progress object will fetch the current error info */
task->mProgress->notifyComplete(rc);
LogRel(("Power up failed (vrc=%Rrc, rc=%Rhrc (%#08X))\n", vrc, rc, rc));
}
/* Notify VBoxSVC and any waiting openRemoteSession progress object. */
console->mControl->EndPowerUp(rc);
#if defined(RT_OS_WINDOWS)
/* uninitialize COM */
CoUninitialize();
#endif
LogFlowFuncLeave();
return VINF_SUCCESS;
}
/**
* Reconfigures a medium attachment (part of taking or deleting an online snapshot).
*
* @param pConsole Reference to the console object.
* @param pVM The VM handle.
* @param lInstance The instance of the controller.
* @param pcszDevice The name of the controller type.
* @param enmBus The storage bus type of the controller.
* @param fSetupMerge Whether to set up a medium merge
* @param uMergeSource Merge source image index
* @param uMergeTarget Merge target image index
* @param aMediumAtt The medium attachment.
* @param aMachineState The current machine state.
* @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE.
* @return VBox status code.
*/
/* static */
DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole,
PVM pVM,
const char *pcszDevice,
unsigned uInstance,
StorageBus_T enmBus,
bool fUseHostIOCache,
bool fSetupMerge,
unsigned uMergeSource,
unsigned uMergeTarget,
IMediumAttachment *aMediumAtt,
MachineState_T aMachineState,
HRESULT *phrc)
{
LogFlowFunc(("pVM=%p aMediumAtt=%p phrc=%p\n", pVM, aMediumAtt, phrc));
int rc;
HRESULT hrc;
Bstr bstr;
*phrc = S_OK;
#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertMsgFailed(("rc=%Rrc\n", rc)); return rc; } } while (0)
#define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%Rhrc (%#x)\n", hrc, hrc)); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0)
/* Ignore attachments other than hard disks, since at the moment they are
* not subject to snapshotting in general. */
DeviceType_T lType;
hrc = aMediumAtt->COMGETTER(Type)(&lType); H();
if (lType != DeviceType_HardDisk)
return VINF_SUCCESS;
/* Determine the base path for the device instance. */
PCFGMNODE pCtlInst;
pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance);
AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
/* Update the device instance configuration. */
rc = pConsole->configMediumAttachment(pCtlInst,
pcszDevice,
uInstance,
enmBus,
fUseHostIOCache,
fSetupMerge,
uMergeSource,
uMergeTarget,
aMediumAtt,
aMachineState,
phrc,
true /* fAttachDetach */,
false /* fForceUnmount */,
pVM,
NULL /* paLedDevType */);
/** @todo this dumps everything attached to this device instance, which
* is more than necessary. Dumping the changed LUN would be enough. */
CFGMR3Dump(pCtlInst);
RC_CHECK();
#undef RC_CHECK
#undef H
LogFlowFunc(("Returns success\n"));
return VINF_SUCCESS;
}
/**
* Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
*/
static void takesnapshotProgressCancelCallback(void *pvUser)
{
PVM pVM = (PVM)pvUser;
SSMR3Cancel(pVM);
}
/**
* Worker thread created by Console::TakeSnapshot.
* @param Thread The current thread (ignored).
* @param pvUser The task.
* @return VINF_SUCCESS (ignored).
*/
/*static*/
DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
{
VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
// taking a snapshot consists of the following:
// 1) creating a diff image for each virtual hard disk, into which write operations go after
// the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
// 2) creating a Snapshot object with the state of the machine (hardware + storage,
// done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
// 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
Console *that = pTask->mConsole;
bool fBeganTakingSnapshot = false;
bool fSuspenededBySave = false;
AutoCaller autoCaller(that);
if (FAILED(autoCaller.rc()))
{
that->mptrCancelableProgress.setNull();
return autoCaller.rc();
}
AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
try
{
/* STEP 1 + 2:
* request creating the diff images on the server and create the snapshot object
* (this will set the machine state to Saving on the server to block
* others from accessing this machine)
*/
rc = that->mControl->BeginTakingSnapshot(that,
pTask->bstrName.raw(),
pTask->bstrDescription.raw(),
pTask->mProgress,
pTask->fTakingSnapshotOnline,
pTask->bstrSavedStateFile.asOutParam());
if (FAILED(rc))
throw rc;
fBeganTakingSnapshot = true;
/*
* state file is non-null only when the VM is paused
* (i.e. creating a snapshot online)
*/
bool f = (!pTask->bstrSavedStateFile.isEmpty() && pTask->fTakingSnapshotOnline)
|| ( pTask->bstrSavedStateFile.isEmpty() && !pTask->fTakingSnapshotOnline);
if (!f)
throw setErrorStatic(E_FAIL, "Invalid state of saved state file");
/* sync the state with the server */
if (pTask->lastMachineState == MachineState_Running)
that->setMachineStateLocally(MachineState_LiveSnapshotting);
else
that->setMachineStateLocally(MachineState_Saving);
// STEP 3: save the VM state (if online)
if (pTask->fTakingSnapshotOnline)
{
Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
pTask->ulMemSize); // operation weight, same as computed when setting up progress object
pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, that->mpVM);
alock.leave();
LogFlowFunc(("VMR3Save...\n"));
int vrc = VMR3Save(that->mpVM,
strSavedStateFile.c_str(),
true /*fContinueAfterwards*/,
Console::stateProgressCallback,
(void*)pTask,
&fSuspenededBySave);
alock.enter();
if (RT_FAILURE(vrc))
throw setErrorStatic(E_FAIL,
tr("Failed to save the machine state to '%s' (%Rrc)"),
strSavedStateFile.c_str(), vrc);
pTask->mProgress->setCancelCallback(NULL, NULL);
if (!pTask->mProgress->notifyPointOfNoReturn())
throw setErrorStatic(E_FAIL, tr("Canceled"));
that->mptrCancelableProgress.setNull();
// STEP 4: reattach hard disks
LogFlowFunc(("Reattaching new differencing hard disks...\n"));
pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
1); // operation weight, same as computed when setting up progress object
com::SafeIfaceArray<IMediumAttachment> atts;
rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
if (FAILED(rc))
throw rc;
for (size_t i = 0;
i < atts.size();
++i)
{
ComPtr<IStorageController> controller;
Bstr controllerName;
ULONG lInstance;
StorageControllerType_T enmController;
StorageBus_T enmBus;
BOOL fUseHostIOCache;
/*
* We can't pass a storage controller object directly
* (g++ complains about not being able to pass non POD types through '...')
* so we have to query needed values here and pass them.
*/
rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
if (FAILED(rc))
throw rc;
rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
controller.asOutParam());
if (FAILED(rc))
throw rc;
rc = controller->COMGETTER(ControllerType)(&enmController);
if (FAILED(rc))
throw rc;
rc = controller->COMGETTER(Instance)(&lInstance);
if (FAILED(rc))
throw rc;
rc = controller->COMGETTER(Bus)(&enmBus);
if (FAILED(rc))
throw rc;
rc = controller->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
if (FAILED(rc))
throw rc;
const char *pcszDevice = Console::convertControllerTypeToDev(enmController);
/*
* don't leave the lock since reconfigureMediumAttachment
* isn't going to need the Console lock.
*/
vrc = VMR3ReqCallWait(that->mpVM,
VMCPUID_ANY,
(PFNRT)reconfigureMediumAttachment,
12,
that,
that->mpVM,
pcszDevice,
lInstance,
enmBus,
fUseHostIOCache,
false /* fSetupMerge */,
0 /* uMergeSource */,
0 /* uMergeTarget */,
atts[i],
that->mMachineState,
&rc);
if (RT_FAILURE(vrc))
throw setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
if (FAILED(rc))
throw rc;
}
}
/*
* finalize the requested snapshot object.
* This will reset the machine state to the state it had right
* before calling mControl->BeginTakingSnapshot().
*/
rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
// do not throw rc here because we can't call EndTakingSnapshot() twice
LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
}
catch (HRESULT rcThrown)
{
/* preserve existing error info */
ErrorInfoKeeper eik;
if (fBeganTakingSnapshot)
that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
rc = rcThrown;
LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
}
Assert(alock.isWriteLockOnCurrentThread());
if (FAILED(rc)) /* Must come before calling setMachineState. */
pTask->mProgress->notifyComplete(rc);
/*
* Fix up the machine state.
*
* For live snapshots we do all the work, for the two other variantions we
* just update the local copy.
*/
MachineState_T enmMachineState;
that->mMachine->COMGETTER(State)(&enmMachineState);
if ( that->mMachineState == MachineState_LiveSnapshotting
|| that->mMachineState == MachineState_Saving)
{
if (!pTask->fTakingSnapshotOnline)
that->setMachineStateLocally(pTask->lastMachineState);
else if (SUCCEEDED(rc))
{
Assert( pTask->lastMachineState == MachineState_Running
|| pTask->lastMachineState == MachineState_Paused);
Assert(that->mMachineState == MachineState_Saving);
if (pTask->lastMachineState == MachineState_Running)
{
LogFlowFunc(("VMR3Resume...\n"));
alock.leave();
int vrc = VMR3Resume(that->mpVM);
alock.enter();
if (RT_FAILURE(vrc))
{
rc = setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
pTask->mProgress->notifyComplete(rc);
if (that->mMachineState == MachineState_Saving)
that->setMachineStateLocally(MachineState_Paused);
}
}
else
that->setMachineStateLocally(MachineState_Paused);
}
else
{
/** @todo this could probably be made more generic and reused elsewhere. */
/* paranoid cleanup on for a failed online snapshot. */
VMSTATE enmVMState = VMR3GetState(that->mpVM);
switch (enmVMState)
{
case VMSTATE_RUNNING:
case VMSTATE_RUNNING_LS:
case VMSTATE_DEBUGGING:
case VMSTATE_DEBUGGING_LS:
case VMSTATE_POWERING_OFF:
case VMSTATE_POWERING_OFF_LS:
case VMSTATE_RESETTING:
case VMSTATE_RESETTING_LS:
Assert(!fSuspenededBySave);
that->setMachineState(MachineState_Running);
break;
case VMSTATE_GURU_MEDITATION:
case VMSTATE_GURU_MEDITATION_LS:
that->setMachineState(MachineState_Stuck);
break;
case VMSTATE_FATAL_ERROR:
case VMSTATE_FATAL_ERROR_LS:
if (pTask->lastMachineState == MachineState_Paused)
that->setMachineStateLocally(pTask->lastMachineState);
else
that->setMachineState(MachineState_Paused);
break;
default:
AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
case VMSTATE_SUSPENDED:
case VMSTATE_SUSPENDED_LS:
case VMSTATE_SUSPENDING:
case VMSTATE_SUSPENDING_LS:
case VMSTATE_SUSPENDING_EXT_LS:
if (fSuspenededBySave)
{
Assert(pTask->lastMachineState == MachineState_Running);
LogFlowFunc(("VMR3Resume (on failure)...\n"));
alock.leave();
int vrc = VMR3Resume(that->mpVM);
alock.enter();
AssertLogRelRC(vrc);
if (RT_FAILURE(vrc))
that->setMachineState(MachineState_Paused);
}
else if (pTask->lastMachineState == MachineState_Paused)
that->setMachineStateLocally(pTask->lastMachineState);
else
that->setMachineState(MachineState_Paused);
break;
}
}
}
/*else: somebody else has change the state... Leave it. */
/* check the remote state to see that we got it right. */
that->mMachine->COMGETTER(State)(&enmMachineState);
AssertLogRelMsg(that->mMachineState == enmMachineState,
("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
Global::stringifyMachineState(enmMachineState) ));
if (SUCCEEDED(rc)) /* The failure cases are handled above. */
pTask->mProgress->notifyComplete(rc);
delete pTask;
LogFlowFuncLeave();
return VINF_SUCCESS;
}
/**
* Thread for executing the saved state operation.
*
* @param Thread The thread handle.
* @param pvUser Pointer to a VMSaveTask structure.
* @return VINF_SUCCESS (ignored).
*
* @note Locks the Console object for writing.
*/
/*static*/
DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser)
{
LogFlowFuncEnter();
std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
AssertReturn(task.get(), VERR_INVALID_PARAMETER);
Assert(task->mSavedStateFile.length());
Assert(!task->mProgress.isNull());
const ComObjPtr<Console> &that = task->mConsole;
Utf8Str errMsg;
HRESULT rc = S_OK;
LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
bool fSuspenededBySave;
int vrc = VMR3Save(that->mpVM,
task->mSavedStateFile.c_str(),
false, /*fContinueAfterwards*/
Console::stateProgressCallback,
static_cast<VMProgressTask*>(task.get()),
&fSuspenededBySave);
if (RT_FAILURE(vrc))
{
errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
task->mSavedStateFile.c_str(), vrc);
rc = E_FAIL;
}
Assert(!fSuspenededBySave);
/* lock the console once we're going to access it */
AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
/*
* finalize the requested save state procedure.
* In case of success, the server will set the machine state to Saved;
* in case of failure it will reset the it to the state it had right
* before calling mControl->BeginSavingState().
*/
that->mControl->EndSavingState(SUCCEEDED(rc));
/* synchronize the state with the server */
if (!FAILED(rc))
{
/*
* The machine has been successfully saved, so power it down
* (vmstateChangeCallback() will set state to Saved on success).
* Note: we release the task's VM caller, otherwise it will
* deadlock.
*/
task->releaseVMCaller();
rc = that->powerDown();
}
/* notify the progress object about operation completion */
if (SUCCEEDED(rc))
task->mProgress->notifyComplete(S_OK);
else
{
if (errMsg.length())
task->mProgress->notifyComplete(rc,
COM_IIDOF(IConsole),
Console::getStaticComponentName(),
errMsg.c_str());
else
task->mProgress->notifyComplete(rc);
}
LogFlowFuncLeave();
return VINF_SUCCESS;
}
/**
* Thread for powering down the Console.
*
* @param Thread The thread handle.
* @param pvUser Pointer to the VMTask structure.
* @return VINF_SUCCESS (ignored).
*
* @note Locks the Console object for writing.
*/
/*static*/
DECLCALLBACK(int) Console::powerDownThread(RTTHREAD Thread, void *pvUser)
{
LogFlowFuncEnter();
std::auto_ptr<VMProgressTask> task(static_cast<VMProgressTask *>(pvUser));
AssertReturn(task.get(), VERR_INVALID_PARAMETER);
AssertReturn(task->isOk(), VERR_GENERAL_FAILURE);
const ComObjPtr<Console> &that = task->mConsole;
/* Note: no need to use addCaller() to protect Console because VMTask does
* that */
/* wait until the method tat started us returns */
AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
/* release VM caller to avoid the powerDown() deadlock */
task->releaseVMCaller();
that->powerDown(task->mProgress);
LogFlowFuncLeave();
return VINF_SUCCESS;
}
/**
* @interface_method_impl{VMM2USERMETHODS,pfnSaveState}
*/
/*static*/
DECLCALLBACK(int) Console::vmm2User_SaveState(PCVMM2USERMETHODS pThis, PVM pVM)
{
Console *pConsole = *(Console **)(pThis + 1); /* lazy bird */
/*
* For now, just call SaveState. We should probably try notify the GUI so
* it can pop up a progress object and stuff.
*/
HRESULT hrc = pConsole->SaveState(NULL);
return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
}
/**
* The Main status driver instance data.
*/
typedef struct DRVMAINSTATUS
{
/** The LED connectors. */
PDMILEDCONNECTORS ILedConnectors;
/** Pointer to the LED ports interface above us. */
PPDMILEDPORTS pLedPorts;
/** Pointer to the array of LED pointers. */
PPDMLED *papLeds;
/** The unit number corresponding to the first entry in the LED array. */
RTUINT iFirstLUN;
/** The unit number corresponding to the last entry in the LED array.
* (The size of the LED array is iLastLUN - iFirstLUN + 1.) */
RTUINT iLastLUN;
} DRVMAINSTATUS, *PDRVMAINSTATUS;
/**
* Notification about a unit which have been changed.
*
* The driver must discard any pointers to data owned by
* the unit and requery it.
*
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param iLUN The unit number.
*/
DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN)
{
PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface;
if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN)
{
PPDMLED pLed;
int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed);
if (RT_FAILURE(rc))
pLed = NULL;
ASMAtomicWritePtr(&pData->papLeds[iLUN - pData->iFirstLUN], pLed);
Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed));
}
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors);
return NULL;
}
/**
* Destruct a status driver instance.
*
* @returns VBox status.
* @param pDrvIns The driver instance data.
*/
DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns)
{
PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
if (pData->papLeds)
{
unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1;
while (iLed-- > 0)
ASMAtomicWriteNullPtr(&pData->papLeds[iLed]);
}
}
/**
* Construct a status driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
PDRVMAINSTATUS pData = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS);
LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance));
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
/*
* Validate configuration.
*/
if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0"))
return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
("Configuration error: Not possible to attach anything to this driver!\n"),
VERR_PDM_DRVINS_NO_ATTACH);
/*
* Data.
*/
pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface;
pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged;
/*
* Read config.
*/
int rc = CFGMR3QueryPtr(pCfg, "papLeds", (void **)&pData->papLeds);
if (RT_FAILURE(rc))
{
AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Rrc\n", rc));
return rc;
}
rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
pData->iFirstLUN = 0;
else if (RT_FAILURE(rc))
{
AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Rrc\n", rc));
return rc;
}
rc = CFGMR3QueryU32(pCfg, "Last", &pData->iLastLUN);
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
pData->iLastLUN = 0;
else if (RT_FAILURE(rc))
{
AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Rrc\n", rc));
return rc;
}
if (pData->iFirstLUN > pData->iLastLUN)
{
AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN));
return VERR_GENERAL_FAILURE;
}
/*
* Get the ILedPorts interface of the above driver/device and
* query the LEDs we want.
*/
pData->pLedPorts = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
AssertMsgReturn(pData->pLedPorts, ("Configuration error: No led ports interface above!\n"),
VERR_PDM_MISSING_INTERFACE_ABOVE);
for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; ++i)
Console::drvStatus_UnitChanged(&pData->ILedConnectors, i);
return VINF_SUCCESS;
}
/**
* Keyboard driver registration record.
*/
const PDMDRVREG Console::DrvStatusReg =
{
/* u32Version */
PDM_DRVREG_VERSION,
/* szName */
"MainStatus",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"Main status driver (Main as in the API).",
/* fFlags */
PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
/* fClass. */
PDM_DRVREG_CLASS_STATUS,
/* cMaxInstances */
~0,
/* cbInstance */
sizeof(DRVMAINSTATUS),
/* pfnConstruct */
Console::drvStatus_Construct,
/* pfnDestruct */
Console::drvStatus_Destruct,
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32EndVersion */
PDM_DRVREG_VERSION
};
/* vi: set tabstop=4 shiftwidth=4 expandtab: */