diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Main/src-server/solaris | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Main/src-server/solaris')
-rw-r--r-- | src/VBox/Main/src-server/solaris/DynLoadLibSolaris.cpp | 85 | ||||
-rw-r--r-- | src/VBox/Main/src-server/solaris/DynLoadLibSolaris.h | 50 | ||||
-rw-r--r-- | src/VBox/Main/src-server/solaris/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/Main/src-server/solaris/NetIf-solaris.cpp | 559 | ||||
-rw-r--r-- | src/VBox/Main/src-server/solaris/PerformanceSolaris.cpp | 743 | ||||
-rw-r--r-- | src/VBox/Main/src-server/solaris/USBProxyBackendSolaris.cpp | 496 |
6 files changed, 1933 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.cpp b/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.cpp new file mode 100644 index 00000000..1b7634dc --- /dev/null +++ b/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.cpp @@ -0,0 +1,85 @@ +/* $Id: DynLoadLibSolaris.cpp $ */ +/** @file + * Dynamically load libraries for Solaris hosts. + */ + +/* + * Copyright (C) 2008-2022 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 "DynLoadLibSolaris.h" + +#include <iprt/errcore.h> +#include <iprt/ldr.h> + + +/** -=-=-=-=-= LIB DLPI -=-=-=-=-=-=- **/ + +/** + * Global pointer to the libdlpi module. This should only be set once all needed libraries + * and symbols have been successfully loaded. + */ +static RTLDRMOD g_hLibDlpi = NIL_RTLDRMOD; + +/** + * Whether we have tried to load libdlpi yet. This flag should only be set + * to "true" after we have either loaded both libraries and all symbols which we need, + * or failed to load something and unloaded. + */ +static bool g_fCheckedForLibDlpi = false; + +/** All the symbols we need from libdlpi. + * @{ + */ +int (*g_pfnLibDlpiWalk)(dlpi_walkfunc_t *, void *, uint_t); +int (*g_pfnLibDlpiOpen)(const char *, dlpi_handle_t *, uint_t); +void (*g_pfnLibDlpiClose)(dlpi_handle_t); +/** @} */ + +bool VBoxSolarisLibDlpiFound(void) +{ + RTLDRMOD hLibDlpi; + + if (g_fCheckedForLibDlpi) + return g_hLibDlpi != NIL_RTLDRMOD; + g_fCheckedForLibDlpi = true; + int rc = RTLdrLoad(LIB_DLPI, &hLibDlpi); + if (RT_SUCCESS(rc)) + { + /* + * Unfortunately; we cannot make use of dlpi_get_physaddr because it requires us to + * open the VNIC/link which requires root permissions :/ + */ + rc = RTLdrGetSymbol(hLibDlpi, "dlpi_walk", (void **)&g_pfnLibDlpiWalk); + rc |= RTLdrGetSymbol(hLibDlpi, "dlpi_close", (void **)&g_pfnLibDlpiClose); + rc |= RTLdrGetSymbol(hLibDlpi, "dlpi_open", (void **)&g_pfnLibDlpiOpen); + if (RT_SUCCESS(rc)) + { + g_hLibDlpi = hLibDlpi; + return true; + } + + RTLdrClose(hLibDlpi); + } + hLibDlpi = NIL_RTLDRMOD; + return false; +} + diff --git a/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.h b/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.h new file mode 100644 index 00000000..3ac3055c --- /dev/null +++ b/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.h @@ -0,0 +1,50 @@ +/* $Id: DynLoadLibSolaris.h $ */ +/** @file + * Dynamically loaded libraries for Solaris hosts, Internal header. + */ + +/* + * Copyright (C) 2008-2022 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_solaris_DynLoadLibSolaris_h +#define MAIN_INCLUDED_SRC_src_server_solaris_DynLoadLibSolaris_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#define LIB_DLPI "libdlpi.so.1" +#ifdef RT_OS_SOLARIS_10 +#include <sys/dlpi.h> +#else +#include <libdlpi.h> +#endif + +typedef boolean_t dlpi_walkfunc_t(const char*, void *); + +extern int (*g_pfnLibDlpiWalk)(dlpi_walkfunc_t *, void *, uint_t); +extern int (*g_pfnLibDlpiOpen)(const char *, dlpi_handle_t *, uint_t); +extern void (*g_pfnLibDlpiClose)(dlpi_handle_t); + +extern bool VBoxSolarisLibDlpiFound(void); + +#endif /* !MAIN_INCLUDED_SRC_src_server_solaris_DynLoadLibSolaris_h */ + diff --git a/src/VBox/Main/src-server/solaris/Makefile.kup b/src/VBox/Main/src-server/solaris/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Main/src-server/solaris/Makefile.kup diff --git a/src/VBox/Main/src-server/solaris/NetIf-solaris.cpp b/src/VBox/Main/src-server/solaris/NetIf-solaris.cpp new file mode 100644 index 00000000..77337463 --- /dev/null +++ b/src/VBox/Main/src-server/solaris/NetIf-solaris.cpp @@ -0,0 +1,559 @@ +/* $Id: NetIf-solaris.cpp $ */ +/** @file + * Main - NetIfList, Solaris implementation. + */ + +/* + * Copyright (C) 2008-2022 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/errcore.h> +#include <iprt/ctype.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <list> + +#include "LoggingNew.h" +#include "HostNetworkInterfaceImpl.h" +#include "netif.h" + +#ifdef VBOX_WITH_HOSTNETIF_API + +#include <map> +#include <iprt/sanitized/string> +#include <fcntl.h> +#include <unistd.h> +#include <stropts.h> +#include <limits.h> +#include <stdio.h> +#include <libdevinfo.h> +#include <net/if.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <net/if_arp.h> +#include <net/if.h> +#include <sys/types.h> +#include <kstat.h> + +#include "DynLoadLibSolaris.h" + +/** @todo Unify this define with VBoxNetFltBow-solaris.c */ +#define VBOXBOW_VNIC_TEMPLATE_NAME "vboxvnic_template" + + +static uint32_t getInstance(const char *pszIfaceName, char *pszDevName) +{ + /* + * Get the instance number from the interface name, then clip it off. + */ + int cbInstance = 0; + size_t cbIface = strlen(pszIfaceName); + const char *pszEnd = pszIfaceName + cbIface - 1; + for (size_t i = 0; i < cbIface - 1; i++) + { + if (!RT_C_IS_DIGIT(*pszEnd)) + break; + cbInstance++; + pszEnd--; + } + + uint32_t uInstance = RTStrToUInt32(pszEnd + 1); + strncpy(pszDevName, pszIfaceName, cbIface - cbInstance); + pszDevName[cbIface - cbInstance] = '\0'; + return uInstance; +} + +static uint32_t kstatGet(const char *name) +{ + kstat_ctl_t *kc; + uint32_t uSpeed = 0; + + if ((kc = kstat_open()) == 0) + { + LogRel(("kstat_open() -> %d\n", errno)); + return 0; + } + + kstat_t *ksAdapter = kstat_lookup(kc, (char *)"link", -1, (char *)name); + if (ksAdapter == 0) + { + char szModule[KSTAT_STRLEN]; + uint32_t uInstance = getInstance(name, szModule); + ksAdapter = kstat_lookup(kc, szModule, uInstance, (char *)"phys"); + if (ksAdapter == 0) + ksAdapter = kstat_lookup(kc, szModule, uInstance, (char*)name); + } + if (ksAdapter == 0) + LogRel(("Failed to get network statistics for %s\n", name)); + else if (kstat_read(kc, ksAdapter, 0) == -1) + LogRel(("kstat_read(%s) -> %d\n", name, errno)); + else + { + kstat_named_t *kn; + if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"ifspeed")) != NULL) + uSpeed = (uint32_t)(kn->value.ul / 1000000); /* bits -> Mbits */ + else + LogRel(("kstat_data_lookup(ifspeed) -> %d, name=%s\n", errno, name)); + } + kstat_close(kc); + LogFlow(("kstatGet(%s) -> %u Mbit/s\n", name, uSpeed)); + return uSpeed; +} + +static void queryIfaceSpeed(PNETIFINFO pInfo) +{ + /* Don't query interface speed for inactive interfaces (see @bugref{6345}). */ + if (pInfo->enmStatus == NETIF_S_UP) + pInfo->uSpeedMbits = kstatGet(pInfo->szShortName); + else + pInfo->uSpeedMbits = 0; + LogFlow(("queryIfaceSpeed(%s) -> %u\n", pInfo->szShortName, pInfo->uSpeedMbits)); +} + +static void vboxSolarisAddHostIface(char *pszIface, int Instance, void *pvHostNetworkInterfaceList) +{ + std::list<ComObjPtr<HostNetworkInterface> > *pList = + (std::list<ComObjPtr<HostNetworkInterface> > *)pvHostNetworkInterfaceList; + Assert(pList); + + typedef std::map <std::string, std::string> NICMap; + typedef std::pair <std::string, std::string> NICPair; + static NICMap SolarisNICMap; + if (SolarisNICMap.empty()) + { + SolarisNICMap.insert(NICPair("afe", "ADMtek Centaur/Comet Fast Ethernet")); + SolarisNICMap.insert(NICPair("atge", "Atheros/Attansic Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("aggr", "Link Aggregation Interface")); + SolarisNICMap.insert(NICPair("bfe", "Broadcom BCM4401 Fast Ethernet")); + SolarisNICMap.insert(NICPair("bge", "Broadcom BCM57xx Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("bnx", "Broadcom NetXtreme Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("bnxe", "Broadcom NetXtreme II 10 Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("ce", "Cassini Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("chxge", "Chelsio Ethernet")); + SolarisNICMap.insert(NICPair("dmfe", "Davicom 9102 Fast Ethernet")); + SolarisNICMap.insert(NICPair("dnet", "DEC 21040/41 21140 Ethernet")); + SolarisNICMap.insert(NICPair("e1000", "Intel PRO/1000 Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("e1000g", "Intel PRO/1000 Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("elx", "3COM Etherlink III Ethernet")); + SolarisNICMap.insert(NICPair("elxl", "3COM Etherlink XL Ethernet")); + SolarisNICMap.insert(NICPair("eri", "eri Fast Ethernet")); + SolarisNICMap.insert(NICPair("ge", "GEM Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("hme", "SUNW,hme Fast-Ethernet")); + SolarisNICMap.insert(NICPair("hxge", "Sun Blade 10 Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("igb", "Intel 82575 PCI-E Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("ipge", "PCI-E Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("iprb", "Intel 82557/58/59 Ethernet")); + SolarisNICMap.insert(NICPair("ixgb", "Intel 82597ex 10 Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("ixgbe", "Intel 10 Gigabit PCI-E Ethernet")); + SolarisNICMap.insert(NICPair("mcxe", "Mellanox ConnectX-2 10 Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("mxfe", "Macronix 98715 Fast Ethernet")); + SolarisNICMap.insert(NICPair("nfo", "Nvidia Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("nge", "Nvidia Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("ntxn", "NetXen 10/1 Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("nxge", "Sun 10/1 Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("pcelx", "3COM EtherLink III PCMCIA Ethernet")); + SolarisNICMap.insert(NICPair("pcn", "AMD PCnet Ethernet")); + SolarisNICMap.insert(NICPair("qfe", "SUNW,qfe Quad Fast-Ethernet")); + SolarisNICMap.insert(NICPair("rge", "Realtek Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("rtls", "Realtek 8139 Fast Ethernet")); + SolarisNICMap.insert(NICPair("sfe", "SiS900 Fast Ethernet")); + SolarisNICMap.insert(NICPair("skge", "SksKonnect Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("spwr", "SMC EtherPower II 10/100 (9432) Ethernet")); + SolarisNICMap.insert(NICPair("vboxnet", "VirtualBox Host Ethernet")); + SolarisNICMap.insert(NICPair(VBOXBOW_VNIC_TEMPLATE_NAME, "VirtualBox VNIC Template")); + SolarisNICMap.insert(NICPair("vlan", "Virtual LAN Ethernet")); + SolarisNICMap.insert(NICPair("vr", "VIA Rhine Fast Ethernet")); + SolarisNICMap.insert(NICPair("vnic", "Virtual Network Interface Ethernet")); + SolarisNICMap.insert(NICPair("xge", "Neterior Xframe 10Gigabit Ethernet")); + SolarisNICMap.insert(NICPair("yge", "Marvell Yukon 2 Fast Ethernet")); + } + + /* + * Try picking up description from our NIC map. + */ + char szNICInstance[128]; + RTStrPrintf(szNICInstance, sizeof(szNICInstance), "%s%d", pszIface, Instance); + char szNICDesc[256]; + std::string Description = SolarisNICMap[pszIface]; + if (Description != "VirtualBox Host Ethernet") + { + if (Description != "") + RTStrPrintf(szNICDesc, sizeof(szNICDesc), "%s - %s", szNICInstance, Description.c_str()); + else if (!strncmp(szNICInstance, RT_STR_TUPLE(VBOXBOW_VNIC_TEMPLATE_NAME))) + { + /* + * We want prefix matching only for "vboxvnic_template" as it's possible to create "vboxvnic_template_abcd123", + * which our Solaris Crossbow NetFilter driver will interpret as a VNIC template. + */ + Description = SolarisNICMap[VBOXBOW_VNIC_TEMPLATE_NAME]; + RTStrPrintf(szNICDesc, sizeof(szNICDesc), "%s - %s", szNICInstance, Description.c_str()); + } + else + RTStrPrintf(szNICDesc, sizeof(szNICDesc), "%s - Ethernet", szNICInstance); + } + else + RTStrPrintf(szNICDesc, sizeof(szNICDesc), "%s", szNICInstance); + + /* + * Try to get IP V4 address and netmask as well as Ethernet address. + */ + NETIFINFO Info; + RT_ZERO(Info); + int Sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (Sock > 0) + { + struct lifreq IfReq; + RTStrCopy(IfReq.lifr_name, sizeof(IfReq.lifr_name), szNICInstance); + if (ioctl(Sock, SIOCGLIFADDR, &IfReq) >= 0) + { + memcpy(Info.IPAddress.au8, &((struct sockaddr_in *)&IfReq.lifr_addr)->sin_addr.s_addr, + sizeof(Info.IPAddress.au8)); + struct arpreq ArpReq; + memcpy(&ArpReq.arp_pa, &IfReq.lifr_addr, sizeof(struct sockaddr_in)); + + /* + * We might fail if the interface has not been assigned an IP address. + * That doesn't matter; as long as it's plumbed we can pick it up. + * But, if it has not acquired an IP address we cannot obtain it's MAC + * address this way, so we just use all zeros there. + */ + if (ioctl(Sock, SIOCGARP, &ArpReq) >= 0) + { + memcpy(&Info.MACAddress, ArpReq.arp_ha.sa_data, sizeof(Info.MACAddress)); + } + + } + + if (ioctl(Sock, SIOCGLIFNETMASK, &IfReq) >= 0) + { + memcpy(Info.IPNetMask.au8, &((struct sockaddr_in *)&IfReq.lifr_addr)->sin_addr.s_addr, + sizeof(Info.IPNetMask.au8)); + } + if (ioctl(Sock, SIOCGLIFFLAGS, &IfReq) >= 0) + { + Info.enmStatus = IfReq.lifr_flags & IFF_UP ? NETIF_S_UP : NETIF_S_DOWN; + } + close(Sock); + } + /* + * Try to get IP V6 address and netmask. + */ + Sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); + if (Sock > 0) + { + struct lifreq IfReq; + RTStrCopy(IfReq.lifr_name, sizeof(IfReq.lifr_name), szNICInstance); + if (ioctl(Sock, SIOCGLIFADDR, &IfReq) >= 0) + { + memcpy(Info.IPv6Address.au8, ((struct sockaddr_in6 *)&IfReq.lifr_addr)->sin6_addr.s6_addr, + sizeof(Info.IPv6Address.au8)); + } + if (ioctl(Sock, SIOCGLIFNETMASK, &IfReq) >= 0) + { + memcpy(Info.IPv6NetMask.au8, ((struct sockaddr_in6 *)&IfReq.lifr_addr)->sin6_addr.s6_addr, + sizeof(Info.IPv6NetMask.au8)); + } + close(Sock); + } + + /* + * Construct UUID with interface name and the MAC address if available. + */ + RTUUID Uuid; + RTUuidClear(&Uuid); + memcpy(&Uuid, szNICInstance, RT_MIN(strlen(szNICInstance), sizeof(Uuid))); + Uuid.Gen.u8ClockSeqHiAndReserved = (Uuid.Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80; + Uuid.Gen.u16TimeHiAndVersion = (Uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000; + Uuid.Gen.au8Node[0] = Info.MACAddress.au8[0]; + Uuid.Gen.au8Node[1] = Info.MACAddress.au8[1]; + Uuid.Gen.au8Node[2] = Info.MACAddress.au8[2]; + Uuid.Gen.au8Node[3] = Info.MACAddress.au8[3]; + Uuid.Gen.au8Node[4] = Info.MACAddress.au8[4]; + Uuid.Gen.au8Node[5] = Info.MACAddress.au8[5]; + Info.Uuid = Uuid; + Info.enmMediumType = NETIF_T_ETHERNET; + strncpy(Info.szShortName, szNICInstance, sizeof(Info.szShortName) - 1); + + HostNetworkInterfaceType_T enmType; + if (strncmp(szNICInstance, RT_STR_TUPLE("vboxnet"))) + enmType = HostNetworkInterfaceType_Bridged; + else + enmType = HostNetworkInterfaceType_HostOnly; + queryIfaceSpeed(&Info); + ComObjPtr<HostNetworkInterface> IfObj; + IfObj.createObject(); + if (SUCCEEDED(IfObj->init(szNICDesc, enmType, &Info))) + pList->push_back(IfObj); +} + +static boolean_t vboxSolarisAddLinkHostIface(const char *pszIface, void *pvHostNetworkInterfaceList) +{ + /* + * Skip IPSEC interfaces. It's at IP level. + */ + if (!strncmp(pszIface, RT_STR_TUPLE("ip.tun"))) + return _B_FALSE; + + /* + * Skip our own dynamic VNICs but don't skip VNIC templates. + * These names originate from VBoxNetFltBow-solaris.c, hardcoded here for now. + * . + * ASSUMES template name is longer than 'vboxvnic'. + */ + if ( strncmp(pszIface, RT_STR_TUPLE(VBOXBOW_VNIC_TEMPLATE_NAME)) + && !strncmp(pszIface, RT_STR_TUPLE("vboxvnic"))) + return _B_FALSE; + + /* + * Clip off the zone instance number from the interface name (if any). + */ + char szIfaceName[128]; + strcpy(szIfaceName, pszIface); + char *pszColon = (char *)memchr(szIfaceName, ':', sizeof(szIfaceName)); + if (pszColon) + *pszColon = '\0'; + + /* + * Get the instance number from the interface name, then clip it off. + */ + int cbInstance = 0; + size_t cbIface = strlen(szIfaceName); + const char *pszEnd = pszIface + cbIface - 1; + for (size_t i = 0; i < cbIface - 1; i++) + { + if (!RT_C_IS_DIGIT(*pszEnd)) + break; + cbInstance++; + pszEnd--; + } + + int Instance = atoi(pszEnd + 1); + strncpy(szIfaceName, pszIface, cbIface - cbInstance); + szIfaceName[cbIface - cbInstance] = '\0'; + + /* + * Add the interface. + */ + vboxSolarisAddHostIface(szIfaceName, Instance, pvHostNetworkInterfaceList); + + /* + * Continue walking... + */ + return _B_FALSE; +} + +static bool vboxSolarisSortNICList(const ComObjPtr<HostNetworkInterface> Iface1, const ComObjPtr<HostNetworkInterface> Iface2) +{ + Bstr Iface1Str; + (*Iface1).COMGETTER(Name) (Iface1Str.asOutParam()); + + Bstr Iface2Str; + (*Iface2).COMGETTER(Name) (Iface2Str.asOutParam()); + + return Iface1Str < Iface2Str; +} + +static bool vboxSolarisSameNIC(const ComObjPtr<HostNetworkInterface> Iface1, const ComObjPtr<HostNetworkInterface> Iface2) +{ + Bstr Iface1Str; + (*Iface1).COMGETTER(Name) (Iface1Str.asOutParam()); + + Bstr Iface2Str; + (*Iface2).COMGETTER(Name) (Iface2Str.asOutParam()); + + return (Iface1Str == Iface2Str); +} + +static int vboxSolarisAddPhysHostIface(di_node_t Node, di_minor_t Minor, void *pvHostNetworkInterfaceList) +{ + NOREF(Minor); + + char *pszDriverName = di_driver_name(Node); + int Instance = di_instance(Node); + + /* + * Skip aggregations. + */ + if (!strcmp(pszDriverName, "aggr")) + return DI_WALK_CONTINUE; + + /* + * Skip softmacs. + */ + if (!strcmp(pszDriverName, "softmac")) + return DI_WALK_CONTINUE; + + /* + * Driver names doesn't always imply the same link name probably since + * S11's vanity names by default (e.g. highly descriptive "net0") names + * was introduced. Try opening the link to find out if it really exists. + * + * This weeds out listing of "e1000g0" as a valid interface on my S11.2 + * Dell Optiplex box. + */ + if (VBoxSolarisLibDlpiFound()) + { + /** @todo should we try also opening "linkname+instance"? */ + dlpi_handle_t hLink; + if (g_pfnLibDlpiOpen(pszDriverName, &hLink, 0) != DLPI_SUCCESS) + return DI_WALK_CONTINUE; + g_pfnLibDlpiClose(hLink); + } + + vboxSolarisAddHostIface(pszDriverName, Instance, pvHostNetworkInterfaceList); + return DI_WALK_CONTINUE; +} + +int NetIfList(std::list<ComObjPtr<HostNetworkInterface> > &list) +{ + /* + * Use libdevinfo for determining all physical interfaces. + */ + di_node_t Root; + Root = di_init("/", DINFOCACHE); + if (Root != DI_NODE_NIL) + { + di_walk_minor(Root, DDI_NT_NET, 0 /* flag */, &list, vboxSolarisAddPhysHostIface); + di_fini(Root); + } + + /* + * Use libdlpi for determining all DLPI interfaces. + */ + if (VBoxSolarisLibDlpiFound()) + g_pfnLibDlpiWalk(vboxSolarisAddLinkHostIface, &list, 0); + + /* + * This gets only the list of all plumbed logical interfaces. + * This is needed for zones which cannot access the device tree + * and in this case we just let them use the list of plumbed interfaces + * on the zone. + */ + int Sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (Sock > 0) + { + struct lifnum IfNum; + RT_ZERO(IfNum); + IfNum.lifn_family = AF_INET; + int rc = ioctl(Sock, SIOCGLIFNUM, &IfNum); + if (!rc) + { + int cIfaces = RT_MIN(1024, IfNum.lifn_count); /* sane limit */ + size_t cbIfaces = (unsigned)RT_MAX(cIfaces, 1) * sizeof(struct lifreq); + struct lifreq *paIfaces = (struct lifreq *)RTMemTmpAlloc(cbIfaces); + if (paIfaces) + { + struct lifconf IfConfig; + RT_ZERO(IfConfig); + IfConfig.lifc_family = AF_INET; + IfConfig.lifc_len = (int)cbIfaces; + IfConfig.lifc_buf = (caddr_t)paIfaces; + rc = ioctl(Sock, SIOCGLIFCONF, &IfConfig); + if (!rc) + { + for (int i = 0; i < cIfaces; i++) + { + /* + * Skip loopback interfaces. + */ + if (!strncmp(paIfaces[i].lifr_name, RT_STR_TUPLE("lo"))) + continue; + +#if 0 + rc = ioctl(Sock, SIOCGLIFADDR, &(paIfaces[i])); + if (rc >= 0) + { + memcpy(Info.IPAddress.au8, ((struct sockaddr *)&paIfaces[i].lifr_addr)->sa_data, + sizeof(Info.IPAddress.au8)); + // SIOCGLIFNETMASK + struct arpreq ArpReq; + memcpy(&ArpReq.arp_pa, &paIfaces[i].lifr_addr, sizeof(struct sockaddr_in)); + + /* + * We might fail if the interface has not been assigned an IP address. + * That doesn't matter; as long as it's plumbed we can pick it up. + * But, if it has not acquired an IP address we cannot obtain it's MAC + * address this way, so we just use all zeros there. + */ + rc = ioctl(Sock, SIOCGARP, &ArpReq); + if (rc >= 0) + memcpy(&Info.MACAddress, ArpReq.arp_ha.sa_data, sizeof(Info.MACAddress)); + + char szNICDesc[LIFNAMSIZ + 256]; + char *pszIface = paIfaces[i].lifr_name; + strcpy(szNICDesc, pszIface); + + vboxSolarisAddLinkHostIface(pszIface, &list); + } +#endif + + vboxSolarisAddLinkHostIface(paIfaces[i].lifr_name, &list); + } + } + RTMemTmpFree(paIfaces); + } + } + close(Sock); + } + + /* + * Weed out duplicates caused by dlpi_walk inconsistencies across Nevadas. + */ + list.sort(vboxSolarisSortNICList); + list.unique(vboxSolarisSameNIC); + + return VINF_SUCCESS; +} + +#else +int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list) +{ + return VERR_NOT_IMPLEMENTED; +} +#endif + +int NetIfGetConfigByName(PNETIFINFO pInfo) +{ + NOREF(pInfo); + 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) +{ + *puMbits = kstatGet(pcszIfName); + return VINF_SUCCESS; +} diff --git a/src/VBox/Main/src-server/solaris/PerformanceSolaris.cpp b/src/VBox/Main/src-server/solaris/PerformanceSolaris.cpp new file mode 100644 index 00000000..54ce7dbf --- /dev/null +++ b/src/VBox/Main/src-server/solaris/PerformanceSolaris.cpp @@ -0,0 +1,743 @@ +/* $Id: PerformanceSolaris.cpp $ */ +/** @file + * VBox Solaris-specific Performance Classes implementation. + */ + +/* + * Copyright (C) 2008-2022 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 +#undef _FILE_OFFSET_BITS +#include <procfs.h> +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <kstat.h> +#include <unistd.h> +#include <sys/sysinfo.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/statvfs.h> + +#include <iprt/ctype.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include <iprt/alloc.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/system.h> + +#include "LoggingNew.h" +#include "Performance.h" + +#include <dlfcn.h> + +#include <libzfs.h> +#include <libnvpair.h> + +#include <map> + +namespace pm { + + typedef libzfs_handle_t *(*PFNZFSINIT)(void); + typedef void (*PFNZFSFINI)(libzfs_handle_t *); + typedef zfs_handle_t *(*PFNZFSOPEN)(libzfs_handle_t *, const char *, int); + typedef void (*PFNZFSCLOSE)(zfs_handle_t *); + typedef uint64_t (*PFNZFSPROPGETINT)(zfs_handle_t *, zfs_prop_t); + typedef zpool_handle_t *(*PFNZPOOLOPEN)(libzfs_handle_t *, const char *); + typedef void (*PFNZPOOLCLOSE)(zpool_handle_t *); + typedef nvlist_t *(*PFNZPOOLGETCONFIG)(zpool_handle_t *, nvlist_t **); + typedef char *(*PFNZPOOLVDEVNAME)(libzfs_handle_t *, zpool_handle_t *, nvlist_t *, boolean_t); + + typedef std::map<RTCString,RTCString> FsMap; + +class CollectorSolaris : public CollectorHAL +{ +public: + CollectorSolaris(); + virtual ~CollectorSolaris(); + virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available); + virtual int getHostFilesystemUsage(const char *name, ULONG *total, ULONG *used, ULONG *available); + virtual int getHostDiskSize(const char *name, uint64_t *size); + virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used); + + virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle); + virtual int getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx); + virtual int getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms); + virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total); + + virtual int getDiskListByFs(const char *name, DiskList& listUsage, DiskList& listLoad); +private: + static uint32_t getInstance(const char *pszIfaceName, char *pszDevName); + uint64_t getZfsTotal(uint64_t cbTotal, const char *szFsType, const char *szFsName); + void updateFilesystemMap(void); + RTCString physToInstName(const char *pcszPhysName); + RTCString pathToInstName(const char *pcszDevPathName); + uint64_t wrapCorrection(uint32_t cur, uint64_t prev, const char *name); + uint64_t wrapDetection(uint64_t cur, uint64_t prev, const char *name); + + kstat_ctl_t *mKC; + kstat_t *mSysPages; + kstat_t *mZFSCache; + + void *mZfsSo; + libzfs_handle_t *mZfsLib; + PFNZFSINIT mZfsInit; + PFNZFSFINI mZfsFini; + PFNZFSOPEN mZfsOpen; + PFNZFSCLOSE mZfsClose; + PFNZFSPROPGETINT mZfsPropGetInt; + PFNZPOOLOPEN mZpoolOpen; + PFNZPOOLCLOSE mZpoolClose; + PFNZPOOLGETCONFIG mZpoolGetConfig; + PFNZPOOLVDEVNAME mZpoolVdevName; + + FsMap mFsMap; + uint32_t mCpus; + ULONG totalRAM; +}; + +CollectorHAL *createHAL() +{ + return new CollectorSolaris(); +} + +// Collector HAL for Solaris + + +CollectorSolaris::CollectorSolaris() + : mKC(0), + mSysPages(0), + mZFSCache(0), + mZfsLib(0), + mCpus(0) +{ + if ((mKC = kstat_open()) == 0) + { + Log(("kstat_open() -> %d\n", errno)); + return; + } + + if ((mSysPages = kstat_lookup(mKC, (char *)"unix", 0, (char *)"system_pages")) == 0) + { + Log(("kstat_lookup(system_pages) -> %d\n", errno)); + return; + } + + if ((mZFSCache = kstat_lookup(mKC, (char *)"zfs", 0, (char *)"arcstats")) == 0) + { + Log(("kstat_lookup(system_pages) -> %d\n", errno)); + } + + /* Try to load libzfs dynamically, it may be missing. */ + mZfsSo = dlopen("libzfs.so", RTLD_LAZY); + if (mZfsSo) + { + mZfsInit = (PFNZFSINIT)(uintptr_t)dlsym(mZfsSo, "libzfs_init"); + mZfsFini = (PFNZFSFINI)(uintptr_t)dlsym(mZfsSo, "libzfs_fini"); + mZfsOpen = (PFNZFSOPEN)(uintptr_t)dlsym(mZfsSo, "zfs_open"); + mZfsClose = (PFNZFSCLOSE)(uintptr_t)dlsym(mZfsSo, "zfs_close"); + mZfsPropGetInt = (PFNZFSPROPGETINT)(uintptr_t)dlsym(mZfsSo, "zfs_prop_get_int"); + mZpoolOpen = (PFNZPOOLOPEN)(uintptr_t)dlsym(mZfsSo, "zpool_open"); + mZpoolClose = (PFNZPOOLCLOSE)(uintptr_t)dlsym(mZfsSo, "zpool_close"); + mZpoolGetConfig = (PFNZPOOLGETCONFIG)(uintptr_t)dlsym(mZfsSo, "zpool_get_config"); + mZpoolVdevName = (PFNZPOOLVDEVNAME)(uintptr_t)dlsym(mZfsSo, "zpool_vdev_name"); + + if ( mZfsInit + && mZfsOpen + && mZfsClose + && mZfsPropGetInt + && mZpoolOpen + && mZpoolClose + && mZpoolGetConfig + && mZpoolVdevName) + mZfsLib = mZfsInit(); + else + LogRel(("Incompatible libzfs? libzfs_init=%p zfs_open=%p zfs_close=%p zfs_prop_get_int=%p\n", + mZfsInit, mZfsOpen, mZfsClose, mZfsPropGetInt)); + } + + updateFilesystemMap(); + /* Notice that mCpus member will be initialized by HostCpuLoadRaw::init() */ + + uint64_t cb; + int rc = RTSystemQueryTotalRam(&cb); + if (RT_FAILURE(rc)) + totalRAM = 0; + else + totalRAM = (ULONG)(cb / 1024); +} + +CollectorSolaris::~CollectorSolaris() +{ + if (mKC) + kstat_close(mKC); + /* Not calling libzfs_fini() causes file descriptor leaks (#6788). */ + if (mZfsFini && mZfsLib) + mZfsFini(mZfsLib); + if (mZfsSo) + dlclose(mZfsSo); +} + +int CollectorSolaris::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle) +{ + int rc = VINF_SUCCESS; + kstat_t *ksp; + uint64_t tmpUser, tmpKernel, tmpIdle; + int cpus; + cpu_stat_t cpu_stats; + + if (mKC == 0) + return VERR_INTERNAL_ERROR; + + tmpUser = tmpKernel = tmpIdle = cpus = 0; + for (ksp = mKC->kc_chain; ksp != NULL; ksp = ksp->ks_next) { + if (strcmp(ksp->ks_module, "cpu_stat") == 0) { + if (kstat_read(mKC, ksp, &cpu_stats) == -1) + { + Log(("kstat_read() -> %d\n", errno)); + return VERR_INTERNAL_ERROR; + } + ++cpus; + tmpUser += cpu_stats.cpu_sysinfo.cpu[CPU_USER]; + tmpKernel += cpu_stats.cpu_sysinfo.cpu[CPU_KERNEL]; + tmpIdle += cpu_stats.cpu_sysinfo.cpu[CPU_IDLE]; + } + } + + if (cpus == 0) + { + Log(("no cpu stats found!\n")); + return VERR_INTERNAL_ERROR; + } + else + mCpus = cpus; + + if (user) *user = tmpUser; + if (kernel) *kernel = tmpKernel; + if (idle) *idle = tmpIdle; + + return rc; +} + +int CollectorSolaris::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total) +{ + int rc = VINF_SUCCESS; + char *pszName; + prusage_t prusage; + + RTStrAPrintf(&pszName, "/proc/%d/usage", process); + Log(("Opening %s...\n", pszName)); + int h = open(pszName, O_RDONLY); + RTStrFree(pszName); + + if (h != -1) + { + if (read(h, &prusage, sizeof(prusage)) == sizeof(prusage)) + { + //Assert((pid_t)process == pstatus.pr_pid); + //Log(("user=%u kernel=%u total=%u\n", prusage.pr_utime.tv_sec, prusage.pr_stime.tv_sec, prusage.pr_tstamp.tv_sec)); + /* + * The CPU time spent must be adjusted by the number of cores for compatibility with + * other platforms (see @bugref{6345}). + */ + Assert(mCpus); + if (mCpus) + { + *user = ((uint64_t)prusage.pr_utime.tv_sec * 1000000000 + prusage.pr_utime.tv_nsec) / mCpus; + *kernel = ((uint64_t)prusage.pr_stime.tv_sec * 1000000000 + prusage.pr_stime.tv_nsec) / mCpus; + } + else + *user = *kernel = 0; + *total = (uint64_t)prusage.pr_tstamp.tv_sec * 1000000000 + prusage.pr_tstamp.tv_nsec; + //Log(("user=%llu kernel=%llu total=%llu\n", *user, *kernel, *total)); + } + else + { + Log(("read() -> %d\n", errno)); + rc = VERR_FILE_IO_ERROR; + } + close(h); + } + else + { + Log(("open() -> %d\n", errno)); + rc = VERR_ACCESS_DENIED; + } + + return rc; +} + +int CollectorSolaris::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available) +{ + AssertReturn(totalRAM, VERR_INTERNAL_ERROR); + uint64_t cb; + int rc = RTSystemQueryAvailableRam(&cb); + if (RT_SUCCESS(rc)) + { + *total = totalRAM; + *available = (ULONG)RT_MIN(cb / 1024, ~(ULONG)0); + *used = *total - *available; + } + return rc; +} + +int CollectorSolaris::getProcessMemoryUsage(RTPROCESS process, ULONG *used) +{ + int rc = VINF_SUCCESS; + char *pszName = NULL; + psinfo_t psinfo; + + RTStrAPrintf(&pszName, "/proc/%d/psinfo", process); + Log(("Opening %s...\n", pszName)); + int h = open(pszName, O_RDONLY); + RTStrFree(pszName); + + if (h != -1) + { + /* psinfo_t keeps growing, so only read what we need to maximize + * cross-version compatibility. The structures are compatible. */ + ssize_t cb = RT_UOFFSETOF(psinfo_t, pr_rssize) + RT_SIZEOFMEMB(psinfo_t, pr_rssize); + AssertCompile(RTASSERT_OFFSET_OF(psinfo_t, pr_rssize) > RTASSERT_OFFSET_OF(psinfo_t, pr_pid)); + if (read(h, &psinfo, cb) == cb) + { + Assert((pid_t)process == psinfo.pr_pid); + *used = (ULONG)RT_MIN(psinfo.pr_rssize, ~(ULONG)0); + } + else + { + Log(("read() -> %d\n", errno)); + rc = VERR_FILE_IO_ERROR; + } + close(h); + } + else + { + Log(("open() -> %d\n", errno)); + rc = VERR_ACCESS_DENIED; + } + + return rc; +} + +uint32_t CollectorSolaris::getInstance(const char *pszIfaceName, char *pszDevName) +{ + /* + * Get the instance number from the interface name, then clip it off. + */ + int cbInstance = 0; + size_t cbIface = strlen(pszIfaceName); + const char *pszEnd = pszIfaceName + cbIface - 1; + for (size_t i = 0; i < cbIface - 1; i++) + { + if (!RT_C_IS_DIGIT(*pszEnd)) + break; + cbInstance++; + pszEnd--; + } + + uint32_t uInstance = RTStrToUInt32(pszEnd + 1); + strncpy(pszDevName, pszIfaceName, cbIface - cbInstance); + pszDevName[cbIface - cbInstance] = '\0'; + return uInstance; +} + +uint64_t CollectorSolaris::wrapCorrection(uint32_t cur, uint64_t prev, const char *name) +{ + NOREF(name); + uint64_t corrected = (prev & 0xffffffff00000000) + cur; + if (cur < (prev & 0xffffffff)) + { + /* wrap has occurred */ + corrected += 0x100000000; + LogFlowThisFunc(("Corrected wrap on %s (%u < %u), returned %llu.\n", + name, cur, (uint32_t)prev, corrected)); + } + return corrected; +} + +uint64_t CollectorSolaris::wrapDetection(uint64_t cur, uint64_t prev, const char *name) +{ + if (cur < prev) + LogRelMax(2, ("Detected wrap on %s (%llu < %llu).\n", name, cur, prev)); + return cur; +} + +/* + * WARNING! This function expects the previous values of rx and tx counter to + * be passed in as well as returnes new values in the same parameters. This is + * needed to provide a workaround for 32-bit counter wrapping. + */ +int CollectorSolaris::getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx) +{ + static bool g_fNotReported = true; + AssertReturn(strlen(name) < KSTAT_STRLEN, VERR_INVALID_PARAMETER); + LogFlowThisFunc(("m=%s i=%d n=%s\n", "link", -1, name)); + kstat_t *ksAdapter = kstat_lookup(mKC, (char *)"link", -1, (char *)name); + if (ksAdapter == 0) + { + char szModule[KSTAT_STRLEN]; + uint32_t uInstance = getInstance(name, szModule); + LogFlowThisFunc(("m=%s i=%u n=%s\n", szModule, uInstance, "phys")); + ksAdapter = kstat_lookup(mKC, szModule, uInstance, (char *)"phys"); + if (ksAdapter == 0) + { + LogFlowThisFunc(("m=%s i=%u n=%s\n", szModule, uInstance, name)); + ksAdapter = kstat_lookup(mKC, szModule, uInstance, (char *)name); + if (ksAdapter == 0) + { + static uint32_t s_tsLogRelLast; + uint32_t tsNow = RTTimeProgramSecTS(); + if ( tsNow < RT_SEC_1HOUR + || (tsNow - s_tsLogRelLast >= 60)) + { + s_tsLogRelLast = tsNow; + LogRel(("Failed to get network statistics for %s. Max one msg/min.\n", name)); + } + return VERR_INTERNAL_ERROR; + } + } + } + if (kstat_read(mKC, ksAdapter, 0) == -1) + { + LogRel(("kstat_read(adapter) -> %d\n", errno)); + return VERR_INTERNAL_ERROR; + } + kstat_named_t *kn; + if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"rbytes64")) == NULL) + { + if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"rbytes")) == NULL) + { + LogRel(("kstat_data_lookup(rbytes) -> %d, name=%s\n", errno, name)); + return VERR_INTERNAL_ERROR; + } +#if ARCH_BITS == 32 + if (g_fNotReported) + { + g_fNotReported = false; + LogRel(("Failed to locate rbytes64, falling back to 32-bit counters...\n")); + } + *rx = wrapCorrection(kn->value.ul, *rx, "rbytes"); +#else + AssertCompile(sizeof(kn->value.ul) == sizeof(uint64_t)); + *rx = wrapDetection(kn->value.ul, *rx, "rbytes"); +#endif + } + else + *rx = wrapDetection(kn->value.ull, *rx, "rbytes64"); + if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"obytes64")) == NULL) + { + if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"obytes")) == NULL) + { + LogRel(("kstat_data_lookup(obytes) -> %d\n", errno)); + return VERR_INTERNAL_ERROR; + } +#if ARCH_BITS == 32 + if (g_fNotReported) + { + g_fNotReported = false; + LogRel(("Failed to locate obytes64, falling back to 32-bit counters...\n")); + } + *tx = wrapCorrection(kn->value.ul, *tx, "obytes"); +#else + AssertCompile(sizeof(kn->value.ul) == sizeof(uint64_t)); + *tx = wrapDetection(kn->value.ul, *tx, "obytes"); +#endif + } + else + *tx = wrapDetection(kn->value.ull, *tx, "obytes64"); + return VINF_SUCCESS; +} + +int CollectorSolaris::getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms) +{ + int rc = VINF_SUCCESS; + AssertReturn(strlen(name) < KSTAT_STRLEN, VERR_INVALID_PARAMETER); + LogFlowThisFunc(("n=%s\n", name)); + kstat_t *ksDisk = kstat_lookup(mKC, NULL, -1, (char *)name); + if (ksDisk != 0) + { + if (kstat_read(mKC, ksDisk, 0) == -1) + { + LogRel(("kstat_read(%s) -> %d\n", name, errno)); + rc = VERR_INTERNAL_ERROR; + } + else + { + kstat_io_t *ksIo = KSTAT_IO_PTR(ksDisk); + /* + * We do not care for wrap possibility here, although we may + * reconsider in about 300 years (9223372036854775807 ns). + */ + *disk_ms = ksIo->rtime / 1000000; + *total_ms = ksDisk->ks_snaptime / 1000000; + } + } + else + { + LogRel(("kstat_lookup(%s) -> %d\n", name, errno)); + rc = VERR_INTERNAL_ERROR; + } + + return rc; +} + +uint64_t CollectorSolaris::getZfsTotal(uint64_t cbTotal, const char *szFsType, const char *szFsName) +{ + if (strcmp(szFsType, "zfs")) + return cbTotal; + FsMap::iterator it = mFsMap.find(szFsName); + if (it == mFsMap.end()) + return cbTotal; + + char *pszDataset = strdup(it->second.c_str()); + char *pszEnd = pszDataset + strlen(pszDataset); + uint64_t uAvail = 0; + while (pszEnd) + { + zfs_handle_t *hDataset; + + *pszEnd = 0; + hDataset = mZfsOpen(mZfsLib, pszDataset, ZFS_TYPE_DATASET); + if (!hDataset) + break; + + if (uAvail == 0) + { + uAvail = mZfsPropGetInt(hDataset, ZFS_PROP_REFQUOTA); + if (uAvail == 0) + uAvail = UINT64_MAX; + } + + uint64_t uQuota = mZfsPropGetInt(hDataset, ZFS_PROP_QUOTA); + if (uQuota && uAvail > uQuota) + uAvail = uQuota; + + pszEnd = strrchr(pszDataset, '/'); + if (!pszEnd) + { + uint64_t uPoolSize = mZfsPropGetInt(hDataset, ZFS_PROP_USED) + + mZfsPropGetInt(hDataset, ZFS_PROP_AVAILABLE); + if (uAvail > uPoolSize) + uAvail = uPoolSize; + } + mZfsClose(hDataset); + } + free(pszDataset); + + return uAvail ? uAvail : cbTotal; +} + +int CollectorSolaris::getHostFilesystemUsage(const char *path, ULONG *total, ULONG *used, ULONG *available) +{ + struct statvfs64 stats; + + if (statvfs64(path, &stats) == -1) + { + LogRel(("Failed to collect %s filesystem usage: errno=%d.\n", path, errno)); + return VERR_ACCESS_DENIED; + } + uint64_t cbBlock = stats.f_frsize ? stats.f_frsize : stats.f_bsize; + *total = (ULONG)(getZfsTotal(cbBlock * stats.f_blocks, stats.f_basetype, path) / _1M); + LogFlowThisFunc(("f_blocks=%llu.\n", stats.f_blocks)); + *used = (ULONG)(cbBlock * (stats.f_blocks - stats.f_bfree) / _1M); + *available = (ULONG)(cbBlock * stats.f_bavail / _1M); + + return VINF_SUCCESS; +} + +int CollectorSolaris::getHostDiskSize(const char *name, uint64_t *size) +{ + int rc = VINF_SUCCESS; + AssertReturn(strlen(name) + 5 < KSTAT_STRLEN, VERR_INVALID_PARAMETER); + LogFlowThisFunc(("n=%s\n", name)); + char szName[KSTAT_STRLEN]; + strcpy(szName, name); + strcat(szName, ",err"); + kstat_t *ksDisk = kstat_lookup(mKC, NULL, -1, szName); + if (ksDisk != 0) + { + if (kstat_read(mKC, ksDisk, 0) == -1) + { + LogRel(("kstat_read(%s) -> %d\n", name, errno)); + rc = VERR_INTERNAL_ERROR; + } + else + { + kstat_named_t *kn; + if ((kn = (kstat_named_t *)kstat_data_lookup(ksDisk, (char *)"Size")) == 0) + { + LogRel(("kstat_data_lookup(rbytes) -> %d, name=%s\n", errno, name)); + return VERR_INTERNAL_ERROR; + } + *size = kn->value.ull; + } + } + else + { + LogRel(("kstat_lookup(%s) -> %d\n", szName, errno)); + rc = VERR_INTERNAL_ERROR; + } + + + return rc; +} + +RTCString CollectorSolaris::physToInstName(const char *pcszPhysName) +{ + FILE *fp = fopen("/etc/path_to_inst", "r"); + if (!fp) + return RTCString(); + + RTCString strInstName; + size_t cbName = strlen(pcszPhysName); + char szBuf[RTPATH_MAX]; + while (fgets(szBuf, sizeof(szBuf), fp)) + { + if (szBuf[0] == '"' && strncmp(szBuf + 1, pcszPhysName, cbName) == 0) + { + char *pszDriver, *pszInstance; + pszDriver = strrchr(szBuf, '"'); + if (pszDriver) + { + *pszDriver = '\0'; + pszDriver = strrchr(szBuf, '"'); + if (pszDriver) + { + *pszDriver++ = '\0'; + pszInstance = strrchr(szBuf, ' '); + if (pszInstance) + { + *pszInstance = '\0'; + pszInstance = strrchr(szBuf, ' '); + if (pszInstance) + { + *pszInstance++ = '\0'; + strInstName = pszDriver; + strInstName += pszInstance; + break; + } + } + } + } + } + } + fclose(fp); + + return strInstName; +} + +RTCString CollectorSolaris::pathToInstName(const char *pcszDevPathName) +{ + char szLink[RTPATH_MAX]; + if (readlink(pcszDevPathName, szLink, sizeof(szLink)) != -1) + { + char *pszStart, *pszEnd; + pszStart = strstr(szLink, "/devices/"); + pszEnd = strrchr(szLink, ':'); + if (pszStart && pszEnd) + { + pszStart += 8; // Skip "/devices" + *pszEnd = '\0'; // Trim partition + return physToInstName(pszStart); + } + } + + return RTCString(pcszDevPathName); +} + +int CollectorSolaris::getDiskListByFs(const char *name, DiskList& listUsage, DiskList& listLoad) +{ + FsMap::iterator it = mFsMap.find(name); + if (it == mFsMap.end()) + return VERR_INVALID_PARAMETER; + + RTCString strName = it->second.substr(0, it->second.find("/")); + if (mZpoolOpen && mZpoolClose && mZpoolGetConfig && !strName.isEmpty()) + { + zpool_handle_t *zh = mZpoolOpen(mZfsLib, strName.c_str()); + if (zh) + { + unsigned int cChildren = 0; + nvlist_t **nvChildren = NULL; + nvlist_t *nvRoot = NULL; + nvlist_t *nvConfig = mZpoolGetConfig(zh, NULL); + if ( !nvlist_lookup_nvlist(nvConfig, ZPOOL_CONFIG_VDEV_TREE, &nvRoot) + && !nvlist_lookup_nvlist_array(nvRoot, ZPOOL_CONFIG_CHILDREN, &nvChildren, &cChildren)) + { + for (unsigned int i = 0; i < cChildren; ++i) + { + uint64_t fHole = 0; + uint64_t fLog = 0; + + nvlist_lookup_uint64(nvChildren[i], ZPOOL_CONFIG_IS_HOLE, &fHole); + nvlist_lookup_uint64(nvChildren[i], ZPOOL_CONFIG_IS_LOG, &fLog); + + if (!fHole && !fLog) + { + char *pszChildName = mZpoolVdevName(mZfsLib, zh, nvChildren[i], _B_FALSE); + Assert(pszChildName); + RTCString strDevPath("/dev/dsk/"); + strDevPath += pszChildName; + char szLink[RTPATH_MAX]; + if (readlink(strDevPath.c_str(), szLink, sizeof(szLink)) != -1) + { + char *pszStart, *pszEnd; + pszStart = strstr(szLink, "/devices/"); + pszEnd = strrchr(szLink, ':'); + if (pszStart && pszEnd) + { + pszStart += 8; // Skip "/devices" + *pszEnd = '\0'; // Trim partition + listUsage.push_back(physToInstName(pszStart)); + } + } + free(pszChildName); + } + } + } + mZpoolClose(zh); + } + } + else + listUsage.push_back(pathToInstName(it->second.c_str())); + listLoad = listUsage; + return VINF_SUCCESS; +} + +void CollectorSolaris::updateFilesystemMap(void) +{ + FILE *fp = fopen("/etc/mnttab", "r"); + if (fp) + { + struct mnttab Entry; + int rc = 0; + resetmnttab(fp); + while ((rc = getmntent(fp, &Entry)) == 0) + mFsMap[Entry.mnt_mountp] = Entry.mnt_special; + fclose(fp); + if (rc != -1) + LogRel(("Error while reading mnttab: %d\n", rc)); + } +} + +} diff --git a/src/VBox/Main/src-server/solaris/USBProxyBackendSolaris.cpp b/src/VBox/Main/src-server/solaris/USBProxyBackendSolaris.cpp new file mode 100644 index 00000000..6631a04f --- /dev/null +++ b/src/VBox/Main/src-server/solaris/USBProxyBackendSolaris.cpp @@ -0,0 +1,496 @@ +/* $Id: USBProxyBackendSolaris.cpp $ */ +/** @file + * VirtualBox USB Proxy Service, Solaris Specialization. + */ + +/* + * Copyright (C) 2005-2022 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 <VBox/usblib.h> +#include <iprt/errcore.h> +#include <iprt/semaphore.h> +#include <iprt/path.h> + +#include <sys/usb/usba.h> +#include <syslog.h> + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int solarisWalkDeviceNode(di_node_t Node, void *pvArg); +static void solarisFreeUSBDevice(PUSBDEVICE pDevice); +static USBDEVICESTATE solarisDetermineUSBDeviceState(PUSBDEVICE pDevice, di_node_t Node); + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct USBDEVICELIST +{ + PUSBDEVICE pHead; + PUSBDEVICE pTail; +} USBDEVICELIST; +typedef USBDEVICELIST *PUSBDEVICELIST; + + +/** + * Initialize data members. + */ +USBProxyBackendSolaris::USBProxyBackendSolaris() + : USBProxyBackend(), mNotifyEventSem(NIL_RTSEMEVENT), mUSBLibInitialized(false) +{ + LogFlowThisFunc(("\n")); +} + +USBProxyBackendSolaris::~USBProxyBackendSolaris() +{ +} + +/** + * Initializes the object (called right after construction). + * + * @returns VBox status code. + */ +int USBProxyBackendSolaris::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 semaphore. + */ + int rc = RTSemEventCreate(&mNotifyEventSem); + if (RT_FAILURE(rc)) + return rc; + + /* + * Initialize the USB library. + */ + rc = USBLibInit(); + if (RT_FAILURE(rc)) + { + /* mNotifyEventSem will be destroyed in uninit */ + return rc; + } + + mUSBLibInitialized = true; + + /* + * Start the poller thread. + */ + start(); + return VINF_SUCCESS; +} + + +/** + * Stop all service threads and free the device chain. + */ +void USBProxyBackendSolaris::uninit() +{ + LogFlowThisFunc(("destruct\n")); + + /* + * Stop the service. + */ + if (isActive()) + stop(); + + /* + * Terminate the USB library + */ + if (mUSBLibInitialized) + { + USBLibTerm(); + mUSBLibInitialized = false; + } + + if (mNotifyEventSem != NIL_RTSEMEVENT) + { + RTSemEventDestroy(mNotifyEventSem); + mNotifyEventSem = NIL_RTSEMEVENT; + } +} + + +void *USBProxyBackendSolaris::insertFilter(PCUSBFILTER aFilter) +{ + return USBLibAddFilter(aFilter); +} + + +void USBProxyBackendSolaris::removeFilter(void *pvID) +{ + USBLibRemoveFilter(pvID); +} + + +int USBProxyBackendSolaris::wait(RTMSINTERVAL aMillies) +{ + return RTSemEventWait(mNotifyEventSem, aMillies < 1000 ? 1000 : RT_MIN(aMillies, 5000)); +} + + +int USBProxyBackendSolaris::interruptWait(void) +{ + return RTSemEventSignal(mNotifyEventSem); +} + + +PUSBDEVICE USBProxyBackendSolaris::getDevices(void) +{ + USBDEVICELIST DevList; + DevList.pHead = NULL; + DevList.pTail = NULL; + di_node_t RootNode = di_init("/", DINFOCPYALL); + if (RootNode != DI_NODE_NIL) + di_walk_node(RootNode, DI_WALK_CLDFIRST, &DevList, solarisWalkDeviceNode); + + di_fini(RootNode); + return DevList.pHead; +} + + +static int solarisWalkDeviceNode(di_node_t Node, void *pvArg) +{ + PUSBDEVICELIST pList = (PUSBDEVICELIST)pvArg; + AssertPtrReturn(pList, DI_WALK_TERMINATE); + + /* + * Check if it's a USB device in the first place. + */ + bool fUSBDevice = false; + char *pszCompatNames = NULL; + int cCompatNames = di_compatible_names(Node, &pszCompatNames); + for (int i = 0; i < cCompatNames; i++, pszCompatNames += strlen(pszCompatNames) + 1) + if (!strncmp(pszCompatNames, RT_STR_TUPLE("usb"))) + { + fUSBDevice = true; + break; + } + + if (!fUSBDevice) + return DI_WALK_CONTINUE; + + /* + * Check if it's a device node or interface. + */ + int *pInt = NULL; + char *pStr = NULL; + int rc = DI_WALK_CONTINUE; + if (di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "interface", &pInt) < 0) + { + /* It's a device node. */ + char *pszDevicePath = di_devfs_path(Node); + PUSBDEVICE pCur = (PUSBDEVICE)RTMemAllocZ(sizeof(*pCur)); + if (!pCur) + { + LogRel(("USBService: failed to allocate %d bytes for PUSBDEVICE.\n", sizeof(*pCur))); + return DI_WALK_TERMINATE; + } + + bool fValidDevice = false; + do + { + AssertBreak(pszDevicePath); + + char *pszDriverName = di_driver_name(Node); + + /* + * Skip hubs + */ + if ( pszDriverName + && !strcmp(pszDriverName, "hubd")) + { + break; + } + + /* + * Mandatory. + * snv_85 and above have usb-dev-descriptor node properties, but older one's do not. + * So if we cannot obtain the entire device descriptor, we try falling back to the + * individual properties (those must not fail, if it does we drop the device). + */ + uchar_t *pDevData = NULL; + int cbProp = di_prop_lookup_bytes(DDI_DEV_T_ANY, Node, "usb-dev-descriptor", &pDevData); + if ( cbProp > 0 + && pDevData) + { + usb_dev_descr_t *pDeviceDescriptor = (usb_dev_descr_t *)pDevData; + pCur->bDeviceClass = pDeviceDescriptor->bDeviceClass; + pCur->bDeviceSubClass = pDeviceDescriptor->bDeviceSubClass; + pCur->bDeviceProtocol = pDeviceDescriptor->bDeviceProtocol; + pCur->idVendor = pDeviceDescriptor->idVendor; + pCur->idProduct = pDeviceDescriptor->idProduct; + pCur->bcdDevice = pDeviceDescriptor->bcdDevice; + pCur->bcdUSB = pDeviceDescriptor->bcdUSB; + pCur->bNumConfigurations = pDeviceDescriptor->bNumConfigurations; + pCur->fPartialDescriptor = false; + } + else + { + AssertBreak(di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "usb-vendor-id", &pInt) > 0); + pCur->idVendor = (uint16_t)*pInt; + + AssertBreak(di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "usb-product-id", &pInt) > 0); + pCur->idProduct = (uint16_t)*pInt; + + AssertBreak(di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "usb-revision-id", &pInt) > 0); + pCur->bcdDevice = (uint16_t)*pInt; + + AssertBreak(di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "usb-release", &pInt) > 0); + pCur->bcdUSB = (uint16_t)*pInt; + + pCur->fPartialDescriptor = true; + } + + char *pszPortAddr = di_bus_addr(Node); + if (pszPortAddr) + pCur->bPort = RTStrToUInt8(pszPortAddr); /* Bus & Port are mixed up (kernel driver/userland) */ + else + pCur->bPort = 0; + + char szBuf[PATH_MAX + 48]; + RTStrPrintf(szBuf, sizeof(szBuf), "%#x:%#x:%d:%s", pCur->idVendor, pCur->idProduct, pCur->bcdDevice, pszDevicePath); + pCur->pszAddress = RTStrDup(szBuf); + AssertBreak(pCur->pszAddress); + + pCur->pszDevicePath = RTStrDup(pszDevicePath); + AssertBreak(pCur->pszDevicePath); + + pCur->pszBackend = RTStrDup("host"); + AssertBreak(pCur->pszBackend); + + /* + * Optional (some devices don't have all these) + */ + char *pszCopy; + if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "usb-product-name", &pStr) > 0) + { + pCur->pszProduct = pszCopy = RTStrDup(pStr); + USBLibPurgeEncoding(pszCopy); + } + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "usb-vendor-name", &pStr) > 0) + { + pCur->pszManufacturer = pszCopy = RTStrDup(pStr); + USBLibPurgeEncoding(pszCopy); + } + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "usb-serialno", &pStr) > 0) + { + pCur->pszSerialNumber = pszCopy = RTStrDup(pStr); + USBLibPurgeEncoding(pszCopy); + } + + if (pCur->bcdUSB == 0x300) + pCur->enmSpeed = USBDEVICESPEED_SUPER; + else if (di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "low-speed", &pInt) >= 0) + pCur->enmSpeed = USBDEVICESPEED_LOW; + else if (di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "high-speed", &pInt) >= 0) + pCur->enmSpeed = USBDEVICESPEED_HIGH; + else + pCur->enmSpeed = USBDEVICESPEED_FULL; + + /* Determine state of the USB device. */ + pCur->enmState = solarisDetermineUSBDeviceState(pCur, Node); + + /* + * Valid device, add it to the list. + */ + fValidDevice = true; + pCur->pPrev = pList->pTail; + if (pList->pTail) + pList->pTail = pList->pTail->pNext = pCur; + else + pList->pTail = pList->pHead = pCur; + + rc = DI_WALK_CONTINUE; + } while (0); + + di_devfs_path_free(pszDevicePath); + if (!fValidDevice) + solarisFreeUSBDevice(pCur); + } + return rc; +} + + +static USBDEVICESTATE solarisDetermineUSBDeviceState(PUSBDEVICE pDevice, di_node_t Node) +{ + char *pszDriverName = di_driver_name(Node); + + /* Not possible unless a user explicitly unbinds the default driver. */ + if (!pszDriverName) + return USBDEVICESTATE_UNUSED; + + if (!strncmp(pszDriverName, RT_STR_TUPLE(VBOXUSB_DRIVER_NAME))) + return USBDEVICESTATE_HELD_BY_PROXY; + + NOREF(pDevice); + return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; +} + + +int USBProxyBackendSolaris::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); + AssertReturn(aDevice->i_getUsbData(), VERR_INVALID_POINTER); + + /* + * Create a one-shot capture filter for the device and reset the device. + */ + USBFILTER Filter; + USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_CAPTURE); + initFilterFromDevice(&Filter, aDevice); + + void *pvId = USBLibAddFilter(&Filter); + if (!pvId) + { + LogRel(("USBService: failed to add filter\n")); + return VERR_GENERAL_FAILURE; + } + + PUSBDEVICE pDev = aDevice->i_getUsbData(); + int rc = USBLibResetDevice(pDev->pszDevicePath, true); + if (RT_SUCCESS(rc)) + aDevice->i_setBackendUserData(pvId); + else + { + USBLibRemoveFilter(pvId); + pvId = NULL; + } + LogFlowThisFunc(("returns %Rrc pvId=%p\n", rc, pvId)); + return rc; +} + + +void USBProxyBackendSolaris::captureDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess) +{ + AssertReturnVoid(aDevice->isWriteLockOnCurrentThread()); + /* + * Remove the one-shot filter if necessary. + */ + LogFlowThisFunc(("aDevice=%s aSuccess=%RTbool mOneShotId=%p\n", aDevice->i_getName().c_str(), aSuccess, aDevice->i_getBackendUserData())); + if (!aSuccess && aDevice->i_getBackendUserData()) + USBLibRemoveFilter(aDevice->i_getBackendUserData()); + aDevice->i_setBackendUserData(NULL); + USBProxyBackend::captureDeviceCompleted(aDevice, aSuccess); +} + + +int USBProxyBackendSolaris::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); + AssertReturn(aDevice->i_getUsbData(), VERR_INVALID_POINTER); + + /* + * Create a one-shot ignore filter for the device and reset it. + */ + USBFILTER Filter; + USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_IGNORE); + initFilterFromDevice(&Filter, aDevice); + + void *pvId = USBLibAddFilter(&Filter); + if (!pvId) + { + LogRel(("USBService: Adding ignore filter failed!\n")); + return VERR_GENERAL_FAILURE; + } + + PUSBDEVICE pDev = aDevice->i_getUsbData(); + int rc = USBLibResetDevice(pDev->pszDevicePath, true /* Re-attach */); + if (RT_SUCCESS(rc)) + aDevice->i_setBackendUserData(pvId); + else + { + USBLibRemoveFilter(pvId); + pvId = NULL; + } + LogFlowThisFunc(("returns %Rrc pvId=%p\n", rc, pvId)); + return rc; +} + + +void USBProxyBackendSolaris::releaseDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess) +{ + AssertReturnVoid(aDevice->isWriteLockOnCurrentThread()); + /* + * Remove the one-shot filter if necessary. + */ + LogFlowThisFunc(("aDevice=%s aSuccess=%RTbool mOneShotId=%p\n", aDevice->i_getName().c_str(), aSuccess, aDevice->i_getBackendUserData())); + if (!aSuccess && aDevice->i_getBackendUserData()) + USBLibRemoveFilter(aDevice->i_getBackendUserData()); + aDevice->i_setBackendUserData(NULL); + USBProxyBackend::releaseDeviceCompleted(aDevice, aSuccess); +} + + +/** + * 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 USBProxyBackendSolaris::i_isDevReEnumerationRequired() +{ + return true; +} + + +/** + * Wrapper called by walkDeviceNode. + * + * @param pDevice The USB device to free. + */ +void solarisFreeUSBDevice(PUSBDEVICE pDevice) +{ + USBProxyBackend::freeDevice(pDevice); +} |