diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Main/src-server/win | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Main/src-server/win')
-rw-r--r-- | src/VBox/Main/src-server/win/HostDnsServiceWin.cpp | 483 | ||||
-rw-r--r-- | src/VBox/Main/src-server/win/HostPowerWin.cpp | 236 | ||||
-rw-r--r-- | src/VBox/Main/src-server/win/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/Main/src-server/win/NetIf-win.cpp | 2001 | ||||
-rw-r--r-- | src/VBox/Main/src-server/win/PerformanceWin.cpp | 356 | ||||
-rw-r--r-- | src/VBox/Main/src-server/win/USBProxyBackendWindows.cpp | 274 | ||||
-rw-r--r-- | src/VBox/Main/src-server/win/VBoxSVC.rc | 78 | ||||
-rw-r--r-- | src/VBox/Main/src-server/win/precomp_vcc.h | 48 | ||||
-rw-r--r-- | src/VBox/Main/src-server/win/svchlp.cpp | 308 | ||||
-rw-r--r-- | src/VBox/Main/src-server/win/svchlp.h | 107 | ||||
-rw-r--r-- | src/VBox/Main/src-server/win/svcmain.cpp | 1212 |
11 files changed, 5103 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/win/HostDnsServiceWin.cpp b/src/VBox/Main/src-server/win/HostDnsServiceWin.cpp new file mode 100644 index 00000000..d493a264 --- /dev/null +++ b/src/VBox/Main/src-server/win/HostDnsServiceWin.cpp @@ -0,0 +1,483 @@ +/* $Id: HostDnsServiceWin.cpp $ */ +/** @file + * Host DNS listener for Windows. + */ + +/* + * Copyright (C) 2014-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +/* + * XXX: need <winsock2.h> to reveal IP_ADAPTER_ADDRESSES in + * <iptypes.h> and it must be included before <windows.h>, which is + * pulled in by IPRT headers. + */ +#include <iprt/win/winsock2.h> + +#include "../HostDnsService.h" + +#include <VBox/com/string.h> +#include <VBox/com/ptr.h> + +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <VBox/log.h> + +#include <iprt/win/windows.h> +#include <windns.h> +#include <iptypes.h> +#include <iprt/win/iphlpapi.h> + +#include <algorithm> +#include <iprt/sanitized/sstream> +#include <iprt/sanitized/string> +#include <vector> + + +DECLINLINE(int) registerNotification(const HKEY &hKey, HANDLE &hEvent) +{ + LONG lrc = RegNotifyChangeKeyValue(hKey, + TRUE, + REG_NOTIFY_CHANGE_LAST_SET, + hEvent, + TRUE); + AssertMsgReturn(lrc == ERROR_SUCCESS, + ("Failed to register event on the key. Please debug me!"), + VERR_INTERNAL_ERROR); + + return VINF_SUCCESS; +} + +static void appendTokenizedStrings(std::vector<std::string> &vecStrings, const std::string &strToAppend, char chDelim = ' ') +{ + if (strToAppend.empty()) + return; + + std::istringstream stream(strToAppend); + std::string substr; + + while (std::getline(stream, substr, chDelim)) + { + if (substr.empty()) + continue; + + if (std::find(vecStrings.cbegin(), vecStrings.cend(), substr) != vecStrings.cend()) + continue; + + vecStrings.push_back(substr); + } +} + + +struct HostDnsServiceWin::Data +{ + HKEY hKeyTcpipParameters; + bool fTimerArmed; + +#define DATA_SHUTDOWN_EVENT 0 +#define DATA_DNS_UPDATE_EVENT 1 +#define DATA_TIMER 2 +#define DATA_MAX_EVENT 3 + HANDLE haDataEvent[DATA_MAX_EVENT]; + + Data() + { + hKeyTcpipParameters = NULL; + fTimerArmed = false; + + for (size_t i = 0; i < DATA_MAX_EVENT; ++i) + haDataEvent[i] = NULL; + } + + ~Data() + { + if (hKeyTcpipParameters != NULL) + RegCloseKey(hKeyTcpipParameters); + + for (size_t i = 0; i < DATA_MAX_EVENT; ++i) + if (haDataEvent[i] != NULL) + CloseHandle(haDataEvent[i]); + } +}; + + +HostDnsServiceWin::HostDnsServiceWin() + : HostDnsServiceBase(true) +{ + m = new Data(); +} + +HostDnsServiceWin::~HostDnsServiceWin() +{ + if (m != NULL) + delete m; +} + +HRESULT HostDnsServiceWin::init(HostDnsMonitorProxy *pProxy) +{ + if (m == NULL) + return E_FAIL; + + bool fRc = true; + LONG lRc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", + 0, + KEY_READ|KEY_NOTIFY, + &m->hKeyTcpipParameters); + if (lRc != ERROR_SUCCESS) + { + LogRel(("HostDnsServiceWin: failed to open key Tcpip\\Parameters (error %d)\n", lRc)); + fRc = false; + } + else + { + for (size_t i = 0; i < DATA_MAX_EVENT; ++i) + { + HANDLE h; + + if (i == DATA_TIMER) + h = CreateWaitableTimer(NULL, FALSE, NULL); + else + h = CreateEvent(NULL, TRUE, FALSE, NULL); + + if (h == NULL) + { + LogRel(("HostDnsServiceWin: failed to create event (error %d)\n", GetLastError())); + fRc = false; + break; + } + + m->haDataEvent[i] = h; + } + } + + if (!fRc) + return E_FAIL; + + HRESULT hrc = HostDnsServiceBase::init(pProxy); + if (FAILED(hrc)) + return hrc; + + return updateInfo(); +} + +void HostDnsServiceWin::uninit(void) +{ + HostDnsServiceBase::uninit(); +} + +int HostDnsServiceWin::monitorThreadShutdown(RTMSINTERVAL uTimeoutMs) +{ + RT_NOREF(uTimeoutMs); + + AssertPtr(m); + SetEvent(m->haDataEvent[DATA_SHUTDOWN_EVENT]); + /** @todo r=andy Wait for thread? Check vrc here. Timeouts? */ + + return VINF_SUCCESS; +} + +int HostDnsServiceWin::monitorThreadProc(void) +{ + Assert(m != NULL); + + registerNotification(m->hKeyTcpipParameters, + m->haDataEvent[DATA_DNS_UPDATE_EVENT]); + + onMonitorThreadInitDone(); + + for (;;) + { + DWORD dwReady; + + dwReady = WaitForMultipleObjects(DATA_MAX_EVENT, m->haDataEvent, + FALSE, INFINITE); + + if (dwReady == WAIT_OBJECT_0 + DATA_SHUTDOWN_EVENT) + break; + + if (dwReady == WAIT_OBJECT_0 + DATA_DNS_UPDATE_EVENT) + { + /* + * Registry updates for multiple values are not atomic, so + * wait a bit to avoid racing and reading partial update. + */ + if (!m->fTimerArmed) + { + LARGE_INTEGER delay; /* in 100ns units */ + delay.QuadPart = -2 * 1000 * 1000 * 10LL; /* relative: 2s */ + + BOOL ok = SetWaitableTimer(m->haDataEvent[DATA_TIMER], &delay, + 0, NULL, NULL, FALSE); + if (ok) + { + m->fTimerArmed = true; + } + else + { + LogRel(("HostDnsServiceWin: failed to arm timer (error %d)\n", GetLastError())); + updateInfo(); + } + } + + ResetEvent(m->haDataEvent[DATA_DNS_UPDATE_EVENT]); + registerNotification(m->hKeyTcpipParameters, + m->haDataEvent[DATA_DNS_UPDATE_EVENT]); + } + else if (dwReady == WAIT_OBJECT_0 + DATA_TIMER) + { + m->fTimerArmed = false; + updateInfo(); + } + else if (dwReady == WAIT_FAILED) + { + LogRel(("HostDnsServiceWin: WaitForMultipleObjects failed: error %d\n", GetLastError())); + return VERR_INTERNAL_ERROR; + } + else + { + LogRel(("HostDnsServiceWin: WaitForMultipleObjects unexpected return value %d\n", dwReady)); + return VERR_INTERNAL_ERROR; + } + } + + return VINF_SUCCESS; +} + +HRESULT HostDnsServiceWin::updateInfo(void) +{ + HostDnsInformation info; + + std::string strDomain; + std::string strSearchList; /* NB: comma separated, no spaces */ + + /* + * We ignore "DhcpDomain" key here since it's not stable. If + * there are two active interfaces that use DHCP (in particular + * when host uses OpenVPN) then DHCP ACKs will take turns updating + * that key. Instead we call GetAdaptersAddresses() below (which + * is what ipconfig.exe seems to do). + */ + for (DWORD regIndex = 0; /**/; ++regIndex) + { + char keyName[256]; + DWORD cbKeyName = sizeof(keyName); + DWORD keyType = 0; + char keyData[1024]; + DWORD cbKeyData = sizeof(keyData); + +/** @todo use unicode API. This isn't UTF-8 clean! */ + LSTATUS lrc = RegEnumValueA(m->hKeyTcpipParameters, regIndex, + keyName, &cbKeyName, 0, + &keyType, (LPBYTE)keyData, &cbKeyData); + + if (lrc == ERROR_NO_MORE_ITEMS) + break; + + if (lrc == ERROR_MORE_DATA) /* buffer too small; handle? */ + continue; + + if (lrc != ERROR_SUCCESS) + { + LogRel2(("HostDnsServiceWin: RegEnumValue error %d\n", (int)lrc)); + return E_FAIL; + } + + if (keyType != REG_SZ) + continue; + + if (cbKeyData > 0 && keyData[cbKeyData - 1] == '\0') + --cbKeyData; /* don't count trailing NUL if present */ + + if (RTStrICmp("Domain", keyName) == 0) + { + strDomain.assign(keyData, cbKeyData); + LogRel2(("HostDnsServiceWin: Domain=\"%s\"\n", strDomain.c_str())); + } + else if (RTStrICmp("DhcpDomain", keyName) == 0) + { + std::string strDhcpDomain(keyData, cbKeyData); + LogRel2(("HostDnsServiceWin: DhcpDomain=\"%s\"\n", strDhcpDomain.c_str())); + } + else if (RTStrICmp("SearchList", keyName) == 0) + { + strSearchList.assign(keyData, cbKeyData); + LogRel2(("HostDnsServiceWin: SearchList=\"%s\"\n", strSearchList.c_str())); + } + } + + /* statically configured domain name */ + if (!strDomain.empty()) + { + info.domain = strDomain; + info.searchList.push_back(strDomain); + } + + /* statically configured search list */ + if (!strSearchList.empty()) + appendTokenizedStrings(info.searchList, strSearchList, ','); + + /* + * When name servers are configured statically it seems that the + * value of Tcpip\Parameters\NameServer is NOT set, inly interface + * specific NameServer value is (which triggers notification for + * us to pick up the change). Fortunately, DnsApi seems to do the + * right thing there. + */ + DNS_STATUS status; + PIP4_ARRAY pIp4Array = NULL; + + // NB: must be set on input it seems, despite docs' claim to the contrary. + DWORD cbBuffer = sizeof(&pIp4Array); + + status = DnsQueryConfig(DnsConfigDnsServerList, + DNS_CONFIG_FLAG_ALLOC, NULL, NULL, + &pIp4Array, &cbBuffer); + + if (status == NO_ERROR && pIp4Array != NULL) + { + for (DWORD i = 0; i < pIp4Array->AddrCount; ++i) + { + char szAddrStr[16] = ""; + RTStrPrintf(szAddrStr, sizeof(szAddrStr), "%RTnaipv4", pIp4Array->AddrArray[i]); + + LogRel2(("HostDnsServiceWin: server %d: %s\n", i+1, szAddrStr)); + info.servers.push_back(szAddrStr); + } + + LocalFree(pIp4Array); + } + + + /** + * DnsQueryConfig(DnsConfigSearchList, ...) is not implemented. + * Call GetAdaptersAddresses() that orders the returned list + * appropriately and collect IP_ADAPTER_ADDRESSES::DnsSuffix. + */ + do { + PIP_ADAPTER_ADDRESSES pAddrBuf = NULL; + ULONG cbAddrBuf = 8 * 1024; + bool fReallocated = false; + ULONG err; + + pAddrBuf = (PIP_ADAPTER_ADDRESSES) malloc(cbAddrBuf); + if (pAddrBuf == NULL) + { + LogRel2(("HostDnsServiceWin: failed to allocate %zu bytes" + " of GetAdaptersAddresses buffer\n", + (size_t)cbAddrBuf)); + break; + } + + while (pAddrBuf != NULL) + { + ULONG cbAddrBufProvided = cbAddrBuf; + + err = GetAdaptersAddresses(AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST + | GAA_FLAG_SKIP_MULTICAST, + NULL, + pAddrBuf, &cbAddrBuf); + if (err == NO_ERROR) + { + break; + } + else if (err == ERROR_BUFFER_OVERFLOW) + { + LogRel2(("HostDnsServiceWin: provided GetAdaptersAddresses with %zu" + " but asked again for %zu bytes\n", + (size_t)cbAddrBufProvided, (size_t)cbAddrBuf)); + + if (RT_UNLIKELY(fReallocated)) /* what? again?! */ + { + LogRel2(("HostDnsServiceWin: ... not going to realloc again\n")); + free(pAddrBuf); + pAddrBuf = NULL; + break; + } + + PIP_ADAPTER_ADDRESSES pNewBuf = (PIP_ADAPTER_ADDRESSES) realloc(pAddrBuf, cbAddrBuf); + if (pNewBuf == NULL) + { + LogRel2(("HostDnsServiceWin: failed to reallocate %zu bytes\n", (size_t)cbAddrBuf)); + free(pAddrBuf); + pAddrBuf = NULL; + break; + } + + /* try again */ + pAddrBuf = pNewBuf; /* cbAddrBuf already updated */ + fReallocated = true; + } + else + { + LogRel2(("HostDnsServiceWin: GetAdaptersAddresses error %d\n", err)); + free(pAddrBuf); + pAddrBuf = NULL; + break; + } + } + + if (pAddrBuf == NULL) + break; + + for (PIP_ADAPTER_ADDRESSES pAdp = pAddrBuf; pAdp != NULL; pAdp = pAdp->Next) + { + LogRel2(("HostDnsServiceWin: %ls (status %u) ...\n", + pAdp->FriendlyName ? pAdp->FriendlyName : L"(null)", pAdp->OperStatus)); + + if (pAdp->OperStatus != IfOperStatusUp) + continue; + + if (pAdp->DnsSuffix == NULL || *pAdp->DnsSuffix == L'\0') + continue; + + char *pszDnsSuffix = NULL; + int vrc = RTUtf16ToUtf8Ex(pAdp->DnsSuffix, RTSTR_MAX, &pszDnsSuffix, 0, /* allocate */ NULL); + if (RT_FAILURE(vrc)) + { + LogRel2(("HostDnsServiceWin: failed to convert DNS suffix \"%ls\": %Rrc\n", pAdp->DnsSuffix, vrc)); + continue; + } + + AssertContinue(pszDnsSuffix != NULL); + AssertContinue(*pszDnsSuffix != '\0'); + LogRel2(("HostDnsServiceWin: ... suffix = \"%s\"\n", pszDnsSuffix)); + + appendTokenizedStrings(info.searchList, pszDnsSuffix); + RTStrFree(pszDnsSuffix); + } + + free(pAddrBuf); + } while (0); + + + if (info.domain.empty() && !info.searchList.empty()) + info.domain = info.searchList[0]; + + if (info.searchList.size() == 1) + info.searchList.clear(); + + HostDnsServiceBase::setInfo(info); + + return S_OK; +} + diff --git a/src/VBox/Main/src-server/win/HostPowerWin.cpp b/src/VBox/Main/src-server/win/HostPowerWin.cpp new file mode 100644 index 00000000..f41a2112 --- /dev/null +++ b/src/VBox/Main/src-server/win/HostPowerWin.cpp @@ -0,0 +1,236 @@ +/* $Id: HostPowerWin.cpp $ */ +/** @file + * VirtualBox interface to host's power notification service + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN_HOST +#include <iprt/win/windows.h> +/* Some SDK versions lack the extern "C" and thus cause linking failures. + * This workaround isn't pretty, but there are not many options. */ +extern "C" { +#include <PowrProf.h> +} + +#include <VBox/com/ptr.h> +#include <iprt/errcore.h> +#include "HostPower.h" +#include "LoggingNew.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static WCHAR gachWindowClassName[] = L"VBoxPowerNotifyClass"; + + +HostPowerServiceWin::HostPowerServiceWin(VirtualBox *aVirtualBox) : HostPowerService(aVirtualBox), mThread(NIL_RTTHREAD) +{ + mHwnd = 0; + + int vrc = RTThreadCreate(&mThread, HostPowerServiceWin::NotificationThread, this, 65536, + RTTHREADTYPE_GUI, RTTHREADFLAGS_WAITABLE, "MainPower"); + + if (RT_FAILURE(vrc)) + { + Log(("HostPowerServiceWin::HostPowerServiceWin: RTThreadCreate failed with %Rrc\n", vrc)); + return; + } +} + +HostPowerServiceWin::~HostPowerServiceWin() +{ + if (mHwnd) + { + Log(("HostPowerServiceWin::!HostPowerServiceWin: destroy window %x\n", mHwnd)); + + /* Poke the thread out of the event loop and wait for it to clean up. */ + PostMessage(mHwnd, WM_CLOSE, 0, 0); + RTThreadWait(mThread, 5000, NULL); + mThread = NIL_RTTHREAD; + } +} + + + +DECLCALLBACK(int) HostPowerServiceWin::NotificationThread(RTTHREAD hThreadSelf, void *pInstance) +{ + RT_NOREF(hThreadSelf); + HostPowerServiceWin *pPowerObj = (HostPowerServiceWin *)pInstance; + HWND hwnd = 0; + + /* Create a window and make it a power event notification handler. */ + int vrc = VINF_SUCCESS; + + HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); + + /* Register the Window Class. */ + WNDCLASS wc; + + wc.style = CS_NOCLOSE; + wc.lpfnWndProc = HostPowerServiceWin::WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = sizeof(void *); + wc.hInstance = hInstance; + wc.hIcon = NULL; + wc.hCursor = NULL; + wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1); + wc.lpszMenuName = NULL; + wc.lpszClassName = gachWindowClassName; + + ATOM atomWindowClass = RegisterClass(&wc); + + if (atomWindowClass == 0) + { + vrc = VERR_NOT_SUPPORTED; + Log(("HostPowerServiceWin::NotificationThread: RegisterClassA failed with %x\n", GetLastError())); + } + else + { + /* Create the window. */ + hwnd = pPowerObj->mHwnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST, + gachWindowClassName, gachWindowClassName, + WS_POPUPWINDOW, + -200, -200, 100, 100, NULL, NULL, hInstance, NULL); + + if (hwnd == NULL) + { + Log(("HostPowerServiceWin::NotificationThread: CreateWindowExA failed with %x\n", GetLastError())); + vrc = VERR_NOT_SUPPORTED; + } + else + { + SetWindowLongPtr(hwnd, 0, (LONG_PTR)pPowerObj); + SetWindowPos(hwnd, HWND_TOPMOST, -200, -200, 0, 0, + SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE); + + MSG msg; + BOOL fRet; + while ((fRet = GetMessage(&msg, NULL, 0, 0)) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + /* + * Window procedure can return error, + * but this is exceptional situation + * that should be identified in testing + */ + Assert(fRet >= 0); + } + } + + Log(("HostPowerServiceWin::NotificationThread: exit thread\n")); + + if (atomWindowClass != 0) + { + UnregisterClass(gachWindowClassName, hInstance); + atomWindowClass = 0; + } + + return 0; +} + +LRESULT CALLBACK HostPowerServiceWin::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_POWERBROADCAST: + { + HostPowerServiceWin *pPowerObj; + + pPowerObj = (HostPowerServiceWin *)GetWindowLongPtr(hwnd, 0); + if (pPowerObj) + { + switch(wParam) + { + case PBT_APMSUSPEND: + pPowerObj->notify(Reason_HostSuspend); + break; + + case PBT_APMRESUMEAUTOMATIC: + pPowerObj->notify(Reason_HostResume); + break; + + case PBT_APMPOWERSTATUSCHANGE: + { + SYSTEM_POWER_STATUS SystemPowerStatus; + + Log(("PBT_APMPOWERSTATUSCHANGE\n")); + if (GetSystemPowerStatus(&SystemPowerStatus) == TRUE) + { + Log(("PBT_APMPOWERSTATUSCHANGE ACLineStatus=%d BatteryFlag=%d\n", SystemPowerStatus.ACLineStatus, + SystemPowerStatus.BatteryFlag)); + + if (SystemPowerStatus.ACLineStatus == 0) /* offline */ + { + if (SystemPowerStatus.BatteryFlag == 2 /* low > 33% */) + { + SYSTEM_BATTERY_STATE BatteryState; + LONG lrc = CallNtPowerInformation(SystemBatteryState, NULL, 0, (PVOID)&BatteryState, + sizeof(BatteryState)); +#ifdef LOG_ENABLED + if (lrc == 0 /* STATUS_SUCCESS */) + Log(("CallNtPowerInformation claims %d seconds of power left\n", + BatteryState.EstimatedTime)); +#endif + if ( lrc == 0 /* STATUS_SUCCESS */ + && BatteryState.EstimatedTime < 60*5) + { + pPowerObj->notify(Reason_HostBatteryLow); + } + } + /* If the machine has less than 5% battery left (and is not connected + * to the AC), then we should save the state. */ + else if (SystemPowerStatus.BatteryFlag == 4 /* critical battery status; less than 5% */) + { + pPowerObj->notify(Reason_HostBatteryLow); + } + } + } + break; + } + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + } + return TRUE; + } + + case WM_DESTROY: + { + /* moved here. it can't work across theads */ + SetWindowLongPtr(hwnd, 0, 0); + PostQuitMessage(0); + return 0; + } + + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } +} diff --git a/src/VBox/Main/src-server/win/Makefile.kup b/src/VBox/Main/src-server/win/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Main/src-server/win/Makefile.kup diff --git a/src/VBox/Main/src-server/win/NetIf-win.cpp b/src/VBox/Main/src-server/win/NetIf-win.cpp new file mode 100644 index 00000000..c0518277 --- /dev/null +++ b/src/VBox/Main/src-server/win/NetIf-win.cpp @@ -0,0 +1,2001 @@ +/* $Id: NetIf-win.cpp $ */ +/** @file + * Main - NetIfList, Windows implementation. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN_HOST + +#define NETIF_WITHOUT_NETCFG + +#include <iprt/errcore.h> +#include <list> + +#define _WIN32_DCOM +#include <iprt/win/winsock2.h> +#include <iprt/win/ws2tcpip.h> +#include <iprt/win/windows.h> +#include <tchar.h> + +#ifdef VBOX_WITH_NETFLT +# include "VBox/VBoxNetCfg-win.h" +# include "devguid.h" +#endif + +#include <iprt/win/iphlpapi.h> +#include <iprt/win/ntddndis.h> + +#include "LoggingNew.h" +#include "HostNetworkInterfaceImpl.h" +#include "ProgressImpl.h" +#include "VirtualBoxImpl.h" +#include "VBoxNls.h" +#include "Global.h" +#include "netif.h" +#include "ThreadTask.h" + +DECLARE_TRANSLATION_CONTEXT(NetIfWin); + +#ifdef VBOX_WITH_NETFLT +# include <Wbemidl.h> + +# include "svchlp.h" + +# include <shellapi.h> +# define INITGUID +# include <guiddef.h> +# include <devguid.h> +# include <iprt/win/objbase.h> +# include <iprt/win/setupapi.h> +# include <iprt/win/shlobj.h> +# include <cfgmgr32.h> + +# define VBOX_APP_NAME L"VirtualBox" + +static int getDefaultInterfaceIndex() +{ + PMIB_IPFORWARDTABLE pIpTable; + DWORD dwSize = sizeof(MIB_IPFORWARDTABLE) * 20; + DWORD dwRC = NO_ERROR; + int iIndex = -1; + + pIpTable = (MIB_IPFORWARDTABLE *)RTMemAlloc(dwSize); + if (GetIpForwardTable(pIpTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) + { + RTMemFree(pIpTable); + pIpTable = (MIB_IPFORWARDTABLE *)RTMemAlloc(dwSize); + if (!pIpTable) + return -1; + } + dwRC = GetIpForwardTable(pIpTable, &dwSize, 0); + if (dwRC == NO_ERROR) + { + for (unsigned int i = 0; i < pIpTable->dwNumEntries; i++) + if (pIpTable->table[i].dwForwardDest == 0) + { + iIndex = pIpTable->table[i].dwForwardIfIndex; + break; + } + } + RTMemFree(pIpTable); + return iIndex; +} + +static int collectNetIfInfo(Bstr &strName, Guid &guid, PNETIFINFO pInfo, int iDefault) +{ + RT_NOREF(strName); + + /* + * Most of the hosts probably have less than 10 adapters, + * so we'll mostly succeed from the first attempt. + */ + ULONG uBufLen = sizeof(IP_ADAPTER_ADDRESSES) * 10; + PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAlloc(uBufLen); + if (!pAddresses) + return VERR_NO_MEMORY; + DWORD dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &uBufLen); + if (dwRc == ERROR_BUFFER_OVERFLOW) + { + /* Impressive! More than 10 adapters! Get more memory and try again. */ + RTMemFree(pAddresses); + pAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAlloc(uBufLen); + if (!pAddresses) + return VERR_NO_MEMORY; + dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &uBufLen); + } + if (dwRc == NO_ERROR) + { + PIP_ADAPTER_ADDRESSES pAdapter; + for (pAdapter = pAddresses; pAdapter; pAdapter = pAdapter->Next) + { + char *pszUuid = RTStrDup(pAdapter->AdapterName); + size_t len = strlen(pszUuid) - 1; + if (pszUuid[0] == '{' && pszUuid[len] == '}') + { + pszUuid[len] = 0; + if (!RTUuidCompareStr(&pInfo->Uuid, pszUuid + 1)) + { + bool fIPFound, fIPv6Found; + PIP_ADAPTER_UNICAST_ADDRESS pAddr; + fIPFound = fIPv6Found = false; + for (pAddr = pAdapter->FirstUnicastAddress; pAddr; pAddr = pAddr->Next) + { + switch (pAddr->Address.lpSockaddr->sa_family) + { + case AF_INET: + if (!fIPFound) + { + fIPFound = true; + memcpy(&pInfo->IPAddress, + &((struct sockaddr_in *)pAddr->Address.lpSockaddr)->sin_addr.s_addr, + sizeof(pInfo->IPAddress)); + } + break; + case AF_INET6: + if (!fIPv6Found) + { + fIPv6Found = true; + memcpy(&pInfo->IPv6Address, + ((struct sockaddr_in6 *)pAddr->Address.lpSockaddr)->sin6_addr.s6_addr, + sizeof(pInfo->IPv6Address)); + } + break; + } + } + PIP_ADAPTER_PREFIX pPrefix; + fIPFound = fIPv6Found = false; + for (pPrefix = pAdapter->FirstPrefix; pPrefix; pPrefix = pPrefix->Next) + { + switch (pPrefix->Address.lpSockaddr->sa_family) + { + case AF_INET: + if (!fIPFound) + { + if (pPrefix->PrefixLength <= sizeof(pInfo->IPNetMask) * 8) + { + fIPFound = true; + RTNetPrefixToMaskIPv4(pPrefix->PrefixLength, &pInfo->IPNetMask); + } + else + LogFunc(("Unexpected IPv4 prefix length of %d\n", + pPrefix->PrefixLength)); + } + break; + case AF_INET6: + if (!fIPv6Found) + { + if (pPrefix->PrefixLength <= sizeof(pInfo->IPv6NetMask) * 8) + { + fIPv6Found = true; + RTNetPrefixToMaskIPv6(pPrefix->PrefixLength, &pInfo->IPv6NetMask); + } + else + LogFunc(("Unexpected IPv6 prefix length of %d\n", + pPrefix->PrefixLength)); + } + break; + } + } + if (sizeof(pInfo->MACAddress) != pAdapter->PhysicalAddressLength) + LogFunc(("Unexpected physical address length: %u\n", pAdapter->PhysicalAddressLength)); + else + memcpy(pInfo->MACAddress.au8, pAdapter->PhysicalAddress, sizeof(pInfo->MACAddress)); + pInfo->enmMediumType = NETIF_T_ETHERNET; + pInfo->enmStatus = pAdapter->OperStatus == IfOperStatusUp ? NETIF_S_UP : NETIF_S_DOWN; + pInfo->fIsDefault = (pAdapter->IfIndex == (DWORD)iDefault); + RTStrFree(pszUuid); + break; + } + } + RTStrFree(pszUuid); + } + + ADAPTER_SETTINGS Settings; + HRESULT hrc = VBoxNetCfgWinGetAdapterSettings((const GUID *)guid.raw(), &Settings); + if (hrc == S_OK) + { + if (Settings.ip) + { + pInfo->IPAddress.u = Settings.ip; + pInfo->IPNetMask.u = Settings.mask; + } + pInfo->fDhcpEnabled = Settings.bDhcp; + } + else + { + pInfo->fDhcpEnabled = false; + } + } + RTMemFree(pAddresses); + + return VINF_SUCCESS; +} + +/* svc helper func */ + +struct StaticIpConfig +{ + ULONG IPAddress; + ULONG IPNetMask; +}; + +struct StaticIpV6Config +{ + char * IPV6Address; + ULONG IPV6NetMaskLength; +}; + +class NetworkInterfaceHelperClientData : public ThreadVoidData +{ +public: + NetworkInterfaceHelperClientData(){}; + ~NetworkInterfaceHelperClientData() + { + if (msgCode == SVCHlpMsg::EnableStaticIpConfigV6 && u.StaticIPV6.IPV6Address) + { + RTStrFree(u.StaticIPV6.IPV6Address); + u.StaticIPV6.IPV6Address = NULL; + } + }; + + SVCHlpMsg::Code msgCode; + /* for SVCHlpMsg::CreateHostOnlyNetworkInterface */ + Bstr name; + ComObjPtr<HostNetworkInterface> iface; + ComObjPtr<VirtualBox> ptrVBox; + /* for SVCHlpMsg::RemoveHostOnlyNetworkInterface */ + Guid guid; + + union + { + StaticIpConfig StaticIP; + StaticIpV6Config StaticIPV6; + } u; + +}; + +static HRESULT netIfNetworkInterfaceHelperClient(SVCHlpClient *aClient, + Progress *aProgress, + void *aUser, int *aVrc) +{ + LogFlowFuncEnter(); + LogFlowFunc(("aClient={%p}, aProgress={%p}, aUser={%p}\n", + aClient, aProgress, aUser)); + + AssertReturn( (aClient == NULL && aProgress == NULL && aVrc == NULL) + || (aClient != NULL && aProgress != NULL && aVrc != NULL), + E_POINTER); + AssertReturn(aUser, E_POINTER); + + NetworkInterfaceHelperClientData* d = static_cast<NetworkInterfaceHelperClientData *>(aUser); + + if (aClient == NULL) + { + /* "cleanup only" mode, just return (it will free aUser) */ + return S_OK; + } + + HRESULT hrc = S_OK; + int vrc = VINF_SUCCESS; + + switch (d->msgCode) + { + case SVCHlpMsg::CreateHostOnlyNetworkInterface: + { + LogFlowFunc(("CreateHostOnlyNetworkInterface:\n")); + LogFlowFunc(("Network connection name = '%ls'\n", d->name.raw())); + + /* write message and parameters */ + vrc = aClient->write(d->msgCode); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(Utf8Str(d->name)); + if (RT_FAILURE(vrc)) break; + + /* wait for a reply */ + bool endLoop = false; + while (!endLoop) + { + SVCHlpMsg::Code reply = SVCHlpMsg::Null; + + vrc = aClient->read(reply); + if (RT_FAILURE(vrc)) break; + + switch (reply) + { + case SVCHlpMsg::CreateHostOnlyNetworkInterface_OK: + { + /* read the GUID */ + Guid guid; + Utf8Str name; + vrc = aClient->read(name); + if (RT_FAILURE(vrc)) break; + vrc = aClient->read(guid); + if (RT_FAILURE(vrc)) break; + + LogFlowFunc(("Network connection GUID = {%RTuuid}\n", guid.raw())); + + /* initialize the object returned to the caller by + * CreateHostOnlyNetworkInterface() */ + hrc = d->iface->init(Bstr(name), Bstr(name), guid, HostNetworkInterfaceType_HostOnly); + if (SUCCEEDED(hrc)) + { + hrc = d->iface->i_setVirtualBox(d->ptrVBox); + if (SUCCEEDED(hrc)) + { + hrc = d->iface->updateConfig(); + if (SUCCEEDED(hrc)) + hrc = d->iface->i_updatePersistentConfig(); + } + } + endLoop = true; + break; + } + case SVCHlpMsg::Error: + { + /* read the error message */ + Utf8Str errMsg; + vrc = aClient->read(errMsg); + if (RT_FAILURE(vrc)) break; + + hrc = E_FAIL; + d->iface->setError(E_FAIL, errMsg.c_str()); + endLoop = true; + break; + } + default: + { + endLoop = true; + hrc = E_FAIL;/// @todo ComAssertMsgFailedBreak(( + //"Invalid message code %d (%08lX)\n", + //reply, reply), + //hrc = E_FAIL); + } + } + } + + break; + } + case SVCHlpMsg::RemoveHostOnlyNetworkInterface: + { + LogFlowFunc(("RemoveHostOnlyNetworkInterface:\n")); + LogFlowFunc(("Network connection GUID = {%RTuuid}\n", d->guid.raw())); + + /* write message and parameters */ + vrc = aClient->write(d->msgCode); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(d->guid); + if (RT_FAILURE(vrc)) break; + + /* wait for a reply */ + bool endLoop = false; + while (!endLoop) + { + SVCHlpMsg::Code reply = SVCHlpMsg::Null; + + vrc = aClient->read(reply); + if (RT_FAILURE(vrc)) break; + + switch (reply) + { + case SVCHlpMsg::OK: + { + /* no parameters */ + hrc = S_OK; + endLoop = true; + break; + } + case SVCHlpMsg::Error: + { + /* read the error message */ + Utf8Str errMsg; + vrc = aClient->read(errMsg); + if (RT_FAILURE(vrc)) break; + + hrc = E_FAIL; + d->iface->setError(E_FAIL, errMsg.c_str()); + endLoop = true; + break; + } + default: + { + endLoop = true; + hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak(( + //"Invalid message code %d (%08lX)\n", + //reply, reply), + //hrc = E_FAIL); + } + } + } + + break; + } + case SVCHlpMsg::EnableDynamicIpConfig: /* see usage in code */ + { + LogFlowFunc(("EnableDynamicIpConfig:\n")); + LogFlowFunc(("Network connection name = '%ls'\n", d->name.raw())); + + /* write message and parameters */ + vrc = aClient->write(d->msgCode); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(d->guid); + if (RT_FAILURE(vrc)) break; + + /* wait for a reply */ + bool endLoop = false; + while (!endLoop) + { + SVCHlpMsg::Code reply = SVCHlpMsg::Null; + + vrc = aClient->read(reply); + if (RT_FAILURE(vrc)) break; + + switch (reply) + { + case SVCHlpMsg::OK: + { + /* no parameters */ + hrc = d->iface->updateConfig(); + endLoop = true; + break; + } + case SVCHlpMsg::Error: + { + /* read the error message */ + Utf8Str errMsg; + vrc = aClient->read(errMsg); + if (RT_FAILURE(vrc)) break; + + hrc = E_FAIL; + d->iface->setError(E_FAIL, errMsg.c_str()); + endLoop = true; + break; + } + default: + { + endLoop = true; + hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak(( + //"Invalid message code %d (%08lX)\n", + //reply, reply), + //hrc = E_FAIL); + } + } + } + + break; + } + case SVCHlpMsg::EnableStaticIpConfig: /* see usage in code */ + { + LogFlowFunc(("EnableStaticIpConfig:\n")); + LogFlowFunc(("Network connection name = '%ls'\n", d->name.raw())); + + /* write message and parameters */ + vrc = aClient->write(d->msgCode); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(d->guid); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(d->u.StaticIP.IPAddress); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(d->u.StaticIP.IPNetMask); + if (RT_FAILURE(vrc)) break; + + /* wait for a reply */ + bool endLoop = false; + while (!endLoop) + { + SVCHlpMsg::Code reply = SVCHlpMsg::Null; + + vrc = aClient->read(reply); + if (RT_FAILURE(vrc)) break; + + switch (reply) + { + case SVCHlpMsg::OK: + { + /* no parameters */ + hrc = d->iface->updateConfig(); + endLoop = true; + break; + } + case SVCHlpMsg::Error: + { + /* read the error message */ + Utf8Str errMsg; + vrc = aClient->read(errMsg); + if (RT_FAILURE(vrc)) break; + + hrc = E_FAIL; + d->iface->setError(E_FAIL, errMsg.c_str()); + endLoop = true; + break; + } + default: + { + endLoop = true; + hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak(( + //"Invalid message code %d (%08lX)\n", + //reply, reply), + //hrc = E_FAIL); + } + } + } + + break; + } + case SVCHlpMsg::EnableStaticIpConfigV6: /* see usage in code */ + { + LogFlowFunc(("EnableStaticIpConfigV6:\n")); + LogFlowFunc(("Network connection name = '%ls'\n", d->name.raw())); + + /* write message and parameters */ + vrc = aClient->write(d->msgCode); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(d->guid); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(d->u.StaticIPV6.IPV6Address); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(d->u.StaticIPV6.IPV6NetMaskLength); + if (RT_FAILURE(vrc)) break; + + /* wait for a reply */ + bool endLoop = false; + while (!endLoop) + { + SVCHlpMsg::Code reply = SVCHlpMsg::Null; + + vrc = aClient->read(reply); + if (RT_FAILURE(vrc)) break; + + switch (reply) + { + case SVCHlpMsg::OK: + { + /* no parameters */ + hrc = d->iface->updateConfig(); + endLoop = true; + break; + } + case SVCHlpMsg::Error: + { + /* read the error message */ + Utf8Str errMsg; + vrc = aClient->read(errMsg); + if (RT_FAILURE(vrc)) break; + + hrc = E_FAIL; + d->iface->setError(E_FAIL, errMsg.c_str()); + endLoop = true; + break; + } + default: + { + endLoop = true; + hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak(( + //"Invalid message code %d (%08lX)\n", + //reply, reply), + //hrc = E_FAIL); + } + } + } + + break; + } + case SVCHlpMsg::DhcpRediscover: /* see usage in code */ + { + LogFlowFunc(("DhcpRediscover:\n")); + LogFlowFunc(("Network connection name = '%ls'\n", d->name.raw())); + + /* write message and parameters */ + vrc = aClient->write(d->msgCode); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(d->guid); + if (RT_FAILURE(vrc)) break; + + /* wait for a reply */ + bool endLoop = false; + while (!endLoop) + { + SVCHlpMsg::Code reply = SVCHlpMsg::Null; + + vrc = aClient->read(reply); + if (RT_FAILURE(vrc)) break; + + switch (reply) + { + case SVCHlpMsg::OK: + { + /* no parameters */ + hrc = d->iface->updateConfig(); + endLoop = true; + break; + } + case SVCHlpMsg::Error: + { + /* read the error message */ + Utf8Str errMsg; + vrc = aClient->read(errMsg); + if (RT_FAILURE(vrc)) break; + + hrc = E_FAIL; + d->iface->setError(E_FAIL, errMsg.c_str()); + endLoop = true; + break; + } + default: + { + endLoop = true; + hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak(( + //"Invalid message code %d (%08lX)\n", + //reply, reply), + //hrc = E_FAIL); + } + } + } + + break; + } + default: + hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak(( +// "Invalid message code %d (%08lX)\n", +// d->msgCode, d->msgCode), +// hrc = E_FAIL); + } + + if (aVrc) + *aVrc = vrc; + + LogFlowFunc(("hrc=0x%08X, vrc=%Rrc\n", hrc, vrc)); + LogFlowFuncLeave(); + return hrc; +} + + +int netIfNetworkInterfaceHelperServer(SVCHlpClient *aClient, + SVCHlpMsg::Code aMsgCode) +{ + LogFlowFuncEnter(); + LogFlowFunc(("aClient={%p}, aMsgCode=%d\n", aClient, aMsgCode)); + + AssertReturn(aClient, VERR_INVALID_POINTER); + + int vrc = VINF_SUCCESS; + HRESULT hrc; + + switch (aMsgCode) + { + case SVCHlpMsg::CreateHostOnlyNetworkInterface: + { + LogFlowFunc(("CreateHostOnlyNetworkInterface:\n")); + + Utf8Str desiredName; + vrc = aClient->read(desiredName); + if (RT_FAILURE(vrc)) break; + + Guid guid; + Utf8Str errMsg; + Bstr name; + Bstr bstrErr; + +#ifdef VBOXNETCFG_DELAYEDRENAME + Bstr devId; + hrc = VBoxNetCfgWinCreateHostOnlyNetworkInterface(NULL, false, Bstr(desiredName).raw(), guid.asOutParam(), devId.asOutParam(), + bstrErr.asOutParam()); +#else /* !VBOXNETCFG_DELAYEDRENAME */ + hrc = VBoxNetCfgWinCreateHostOnlyNetworkInterface(NULL, false, Bstr(desiredName).raw(), guid.asOutParam(), name.asOutParam(), + bstrErr.asOutParam()); +#endif /* !VBOXNETCFG_DELAYEDRENAME */ + + if (hrc == S_OK) + { + ULONG ip, mask; + hrc = VBoxNetCfgWinGenHostOnlyNetworkNetworkIp(&ip, &mask); + if (hrc == S_OK) + { + /* ip returned by VBoxNetCfgWinGenHostOnlyNetworkNetworkIp is a network ip, + * i.e. 192.168.xxx.0, assign 192.168.xxx.1 for the hostonly adapter */ + ip = ip | (1 << 24); + hrc = VBoxNetCfgWinEnableStaticIpConfig((const GUID*)guid.raw(), ip, mask); + if (hrc != S_OK) + LogRel(("VBoxNetCfgWinEnableStaticIpConfig failed (0x%x)\n", hrc)); + } + else + LogRel(("VBoxNetCfgWinGenHostOnlyNetworkNetworkIp failed (0x%x)\n", hrc)); +#ifdef VBOXNETCFG_DELAYEDRENAME + hrc = VBoxNetCfgWinRenameHostOnlyConnection((const GUID*)guid.raw(), devId.raw(), name.asOutParam()); + if (hrc != S_OK) + LogRel(("VBoxNetCfgWinRenameHostOnlyConnection failed, error = 0x%x", hrc)); +#endif /* VBOXNETCFG_DELAYEDRENAME */ + /* write success followed by GUID */ + vrc = aClient->write(SVCHlpMsg::CreateHostOnlyNetworkInterface_OK); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(Utf8Str(name)); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(guid); + if (RT_FAILURE(vrc)) break; + } + else + { + vrc = VERR_GENERAL_FAILURE; + errMsg = Utf8Str(bstrErr); + /* write failure followed by error message */ + if (errMsg.isEmpty()) + errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc); + vrc = aClient->write(SVCHlpMsg::Error); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(errMsg); + if (RT_FAILURE(vrc)) break; + } + + break; + } + case SVCHlpMsg::RemoveHostOnlyNetworkInterface: + { + LogFlowFunc(("RemoveHostOnlyNetworkInterface:\n")); + + Guid guid; + Bstr bstrErr; + + vrc = aClient->read(guid); + if (RT_FAILURE(vrc)) break; + + Utf8Str errMsg; + hrc = VBoxNetCfgWinRemoveHostOnlyNetworkInterface((const GUID*)guid.raw(), bstrErr.asOutParam()); + + if (hrc == S_OK) + { + /* write parameter-less success */ + vrc = aClient->write(SVCHlpMsg::OK); + if (RT_FAILURE(vrc)) break; + } + else + { + vrc = VERR_GENERAL_FAILURE; + errMsg = Utf8Str(bstrErr); + /* write failure followed by error message */ + if (errMsg.isEmpty()) + errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc); + vrc = aClient->write(SVCHlpMsg::Error); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(errMsg); + if (RT_FAILURE(vrc)) break; + } + + break; + } + case SVCHlpMsg::EnableStaticIpConfigV6: + { + LogFlowFunc(("EnableStaticIpConfigV6:\n")); + + Guid guid; + Utf8Str ipV6; + ULONG maskLengthV6; + vrc = aClient->read(guid); + if (RT_FAILURE(vrc)) break; + vrc = aClient->read(ipV6); + if (RT_FAILURE(vrc)) break; + vrc = aClient->read(maskLengthV6); + if (RT_FAILURE(vrc)) break; + + Utf8Str errMsg; + vrc = VERR_NOT_IMPLEMENTED; + + if (RT_SUCCESS(vrc)) + { + /* write success followed by GUID */ + vrc = aClient->write(SVCHlpMsg::OK); + if (RT_FAILURE(vrc)) break; + } + else + { + /* write failure followed by error message */ + if (errMsg.isEmpty()) + errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc); + vrc = aClient->write(SVCHlpMsg::Error); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(errMsg); + if (RT_FAILURE(vrc)) break; + } + + break; + } + case SVCHlpMsg::EnableStaticIpConfig: + { + LogFlowFunc(("EnableStaticIpConfig:\n")); + + Guid guid; + ULONG ip, mask; + vrc = aClient->read(guid); + if (RT_FAILURE(vrc)) break; + vrc = aClient->read(ip); + if (RT_FAILURE(vrc)) break; + vrc = aClient->read(mask); + if (RT_FAILURE(vrc)) break; + + Utf8Str errMsg; + hrc = VBoxNetCfgWinEnableStaticIpConfig((const GUID *)guid.raw(), ip, mask); + + if (hrc == S_OK) + { + /* write success followed by GUID */ + vrc = aClient->write(SVCHlpMsg::OK); + if (RT_FAILURE(vrc)) break; + } + else + { + vrc = VERR_GENERAL_FAILURE; + /* write failure followed by error message */ + if (errMsg.isEmpty()) + errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc); + vrc = aClient->write(SVCHlpMsg::Error); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(errMsg); + if (RT_FAILURE(vrc)) break; + } + + break; + } + case SVCHlpMsg::EnableDynamicIpConfig: + { + LogFlowFunc(("EnableDynamicIpConfig:\n")); + + Guid guid; + vrc = aClient->read(guid); + if (RT_FAILURE(vrc)) break; + + Utf8Str errMsg; + hrc = VBoxNetCfgWinEnableDynamicIpConfig((const GUID *)guid.raw()); + + if (hrc == S_OK) + { + /* write success followed by GUID */ + vrc = aClient->write(SVCHlpMsg::OK); + if (RT_FAILURE(vrc)) break; + } + else + { + vrc = VERR_GENERAL_FAILURE; + /* write failure followed by error message */ + if (errMsg.isEmpty()) + errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc); + vrc = aClient->write(SVCHlpMsg::Error); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(errMsg); + if (RT_FAILURE(vrc)) break; + } + + break; + } + case SVCHlpMsg::DhcpRediscover: + { + LogFlowFunc(("DhcpRediscover:\n")); + + Guid guid; + vrc = aClient->read(guid); + if (RT_FAILURE(vrc)) break; + + Utf8Str errMsg; + hrc = VBoxNetCfgWinDhcpRediscover((const GUID *)guid.raw()); + + if (hrc == S_OK) + { + /* write success followed by GUID */ + vrc = aClient->write(SVCHlpMsg::OK); + if (RT_FAILURE(vrc)) break; + } + else + { + vrc = VERR_GENERAL_FAILURE; + /* write failure followed by error message */ + if (errMsg.isEmpty()) + errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc); + vrc = aClient->write(SVCHlpMsg::Error); + if (RT_FAILURE(vrc)) break; + vrc = aClient->write(errMsg); + if (RT_FAILURE(vrc)) break; + } + + break; + } + default: + AssertMsgFailedBreakStmt( + ("Invalid message code %d (%08lX)\n", aMsgCode, aMsgCode), + VERR_GENERAL_FAILURE); + } + + LogFlowFunc(("vrc=%Rrc\n", vrc)); + LogFlowFuncLeave(); + return vrc; +} + +/** @todo REMOVE. OBSOLETE NOW. */ +/** + * Returns TRUE if the Windows version is 6.0 or greater (i.e. it's Vista and + * later OSes) and it has the UAC (User Account Control) feature enabled. + */ +static BOOL IsUACEnabled() +{ + OSVERSIONINFOEX info; + ZeroMemory(&info, sizeof(OSVERSIONINFOEX)); + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + BOOL frc = GetVersionEx((OSVERSIONINFO *) &info); + AssertReturn(frc != FALSE, FALSE); + + LogFlowFunc(("dwMajorVersion=%d, dwMinorVersion=%d\n", info.dwMajorVersion, info.dwMinorVersion)); + + /* we are interested only in Vista (and newer versions...). In all + * earlier versions UAC is not present. */ + if (info.dwMajorVersion < 6) + return FALSE; + + /* the default EnableLUA value is 1 (Enabled) */ + DWORD dwEnableLUA = 1; + + HKEY hKey; + LSTATUS lrc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", + 0, KEY_QUERY_VALUE, &hKey); + + Assert(lrc == ERROR_SUCCESS || lrc == ERROR_PATH_NOT_FOUND); + if (lrc == ERROR_SUCCESS) + { + + DWORD cbEnableLUA = sizeof(dwEnableLUA); + lrc = RegQueryValueExA(hKey, "EnableLUA", NULL, NULL, (LPBYTE) &dwEnableLUA, &cbEnableLUA); + + RegCloseKey(hKey); + + Assert(lrc == ERROR_SUCCESS || lrc == ERROR_FILE_NOT_FOUND); + } + + LogFlowFunc(("lrc=%d, dwEnableLUA=%d\n", lrc, dwEnableLUA)); + + return dwEnableLUA == 1; +} + +/* end */ + +static int vboxNetWinAddComponent(std::list<ComObjPtr<HostNetworkInterface> > * pPist, + INetCfgComponent * pncc, HostNetworkInterfaceType enmType, + int iDefaultInterface) +{ + int vrc = VERR_GENERAL_FAILURE; + + LPWSTR lpszName; + HRESULT hrc = pncc->GetDisplayName(&lpszName); + Assert(hrc == S_OK); + if (hrc == S_OK) + { + Bstr name(lpszName); + + GUID IfGuid; + hrc = pncc->GetInstanceGuid(&IfGuid); + Assert(hrc == S_OK); + if (hrc == S_OK) + { + Guid guidIfCopy(IfGuid); + NETIFINFO Info; + RT_ZERO(Info); + Info.Uuid = *guidIfCopy.raw(); + vrc = collectNetIfInfo(name, guidIfCopy, &Info, iDefaultInterface); + if (RT_FAILURE(vrc)) + LogRelFunc(("collectNetIfInfo() -> %Rrc\n", vrc)); + LogFunc(("adding %ls\n", lpszName)); + /* create a new object and add it to the list */ + ComObjPtr<HostNetworkInterface> iface; + iface.createObject(); + /* remove the curly bracket at the end */ + vrc = iface->init(name, enmType, &Info); + if (SUCCEEDED(vrc)) + { + if (Info.fIsDefault) + pPist->push_front(iface); + else + pPist->push_back(iface); + } + else + { + LogRelFunc(("HostNetworkInterface::init() -> %Rrc\n", vrc)); + AssertComRC(vrc); + } + } + else + LogRelFunc(("failed to get device instance GUID (0x%x)\n", hrc)); + CoTaskMemFree(lpszName); + } + else + LogRelFunc(("failed to get device display name (0x%x)\n", hrc)); + + return vrc; +} + +#endif /* VBOX_WITH_NETFLT */ + + +static int netIfListHostAdapters(INetCfg *pNc, std::list<ComObjPtr<HostNetworkInterface> > &list) +{ +#ifndef VBOX_WITH_NETFLT + /* VBoxNetAdp is available only when VBOX_WITH_NETFLT is enabled */ + return VERR_NOT_IMPLEMENTED; +#else /* # if defined VBOX_WITH_NETFLT */ + IEnumNetCfgComponent *pEnumComponent; + HRESULT hrc = pNc->EnumComponents(&GUID_DEVCLASS_NET, &pEnumComponent); + if (hrc == S_OK) + { + INetCfgComponent *pMpNcc; + while ((hrc = pEnumComponent->Next(1, &pMpNcc, NULL)) == S_OK) + { + LPWSTR pwszName; + ULONG uComponentStatus; + hrc = pMpNcc->GetDisplayName(&pwszName); + if (hrc == S_OK) + LogFunc(("%ls\n", pwszName)); + else + LogRelFunc(("failed to get device display name (0x%x)\n", hrc)); + hrc = pMpNcc->GetDeviceStatus(&uComponentStatus); + if (hrc == S_OK) + { + if (uComponentStatus == 0) + { + LPWSTR pId; + hrc = pMpNcc->GetId(&pId); + Assert(hrc == S_OK); + if (hrc == S_OK) + { + LogFunc(("id = %ls\n", pId)); + if (!_wcsnicmp(pId, L"sun_VBoxNetAdp", sizeof(L"sun_VBoxNetAdp")/2)) + vboxNetWinAddComponent(&list, pMpNcc, HostNetworkInterfaceType_HostOnly, -1); + CoTaskMemFree(pId); + } + else + LogRelFunc(("failed to get device id (0x%x)\n", hrc)); + } + } + else + LogRelFunc(("failed to get device status (0x%x)\n", hrc)); + pMpNcc->Release(); + } + Assert(hrc == S_OK || hrc == S_FALSE); + + pEnumComponent->Release(); + } + else + LogRelFunc(("EnumComponents error (0x%x)\n", hrc)); +#endif /* # if defined VBOX_WITH_NETFLT */ + return VINF_SUCCESS; +} + +int NetIfGetConfig(HostNetworkInterface * pIf, NETIFINFO *pInfo) +{ +#ifndef VBOX_WITH_NETFLT + return VERR_NOT_IMPLEMENTED; +#else + Bstr name; + HRESULT hrc = pIf->COMGETTER(Name)(name.asOutParam()); + if (hrc == S_OK) + { + Bstr IfGuid; + hrc = pIf->COMGETTER(Id)(IfGuid.asOutParam()); + Assert(hrc == S_OK); + if (hrc == S_OK) + { + memset(pInfo, 0, sizeof(NETIFINFO)); + Guid guid(IfGuid); + pInfo->Uuid = *(guid.raw()); + + return collectNetIfInfo(name, guid, pInfo, getDefaultInterfaceIndex()); + } + } + return VERR_GENERAL_FAILURE; +#endif +} + +int NetIfGetConfigByName(PNETIFINFO) +{ + return VERR_NOT_IMPLEMENTED; +} + +/** + * Obtain the current state of the interface. + * + * @returns VBox status code. + * + * @param pcszIfName Interface name. + * @param penmState Where to store the retrieved state. + */ +int NetIfGetState(const char *pcszIfName, NETIFSTATUS *penmState) +{ + RT_NOREF(pcszIfName, penmState); + return VERR_NOT_IMPLEMENTED; +} + +/** + * Retrieve the physical link speed in megabits per second. If the interface is + * not up or otherwise unavailable the zero speed is returned. + * + * @returns VBox status code. + * + * @param pcszIfName Interface name. + * @param puMbits Where to store the link speed. + */ +int NetIfGetLinkSpeed(const char *pcszIfName, uint32_t *puMbits) +{ + RT_NOREF(pcszIfName, puMbits); + return VERR_NOT_IMPLEMENTED; +} + +int NetIfCreateHostOnlyNetworkInterface(VirtualBox *pVirtualBox, + IHostNetworkInterface **aHostNetworkInterface, + IProgress **aProgress, + IN_BSTR aName) +{ +#ifndef VBOX_WITH_NETFLT + return VERR_NOT_IMPLEMENTED; +#else + /* create a progress object */ + ComObjPtr<Progress> progress; + HRESULT hrc = progress.createObject(); + AssertComRCReturn(hrc, Global::vboxStatusCodeFromCOM(hrc)); + + ComPtr<IHost> host; + hrc = pVirtualBox->COMGETTER(Host)(host.asOutParam()); + if (SUCCEEDED(hrc)) + { + hrc = progress->init(pVirtualBox, host, + Bstr(NetIfWin::tr("Creating host only network interface")).raw(), + FALSE /* aCancelable */); + if (SUCCEEDED(hrc)) + { + progress.queryInterfaceTo(aProgress); + + /* create a new uninitialized host interface object */ + ComObjPtr<HostNetworkInterface> iface; + iface.createObject(); + iface.queryInterfaceTo(aHostNetworkInterface); + + /* create the networkInterfaceHelperClient() argument */ + NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData(); + + d->msgCode = SVCHlpMsg::CreateHostOnlyNetworkInterface; + d->name = aName; + d->iface = iface; + d->ptrVBox = pVirtualBox; + + hrc = pVirtualBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */, + netIfNetworkInterfaceHelperClient, + static_cast<void *>(d), + progress); + /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */ + + } + } + + return Global::vboxStatusCodeFromCOM(hrc); +#endif +} + +int NetIfRemoveHostOnlyNetworkInterface(VirtualBox *pVirtualBox, const Guid &aId, + IProgress **aProgress) +{ +#ifndef VBOX_WITH_NETFLT + return VERR_NOT_IMPLEMENTED; +#else + /* create a progress object */ + ComObjPtr<Progress> progress; + HRESULT hrc = progress.createObject(); + AssertComRCReturn(hrc, Global::vboxStatusCodeFromCOM(hrc)); + + ComPtr<IHost> host; + hrc = pVirtualBox->COMGETTER(Host)(host.asOutParam()); + if (SUCCEEDED(hrc)) + { + hrc = progress->init(pVirtualBox, host, + Bstr(NetIfWin::tr("Removing host network interface")).raw(), + FALSE /* aCancelable */); + if (SUCCEEDED(hrc)) + { + progress.queryInterfaceTo(aProgress); + + /* create the networkInterfaceHelperClient() argument */ + NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData(); + + d->msgCode = SVCHlpMsg::RemoveHostOnlyNetworkInterface; + d->guid = aId; + + hrc = pVirtualBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */, + netIfNetworkInterfaceHelperClient, + static_cast<void *>(d), + progress); + /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */ + + } + } + + return Global::vboxStatusCodeFromCOM(hrc); +#endif +} + +int NetIfEnableStaticIpConfig(VirtualBox *pVBox, HostNetworkInterface * pIf, ULONG aOldIp, ULONG ip, ULONG mask) +{ + RT_NOREF(aOldIp); +#ifndef VBOX_WITH_NETFLT + return VERR_NOT_IMPLEMENTED; +#else + Bstr guid; + HRESULT hrc = pIf->COMGETTER(Id)(guid.asOutParam()); + if (SUCCEEDED(hrc)) + { +// ComPtr<VirtualBox> pVBox; +// hrc = pIf->getVirtualBox(pVBox.asOutParam()); +// if (SUCCEEDED(hrc)) + { + /* create a progress object */ + ComObjPtr<Progress> progress; + progress.createObject(); +// ComPtr<IHost> host; +// HRESULT hrc = pVBox->COMGETTER(Host)(host.asOutParam()); +// if (SUCCEEDED(hrc)) + { + hrc = progress->init(pVBox, (IHostNetworkInterface*)pIf, + Bstr(NetIfWin::tr("Enabling Dynamic Ip Configuration")).raw(), + FALSE /* aCancelable */); + if (SUCCEEDED(hrc)) + { + if (FAILED(hrc)) return hrc; +// progress.queryInterfaceTo(aProgress); + + /* create the networkInterfaceHelperClient() argument */ + NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData(); + + d->msgCode = SVCHlpMsg::EnableStaticIpConfig; + d->guid = Guid(guid); + d->iface = pIf; + d->u.StaticIP.IPAddress = ip; + d->u.StaticIP.IPNetMask = mask; + + hrc = pVBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */, + netIfNetworkInterfaceHelperClient, + static_cast<void *>(d), + progress); + /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */ + + if (SUCCEEDED(hrc)) + { + progress->WaitForCompletion(-1); + } + } + } + } + } + + return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; +#endif +} + +int NetIfEnableStaticIpConfigV6(VirtualBox *pVBox, HostNetworkInterface * pIf, const Utf8Str &aOldIPV6Address, + const Utf8Str &aIPV6Address, ULONG aIPV6MaskPrefixLength) +{ + RT_NOREF(aOldIPV6Address); +#ifndef VBOX_WITH_NETFLT + return VERR_NOT_IMPLEMENTED; +#else + Bstr guid; + HRESULT hrc = pIf->COMGETTER(Id)(guid.asOutParam()); + if (SUCCEEDED(hrc)) + { +// ComPtr<VirtualBox> pVBox; +// hrc = pIf->getVirtualBox(pVBox.asOutParam()); +// if (SUCCEEDED(hrc)) + { + /* create a progress object */ + ComObjPtr<Progress> progress; + progress.createObject(); +// ComPtr<IHost> host; +// HRESULT hrc = pVBox->COMGETTER(Host)(host.asOutParam()); +// if (SUCCEEDED(hrc)) + { + hrc = progress->init(pVBox, (IHostNetworkInterface*)pIf, + Bstr(NetIfWin::tr("Enabling Dynamic Ip Configuration")).raw(), + FALSE /* aCancelable */); + if (SUCCEEDED(hrc)) + { + if (FAILED(hrc)) return hrc; +// progress.queryInterfaceTo(aProgress); + + /* create the networkInterfaceHelperClient() argument */ + NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData(); + + d->msgCode = SVCHlpMsg::EnableStaticIpConfigV6; + d->guid = guid; + d->iface = pIf; + d->u.StaticIPV6.IPV6Address = RTStrDup(aIPV6Address.c_str()); + d->u.StaticIPV6.IPV6NetMaskLength = aIPV6MaskPrefixLength; + + hrc = pVBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */, + netIfNetworkInterfaceHelperClient, + static_cast<void *>(d), + progress); + /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */ + + if (SUCCEEDED(hrc)) + { + progress->WaitForCompletion(-1); + } + } + } + } + } + + return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; +#endif +} + +int NetIfEnableDynamicIpConfig(VirtualBox *pVBox, HostNetworkInterface * pIf) +{ +#ifndef VBOX_WITH_NETFLT + return VERR_NOT_IMPLEMENTED; +#else + HRESULT hrc; + Bstr guid; + hrc = pIf->COMGETTER(Id)(guid.asOutParam()); + if (SUCCEEDED(hrc)) + { +// ComPtr<VirtualBox> pVBox; +// hrc = pIf->getVirtualBox(pVBox.asOutParam()); +// if (SUCCEEDED(hrc)) + { + /* create a progress object */ + ComObjPtr<Progress> progress; + progress.createObject(); +// ComPtr<IHost> host; +// HRESULT hrc = pVBox->COMGETTER(Host)(host.asOutParam()); +// if (SUCCEEDED(hrc)) + { + hrc = progress->init(pVBox, (IHostNetworkInterface*)pIf, + Bstr(NetIfWin::tr("Enabling Dynamic Ip Configuration")).raw(), + FALSE /* aCancelable */); + if (SUCCEEDED(hrc)) + { + if (FAILED(hrc)) return hrc; +// progress.queryInterfaceTo(aProgress); + + /* create the networkInterfaceHelperClient() argument */ + NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData(); + + d->msgCode = SVCHlpMsg::EnableDynamicIpConfig; + d->guid = guid; + d->iface = pIf; + + hrc = pVBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */, + netIfNetworkInterfaceHelperClient, + static_cast<void *>(d), + progress); + /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */ + + if (SUCCEEDED(hrc)) + { + progress->WaitForCompletion(-1); + } + } + } + } + } + + return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; +#endif +} + +int NetIfDhcpRediscover(VirtualBox *pVBox, HostNetworkInterface * pIf) +{ +#ifndef VBOX_WITH_NETFLT + return VERR_NOT_IMPLEMENTED; +#else + HRESULT hrc; + Bstr guid; + hrc = pIf->COMGETTER(Id)(guid.asOutParam()); + if (SUCCEEDED(hrc)) + { +// ComPtr<VirtualBox> pVBox; +// hrc = pIf->getVirtualBox(pVBox.asOutParam()); +// if (SUCCEEDED(hrc)) + { + /* create a progress object */ + ComObjPtr<Progress> progress; + progress.createObject(); +// ComPtr<IHost> host; +// HRESULT hrc = pVBox->COMGETTER(Host)(host.asOutParam()); +// if (SUCCEEDED(hrc)) + { + hrc = progress->init(pVBox, (IHostNetworkInterface*)pIf, + Bstr(NetIfWin::tr("Enabling Dynamic Ip Configuration")).raw(), + FALSE /* aCancelable */); + if (SUCCEEDED(hrc)) + { + if (FAILED(hrc)) return hrc; +// progress.queryInterfaceTo(aProgress); + + /* create the networkInterfaceHelperClient() argument */ + NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData(); + + d->msgCode = SVCHlpMsg::DhcpRediscover; + d->guid = guid; + d->iface = pIf; + + hrc = pVBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */, + netIfNetworkInterfaceHelperClient, + static_cast<void *>(d), + progress); + /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */ + + if (SUCCEEDED(hrc)) + { + progress->WaitForCompletion(-1); + } + } + } + } + } + + return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE; +#endif +} + + +#define netIfLog LogFunc + +struct BoundAdapter +{ + LPWSTR pName; + LPWSTR pHwId; + RTUUID guid; + PIP_ADAPTER_ADDRESSES pAdapter; + BOOL fWireless; +}; + +static int netIfGetUnboundHostOnlyAdapters(INetCfg *pNetCfg, std::list<BoundAdapter> &adapters) +{ + IEnumNetCfgComponent *pEnumComponent; + HRESULT hrc = pNetCfg->EnumComponents(&GUID_DEVCLASS_NET, &pEnumComponent); + if (hrc != S_OK) + LogRelFunc(("failed to enumerate network adapter components (0x%x)\n", hrc)); + else + { + INetCfgComponent *pMiniport; + while ((hrc = pEnumComponent->Next(1, &pMiniport, NULL)) == S_OK) + { + GUID guid; + ULONG uComponentStatus; + struct BoundAdapter adapter; + memset(&adapter, 0, sizeof(adapter)); + if ((hrc = pMiniport->GetDisplayName(&adapter.pName)) != S_OK) + LogRelFunc(("failed to get device display name (0x%x)\n", hrc)); + else if ((hrc = pMiniport->GetDeviceStatus(&uComponentStatus)) != S_OK) + netIfLog(("failed to get device status (0x%x)\n", hrc)); + else if (uComponentStatus != 0) + netIfLog(("wrong device status (0x%x)\n", uComponentStatus)); + else if ((hrc = pMiniport->GetId(&adapter.pHwId)) != S_OK) + LogRelFunc(("failed to get device id (0x%x)\n", hrc)); + else if (_wcsnicmp(adapter.pHwId, L"sun_VBoxNetAdp", sizeof(L"sun_VBoxNetAdp")/2)) + netIfLog(("not host-only id = %ls, ignored\n", adapter.pHwId)); + else if ((hrc = pMiniport->GetInstanceGuid(&guid)) != S_OK) + LogRelFunc(("failed to get instance id (0x%x)\n", hrc)); + else + { + adapter.guid = *(Guid(guid).raw()); + netIfLog(("guid=%RTuuid, name=%ls id = %ls\n", &adapter.guid, adapter.pName, adapter.pHwId)); + adapters.push_back(adapter); + adapter.pName = adapter.pHwId = NULL; /* do not free, will be done later */ + } + if (adapter.pHwId) + CoTaskMemFree(adapter.pHwId); + if (adapter.pName) + CoTaskMemFree(adapter.pName); + pMiniport->Release(); + } + Assert(hrc == S_OK || hrc == S_FALSE); + + pEnumComponent->Release(); + } + netIfLog(("return\n")); + return VINF_SUCCESS; +} + +#define DEVNAME_PREFIX L"\\\\.\\" + +static BOOL netIfIsWireless(INetCfgComponent *pAdapter) +{ + bool fWireless = false; + + /* Construct a device name. */ + LPWSTR pwszBindName = NULL; + HRESULT hrc = pAdapter->GetBindName(&pwszBindName); + if (SUCCEEDED(hrc) && pwszBindName) + { + WCHAR wszFileName[MAX_PATH]; + int vrc = RTUtf16Copy(wszFileName, MAX_PATH, DEVNAME_PREFIX); + if (RT_SUCCESS(vrc)) + vrc = RTUtf16Cat(wszFileName, MAX_PATH, pwszBindName); + if (RT_SUCCESS(vrc)) + { + /* open the device */ + HANDLE hDevice = CreateFileW(wszFileName, + GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hDevice != INVALID_HANDLE_VALUE) + { + /* now issue the OID_GEN_PHYSICAL_MEDIUM query */ + DWORD Oid = OID_GEN_PHYSICAL_MEDIUM; + NDIS_PHYSICAL_MEDIUM PhMedium = NdisPhysicalMediumUnspecified; + DWORD cbResultIgn = 0; + if (DeviceIoControl(hDevice, + IOCTL_NDIS_QUERY_GLOBAL_STATS, + &Oid, + sizeof(Oid), + &PhMedium, + sizeof(PhMedium), + &cbResultIgn, + NULL)) + { + /* that was simple, now examine PhMedium */ + fWireless = PhMedium == NdisPhysicalMediumWirelessWan + || PhMedium == NdisPhysicalMediumWirelessLan + || PhMedium == NdisPhysicalMediumNative802_11 + || PhMedium == NdisPhysicalMediumBluetooth; + } + else + { + DWORD rcWin = GetLastError(); + LogRel(("netIfIsWireless: DeviceIoControl to '%ls' failed with rcWin=%u (%#x) - ignoring\n", + wszFileName, rcWin, rcWin)); + Assert(rcWin == ERROR_INVALID_PARAMETER || rcWin == ERROR_NOT_SUPPORTED || rcWin == ERROR_BAD_COMMAND); + } + CloseHandle(hDevice); + } + else + { + DWORD rcWin = GetLastError(); +#if 0 /* bird: Triggers on each VBoxSVC startup so, disabled. Whoever want it, can enable using DEBUG_xxxx. */ + AssertLogRelMsgFailed(("netIfIsWireless: CreateFile on '%ls' failed with rcWin=%u (%#x) - ignoring\n", + wszFileName, rcWin, rcWin)); +#else + LogRel(("netIfIsWireless: CreateFile on '%ls' failed with rcWin=%u (%#x) - ignoring\n", + wszFileName, rcWin, rcWin)); +#endif + } + } + CoTaskMemFree(pwszBindName); + } + else + LogRel(("netIfIsWireless: GetBindName failed hrc=%Rhrc\n", hrc)); + + return fWireless; +} + +static HRESULT netIfGetBoundAdapters(std::list<BoundAdapter> &boundAdapters) +{ + + netIfLog(("building the list of interfaces\n")); + /* we are using the INetCfg API for getting the list of miniports */ + INetCfg *pNetCfg = NULL; + LPWSTR lpszApp = NULL; + HRESULT hrc = VBoxNetCfgWinQueryINetCfg(&pNetCfg, FALSE, VBOX_APP_NAME, 10000, &lpszApp); + Assert(hrc == S_OK); + if (hrc != S_OK) + { + LogRelFunc(("failed to query INetCfg (0x%x)\n", hrc)); + return hrc; + } + + INetCfgComponent *pFilter; + if ((hrc = pNetCfg->FindComponent(L"oracle_VBoxNetLwf", &pFilter)) != S_OK + /* fall back to NDIS5 miniport lookup */ + && (hrc = pNetCfg->FindComponent(L"sun_VBoxNetFlt", &pFilter))) + LogRelFunc(("could not find either 'oracle_VBoxNetLwf' or 'sun_VBoxNetFlt' components (0x%x)\n", hrc)); + else + { + INetCfgComponentBindings *pFilterBindings; + if ((pFilter->QueryInterface(IID_INetCfgComponentBindings, (PVOID*)&pFilterBindings)) != S_OK) + LogRelFunc(("failed to query INetCfgComponentBindings (0x%x)\n", hrc)); + else + { + IEnumNetCfgBindingPath *pEnumBp; + INetCfgBindingPath *pBp; + if ((pFilterBindings->EnumBindingPaths(EBP_BELOW, &pEnumBp)) != S_OK) + LogRelFunc(("failed to enumerate binding paths (0x%x)\n", hrc)); + else + { + pEnumBp->Reset(); + while ((hrc = pEnumBp->Next(1, &pBp, NULL)) == S_OK) + { + IEnumNetCfgBindingInterface *pEnumBi; + INetCfgBindingInterface *pBi; + if (pBp->IsEnabled() != S_OK) + { + /** @todo some id of disabled path could be useful. */ + netIfLog(("INetCfgBindingPath is disabled (0x%x)\n", hrc)); + pBp->Release(); + continue; + } + if ((pBp->EnumBindingInterfaces(&pEnumBi)) != S_OK) + LogRelFunc(("failed to enumerate binding interfaces (0x%x)\n", hrc)); + else + { + hrc = pEnumBi->Reset(); + while ((hrc = pEnumBi->Next(1, &pBi, NULL)) == S_OK) + { + INetCfgComponent *pAdapter; + if ((hrc = pBi->GetLowerComponent(&pAdapter)) != S_OK) + LogRelFunc(("failed to get lower component (0x%x)\n", hrc)); + else + { + LPWSTR pwszName = NULL; + if ((hrc = pAdapter->GetDisplayName(&pwszName)) != S_OK) + LogRelFunc(("failed to get display name (0x%x)\n", hrc)); + else + { + ULONG uStatus; + DWORD dwChars; + if ((hrc = pAdapter->GetDeviceStatus(&uStatus)) != S_OK) + netIfLog(("%ls: failed to get device status (0x%x)\n", + pwszName, hrc)); + else if ((hrc = pAdapter->GetCharacteristics(&dwChars)) != S_OK) + netIfLog(("%ls: failed to get device characteristics (0x%x)\n", + pwszName, hrc)); + else if (uStatus != 0) + netIfLog(("%ls: wrong status 0x%x\n", + pwszName, uStatus)); + else if (dwChars & NCF_HIDDEN) + netIfLog(("%ls: wrong characteristics 0x%x\n", + pwszName, dwChars)); + else + { + GUID guid; + LPWSTR pwszHwId = NULL; + if ((hrc = pAdapter->GetId(&pwszHwId)) != S_OK) + LogRelFunc(("%ls: failed to get hardware id (0x%x)\n", + pwszName, hrc)); + else if (!_wcsnicmp(pwszHwId, L"sun_VBoxNetAdp", sizeof(L"sun_VBoxNetAdp")/2)) + netIfLog(("host-only adapter %ls, ignored\n", pwszName)); + else if ((hrc = pAdapter->GetInstanceGuid(&guid)) != S_OK) + LogRelFunc(("%ls: failed to get instance GUID (0x%x)\n", + pwszName, hrc)); + else + { + struct BoundAdapter adapter; + adapter.pName = pwszName; + adapter.pHwId = pwszHwId; + adapter.guid = *(Guid(guid).raw()); + adapter.pAdapter = NULL; + adapter.fWireless = netIfIsWireless(pAdapter); + netIfLog(("guid=%RTuuid, name=%ls, hwid=%ls, status=%x, chars=%x\n", + &adapter.guid, pwszName, pwszHwId, uStatus, dwChars)); + boundAdapters.push_back(adapter); + pwszName = pwszHwId = NULL; /* do not free, will be done later */ + } + if (pwszHwId) + CoTaskMemFree(pwszHwId); + } + if (pwszName) + CoTaskMemFree(pwszName); + } + + pAdapter->Release(); + } + pBi->Release(); + } + pEnumBi->Release(); + } + pBp->Release(); + } + pEnumBp->Release(); + } + pFilterBindings->Release(); + } + pFilter->Release(); + } + /* Host-only adapters are not necessarily bound, add them separately. */ + netIfGetUnboundHostOnlyAdapters(pNetCfg, boundAdapters); + VBoxNetCfgWinReleaseINetCfg(pNetCfg, FALSE); + + return S_OK; +} + +#if 0 +static HRESULT netIfGetBoundAdaptersFallback(std::list<BoundAdapter> &boundAdapters) +{ + return CO_E_NOT_SUPPORTED; +} +#endif + +/** + * Walk through the list of adpater addresses and extract the required + * information. XP and older don't not have the OnLinkPrefixLength field. + */ +static void netIfFillInfoWithAddressesXp(PNETIFINFO pInfo, PIP_ADAPTER_ADDRESSES pAdapter) +{ + PIP_ADAPTER_UNICAST_ADDRESS pAddr; + bool fIPFound = false; + bool fIPv6Found = false; + for (pAddr = pAdapter->FirstUnicastAddress; pAddr; pAddr = pAddr->Next) + { + switch (pAddr->Address.lpSockaddr->sa_family) + { + case AF_INET: + if (!fIPFound) + { + fIPFound = true; + memcpy(&pInfo->IPAddress, + &((struct sockaddr_in *)pAddr->Address.lpSockaddr)->sin_addr.s_addr, + sizeof(pInfo->IPAddress)); + } + break; + case AF_INET6: + if (!fIPv6Found) + { + fIPv6Found = true; + memcpy(&pInfo->IPv6Address, + ((struct sockaddr_in6 *)pAddr->Address.lpSockaddr)->sin6_addr.s6_addr, + sizeof(pInfo->IPv6Address)); + } + break; + } + } + PIP_ADAPTER_PREFIX pPrefix; + ULONG uPrefixLenV4 = 0; + ULONG uPrefixLenV6 = 0; + for (pPrefix = pAdapter->FirstPrefix; pPrefix && !(uPrefixLenV4 && uPrefixLenV6); pPrefix = pPrefix->Next) + { + switch (pPrefix->Address.lpSockaddr->sa_family) + { + case AF_INET: + if (!uPrefixLenV4) + { + ULONG ip = ((PSOCKADDR_IN)(pPrefix->Address.lpSockaddr))->sin_addr.s_addr; + netIfLog(("prefix=%RTnaipv4 len=%u\n", ip, pPrefix->PrefixLength)); + if ( pPrefix->PrefixLength < sizeof(pInfo->IPNetMask) * 8 + && pPrefix->PrefixLength > 0 + && (ip & 0xF0) < 224) + { + uPrefixLenV4 = pPrefix->PrefixLength; + RTNetPrefixToMaskIPv4(pPrefix->PrefixLength, &pInfo->IPNetMask); + } + else + netIfLog(("Unexpected IPv4 prefix length of %d\n", + pPrefix->PrefixLength)); + } + break; + case AF_INET6: + if (!uPrefixLenV6) + { + PBYTE ipv6 = ((PSOCKADDR_IN6)(pPrefix->Address.lpSockaddr))->sin6_addr.s6_addr; + netIfLog(("prefix=%RTnaipv6 len=%u\n", ipv6, pPrefix->PrefixLength)); + if ( pPrefix->PrefixLength < sizeof(pInfo->IPv6NetMask) * 8 + && pPrefix->PrefixLength > 0 + && ipv6[0] != 0xFF) + { + uPrefixLenV6 = pPrefix->PrefixLength; + RTNetPrefixToMaskIPv6(pPrefix->PrefixLength, &pInfo->IPv6NetMask); + } + else + netIfLog(("Unexpected IPv6 prefix length of %d\n", pPrefix->PrefixLength)); + } + break; + } + } + netIfLog(("%RTnaipv4/%u\n", pInfo->IPAddress, uPrefixLenV4)); + netIfLog(("%RTnaipv6/%u\n", &pInfo->IPv6Address, uPrefixLenV6)); +} + +/** + * Walk through the list of adpater addresses and extract the required + * information. XP and older don't not have the OnLinkPrefixLength field. + */ +static void netIfFillInfoWithAddressesVista(PNETIFINFO pInfo, PIP_ADAPTER_ADDRESSES pAdapter) +{ + PIP_ADAPTER_UNICAST_ADDRESS pAddr; + + if (sizeof(pInfo->MACAddress) != pAdapter->PhysicalAddressLength) + netIfLog(("Unexpected physical address length: %u\n", pAdapter->PhysicalAddressLength)); + else + memcpy(pInfo->MACAddress.au8, pAdapter->PhysicalAddress, sizeof(pInfo->MACAddress)); + + bool fIPFound = false; + bool fIPv6Found = false; + for (pAddr = pAdapter->FirstUnicastAddress; pAddr; pAddr = pAddr->Next) + { + PIP_ADAPTER_UNICAST_ADDRESS_LH pAddrLh = (PIP_ADAPTER_UNICAST_ADDRESS_LH)pAddr; + switch (pAddrLh->Address.lpSockaddr->sa_family) + { + case AF_INET: + if (!fIPFound) + { + fIPFound = true; + memcpy(&pInfo->IPAddress, + &((struct sockaddr_in *)pAddrLh->Address.lpSockaddr)->sin_addr.s_addr, + sizeof(pInfo->IPAddress)); + if (pAddrLh->OnLinkPrefixLength > 32) + netIfLog(("Invalid IPv4 prefix length of %d\n", pAddrLh->OnLinkPrefixLength)); + else + RTNetPrefixToMaskIPv4(pAddrLh->OnLinkPrefixLength, &pInfo->IPNetMask); + } + break; + case AF_INET6: + if (!fIPv6Found) + { + fIPv6Found = true; + memcpy(&pInfo->IPv6Address, + ((struct sockaddr_in6 *)pAddrLh->Address.lpSockaddr)->sin6_addr.s6_addr, + sizeof(pInfo->IPv6Address)); + if (pAddrLh->OnLinkPrefixLength > 128) + netIfLog(("Invalid IPv6 prefix length of %d\n", pAddrLh->OnLinkPrefixLength)); + else + RTNetPrefixToMaskIPv6(pAddrLh->OnLinkPrefixLength, &pInfo->IPv6NetMask); + } + break; + } + } + + if (fIPFound) + { + int iPrefixIPv4 = -1; + RTNetMaskToPrefixIPv4(&pInfo->IPNetMask, &iPrefixIPv4); + netIfLog(("%RTnaipv4/%u\n", pInfo->IPAddress, iPrefixIPv4)); + } + if (fIPv6Found) + { + int iPrefixIPv6 = -1; + RTNetMaskToPrefixIPv6(&pInfo->IPv6NetMask, &iPrefixIPv6); + netIfLog(("%RTnaipv6/%u\n", &pInfo->IPv6Address, iPrefixIPv6)); + } +} + +#if (NTDDI_VERSION >= NTDDI_VISTA) +#define NETIF_GAA_FLAGS GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST +#else /* (NTDDI_VERSION < NTDDI_VISTA) */ +#define NETIF_GAA_FLAGS GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST +#endif /* (NTDDI_VERSION < NTDDI_VISTA) */ + +int NetIfList(std::list<ComObjPtr<HostNetworkInterface> > &list) +{ + int iDefault = getDefaultInterfaceIndex(); + /* MSDN recommends to pre-allocate a 15KB buffer. */ + ULONG uBufLen = 15 * 1024; + PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAlloc(uBufLen); + if (!pAddresses) + return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + DWORD dwRc = GetAdaptersAddresses(AF_UNSPEC, NETIF_GAA_FLAGS, NULL, pAddresses, &uBufLen); + for (int tries = 0; tries < 3 && dwRc == ERROR_BUFFER_OVERFLOW; ++tries) + { + /* Get more memory and try again. */ + RTMemFree(pAddresses); + pAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAlloc(uBufLen); + if (!pAddresses) + return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + dwRc = GetAdaptersAddresses(AF_UNSPEC, NETIF_GAA_FLAGS, NULL, pAddresses, &uBufLen); + } + HRESULT hrc; + if (dwRc != NO_ERROR) + { + LogRelFunc(("GetAdaptersAddresses failed (0x%x)\n", dwRc)); + hrc = HRESULT_FROM_WIN32(dwRc); + } + else + { + std::list<BoundAdapter> boundAdapters; + hrc = netIfGetBoundAdapters(boundAdapters); +#if 0 + if (hrc != S_OK) + hrc = netIfGetBoundAdaptersFallback(boundAdapters); +#endif + if (hrc != S_OK) + LogRelFunc(("netIfGetBoundAdapters failed (0x%x)\n", hrc)); + else + { + PIP_ADAPTER_ADDRESSES pAdapter; + + for (pAdapter = pAddresses; pAdapter; pAdapter = pAdapter->Next) + { + char *pszUuid = RTStrDup(pAdapter->AdapterName); + if (!pszUuid) + { + LogRelFunc(("out of memory\n")); + break; + } + size_t len = strlen(pszUuid) - 1; + if (pszUuid[0] != '{' || pszUuid[len] != '}') + LogRelFunc(("ignoring invalid GUID %s\n", pAdapter->AdapterName)); + else + { + std::list<BoundAdapter>::iterator it; + pszUuid[len] = 0; + for (it = boundAdapters.begin(); it != boundAdapters.end(); ++it) + { + if (!RTUuidCompareStr(&(*it).guid, pszUuid + 1)) + { + (*it).pAdapter = pAdapter; + break; + } + } + } + RTStrFree(pszUuid); + } + std::list<BoundAdapter>::iterator it; + for (it = boundAdapters.begin(); it != boundAdapters.end(); ++it) + { + NETIFINFO info; + memset(&info, 0, sizeof(info)); + info.Uuid = (*it).guid; + info.enmMediumType = NETIF_T_ETHERNET; + info.fWireless = (*it).fWireless; + pAdapter = (*it).pAdapter; + if (pAdapter) + { + info.enmStatus = pAdapter->OperStatus == IfOperStatusUp ? NETIF_S_UP : NETIF_S_DOWN; + info.fIsDefault = (pAdapter->IfIndex == (DWORD)iDefault); + info.fDhcpEnabled = pAdapter->Flags & IP_ADAPTER_DHCP_ENABLED; + OSVERSIONINFOEX OSInfoEx; + RT_ZERO(OSInfoEx); + OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if ( GetVersionEx((LPOSVERSIONINFO)&OSInfoEx) + && OSInfoEx.dwMajorVersion < 6) + netIfFillInfoWithAddressesXp(&info, pAdapter); + else + netIfFillInfoWithAddressesVista(&info, pAdapter); + } + else + info.enmStatus = NETIF_S_DOWN; + /* create a new object and add it to the list */ + ComObjPtr<HostNetworkInterface> iface; + iface.createObject(); + HostNetworkInterfaceType enmType = _wcsnicmp((*it).pHwId, L"sun_VBoxNetAdp", sizeof(L"sun_VBoxNetAdp") / 2) + ? HostNetworkInterfaceType_Bridged : HostNetworkInterfaceType_HostOnly; + netIfLog(("Adding %ls as %s\n", (*it).pName, + enmType == HostNetworkInterfaceType_Bridged ? "bridged" : + enmType == HostNetworkInterfaceType_HostOnly ? "host-only" : "unknown")); + hrc = iface->init((*it).pName, enmType, &info); + if (FAILED(hrc)) + LogRelFunc(("HostNetworkInterface::init() -> %Rrc\n", hrc)); + else + { + if (info.fIsDefault) + list.push_front(iface); + else + list.push_back(iface); + } + if ((*it).pHwId) + CoTaskMemFree((*it).pHwId); + if ((*it).pName) + CoTaskMemFree((*it).pName); + } + } + } + RTMemFree(pAddresses); + + return hrc; +} diff --git a/src/VBox/Main/src-server/win/PerformanceWin.cpp b/src/VBox/Main/src-server/win/PerformanceWin.cpp new file mode 100644 index 00000000..c7ae3335 --- /dev/null +++ b/src/VBox/Main/src-server/win/PerformanceWin.cpp @@ -0,0 +1,356 @@ +/* $Id: PerformanceWin.cpp $ */ +/** @file + * VBox Windows-specific Performance Classes implementation. + */ + +/* + * Copyright (C) 2008-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#define LOG_GROUP LOG_GROUP_MAIN_PERFORMANCECOLLECTOR +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0500 +#else /* !_WIN32_WINNT */ +# if (_WIN32_WINNT < 0x0500) +# error Win XP or later required! +# endif /* _WIN32_WINNT < 0x0500 */ +#endif /* !_WIN32_WINNT */ + +#include <iprt/win/windows.h> +#include <winternl.h> +#include <psapi.h> +extern "C" { +#include <powrprof.h> +} + +#include <iprt/errcore.h> +#include <iprt/ldr.h> +#include <iprt/mp.h> +#include <iprt/mem.h> +#include <iprt/system.h> + +#include <map> + +#include "LoggingNew.h" +#include "Performance.h" + +#ifndef NT_ERROR +#define NT_ERROR(Status) ((ULONG)(Status) >> 30 == 3) +#endif + +namespace pm { + +class CollectorWin : public CollectorHAL +{ +public: + CollectorWin(); + virtual ~CollectorWin(); + virtual int preCollect(const CollectorHints& hints, uint64_t /* iTick */); + virtual int getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle); + virtual int getHostCpuMHz(ULONG *mhz); + virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available); + virtual int getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel); + virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used); + + virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle); + virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total); + +private: + struct VMProcessStats + { + uint64_t cpuUser; + uint64_t cpuKernel; + uint64_t cpuTotal; + uint64_t ramUsed; + }; + + typedef std::map<RTPROCESS, VMProcessStats> VMProcessMap; + + VMProcessMap mProcessStats; + + typedef BOOL (WINAPI *PFNGST)(LPFILETIME lpIdleTime, + LPFILETIME lpKernelTime, + LPFILETIME lpUserTime); + typedef NTSTATUS (WINAPI *PFNNQSI)(SYSTEM_INFORMATION_CLASS SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength); + + PFNGST mpfnGetSystemTimes; + PFNNQSI mpfnNtQuerySystemInformation; + + ULONG totalRAM; +}; + +CollectorHAL *createHAL() +{ + return new CollectorWin(); +} + +CollectorWin::CollectorWin() : CollectorHAL(), mpfnNtQuerySystemInformation(NULL) +{ + /* Note! Both kernel32.dll and ntdll.dll can be assumed to always be present. */ + mpfnGetSystemTimes = (PFNGST)RTLdrGetSystemSymbol("kernel32.dll", "GetSystemTimes"); + if (!mpfnGetSystemTimes) + { + /* Fall back to deprecated NtQuerySystemInformation */ + mpfnNtQuerySystemInformation = (PFNNQSI)RTLdrGetSystemSymbol("ntdll.dll", "NtQuerySystemInformation"); + if (!mpfnNtQuerySystemInformation) + LogRel(("Warning! Neither GetSystemTimes() nor NtQuerySystemInformation() is not available.\n" + " CPU and VM metrics will not be collected! (lasterr %u)\n", GetLastError())); + } + + uint64_t cb; + int vrc = RTSystemQueryTotalRam(&cb); + if (RT_FAILURE(vrc)) + totalRAM = 0; + else + totalRAM = (ULONG)(cb / 1024); +} + +CollectorWin::~CollectorWin() +{ +} + +#define FILETTIME_TO_100NS(ft) (((uint64_t)ft.dwHighDateTime << 32) + ft.dwLowDateTime) + +int CollectorWin::preCollect(const CollectorHints& hints, uint64_t /* iTick */) +{ + LogFlowThisFuncEnter(); + + uint64_t user, kernel, idle, total; + int vrc = getRawHostCpuLoad(&user, &kernel, &idle); + if (RT_FAILURE(vrc)) + return vrc; + total = user + kernel + idle; + + DWORD dwError; + const CollectorHints::ProcessList& processes = hints.getProcessFlags(); + CollectorHints::ProcessList::const_iterator it; + + mProcessStats.clear(); + + for (it = processes.begin(); it != processes.end() && RT_SUCCESS(vrc); ++it) + { + RTPROCESS process = it->first; + HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process); + + if (!h) + { + dwError = GetLastError(); + Log (("OpenProcess() -> 0x%x\n", dwError)); + vrc = RTErrConvertFromWin32(dwError); + break; + } + + VMProcessStats vmStats; + RT_ZERO(vmStats); + if ((it->second & COLLECT_CPU_LOAD) != 0) + { + FILETIME ftCreate, ftExit, ftKernel, ftUser; + if (!GetProcessTimes(h, &ftCreate, &ftExit, &ftKernel, &ftUser)) + { + dwError = GetLastError(); + Log (("GetProcessTimes() -> 0x%x\n", dwError)); + vrc = RTErrConvertFromWin32(dwError); + } + else + { + vmStats.cpuKernel = FILETTIME_TO_100NS(ftKernel); + vmStats.cpuUser = FILETTIME_TO_100NS(ftUser); + vmStats.cpuTotal = total; + } + } + if (RT_SUCCESS(vrc) && (it->second & COLLECT_RAM_USAGE) != 0) + { + PROCESS_MEMORY_COUNTERS pmc; + if (!GetProcessMemoryInfo(h, &pmc, sizeof(pmc))) + { + dwError = GetLastError(); + Log (("GetProcessMemoryInfo() -> 0x%x\n", dwError)); + vrc = RTErrConvertFromWin32(dwError); + } + else + vmStats.ramUsed = pmc.WorkingSetSize; + } + CloseHandle(h); + mProcessStats[process] = vmStats; + } + + LogFlowThisFuncLeave(); + + return vrc; +} + +int CollectorWin::getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle) +{ + RT_NOREF(user, kernel, idle); + return VERR_NOT_IMPLEMENTED; +} + +typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION +{ + LARGE_INTEGER IdleTime; + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER Reserved1[2]; + ULONG Reserved2; +} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; + +int CollectorWin::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle) +{ + LogFlowThisFuncEnter(); + + FILETIME ftIdle, ftKernel, ftUser; + + if (mpfnGetSystemTimes) + { + if (!mpfnGetSystemTimes(&ftIdle, &ftKernel, &ftUser)) + { + DWORD dwError = GetLastError(); + Log (("GetSystemTimes() -> 0x%x\n", dwError)); + return RTErrConvertFromWin32(dwError); + } + + *user = FILETTIME_TO_100NS(ftUser); + *idle = FILETTIME_TO_100NS(ftIdle); + *kernel = FILETTIME_TO_100NS(ftKernel) - *idle; + } + else + { + /* GetSystemTimes is not available, fall back to NtQuerySystemInformation */ + if (!mpfnNtQuerySystemInformation) + return VERR_NOT_IMPLEMENTED; + + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION sppi[MAXIMUM_PROCESSORS]; + ULONG ulReturned; + NTSTATUS status = mpfnNtQuerySystemInformation( + SystemProcessorPerformanceInformation, &sppi, sizeof(sppi), &ulReturned); + if (NT_ERROR(status)) + { + Log(("NtQuerySystemInformation() -> 0x%x\n", status)); + return RTErrConvertFromNtStatus(status); + } + /* Sum up values across all processors */ + *user = *kernel = *idle = 0; + for (unsigned i = 0; i < ulReturned / sizeof(sppi[0]); ++i) + { + *idle += sppi[i].IdleTime.QuadPart; + *kernel += sppi[i].KernelTime.QuadPart - sppi[i].IdleTime.QuadPart; + *user += sppi[i].UserTime.QuadPart; + } + } + + LogFlowThisFunc(("user=%lu kernel=%lu idle=%lu\n", *user, *kernel, *idle)); + LogFlowThisFuncLeave(); + + return VINF_SUCCESS; +} + +typedef struct _PROCESSOR_POWER_INFORMATION { + ULONG Number; + ULONG MaxMhz; + ULONG CurrentMhz; + ULONG MhzLimit; + ULONG MaxIdleState; + ULONG CurrentIdleState; +} PROCESSOR_POWER_INFORMATION , *PPROCESSOR_POWER_INFORMATION; + +int CollectorWin::getHostCpuMHz(ULONG *mhz) +{ + uint64_t uTotalMhz = 0; + RTCPUID nProcessors = RTMpGetCount(); + PPROCESSOR_POWER_INFORMATION ppi = (PPROCESSOR_POWER_INFORMATION) + RTMemAllocZ(nProcessors * sizeof(PROCESSOR_POWER_INFORMATION)); + + if (!ppi) + return VERR_NO_MEMORY; + + LONG ns = CallNtPowerInformation(ProcessorInformation, NULL, 0, ppi, + nProcessors * sizeof(PROCESSOR_POWER_INFORMATION)); + if (ns) + { + Log(("CallNtPowerInformation() -> %x\n", ns)); + RTMemFree(ppi); + return VERR_INTERNAL_ERROR; + } + + /* Compute an average over all CPUs */ + for (unsigned i = 0; i < nProcessors; i++) + uTotalMhz += ppi[i].CurrentMhz; + *mhz = (ULONG)(uTotalMhz / nProcessors); + + RTMemFree(ppi); + LogFlowThisFunc(("mhz=%u\n", *mhz)); + LogFlowThisFuncLeave(); + + return VINF_SUCCESS; +} + +int CollectorWin::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available) +{ + AssertReturn(totalRAM, VERR_INTERNAL_ERROR); + uint64_t cb; + int vrc = RTSystemQueryAvailableRam(&cb); + if (RT_SUCCESS(vrc)) + { + *total = totalRAM; + *available = (ULONG)(cb / 1024); + *used = *total - *available; + } + return vrc; +} + +int CollectorWin::getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel) +{ + RT_NOREF(process, user, kernel); + return VERR_NOT_IMPLEMENTED; +} + +int CollectorWin::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total) +{ + VMProcessMap::const_iterator it = mProcessStats.find(process); + + if (it == mProcessStats.end()) + { + Log (("No stats pre-collected for process %x\n", process)); + return VERR_INTERNAL_ERROR; + } + *user = it->second.cpuUser; + *kernel = it->second.cpuKernel; + *total = it->second.cpuTotal; + return VINF_SUCCESS; +} + +int CollectorWin::getProcessMemoryUsage(RTPROCESS process, ULONG *used) +{ + VMProcessMap::const_iterator it = mProcessStats.find(process); + + if (it == mProcessStats.end()) + { + Log (("No stats pre-collected for process %x\n", process)); + return VERR_INTERNAL_ERROR; + } + *used = (ULONG)(it->second.ramUsed / 1024); + return VINF_SUCCESS; +} + +} diff --git a/src/VBox/Main/src-server/win/USBProxyBackendWindows.cpp b/src/VBox/Main/src-server/win/USBProxyBackendWindows.cpp new file mode 100644 index 00000000..c76671f0 --- /dev/null +++ b/src/VBox/Main/src-server/win/USBProxyBackendWindows.cpp @@ -0,0 +1,274 @@ +/* $Id: USBProxyBackendWindows.cpp $ */ +/** @file + * VirtualBox USB Proxy Service, Windows Specialization. + */ + +/* + * Copyright (C) 2005-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN_USBPROXYBACKEND +#include "USBProxyBackend.h" +#include "LoggingNew.h" + +#include <VBox/usb.h> +#include <iprt/errcore.h> + +#include <iprt/string.h> +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/file.h> +#include <iprt/errcore.h> + +#include <VBox/usblib.h> + + +/** + * Initialize data members. + */ +USBProxyBackendWindows::USBProxyBackendWindows() + : USBProxyBackend(), mhEventInterrupt(INVALID_HANDLE_VALUE) +{ + LogFlowThisFunc(("\n")); +} + +USBProxyBackendWindows::~USBProxyBackendWindows() +{ +} + +/** + * Initializes the object (called right after construction). + * + * @returns S_OK on success and non-fatal failures, some COM error otherwise. + */ +int USBProxyBackendWindows::init(USBProxyService *aUsbProxyService, const com::Utf8Str &strId, + const com::Utf8Str &strAddress, bool fLoadingSettings) +{ + USBProxyBackend::init(aUsbProxyService, strId, strAddress, fLoadingSettings); + + unconst(m_strBackend) = Utf8Str("host"); + + /* + * Create the semaphore (considered fatal). + */ + mhEventInterrupt = CreateEvent(NULL, FALSE, FALSE, NULL); + AssertReturn(mhEventInterrupt != INVALID_HANDLE_VALUE, VERR_OUT_OF_RESOURCES); + + /* + * Initialize the USB lib and stuff. + */ + int vrc = USBLibInit(); + if (RT_SUCCESS(vrc)) + { + /* + * Start the poller thread. + */ + vrc = start(); + if (RT_SUCCESS(vrc)) + { + LogFlowThisFunc(("returns successfully\n")); + return VINF_SUCCESS; + } + + USBLibTerm(); + } + + CloseHandle(mhEventInterrupt); + mhEventInterrupt = INVALID_HANDLE_VALUE; + + LogFlowThisFunc(("returns failure!!! (vrc=%Rrc)\n", vrc)); + return vrc; +} + + +/** + * Stop all service threads and free the device chain. + */ +void USBProxyBackendWindows::uninit() +{ + LogFlowThisFunc(("\n")); + + /* + * Stop the service. + */ + if (isActive()) + stop(); + + if (mhEventInterrupt != INVALID_HANDLE_VALUE) + CloseHandle(mhEventInterrupt); + mhEventInterrupt = INVALID_HANDLE_VALUE; + + /* + * Terminate the library... + */ + int vrc = USBLibTerm(); + AssertRC(vrc); + USBProxyBackend::uninit(); +} + + +void *USBProxyBackendWindows::insertFilter(PCUSBFILTER aFilter) +{ + AssertReturn(aFilter, NULL); + + LogFlow(("USBProxyBackendWindows::insertFilter()\n")); + + void *pvId = USBLibAddFilter(aFilter); + + LogFlow(("USBProxyBackendWindows::insertFilter(): returning pvId=%p\n", pvId)); + + return pvId; +} + + +void USBProxyBackendWindows::removeFilter(void *aID) +{ + LogFlow(("USBProxyBackendWindows::removeFilter(): id=%p\n", aID)); + + AssertReturnVoid(aID); + + USBLibRemoveFilter(aID); +} + + +int USBProxyBackendWindows::captureDevice(HostUSBDevice *aDevice) +{ + /* + * Check preconditions. + */ + AssertReturn(aDevice, VERR_GENERAL_FAILURE); + AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE); + + AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS); + LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str())); + + Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing); + + /* + * Create a one-shot ignore filter for the device + * and trigger a re-enumeration of it. + */ + USBFILTER Filter; + USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_CAPTURE); + initFilterFromDevice(&Filter, aDevice); + Log(("USBFILTERIDX_PORT=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_PORT))); + Log(("USBFILTERIDX_BUS=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_BUS))); + + void *pvId = USBLibAddFilter(&Filter); + if (!pvId) + { + AssertMsgFailed(("Add one-shot Filter failed\n")); + return VERR_GENERAL_FAILURE; + } + + int vrc = USBLibRunFilters(); + if (!RT_SUCCESS(vrc)) + { + AssertMsgFailed(("Run Filters failed\n")); + USBLibRemoveFilter(pvId); + return vrc; + } + + return VINF_SUCCESS; +} + + +int USBProxyBackendWindows::releaseDevice(HostUSBDevice *aDevice) +{ + /* + * Check preconditions. + */ + AssertReturn(aDevice, VERR_GENERAL_FAILURE); + AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE); + + AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS); + LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str())); + + Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost); + + /* + * Create a one-shot ignore filter for the device + * and trigger a re-enumeration of it. + */ + USBFILTER Filter; + USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_IGNORE); + initFilterFromDevice(&Filter, aDevice); + Log(("USBFILTERIDX_PORT=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_PORT))); + Log(("USBFILTERIDX_BUS=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_BUS))); + + void *pvId = USBLibAddFilter(&Filter); + if (!pvId) + { + AssertMsgFailed(("Add one-shot Filter failed\n")); + return VERR_GENERAL_FAILURE; + } + + int vrc = USBLibRunFilters(); + if (!RT_SUCCESS(vrc)) + { + AssertMsgFailed(("Run Filters failed\n")); + USBLibRemoveFilter(pvId); + return vrc; + } + + + return VINF_SUCCESS; +} + + +/** + * Returns whether devices reported by this backend go through a de/re-attach + * and device re-enumeration cycle when they are captured or released. + */ +bool USBProxyBackendWindows::i_isDevReEnumerationRequired() +{ + return true; +} + + +int USBProxyBackendWindows::wait(unsigned aMillies) +{ + return USBLibWaitChange(aMillies); +} + + +int USBProxyBackendWindows::interruptWait(void) +{ + return USBLibInterruptWaitChange(); +} + +/** + * Gets a list of all devices the VM can grab + */ +PUSBDEVICE USBProxyBackendWindows::getDevices(void) +{ + PUSBDEVICE pDevices = NULL; + uint32_t cDevices = 0; + + Log(("USBProxyBackendWindows::getDevices\n")); + USBLibGetDevices(&pDevices, &cDevices); + return pDevices; +} + diff --git a/src/VBox/Main/src-server/win/VBoxSVC.rc b/src/VBox/Main/src-server/win/VBoxSVC.rc new file mode 100644 index 00000000..e50a85e6 --- /dev/null +++ b/src/VBox/Main/src-server/win/VBoxSVC.rc @@ -0,0 +1,78 @@ +/* $Id: VBoxSVC.rc $ */ +/** @file + * VBoxSVC - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include <windows.h> +#include <VBox/version.h> + +#include "win/resource.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_APP + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual + BEGIN + VALUE "FileDescription", "VirtualBox Interface\0" + VALUE "InternalName", "VBoxSVC\0" + VALUE "OriginalFilename", "VBoxSVC.exe\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + + VALUE "OLESelfRegister", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +/* Creates the application icon. */ +#include "VBoxSVC-icon.rc" + + +#ifndef VBOX_WITH_MIDL_PROXY_STUB +///////////////////////////////////////////////////////////////////////////// +// +// REGISTRY +// + +IDR_VIRTUALBOX REGISTRY "VBoxSVC.rgs" +#endif + +1 TYPELIB "VirtualBox.tlb" diff --git a/src/VBox/Main/src-server/win/precomp_vcc.h b/src/VBox/Main/src-server/win/precomp_vcc.h new file mode 100644 index 00000000..64b62d1c --- /dev/null +++ b/src/VBox/Main/src-server/win/precomp_vcc.h @@ -0,0 +1,48 @@ +/* $Id: precomp_vcc.h $ */ +/** @file + * VirtualBox COM - Visual C++ precompiled header for VBoxSVC. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +#include <iprt/cdefs.h> +#include <iprt/win/winsock2.h> +#include <iprt/win/windows.h> +#include <VBox/cdefs.h> +#include <iprt/types.h> +#include <iprt/cpp/list.h> +#include <iprt/cpp/meta.h> +#include <iprt/cpp/ministring.h> +#include <VBox/com/microatl.h> +#include <VBox/com/com.h> +#include <VBox/com/array.h> +#include <VBox/com/Guid.h> +#include <VBox/com/string.h> + +#include "VBox/com/VirtualBox.h" + +#if defined(Log) || defined(LogIsEnabled) +# error "Log() from iprt/log.h cannot be defined in the precompiled header!" +#endif + diff --git a/src/VBox/Main/src-server/win/svchlp.cpp b/src/VBox/Main/src-server/win/svchlp.cpp new file mode 100644 index 00000000..262c32a6 --- /dev/null +++ b/src/VBox/Main/src-server/win/svchlp.cpp @@ -0,0 +1,308 @@ +/* $Id: svchlp.cpp $ */ +/** @file + * Definition of SVC Helper Process control routines. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#define LOG_GROUP LOG_GROUP_MAIN +#include "svchlp.h" + +//#include "HostImpl.h" +#include "LoggingNew.h" + +#include <iprt/errcore.h> + +int netIfNetworkInterfaceHelperServer(SVCHlpClient *aClient, SVCHlpMsg::Code aMsgCode); + +using namespace com; + +enum { PipeBufSize = 1024 }; + +//////////////////////////////////////////////////////////////////////////////// + +/** + * GetLastError() is known to return NO_ERROR even after the Win32 API + * function (i.e. Write() to a non-connected server end of a pipe) returns + * FALSE... This method ensures that at least VERR_GENERAL_FAILURE is returned + * in cases like that. Intended to be called immediately after a failed API + * call. + */ +static inline int rtErrConvertFromWin32OnFailure() +{ + DWORD err = GetLastError(); + return err == NO_ERROR ? VERR_GENERAL_FAILURE + : RTErrConvertFromWin32 (err); +} + +//////////////////////////////////////////////////////////////////////////////// + +SVCHlpClient::SVCHlpClient() + : mIsOpen (false), mIsServer (false) + , mReadEnd (NULL), mWriteEnd (NULL) +{ +} + +SVCHlpClient::~SVCHlpClient() +{ + close(); +} + +int SVCHlpClient::create(const char *aName) +{ + AssertReturn(aName, VERR_INVALID_PARAMETER); + + if (mIsOpen) + return VERR_WRONG_ORDER; + + Bstr pipeName = Utf8StrFmt("\\\\.\\pipe\\%s", aName); + + HANDLE pipe = CreateNamedPipe(pipeName.raw(), + PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + 1, // PIPE_UNLIMITED_INSTANCES, + PipeBufSize, PipeBufSize, + NMPWAIT_USE_DEFAULT_WAIT, + NULL); + + if (pipe == INVALID_HANDLE_VALUE) + rtErrConvertFromWin32OnFailure(); + + mIsOpen = true; + mIsServer = true; + mReadEnd = pipe; + mWriteEnd = pipe; + mName = aName; + + return VINF_SUCCESS; +} + +int SVCHlpClient::open(const char *aName) +{ + AssertReturn(aName, VERR_INVALID_PARAMETER); + + if (mIsOpen) + return VERR_WRONG_ORDER; + + Bstr pipeName = Utf8StrFmt("\\\\.\\pipe\\%s", aName); + + HANDLE pipe = CreateFile(pipeName.raw(), + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (pipe == INVALID_HANDLE_VALUE) + rtErrConvertFromWin32OnFailure(); + + mIsOpen = true; + mIsServer = false; + mReadEnd = pipe; + mWriteEnd = pipe; + mName = aName; + + return VINF_SUCCESS; +} + +int SVCHlpClient::connect() +{ + if (!mIsOpen || !mIsServer) + return VERR_WRONG_ORDER; + + BOOL ok = ConnectNamedPipe (mReadEnd, NULL); + if (!ok && GetLastError() != ERROR_PIPE_CONNECTED) + rtErrConvertFromWin32OnFailure(); + + return VINF_SUCCESS; +} + +int SVCHlpClient::close() +{ + if (!mIsOpen) + return VERR_WRONG_ORDER; + + if (mWriteEnd != NULL && mWriteEnd != mReadEnd) + { + if (!CloseHandle (mWriteEnd)) + rtErrConvertFromWin32OnFailure(); + mWriteEnd = NULL; + } + + if (mReadEnd != NULL) + { + if (!CloseHandle (mReadEnd)) + rtErrConvertFromWin32OnFailure(); + mReadEnd = NULL; + } + + mIsOpen = false; + mIsServer = false; + mName.setNull(); + + return VINF_SUCCESS; +} + +int SVCHlpClient::write (const void *aVal, size_t aLen) +{ + AssertReturn(aVal != NULL, VERR_INVALID_PARAMETER); + AssertReturn(aLen != 0, VERR_INVALID_PARAMETER); + + if (!mIsOpen) + return VERR_WRONG_ORDER; + + DWORD written = 0; + BOOL ok = WriteFile (mWriteEnd, aVal, (ULONG)aLen, &written, NULL); + AssertReturn(!ok || written == aLen, VERR_GENERAL_FAILURE); + return ok ? VINF_SUCCESS : rtErrConvertFromWin32OnFailure(); +} + +int SVCHlpClient::write (const Utf8Str &aVal) +{ + if (!mIsOpen) + return VERR_WRONG_ORDER; + + /* write -1 for NULL strings */ + if (aVal.isEmpty()) + return write ((size_t) ~0); + + size_t len = aVal.length(); + + /* write string length */ + int vrc = write (len); + if (RT_SUCCESS(vrc)) + { + /* write string data */ + vrc = write (aVal.c_str(), len); + } + + return vrc; +} + +int SVCHlpClient::write (const Guid &aGuid) +{ + Utf8Str guidStr = aGuid.toString(); + return write (guidStr); +} + +int SVCHlpClient::read (void *aVal, size_t aLen) +{ + AssertReturn(aVal != NULL, VERR_INVALID_PARAMETER); + AssertReturn(aLen != 0, VERR_INVALID_PARAMETER); + + if (!mIsOpen) + return VERR_WRONG_ORDER; + + DWORD read = 0; + BOOL ok = ReadFile (mReadEnd, aVal, (ULONG)aLen, &read, NULL); + AssertReturn(!ok || read == aLen, VERR_GENERAL_FAILURE); + return ok ? VINF_SUCCESS : rtErrConvertFromWin32OnFailure(); +} + +int SVCHlpClient::read (Utf8Str &aVal) +{ + if (!mIsOpen) + return VERR_WRONG_ORDER; + + size_t len = 0; + + /* read string length */ + int vrc = read (len); + if (RT_FAILURE(vrc)) + return vrc; + + /* length -1 means a NULL string */ + if (len == (size_t) ~0) + { + aVal.setNull(); + return VINF_SUCCESS; + } + + aVal.reserve(len + 1); + aVal.mutableRaw()[len] = 0; + + /* read string data */ + vrc = read (aVal.mutableRaw(), len); + + return vrc; +} + +int SVCHlpClient::read (Guid &aGuid) +{ + Utf8Str guidStr; + int vrc = read (guidStr); + if (RT_SUCCESS(vrc)) + aGuid = Guid (guidStr.c_str()); + return vrc; +} + +//////////////////////////////////////////////////////////////////////////////// + +SVCHlpServer::SVCHlpServer () +{ +} + +int SVCHlpServer::run() +{ + int vrc = VINF_SUCCESS; + SVCHlpMsg::Code msgCode = SVCHlpMsg::Null; + + do + { + vrc = read (msgCode); + if (RT_FAILURE(vrc)) + return vrc; + + /* terminate request received */ + if (msgCode == SVCHlpMsg::Null) + return VINF_SUCCESS; + + switch (msgCode) + { + case SVCHlpMsg::CreateHostOnlyNetworkInterface: + case SVCHlpMsg::RemoveHostOnlyNetworkInterface: + case SVCHlpMsg::EnableDynamicIpConfig: + case SVCHlpMsg::EnableStaticIpConfig: + case SVCHlpMsg::EnableStaticIpConfigV6: + case SVCHlpMsg::DhcpRediscover: + { +#ifdef VBOX_WITH_NETFLT + vrc = netIfNetworkInterfaceHelperServer(this, msgCode); +#endif + break; + } + default: + AssertMsgFailedReturn(("Invalid message code %d (%08lX)\n", msgCode, msgCode), + VERR_GENERAL_FAILURE); + } + + if (RT_FAILURE(vrc)) + return vrc; + } + while (1); + + /* we never get here */ + AssertFailed(); + return VERR_GENERAL_FAILURE; +} diff --git a/src/VBox/Main/src-server/win/svchlp.h b/src/VBox/Main/src-server/win/svchlp.h new file mode 100644 index 00000000..a02111ba --- /dev/null +++ b/src/VBox/Main/src-server/win/svchlp.h @@ -0,0 +1,107 @@ +/* $Id: svchlp.h $ */ +/** @file + * Declaration of SVC Helper Process control routines. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#ifndef MAIN_INCLUDED_SRC_src_server_win_svchlp_h +#define MAIN_INCLUDED_SRC_src_server_win_svchlp_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "VBox/com/string.h" +#include "VBox/com/guid.h" + +#include <VBox/err.h> + +#include <iprt/win/windows.h> + +struct SVCHlpMsg +{ + enum Code + { + Null = 0, /* no parameters */ + OK, /* no parameters */ + Error, /* Utf8Str string (may be null but must present) */ + + CreateHostOnlyNetworkInterface = 100, /* see usage in code */ + CreateHostOnlyNetworkInterface_OK, /* see usage in code */ + RemoveHostOnlyNetworkInterface, /* see usage in code */ + EnableDynamicIpConfig, /* see usage in code */ + EnableStaticIpConfig, /* see usage in code */ + EnableStaticIpConfigV6, /* see usage in code */ + DhcpRediscover, /* see usage in code */ + }; +}; + +class SVCHlpClient +{ +public: + + SVCHlpClient(); + virtual ~SVCHlpClient(); + + int create (const char *aName); + int connect(); + int open (const char *aName); + int close(); + + bool isOpen() const { return mIsOpen; } + bool isServer() const { return mIsServer; } + const com::Utf8Str &name() const { return mName; } + + int write (const void *aVal, size_t aLen); + template <typename Scalar> + int write (Scalar aVal) { return write (&aVal, sizeof (aVal)); } + int write (const com::Utf8Str &aVal); + int write (const com::Guid &aGuid); + + int read (void *aVal, size_t aLen); + template <typename Scalar> + int read (Scalar &aVal) { return read (&aVal, sizeof (aVal)); } + int read (com::Utf8Str &aVal); + int read (com::Guid &aGuid); + +private: + + bool mIsOpen : 1; + bool mIsServer : 1; + + HANDLE mReadEnd; + HANDLE mWriteEnd; + com::Utf8Str mName; +}; + +class SVCHlpServer : public SVCHlpClient +{ +public: + + SVCHlpServer(); + + int run(); +}; + +#endif /* !MAIN_INCLUDED_SRC_src_server_win_svchlp_h */ + diff --git a/src/VBox/Main/src-server/win/svcmain.cpp b/src/VBox/Main/src-server/win/svcmain.cpp new file mode 100644 index 00000000..c1aa56a7 --- /dev/null +++ b/src/VBox/Main/src-server/win/svcmain.cpp @@ -0,0 +1,1212 @@ +/* $Id: svcmain.cpp $ */ +/** @file + * SVCMAIN - COM out-of-proc server main entry + */ + +/* + * Copyright (C) 2004-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN_VBOXSVC +#include <iprt/win/windows.h> +#ifdef DEBUG_bird +# include <RpcAsync.h> +#endif + +#include "VBox/com/defs.h" +#include "VBox/com/com.h" +#include "VBox/com/VirtualBox.h" + +#include "VirtualBoxImpl.h" +#include "LoggingNew.h" + +#include "svchlp.h" + +#include <iprt/errcore.h> +#include <iprt/buildconfig.h> +#include <iprt/initterm.h> +#include <iprt/string.h> +#include <iprt/path.h> +#include <iprt/getopt.h> +#include <iprt/message.h> +#include <iprt/asm.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define MAIN_WND_CLASS L"VirtualBox Interface" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +class CExeModule : public ATL::CComModule +{ +public: + LONG Unlock() throw(); + DWORD dwThreadID; + HANDLE hEventShutdown; + void MonitorShutdown(); + bool StartMonitor(); + bool HasActiveConnection(); + bool bActivity; + static bool isIdleLockCount(LONG cLocks); +}; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +BEGIN_OBJECT_MAP(ObjectMap) + OBJECT_ENTRY(CLSID_VirtualBox, VirtualBox) +END_OBJECT_MAP() + +CExeModule *g_pModule = NULL; +HWND g_hMainWindow = NULL; +HINSTANCE g_hInstance = NULL; +#ifdef VBOX_WITH_SDS +/** This is set if we're connected to SDS. + * + * It means that we should discount a server lock that it is holding when + * deciding whether we're idle or not. + * + * Also, when set we deregister with SDS during class factory destruction. We + * exploit this to prevent attempts to deregister during or after COM shutdown. + */ +bool g_fRegisteredWithVBoxSDS = false; +#endif + +/* Normal timeout usually used in Shutdown Monitor */ +const DWORD dwNormalTimeout = 5000; +volatile uint32_t dwTimeOut = dwNormalTimeout; /* time for EXE to be idle before shutting down. Can be decreased at system shutdown phase. */ + + + +/** Passed to CreateThread to monitor the shutdown event. */ +static DWORD WINAPI MonitorProc(void *pv) RT_NOTHROW_DEF +{ + CExeModule *p = (CExeModule *)pv; + p->MonitorShutdown(); + return 0; +} + +LONG CExeModule::Unlock() throw() +{ + LONG cLocks = ATL::CComModule::Unlock(); + if (isIdleLockCount(cLocks)) + { + bActivity = true; + SetEvent(hEventShutdown); /* tell monitor that we transitioned to zero */ + } + return cLocks; +} + +bool CExeModule::HasActiveConnection() +{ + return bActivity || !isIdleLockCount(GetLockCount()); +} + +/** + * Checks if @a cLocks signifies an IDLE server lock load. + * + * This takes VBoxSDS into account (i.e. ignores it). + */ +/*static*/ bool CExeModule::isIdleLockCount(LONG cLocks) +{ +#ifdef VBOX_WITH_SDS + if (g_fRegisteredWithVBoxSDS) + return cLocks <= 1; +#endif + return cLocks <= 0; +} + +/* Monitors the shutdown event */ +void CExeModule::MonitorShutdown() +{ + while (1) + { + WaitForSingleObject(hEventShutdown, INFINITE); + DWORD dwWait; + do + { + bActivity = false; + dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut); + } while (dwWait == WAIT_OBJECT_0); + /* timed out */ + if (!HasActiveConnection()) /* if no activity let's really bail */ + { + /* Disable log rotation at this point, worst case a log file + * becomes slightly bigger than it should. Avoids quirks with + * log rotation: there might be another API service process + * running at this point which would rotate the logs concurrently, + * creating a mess. */ + PRTLOGGER pReleaseLogger = RTLogRelGetDefaultInstance(); + if (pReleaseLogger) + { + char szDest[1024]; + int vrc = RTLogQueryDestinations(pReleaseLogger, szDest, sizeof(szDest)); + if (RT_SUCCESS(vrc)) + { + vrc = RTStrCat(szDest, sizeof(szDest), " nohistory"); + if (RT_SUCCESS(vrc)) + { + vrc = RTLogDestinations(pReleaseLogger, szDest); + AssertRC(vrc); + } + } + } +#if _WIN32_WINNT >= 0x0400 + CoSuspendClassObjects(); + if (!HasActiveConnection()) +#endif + break; + } + } + CloseHandle(hEventShutdown); + PostThreadMessage(dwThreadID, WM_QUIT, 0, 0); +} + +bool CExeModule::StartMonitor() +{ + hEventShutdown = CreateEvent(NULL, false, false, NULL); + if (hEventShutdown == NULL) + return false; + DWORD idThreadIgnored; + HANDLE h = CreateThread(NULL, 0, MonitorProc, this, 0, &idThreadIgnored); + return (h != NULL); +} + + +#ifdef VBOX_WITH_SDS + +class VBoxSVCRegistration; + +/** + * Custom class factory for the VirtualBox singleton. + * + * The implementation of CreateInstance is found in win/svcmain.cpp. + */ +class VirtualBoxClassFactory : public ATL::CComClassFactory +{ +private: + /** Tri state: 0=uninitialized or initializing; 1=success; -1=failure. + * This will be updated after both m_hrcCreate and m_pObj have been set. */ + volatile int32_t m_iState; + /** The result of the instantiation attempt. */ + HRESULT m_hrcCreate; + /** The IUnknown of the VirtualBox object/interface we're working with. */ + IUnknown *m_pObj; + /** Pointer to the IVBoxSVCRegistration implementation that VBoxSDS works with. */ + VBoxSVCRegistration *m_pVBoxSVC; + /** The VBoxSDS interface. */ + ComPtr<IVirtualBoxSDS> m_ptrVirtualBoxSDS; + +public: + VirtualBoxClassFactory() : m_iState(0), m_hrcCreate(S_OK), m_pObj(NULL), m_pVBoxSVC(NULL) + { } + + virtual ~VirtualBoxClassFactory() + { + if (m_pObj) + { + m_pObj->Release(); + m_pObj = NULL; + } + + /* We usually get here during g_pModule->Term() via CoRevokeClassObjec, so COM + probably working well enough to talk to SDS when we get here. */ + if (g_fRegisteredWithVBoxSDS) + i_deregisterWithSds(); + } + + // IClassFactory + STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj); + + /** Worker for VBoxSVCRegistration::getVirtualBox. */ + HRESULT i_getVirtualBox(IUnknown **ppResult); + +private: + HRESULT i_registerWithSds(IUnknown **ppOtherVirtualBox); + void i_deregisterWithSds(void); + + friend VBoxSVCRegistration; +}; + + +/** + * The VBoxSVC class is handed to VBoxSDS so it can call us back and ask for the + * VirtualBox object when the next VBoxSVC for this user registers itself. + */ +class VBoxSVCRegistration : public IVBoxSVCRegistration +{ +private: + /** Number of references. */ + uint32_t volatile m_cRefs; + +public: + /** Pointer to the factory. */ + VirtualBoxClassFactory *m_pFactory; + +public: + VBoxSVCRegistration(VirtualBoxClassFactory *pFactory) + : m_cRefs(1), m_pFactory(pFactory) + { } + virtual ~VBoxSVCRegistration() + { + if (m_pFactory) + { + if (m_pFactory->m_pVBoxSVC) + m_pFactory->m_pVBoxSVC = NULL; + m_pFactory = NULL; + } + } + RTMEMEF_NEW_AND_DELETE_OPERATORS(); + + // IUnknown + STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject) + { + if (riid == __uuidof(IUnknown)) + *ppvObject = (void *)(IUnknown *)this; + else if (riid == __uuidof(IVBoxSVCRegistration)) + *ppvObject = (void *)(IVBoxSVCRegistration *)this; + else + { + return E_NOINTERFACE; + } + AddRef(); + return S_OK; + + } + + STDMETHOD_(ULONG,AddRef)(void) + { + uint32_t cRefs = ASMAtomicIncU32(&m_cRefs); + return cRefs; + } + + STDMETHOD_(ULONG,Release)(void) + { + uint32_t cRefs = ASMAtomicDecU32(&m_cRefs); + if (cRefs == 0) + delete this; + return cRefs; + } + + // IVBoxSVCRegistration + STDMETHOD(GetVirtualBox)(IUnknown **ppResult) + { + if (m_pFactory) + return m_pFactory->i_getVirtualBox(ppResult); + return E_FAIL; + } +}; + + +HRESULT VirtualBoxClassFactory::i_registerWithSds(IUnknown **ppOtherVirtualBox) +{ +# ifdef DEBUG_bird + RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL }; + RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs); + LogRel(("i_registerWithSds: RpcServerInqCallAttributesW -> %#x ClientPID=%#x IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid\n", + rcRpc, CallAttribs.ClientPID, CallAttribs.IsClientLocal, CallAttribs.ProtocolSequence, CallAttribs.CallStatus, + CallAttribs.CallType, CallAttribs.OpNum, &CallAttribs.InterfaceUuid)); +# endif + + /* + * Connect to VBoxSDS. + */ + HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxSDS, + (void **)m_ptrVirtualBoxSDS.asOutParam()); + if (SUCCEEDED(hrc)) + { + /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows + ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket + elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS + service to access the files. */ + hrc = CoSetProxyBlanket(m_ptrVirtualBoxSDS, + RPC_C_AUTHN_DEFAULT, + RPC_C_AUTHZ_DEFAULT, + COLE_DEFAULT_PRINCIPAL, + RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_DEFAULT); + if (SUCCEEDED(hrc)) + { + /* + * Create VBoxSVCRegistration object and hand that to VBoxSDS. + */ + m_pVBoxSVC = new VBoxSVCRegistration(this); + hrc = E_PENDING; + /* we try to register IVirtualBox 10 times */ + for (int regTimes = 0; hrc == E_PENDING && regTimes < 10; --regTimes) + { + hrc = m_ptrVirtualBoxSDS->RegisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId(), ppOtherVirtualBox); + if (SUCCEEDED(hrc)) + { + g_fRegisteredWithVBoxSDS = !*ppOtherVirtualBox; + return hrc; + } + /* sleep to give a time for windows session 0 registration */ + if (hrc == E_PENDING) + RTThreadSleep(1000); + } + m_pVBoxSVC->Release(); + } + } + m_ptrVirtualBoxSDS.setNull(); + m_pVBoxSVC = NULL; + *ppOtherVirtualBox = NULL; + return hrc; +} + + +void VirtualBoxClassFactory::i_deregisterWithSds(void) +{ + Log(("VirtualBoxClassFactory::i_deregisterWithSds\n")); + + if (m_ptrVirtualBoxSDS.isNotNull()) + { + if (m_pVBoxSVC) + { + HRESULT hrc = m_ptrVirtualBoxSDS->DeregisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId()); + NOREF(hrc); + } + m_ptrVirtualBoxSDS.setNull(); + g_fRegisteredWithVBoxSDS = false; + } + if (m_pVBoxSVC) + { + m_pVBoxSVC->m_pFactory = NULL; + m_pVBoxSVC->Release(); + m_pVBoxSVC = NULL; + } +} + + +HRESULT VirtualBoxClassFactory::i_getVirtualBox(IUnknown **ppResult) +{ +# ifdef DEBUG_bird + RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL }; + RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs); + LogRel(("i_getVirtualBox: RpcServerInqCallAttributesW -> %#x ClientPID=%#x IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid\n", + rcRpc, CallAttribs.ClientPID, CallAttribs.IsClientLocal, CallAttribs.ProtocolSequence, CallAttribs.CallStatus, + CallAttribs.CallType, CallAttribs.OpNum, &CallAttribs.InterfaceUuid)); +# endif + IUnknown *pObj = m_pObj; + if (pObj) + { + /** @todo Do we need to do something regarding server locking? Hopefully COM + * deals with that........... */ + pObj->AddRef(); + *ppResult = pObj; + Log(("VirtualBoxClassFactory::GetVirtualBox: S_OK - %p\n", pObj)); + return S_OK; + } + *ppResult = NULL; + Log(("VirtualBoxClassFactory::GetVirtualBox: E_FAIL\n")); + return E_FAIL; +} + + +/** + * Custom instantiation of CComObjectCached. + * + * This catches certain QueryInterface callers for the purpose of watching for + * abnormal client process termination (@bugref{3300}). + * + * @todo just merge this into class VirtualBox VirtualBoxImpl.h + */ +class VirtualBoxObjectCached : public VirtualBox +{ +public: + VirtualBoxObjectCached(void * = NULL) + : VirtualBox() + { + } + + virtual ~VirtualBoxObjectCached() + { + m_iRef = LONG_MIN / 2; /* Catch refcount screwups by setting refcount something insane. */ + FinalRelease(); + } + + /** @name IUnknown implementation for VirtualBox + * @{ */ + + STDMETHOD_(ULONG, AddRef)() throw() + { + ULONG cRefs = InternalAddRef(); + if (cRefs == 2) + { + AssertMsg(ATL::_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n")); + ATL::_pAtlModule->Lock(); + } + return cRefs; + } + + STDMETHOD_(ULONG, Release)() throw() + { + ULONG cRefs = InternalRelease(); + if (cRefs == 0) + delete this; + else if (cRefs == 1) + { + AssertMsg(ATL::_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n")); + ATL::_pAtlModule->Unlock(); + } + return cRefs; + } + + STDMETHOD(QueryInterface)(REFIID iid, void **ppvObj) throw() + { + HRESULT hrc = _InternalQueryInterface(iid, ppvObj); +#ifdef VBOXSVC_WITH_CLIENT_WATCHER + i_logCaller("QueryInterface %RTuuid -> %Rhrc %p", &iid, hrc, *ppvObj); +#endif + return hrc; + } + + /** @} */ + + static HRESULT WINAPI CreateInstance(VirtualBoxObjectCached **ppObj) throw() + { + AssertReturn(ppObj, E_POINTER); + *ppObj = NULL; + + HRESULT hrc = E_OUTOFMEMORY; + VirtualBoxObjectCached *p = new (std::nothrow) VirtualBoxObjectCached(); + if (p) + { + p->SetVoid(NULL); + p->InternalFinalConstructAddRef(); + hrc = p->_AtlInitialConstruct(); + if (SUCCEEDED(hrc)) + hrc = p->FinalConstruct(); + p->InternalFinalConstructRelease(); + if (FAILED(hrc)) + delete p; + else + *ppObj = p; + } + return hrc; + } +}; + + +/** + * Custom class factory impl for the VirtualBox singleton. + * + * This will consult with VBoxSDS on whether this VBoxSVC instance should + * provide the actual VirtualBox instance or just forward the instance from + * some other SVC instance. + * + * @param pUnkOuter This must be NULL. + * @param riid Reference to the interface ID to provide. + * @param ppvObj Where to return the pointer to the riid instance. + * + * @return COM status code. + */ +STDMETHODIMP VirtualBoxClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj) +{ +# ifdef VBOXSVC_WITH_CLIENT_WATCHER + VirtualBox::i_logCaller("VirtualBoxClassFactory::CreateInstance: %RTuuid", riid); +# endif + HRESULT hrc = E_POINTER; + if (ppvObj != NULL) + { + *ppvObj = NULL; + // no aggregation for singletons + AssertReturn(pUnkOuter == NULL, CLASS_E_NOAGGREGATION); + + /* + * We must make sure there is only one instance around. + * So, we check without locking and then again after locking. + */ + if (ASMAtomicReadS32(&m_iState) == 0) + { + Lock(); + __try + { + if (ASMAtomicReadS32(&m_iState) == 0) + { + /* + * lock the module to indicate activity + * (necessary for the monitor shutdown thread to correctly + * terminate the module in case when CreateInstance() fails) + */ + ATL::_pAtlModule->Lock(); + __try + { + /* + * Now we need to connect to VBoxSDS to register ourselves. + */ + IUnknown *pOtherVirtualBox = NULL; + m_hrcCreate = hrc = i_registerWithSds(&pOtherVirtualBox); + if (SUCCEEDED(hrc) && pOtherVirtualBox) + m_pObj = pOtherVirtualBox; + else if (SUCCEEDED(hrc)) + { + ATL::_pAtlModule->Lock(); + VirtualBoxObjectCached *p; + m_hrcCreate = hrc = VirtualBoxObjectCached::CreateInstance(&p); + if (SUCCEEDED(hrc)) + { + m_hrcCreate = hrc = p->QueryInterface(IID_IUnknown, (void **)&m_pObj); + if (SUCCEEDED(hrc)) + RTLogClearFileDelayFlag(RTLogRelGetDefaultInstance(), NULL); + else + { + delete p; + i_deregisterWithSds(); + m_pObj = NULL; + } + } + } + ASMAtomicWriteS32(&m_iState, SUCCEEDED(hrc) ? 1 : -1); + } + __finally + { + ATL::_pAtlModule->Unlock(); + } + } + } + __finally + { + if (ASMAtomicReadS32(&m_iState) == 0) + { + ASMAtomicWriteS32(&m_iState, -1); + if (SUCCEEDED(m_hrcCreate)) + m_hrcCreate = E_FAIL; + } + Unlock(); + } + } + + /* + * Query the requested interface from the IUnknown one we're keeping around. + */ + if (m_hrcCreate == S_OK) + hrc = m_pObj->QueryInterface(riid, ppvObj); + else + hrc = m_hrcCreate; + } + return hrc; +} + +#endif // VBOX_WITH_SDS + + +/* +* Wrapper for Win API function ShutdownBlockReasonCreate +* This function defined starting from Vista only. +*/ +static BOOL ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason) +{ + typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNSHUTDOWNBLOCKREASONCREATE,(HWND hWnd, LPCWSTR pwszReason)); + + PFNSHUTDOWNBLOCKREASONCREATE pfn + = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate"); + AssertPtr(pfn); + + BOOL fResult = FALSE; + if (pfn) + fResult = pfn(hWnd, pwszReason); + return fResult; +} + +/* +* Wrapper for Win API function ShutdownBlockReasonDestroy +* This function defined starting from Vista only. +*/ +static BOOL ShutdownBlockReasonDestroyAPI(HWND hWnd) +{ + typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNSHUTDOWNBLOCKREASONDESTROY,(HWND hWnd)); + PFNSHUTDOWNBLOCKREASONDESTROY pfn + = (PFNSHUTDOWNBLOCKREASONDESTROY)GetProcAddress(GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonDestroy"); + AssertPtr(pfn); + + BOOL fResult = FALSE; + if (pfn) + fResult = pfn(hWnd); + return fResult; +} + +static LRESULT CALLBACK WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LRESULT lResult = 0; + + switch (msg) + { + case WM_QUERYENDSESSION: + { + LogRel(("WM_QUERYENDSESSION:%s%s%s%s (0x%08lx)\n", + lParam == 0 ? " shutdown" : "", + lParam & ENDSESSION_CRITICAL ? " critical" : "", + lParam & ENDSESSION_LOGOFF ? " logoff" : "", + lParam & ENDSESSION_CLOSEAPP ? " close" : "", + (unsigned long)lParam)); + if (g_pModule) + { + bool fActiveConnection = g_pModule->HasActiveConnection(); + if (fActiveConnection) + { + lResult = FALSE; + LogRel(("VBoxSvc has active connections:" + " bActivity = %RTbool, lock count = %d\n", + g_pModule->bActivity, g_pModule->GetLockCount())); + + /* place the VBoxSVC into system shutdown list */ + ShutdownBlockReasonCreateAPI(hwnd, L"Has active connections."); + /* decrease a latency of MonitorShutdown loop */ + ASMAtomicXchgU32(&dwTimeOut, 100); + Log(("VBoxSVCWinMain: WM_QUERYENDSESSION: VBoxSvc has active connections." + " bActivity = %d. Lock count = %d\n", + g_pModule->bActivity, g_pModule->GetLockCount())); + } + else + { + LogRel(("No active connections:" + " bActivity = %RTbool, lock count = %d\n", + g_pModule->bActivity, g_pModule->GetLockCount())); + lResult = TRUE; + } + } + else + AssertMsgFailed(("VBoxSVCWinMain: WM_QUERYENDSESSION: Error: g_pModule is NULL")); + break; + } + case WM_ENDSESSION: + { + LogRel(("WM_ENDSESSION:%s%s%s%s%s (%s/0x%08lx)\n", + lParam == 0 ? " shutdown" : "", + lParam & ENDSESSION_CRITICAL ? " critical" : "", + lParam & ENDSESSION_LOGOFF ? " logoff" : "", + lParam & ENDSESSION_CLOSEAPP ? " close" : "", + wParam == FALSE ? " cancelled" : "", + wParam ? "TRUE" : "FALSE", + (unsigned long)lParam)); + + /* Restore timeout of Monitor Shutdown if user canceled system shutdown */ + if (wParam == FALSE) + { + Log(("VBoxSVCWinMain: user canceled system shutdown.\n")); + ASMAtomicXchgU32(&dwTimeOut, dwNormalTimeout); + ShutdownBlockReasonDestroyAPI(hwnd); + } + break; + } + case WM_DESTROY: + { + ShutdownBlockReasonDestroyAPI(hwnd); + PostQuitMessage(0); + break; + } + + default: + { + lResult = DefWindowProc(hwnd, msg, wParam, lParam); + break; + } + } + return lResult; +} + +static int CreateMainWindow() +{ + int vrc = VINF_SUCCESS; + Assert(g_hMainWindow == NULL); + + LogFlow(("CreateMainWindow\n")); + + g_hInstance = (HINSTANCE)GetModuleHandle(NULL); + + /* Register the Window Class. */ + WNDCLASS wc; + RT_ZERO(wc); + + wc.style = CS_NOCLOSE; + wc.lpfnWndProc = WinMainWndProc; + wc.hInstance = g_hInstance; + wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1); + wc.lpszClassName = MAIN_WND_CLASS; + + ATOM atomWindowClass = RegisterClass(&wc); + if (atomWindowClass == 0) + { + LogRel(("Failed to register window class for session monitoring\n")); + vrc = VERR_NOT_SUPPORTED; + } + else + { + /* Create the window. */ + g_hMainWindow = CreateWindowEx(0, MAIN_WND_CLASS, MAIN_WND_CLASS, 0, + 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL); + if (g_hMainWindow == NULL) + { + LogRel(("Failed to create window for session monitoring\n")); + vrc = VERR_NOT_SUPPORTED; + } + } + return vrc; +} + + +static void DestroyMainWindow() +{ + Assert(g_hMainWindow != NULL); + Log(("SVCMain: DestroyMainWindow \n")); + if (g_hMainWindow != NULL) + { + DestroyWindow(g_hMainWindow); + g_hMainWindow = NULL; + if (g_hInstance != NULL) + { + UnregisterClass(MAIN_WND_CLASS, g_hInstance); + g_hInstance = NULL; + } + } +} + + +static const char * const ctrl_event_names[] = { + "CTRL_C_EVENT", + "CTRL_BREAK_EVENT", + "CTRL_CLOSE_EVENT", + /* reserved, not used */ + "<console control event 3>", + "<console control event 4>", + /* not sent to processes that load gdi32.dll or user32.dll */ + "CTRL_LOGOFF_EVENT", + "CTRL_SHUTDOWN_EVENT", +}; + +/** @todo r=uwe placeholder */ +BOOL WINAPI +ConsoleCtrlHandler(DWORD dwCtrlType) RT_NOTHROW_DEF +{ + const char *signame; + char namebuf[48]; + // int vrc; + + if (dwCtrlType < RT_ELEMENTS(ctrl_event_names)) + signame = ctrl_event_names[dwCtrlType]; + else + { + /* should not happen, but be prepared */ + RTStrPrintf(namebuf, sizeof(namebuf), + "<console control event %lu>", (unsigned long)dwCtrlType); + signame = namebuf; + } + LogRel(("Got %s\n", signame)); + + if (RT_UNLIKELY(g_pModule == NULL)) + { + LogRel(("%s: g_pModule == NULL\n", __FUNCTION__)); + return TRUE; + } + + /* decrease latency of the MonitorShutdown loop */ + ASMAtomicXchgU32(&dwTimeOut, 100); + + bool fHasClients = g_pModule->HasActiveConnection(); + if (!fHasClients) + { + LogRel(("No clients, closing the shop.\n")); + return TRUE; + } + + LogRel(("VBoxSvc has clients: bActivity = %RTbool, lock count = %d\n", + g_pModule->bActivity, g_pModule->GetLockCount())); + + /** @todo r=uwe wait for clients to disconnect */ + return TRUE; +} + + + +/** Special export that make VBoxProxyStub not register this process as one that + * VBoxSDS should be watching. + */ +extern "C" DECLEXPORT(void) VBOXCALL Is_VirtualBox_service_process_like_VBoxSDS_And_VBoxSDS(void) +{ + /* never called, just need to be here */ +} + + +/* thread for registering the VBoxSVC started in session 0 */ +static DWORD WINAPI threadRegisterVirtualBox(LPVOID lpParam) throw() +{ + HANDLE hEvent = (HANDLE)lpParam; + HRESULT hrc = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (SUCCEEDED(hrc)) + { + /* create IVirtualBox instance */ + ComPtr<IVirtualBox> pVirtualBox; + hrc = CoCreateInstance(CLSID_VirtualBox, NULL, CLSCTX_INPROC_SERVER /*CLSCTX_LOCAL_SERVER */, IID_IVirtualBox, + (void **)pVirtualBox.asOutParam()); + if (SUCCEEDED(hrc)) + { + /* wait a minute allowing clients to connect to the instance */ + WaitForSingleObject(hEvent, 60 * 1000); + /* remove reference. If anybody connected to IVirtualBox it will stay alive. */ + pVirtualBox.setNull(); + } + CoUninitialize(); + } + return 0L; +} + + +///////////////////////////////////////////////////////////////////////////// +// +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/) +{ + int argc = __argc; + char **argv = __argv; + + /* + * Need to parse the command line before initializing the VBox runtime so we can + * change to the user home directory before logs are being created. + */ + for (int i = 1; i < argc; i++) + if ( (argv[i][0] == '/' || argv[i][0] == '-') + && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */ + { + /* %HOMEDRIVE%%HOMEPATH% */ + wchar_t wszHome[RTPATH_MAX]; + DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX); + if (cEnv && cEnv < RTPATH_MAX) + { + DWORD cwc = cEnv; /* doesn't include NUL */ + cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc); + if (cEnv && cEnv < RTPATH_MAX - cwc) + { + /* If this fails there is nothing we can do. Ignore. */ + SetCurrentDirectory(wszHome); + } + } + } + + /* + * Initialize the VBox runtime without loading + * the support driver. + */ + RTR3InitExe(argc, &argv, 0); + + static const RTGETOPTDEF s_aOptions[] = + { + { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE }, + { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE }, + { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE }, + { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE }, + { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE }, + { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE }, + { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE }, + { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE }, + { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE }, + { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE }, + { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE }, + { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE }, + { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE }, + { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE }, + { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE }, + { "--registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "-registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + { "/registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE }, + }; + + bool fRun = true; + bool fRegister = false; + bool fUnregister = false; + const char *pszPipeName = NULL; + const char *pszLogFile = NULL; + uint32_t cHistory = 10; // enable log rotation, 10 files + uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file + uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file + bool fRegisterVBox = false; + + RTGETOPTSTATE GetOptState; + int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/); + AssertRC(vrc); + + RTGETOPTUNION ValueUnion; + while ((vrc = RTGetOpt(&GetOptState, &ValueUnion))) + { + switch (vrc) + { + case 'e': + /* already handled above */ + break; + + case 'u': + fUnregister = true; + fRun = false; + break; + + case 'r': + fRegister = true; + fRun = false; + break; + + case 'f': + fUnregister = true; + fRegister = true; + fRun = false; + break; + + case 'H': + pszPipeName = ValueUnion.psz; + if (!pszPipeName) + pszPipeName = ""; + fRun = false; + break; + + case 'F': + pszLogFile = ValueUnion.psz; + break; + + case 'R': + cHistory = ValueUnion.u32; + break; + + case 'S': + uHistoryFileSize = ValueUnion.u64; + break; + + case 'I': + uHistoryFileTime = ValueUnion.u32; + break; + + case 'h': + { + static const WCHAR s_wszText[] = L"Options:\n\n" + L"/RegServer:\tregister COM out-of-proc server\n" + L"/UnregServer:\tunregister COM out-of-proc server\n" + L"/ReregServer:\tunregister and register COM server\n" + L"no options:\trun the server"; + static const WCHAR s_wszTitle[] = L"Usage"; + fRun = false; + MessageBoxW(NULL, s_wszText, s_wszTitle, MB_OK); + return 0; + } + + case 'V': + { + static const WCHAR s_wszTitle[] = L"Version"; + char *pszText = NULL; + RTStrAPrintf(&pszText, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr()); + PRTUTF16 pwszText = NULL; + RTStrToUtf16(pszText, &pwszText); + RTStrFree(pszText); + MessageBoxW(NULL, pwszText, s_wszTitle, MB_OK); + RTUtf16Free(pwszText); + fRun = false; + return 0; + } + + case 'b': + fRegisterVBox = true; + break; + + default: + /** @todo this assumes that stderr is visible, which is not + * true for standard Windows applications. */ + /* continue on command line errors... */ + RTGetOptPrintError(vrc, &ValueUnion); + } + } + + /* Only create the log file when running VBoxSVC normally, but not when + * registering/unregistering or calling the helper functionality. */ + if (fRun) + { + /** @todo Merge this code with server.cpp (use Logging.cpp?). */ + char szLogFile[RTPATH_MAX]; + if (!pszLogFile || !*pszLogFile) + { + vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile)); + if (RT_SUCCESS(vrc)) + vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log"); + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to construct release log filename, vrc=%Rrc", vrc); + pszLogFile = szLogFile; + } + + RTERRINFOSTATIC ErrInfo; + vrc = com::VBoxLogRelCreate("COM Server", pszLogFile, + RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG, + VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG", +#ifdef VBOX_WITH_SDS + RTLOGDEST_FILE | RTLOGDEST_F_DELAY_FILE, +#else + RTLOGDEST_FILE, +#endif + UINT32_MAX /* cMaxEntriesPerGroup */, cHistory, uHistoryFileTime, uHistoryFileSize, + RTErrInfoInitStatic(&ErrInfo)); + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc); + } + + /* Set up a build identifier so that it can be seen from core dumps what + * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */ + static char saBuildID[48]; + RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s", + "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D"); + + AssertCompile(VBOX_COM_INIT_F_DEFAULT == VBOX_COM_INIT_F_AUTO_REG_UPDATE); + HRESULT hRes = com::Initialize(fRun ? VBOX_COM_INIT_F_AUTO_REG_UPDATE : 0); + AssertLogRelMsg(SUCCEEDED(hRes), ("SVCMAIN: init failed: %Rhrc\n", hRes)); + + g_pModule = new CExeModule(); + if(g_pModule == NULL) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "not enough memory to create ExeModule."); + g_pModule->Init(ObjectMap, hInstance, &LIBID_VirtualBox); + g_pModule->dwThreadID = GetCurrentThreadId(); + + int nRet = 0; + if (!fRun) + { +#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */ + if (fUnregister) + { + g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE); + nRet = g_pModule->UnregisterServer(TRUE); + } + if (fRegister) + { + g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE); + nRet = g_pModule->RegisterServer(TRUE); + } +#endif + if (pszPipeName) + { + Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName)); + + if (!*pszPipeName) + vrc = VERR_INVALID_PARAMETER; + + if (RT_SUCCESS(vrc)) + { + /* do the helper job */ + SVCHlpServer server; + vrc = server.open(pszPipeName); + if (RT_SUCCESS(vrc)) + vrc = server.run(); + } + if (RT_FAILURE(vrc)) + { + Log(("SVCMAIN: Failed to process Helper request (%Rrc).\n", vrc)); + nRet = 1; + } + } + } + else + { + + g_pModule->StartMonitor(); +#if _WIN32_WINNT >= 0x0400 + hRes = g_pModule->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED); + _ASSERTE(SUCCEEDED(hRes)); + hRes = CoResumeClassObjects(); +#else + hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE); +#endif + _ASSERTE(SUCCEEDED(hRes)); + + /* + * Register windows console signal handler to react to Ctrl-C, + * Ctrl-Break, Close; but more importantly - to get notified + * about shutdown when we are running in the context of the + * autostart service - we won't get WM_ENDSESSION in that + * case. + */ + ::SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE); + + + if (RT_SUCCESS(CreateMainWindow())) + Log(("SVCMain: Main window succesfully created\n")); + else + Log(("SVCMain: Failed to create main window\n")); + + /* create thread to register IVirtualBox in VBoxSDS + * It is used for starting the VBoxSVC in the windows + * session 0. */ + HANDLE hWaitEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + HANDLE hRegisterVBoxThread = NULL; + if (fRegisterVBox) + { + DWORD dwThreadId = 0; + hRegisterVBoxThread = CreateThread(NULL, 0, threadRegisterVirtualBox, (LPVOID)hWaitEvent, + 0, &dwThreadId); + } + + MSG msg; + while (GetMessage(&msg, 0, 0, 0) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + DestroyMainWindow(); + + if (fRegisterVBox) + { + SetEvent(hWaitEvent); + WaitForSingleObject(hRegisterVBoxThread, INFINITE); + CloseHandle(hRegisterVBoxThread); + CloseHandle(hWaitEvent); + } + + g_pModule->RevokeClassObjects(); + } + + g_pModule->Term(); + +#ifdef VBOX_WITH_SDS + g_fRegisteredWithVBoxSDS = false; /* Don't trust COM LPC to work right from now on. */ +#endif + com::Shutdown(); + + if(g_pModule) + delete g_pModule; + g_pModule = NULL; + + Log(("SVCMAIN: Returning, COM server process ends.\n")); + return nRet; +} |