summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/darwin
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/Main/src-server/darwin/HostDnsServiceDarwin.cpp281
-rw-r--r--src/VBox/Main/src-server/darwin/HostPowerDarwin.cpp252
-rw-r--r--src/VBox/Main/src-server/darwin/Makefile.kup0
-rw-r--r--src/VBox/Main/src-server/darwin/NetIf-darwin.cpp558
-rw-r--r--src/VBox/Main/src-server/darwin/PerformanceDarwin.cpp191
-rw-r--r--src/VBox/Main/src-server/darwin/USBProxyBackendDarwin.cpp195
-rw-r--r--src/VBox/Main/src-server/darwin/iokit.cpp1993
-rw-r--r--src/VBox/Main/src-server/darwin/iokit.h117
8 files changed, 3587 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/darwin/HostDnsServiceDarwin.cpp b/src/VBox/Main/src-server/darwin/HostDnsServiceDarwin.cpp
new file mode 100644
index 00000000..0d0b0d5c
--- /dev/null
+++ b/src/VBox/Main/src-server/darwin/HostDnsServiceDarwin.cpp
@@ -0,0 +1,281 @@
+/* $Id: HostDnsServiceDarwin.cpp $ */
+/** @file
+ * Darwin specific DNS information fetching.
+ */
+
+/*
+ * 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
+ */
+
+#include <VBox/com/string.h>
+#include <VBox/com/ptr.h>
+
+
+#include <iprt/asm.h>
+#include <iprt/errcore.h>
+#include <iprt/thread.h>
+#include <iprt/semaphore.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SystemConfiguration/SCDynamicStore.h>
+
+#include <iprt/sanitized/string>
+#include <vector>
+#include "../HostDnsService.h"
+
+
+struct HostDnsServiceDarwin::Data
+{
+ Data()
+ : m_fStop(false) { }
+
+ SCDynamicStoreRef m_store;
+ CFRunLoopSourceRef m_DnsWatcher;
+ CFRunLoopRef m_RunLoopRef;
+ CFRunLoopSourceRef m_SourceStop;
+ volatile bool m_fStop;
+ RTSEMEVENT m_evtStop;
+ static void performShutdownCallback(void *);
+};
+
+
+static const CFStringRef kStateNetworkGlobalDNSKey = CFSTR("State:/Network/Global/DNS");
+
+
+HostDnsServiceDarwin::HostDnsServiceDarwin()
+ : HostDnsServiceBase(true /* fThreaded */)
+ , m(NULL)
+{
+ m = new HostDnsServiceDarwin::Data();
+}
+
+HostDnsServiceDarwin::~HostDnsServiceDarwin()
+{
+ if (m != NULL)
+ delete m;
+}
+
+HRESULT HostDnsServiceDarwin::init(HostDnsMonitorProxy *pProxy)
+{
+ SCDynamicStoreContext ctx;
+ RT_ZERO(ctx);
+
+ ctx.info = this;
+
+ m->m_store = SCDynamicStoreCreate(NULL, CFSTR("org.virtualbox.VBoxSVC.HostDNS"),
+ (SCDynamicStoreCallBack)HostDnsServiceDarwin::hostDnsServiceStoreCallback,
+ &ctx);
+ AssertReturn(m->m_store, E_FAIL);
+
+ m->m_DnsWatcher = SCDynamicStoreCreateRunLoopSource(NULL, m->m_store, 0);
+ if (!m->m_DnsWatcher)
+ return E_OUTOFMEMORY;
+
+ int vrc = RTSemEventCreate(&m->m_evtStop);
+ AssertRCReturn(vrc, E_FAIL);
+
+ CFRunLoopSourceContext sctx;
+ RT_ZERO(sctx);
+ sctx.info = this;
+ sctx.perform = HostDnsServiceDarwin::Data::performShutdownCallback;
+
+ m->m_SourceStop = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &sctx);
+ AssertReturn(m->m_SourceStop, E_FAIL);
+
+ HRESULT hrc = HostDnsServiceBase::init(pProxy);
+ return hrc;
+}
+
+void HostDnsServiceDarwin::uninit(void)
+{
+ HostDnsServiceBase::uninit();
+
+ CFRelease(m->m_SourceStop);
+ CFRelease(m->m_RunLoopRef);
+ CFRelease(m->m_DnsWatcher);
+ CFRelease(m->m_store);
+
+ RTSemEventDestroy(m->m_evtStop);
+}
+
+int HostDnsServiceDarwin::monitorThreadShutdown(RTMSINTERVAL uTimeoutMs)
+{
+ RTCLock grab(m_LockMtx);
+ if (!m->m_fStop)
+ {
+ ASMAtomicXchgBool(&m->m_fStop, true);
+ CFRunLoopSourceSignal(m->m_SourceStop);
+ CFRunLoopStop(m->m_RunLoopRef);
+
+ RTSemEventWait(m->m_evtStop, uTimeoutMs);
+ }
+
+ return VINF_SUCCESS;
+}
+
+int HostDnsServiceDarwin::monitorThreadProc(void)
+{
+ m->m_RunLoopRef = CFRunLoopGetCurrent();
+ AssertReturn(m->m_RunLoopRef, VERR_INTERNAL_ERROR);
+
+ CFRetain(m->m_RunLoopRef);
+
+ CFRunLoopAddSource(m->m_RunLoopRef, m->m_SourceStop, kCFRunLoopCommonModes);
+
+ CFArrayRef watchingArrayRef = CFArrayCreate(NULL,
+ (const void **)&kStateNetworkGlobalDNSKey,
+ 1, &kCFTypeArrayCallBacks);
+ if (!watchingArrayRef)
+ {
+ CFRelease(m->m_DnsWatcher);
+ return VERR_NO_MEMORY;
+ }
+
+ if (SCDynamicStoreSetNotificationKeys(m->m_store, watchingArrayRef, NULL))
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), m->m_DnsWatcher, kCFRunLoopCommonModes);
+
+ CFRelease(watchingArrayRef);
+
+ onMonitorThreadInitDone();
+
+ /* Trigger initial update. */
+ int vrc = updateInfo();
+ AssertRC(vrc); /* Not fatal in release builds. */ /** @todo r=bird: The function always returns VINF_SUCCESS. */
+
+ while (!ASMAtomicReadBool(&m->m_fStop))
+ {
+ CFRunLoopRun();
+ }
+
+ CFRunLoopRemoveSource(m->m_RunLoopRef, m->m_SourceStop, kCFRunLoopCommonModes);
+
+ /* We're notifying stopper thread. */
+ RTSemEventSignal(m->m_evtStop);
+
+ return VINF_SUCCESS;
+}
+
+int HostDnsServiceDarwin::updateInfo(void)
+{
+ CFPropertyListRef propertyRef = SCDynamicStoreCopyValue(m->m_store, kStateNetworkGlobalDNSKey);
+ /**
+ * # scutil
+ * \> get State:/Network/Global/DNS
+ * \> d.show
+ * \<dictionary\> {
+ * DomainName : vvl-domain
+ * SearchDomains : \<array\> {
+ * 0 : vvl-domain
+ * 1 : de.vvl-domain.com
+ * }
+ * ServerAddresses : \<array\> {
+ * 0 : 192.168.1.4
+ * 1 : 192.168.1.1
+ * 2 : 8.8.4.4
+ * }
+ * }
+ */
+
+ if (!propertyRef)
+ return VINF_SUCCESS;
+
+ HostDnsInformation info;
+ CFStringRef domainNameRef = (CFStringRef)CFDictionaryGetValue(static_cast<CFDictionaryRef>(propertyRef), CFSTR("DomainName"));
+ if (domainNameRef)
+ {
+ const char *pszDomainName = CFStringGetCStringPtr(domainNameRef, CFStringGetSystemEncoding());
+ if (pszDomainName)
+ info.domain = pszDomainName;
+ }
+
+ CFArrayRef serverArrayRef = (CFArrayRef)CFDictionaryGetValue(static_cast<CFDictionaryRef>(propertyRef),
+ CFSTR("ServerAddresses"));
+ if (serverArrayRef)
+ {
+ CFIndex const cItems = CFArrayGetCount(serverArrayRef);
+ for (CFIndex i = 0; i < cItems; ++i)
+ {
+ CFStringRef serverAddressRef = (CFStringRef)CFArrayGetValueAtIndex(serverArrayRef, i);
+ if (!serverArrayRef)
+ continue;
+
+ /** @todo r=bird: This code is messed up as CFStringGetCStringPtr is documented
+ * to return NULL even if the string is valid. Furthermore, we must have
+ * UTF-8 - some joker might decide latin-1 is better here for all we know
+ * and we'll end up with evil invalid UTF-8 sequences. */
+ const char *pszServerAddress = CFStringGetCStringPtr(serverAddressRef, CFStringGetSystemEncoding());
+ if (!pszServerAddress)
+ continue;
+
+ /** @todo r=bird: Why on earth are we using std::string and not Utf8Str? */
+ info.servers.push_back(std::string(pszServerAddress));
+ }
+ }
+
+ CFArrayRef searchArrayRef = (CFArrayRef)CFDictionaryGetValue(static_cast<CFDictionaryRef>(propertyRef),
+ CFSTR("SearchDomains"));
+ if (searchArrayRef)
+ {
+ CFIndex const cItems = CFArrayGetCount(searchArrayRef);
+ for (CFIndex i = 0; i < cItems; ++i)
+ {
+ CFStringRef searchStringRef = (CFStringRef)CFArrayGetValueAtIndex(searchArrayRef, i);
+ if (!searchArrayRef)
+ continue;
+
+ /** @todo r=bird: This code is messed up as CFStringGetCStringPtr is documented
+ * to return NULL even if the string is valid. Furthermore, we must have
+ * UTF-8 - some joker might decide latin-1 is better here for all we know
+ * and we'll end up with evil invalid UTF-8 sequences. */
+ const char *pszSearchString = CFStringGetCStringPtr(searchStringRef, CFStringGetSystemEncoding());
+ if (!pszSearchString)
+ continue;
+
+ /** @todo r=bird: Why on earth are we using std::string and not Utf8Str? */
+ info.searchList.push_back(std::string(pszSearchString));
+ }
+ }
+
+ CFRelease(propertyRef);
+
+ setInfo(info);
+
+ return VINF_SUCCESS;
+}
+
+void HostDnsServiceDarwin::hostDnsServiceStoreCallback(void *, void *, void *pInfo)
+{
+ HostDnsServiceDarwin *pThis = (HostDnsServiceDarwin *)pInfo;
+ AssertPtrReturnVoid(pThis);
+
+ RTCLock grab(pThis->m_LockMtx);
+ pThis->updateInfo();
+}
+
+void HostDnsServiceDarwin::Data::performShutdownCallback(void *pInfo)
+{
+ HostDnsServiceDarwin *pThis = (HostDnsServiceDarwin *)pInfo;
+ AssertPtrReturnVoid(pThis);
+
+ AssertPtrReturnVoid(pThis->m);
+ ASMAtomicXchgBool(&pThis->m->m_fStop, true);
+}
+
diff --git a/src/VBox/Main/src-server/darwin/HostPowerDarwin.cpp b/src/VBox/Main/src-server/darwin/HostPowerDarwin.cpp
new file mode 100644
index 00000000..c86924a8
--- /dev/null
+++ b/src/VBox/Main/src-server/darwin/HostPowerDarwin.cpp
@@ -0,0 +1,252 @@
+/* $Id: HostPowerDarwin.cpp $ */
+/** @file
+ * VirtualBox interface to host's power notification service, darwin specifics.
+ */
+
+/*
+ * 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_HOST
+#include "HostPower.h"
+#include "LoggingNew.h"
+#include <iprt/errcore.h>
+
+#include <IOKit/IOMessage.h>
+#include <IOKit/ps/IOPowerSources.h>
+#include <IOKit/ps/IOPSKeys.h>
+
+#define POWER_SOURCE_OUTLET 1
+#define POWER_SOURCE_BATTERY 2
+
+HostPowerServiceDarwin::HostPowerServiceDarwin(VirtualBox *aVirtualBox)
+ : HostPowerService(aVirtualBox)
+ , mThread(NULL)
+ , mRootPort(MACH_PORT_NULL)
+ , mNotifyPort(nil)
+ , mRunLoop(nil)
+ , mCritical(false)
+{
+ /* Create the new worker thread. */
+ int vrc = RTThreadCreate(&mThread, HostPowerServiceDarwin::powerChangeNotificationThread, this, 65536,
+ RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "MainPower");
+ AssertLogRelRC(vrc);
+}
+
+HostPowerServiceDarwin::~HostPowerServiceDarwin()
+{
+ /* Jump out of the run loop. */
+ CFRunLoopStop(mRunLoop);
+ /* Remove the sleep notification port from the application runloop. */
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
+ IONotificationPortGetRunLoopSource(mNotifyPort),
+ kCFRunLoopCommonModes);
+ /* Deregister for system sleep notifications. */
+ IODeregisterForSystemPower(&mNotifierObject);
+ /* IORegisterForSystemPower implicitly opens the Root Power Domain
+ * IOService so we close it here. */
+ IOServiceClose(mRootPort);
+ /* Destroy the notification port allocated by IORegisterForSystemPower */
+ IONotificationPortDestroy(mNotifyPort);
+}
+
+
+DECLCALLBACK(int) HostPowerServiceDarwin::powerChangeNotificationThread(RTTHREAD /* ThreadSelf */, void *pInstance)
+{
+ HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *>(pInstance);
+
+ /* We have to initial set the critical state of the battery, cause we want
+ * not the HostPowerService to inform about that state when a VM starts.
+ * See lowPowerHandler for more info. */
+ pPowerObj->checkBatteryCriticalLevel();
+
+ /* Register to receive system sleep notifications */
+ pPowerObj->mRootPort = IORegisterForSystemPower(pPowerObj, &pPowerObj->mNotifyPort,
+ HostPowerServiceDarwin::powerChangeNotificationHandler,
+ &pPowerObj->mNotifierObject);
+ if (pPowerObj->mRootPort == MACH_PORT_NULL)
+ {
+ LogFlow(("IORegisterForSystemPower failed\n"));
+ return VERR_NOT_SUPPORTED;
+ }
+ pPowerObj->mRunLoop = CFRunLoopGetCurrent();
+ /* Add the notification port to the application runloop */
+ CFRunLoopAddSource(pPowerObj->mRunLoop,
+ IONotificationPortGetRunLoopSource(pPowerObj->mNotifyPort),
+ kCFRunLoopCommonModes);
+
+ /* Register for all battery change events. The handler will check for low
+ * power events itself. */
+ CFRunLoopSourceRef runLoopSource = IOPSNotificationCreateRunLoopSource(HostPowerServiceDarwin::lowPowerHandler,
+ pPowerObj);
+ CFRunLoopAddSource(pPowerObj->mRunLoop,
+ runLoopSource,
+ kCFRunLoopCommonModes);
+
+ /* Start the run loop. This blocks. */
+ CFRunLoopRun();
+ return VINF_SUCCESS;
+}
+
+void HostPowerServiceDarwin::powerChangeNotificationHandler(void *pvData, io_service_t /* service */, natural_t messageType, void *pMessageArgument)
+{
+ HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *>(pvData);
+ Log(( "powerChangeNotificationHandler: messageType %08lx, arg %08lx\n", (long unsigned int)messageType, (long unsigned int)pMessageArgument));
+
+ switch (messageType)
+ {
+ case kIOMessageCanSystemSleep:
+ {
+ /* Idle sleep is about to kick in. This message will not be
+ * sent for forced sleep. Applications have a chance to prevent
+ * sleep by calling IOCancelPowerChange. Most applications
+ * should not prevent idle sleep. Power Management waits up to
+ * 30 seconds for you to either allow or deny idle sleep. If
+ * you don't acknowledge this power change by calling either
+ * IOAllowPowerChange or IOCancelPowerChange, the system will
+ * wait 30 seconds then go to sleep. */
+ IOAllowPowerChange(pPowerObj->mRootPort, reinterpret_cast<long>(pMessageArgument));
+ break;
+ }
+ case kIOMessageSystemWillSleep:
+ {
+ /* The system will go for sleep. */
+ pPowerObj->notify(Reason_HostSuspend);
+ /* If you do not call IOAllowPowerChange or IOCancelPowerChange to
+ * acknowledge this message, sleep will be delayed by 30 seconds.
+ * NOTE: If you call IOCancelPowerChange to deny sleep it returns
+ * kIOReturnSuccess, however the system WILL still go to sleep. */
+ IOAllowPowerChange(pPowerObj->mRootPort, reinterpret_cast<long>(pMessageArgument));
+ break;
+ }
+ case kIOMessageSystemWillPowerOn:
+ {
+ /* System has started the wake up process. */
+ break;
+ }
+ case kIOMessageSystemHasPoweredOn:
+ {
+ /* System has finished the wake up process. */
+ pPowerObj->notify(Reason_HostResume);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void HostPowerServiceDarwin::lowPowerHandler(void *pvData)
+{
+ HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *>(pvData);
+
+ /* Following role for sending the BatteryLow event(5% is critical):
+ * - Not at VM start even if the battery is in an critical state already.
+ * - When the power cord is removed so the power supply change from AC to
+ * battery & the battery is in an critical state nothing is triggered.
+ * This has to be discussed.
+ * - When the power supply is the battery & the state of the battery level
+ * changed from normal to critical. The state transition from critical to
+ * normal triggers nothing. */
+ bool fCriticalStateChanged = false;
+ pPowerObj->checkBatteryCriticalLevel(&fCriticalStateChanged);
+ if (fCriticalStateChanged)
+ pPowerObj->notify(Reason_HostBatteryLow);
+}
+
+void HostPowerServiceDarwin::checkBatteryCriticalLevel(bool *pfCriticalChanged)
+{
+ CFTypeRef pBlob = IOPSCopyPowerSourcesInfo();
+ CFArrayRef pSources = IOPSCopyPowerSourcesList(pBlob);
+
+ CFDictionaryRef pSource = NULL;
+ const void *psValue;
+ bool result;
+ int powerSource = POWER_SOURCE_OUTLET;
+ bool critical = false;
+
+ if (CFArrayGetCount(pSources) > 0)
+ {
+ for (int i = 0; i < CFArrayGetCount(pSources); ++i)
+ {
+ pSource = IOPSGetPowerSourceDescription(pBlob, CFArrayGetValueAtIndex(pSources, i));
+ /* If the source is empty skip over to the next one. */
+ if (!pSource)
+ continue;
+ /* Skip all power sources which are currently not present like a
+ * second battery. */
+ if (CFDictionaryGetValue(pSource, CFSTR(kIOPSIsPresentKey)) == kCFBooleanFalse)
+ continue;
+ /* Only internal power types are of interest. */
+ result = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSTransportTypeKey), &psValue);
+ if (result &&
+ CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSInternalType), 0) == kCFCompareEqualTo)
+ {
+ /* First check which power source we are connect on. */
+ result = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSPowerSourceStateKey), &psValue);
+ if (result &&
+ CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSACPowerValue), 0) == kCFCompareEqualTo)
+ powerSource = POWER_SOURCE_OUTLET;
+ else if (result &&
+ CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSBatteryPowerValue), 0) == kCFCompareEqualTo)
+ powerSource = POWER_SOURCE_BATTERY;
+
+
+ /* Fetch the current capacity value of the power source */
+ int curCapacity = 0;
+ result = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSCurrentCapacityKey), &psValue);
+ if (result)
+ CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &curCapacity);
+
+ /* Fetch the maximum capacity value of the power source */
+ int maxCapacity = 1;
+ result = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSMaxCapacityKey), &psValue);
+ if (result)
+ CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &maxCapacity);
+
+ /* Calculate the remaining capacity in percent */
+ float remCapacity = ((float)curCapacity/(float)maxCapacity * 100.0f);
+
+ /* Check for critical. 5 percent is default. */
+ int criticalValue = 5;
+ result = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSDeadWarnLevelKey), &psValue);
+ if (result)
+ CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &criticalValue);
+ critical = remCapacity < criticalValue;
+
+ /* We have to take action only if we are on battery, the
+ * previous state wasn't critical, the state has changed & the
+ * user requested that info. */
+ if (powerSource == POWER_SOURCE_BATTERY &&
+ mCritical == false &&
+ mCritical != critical &&
+ pfCriticalChanged)
+ *pfCriticalChanged = true;
+ Log(("checkBatteryCriticalLevel: Remains: %d.%d%% Critical: %d Critical State Changed: %d\n", (int)remCapacity, (int)(remCapacity * 10) % 10, critical, pfCriticalChanged?*pfCriticalChanged:-1));
+ }
+ }
+ }
+ /* Save the new state */
+ mCritical = critical;
+
+ CFRelease(pBlob);
+ CFRelease(pSources);
+}
+
diff --git a/src/VBox/Main/src-server/darwin/Makefile.kup b/src/VBox/Main/src-server/darwin/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Main/src-server/darwin/Makefile.kup
diff --git a/src/VBox/Main/src-server/darwin/NetIf-darwin.cpp b/src/VBox/Main/src-server/darwin/NetIf-darwin.cpp
new file mode 100644
index 00000000..074c297c
--- /dev/null
+++ b/src/VBox/Main/src-server/darwin/NetIf-darwin.cpp
@@ -0,0 +1,558 @@
+/* $Id: NetIf-darwin.cpp $ */
+/** @file
+ * Main - NetIfList, Darwin 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
+
+/*
+ * Deal with conflicts first.
+ * PVM - BSD mess, that FreeBSD has correct a long time ago.
+ * iprt/types.h before sys/param.h - prevents UINT32_C and friends.
+ */
+#include <iprt/types.h>
+#include <sys/param.h>
+#undef PVM
+
+#include <iprt/errcore.h>
+#include <iprt/alloc.h>
+
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <ifaddrs.h>
+#include <errno.h>
+#include <unistd.h>
+#include <list>
+
+#include "HostNetworkInterfaceImpl.h"
+#include "netif.h"
+#include "iokit.h"
+#include "LoggingNew.h"
+
+#if 0
+int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list)
+{
+ int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sock < 0)
+ {
+ Log(("NetIfList: socket() -> %d\n", errno));
+ return NULL;
+ }
+ struct ifaddrs *IfAddrs, *pAddr;
+ int iRc = getifaddrs(&IfAddrs);
+ if (iRc)
+ {
+ close(sock);
+ Log(("NetIfList: getifaddrs() -> %d\n", iRc));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ PDARWINETHERNIC pEtherNICs = DarwinGetEthernetControllers();
+ while (pEtherNICs)
+ {
+ size_t cbNameLen = strlen(pEtherNICs->szName) + 1;
+ PNETIFINFO pNew = (PNETIFINFO)RTMemAllocZ(RT_OFFSETOF(NETIFINFO, szName[cbNameLen]));
+ pNew->MACAddress = pEtherNICs->Mac;
+ pNew->enmMediumType = NETIF_T_ETHERNET;
+ pNew->Uuid = pEtherNICs->Uuid;
+ Assert(sizeof(pNew->szShortName) > sizeof(pEtherNICs->szBSDName));
+ memcpy(pNew->szShortName, pEtherNICs->szBSDName, sizeof(pEtherNICs->szBSDName));
+ pNew->szShortName[sizeof(pEtherNICs->szBSDName)] = '\0';
+ memcpy(pNew->szName, pEtherNICs->szName, cbNameLen);
+
+ struct ifreq IfReq;
+ RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pNew->szShortName);
+ if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0)
+ {
+ Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno));
+ pNew->enmStatus = NETIF_S_UNKNOWN;
+ }
+ else
+ pNew->enmStatus = (IfReq.ifr_flags & IFF_UP) ? NETIF_S_UP : NETIF_S_DOWN;
+
+ for (pAddr = IfAddrs; pAddr != NULL; pAddr = pAddr->ifa_next)
+ {
+ if (strcmp(pNew->szShortName, pAddr->ifa_name))
+ continue;
+
+ struct sockaddr_in *pIPAddr, *pIPNetMask;
+ struct sockaddr_in6 *pIPv6Addr, *pIPv6NetMask;
+
+ switch (pAddr->ifa_addr->sa_family)
+ {
+ case AF_INET:
+ if (pNew->IPAddress.u)
+ break;
+ pIPAddr = (struct sockaddr_in *)pAddr->ifa_addr;
+ Assert(sizeof(pNew->IPAddress) == sizeof(pIPAddr->sin_addr));
+ pNew->IPAddress.u = pIPAddr->sin_addr.s_addr;
+ pIPNetMask = (struct sockaddr_in *)pAddr->ifa_netmask;
+ Assert(pIPNetMask->sin_family == AF_INET);
+ Assert(sizeof(pNew->IPNetMask) == sizeof(pIPNetMask->sin_addr));
+ pNew->IPNetMask.u = pIPNetMask->sin_addr.s_addr;
+ break;
+ case AF_INET6:
+ if (pNew->IPv6Address.s.Lo || pNew->IPv6Address.s.Hi)
+ break;
+ pIPv6Addr = (struct sockaddr_in6 *)pAddr->ifa_addr;
+ Assert(sizeof(pNew->IPv6Address) == sizeof(pIPv6Addr->sin6_addr));
+ memcpy(pNew->IPv6Address.au8,
+ pIPv6Addr->sin6_addr.__u6_addr.__u6_addr8,
+ sizeof(pNew->IPv6Address));
+ pIPv6NetMask = (struct sockaddr_in6 *)pAddr->ifa_netmask;
+ Assert(pIPv6NetMask->sin6_family == AF_INET6);
+ Assert(sizeof(pNew->IPv6NetMask) == sizeof(pIPv6NetMask->sin6_addr));
+ memcpy(pNew->IPv6NetMask.au8,
+ pIPv6NetMask->sin6_addr.__u6_addr.__u6_addr8,
+ sizeof(pNew->IPv6NetMask));
+ break;
+ }
+ }
+
+ ComObjPtr<HostNetworkInterface> IfObj;
+ IfObj.createObject();
+ if (SUCCEEDED(IfObj->init(Bstr(pEtherNICs->szName), HostNetworkInterfaceType_Bridged, pNew)))
+ list.push_back(IfObj);
+ RTMemFree(pNew);
+
+ /* next, free current */
+ void *pvFree = pEtherNICs;
+ pEtherNICs = pEtherNICs->pNext;
+ RTMemFree(pvFree);
+ }
+
+ freeifaddrs(IfAddrs);
+ close(sock);
+ return VINF_SUCCESS;
+}
+#else
+
+#define ROUNDUP(a) \
+ (((a) & (sizeof(u_long) - 1)) ? (1 + ((a) | (sizeof(u_long) - 1))) : (a))
+#define ADVANCE(x, n) (x += (n)->sa_len ? ROUNDUP((n)->sa_len) : sizeof(u_long))
+
+void extractAddresses(int iAddrMask, caddr_t cp, caddr_t cplim, struct sockaddr **pAddresses)
+{
+ struct sockaddr *sa;
+
+ for (int i = 0; i < RTAX_MAX && cp < cplim; i++) {
+ if (iAddrMask & (1 << i))
+ {
+ sa = (struct sockaddr *)cp;
+
+ pAddresses[i] = sa;
+
+ ADVANCE(cp, sa);
+ }
+ else
+ pAddresses[i] = NULL;
+ }
+}
+
+void extractAddressesToNetInfo(int iAddrMask, caddr_t cp, caddr_t cplim, PNETIFINFO pInfo)
+{
+ struct sockaddr *addresses[RTAX_MAX];
+
+ extractAddresses(iAddrMask, cp, cplim, addresses);
+ switch (addresses[RTAX_IFA]->sa_family)
+ {
+ case AF_INET:
+ if (!pInfo->IPAddress.u)
+ {
+ pInfo->IPAddress.u = ((struct sockaddr_in *)addresses[RTAX_IFA])->sin_addr.s_addr;
+ pInfo->IPNetMask.u = ((struct sockaddr_in *)addresses[RTAX_NETMASK])->sin_addr.s_addr;
+ }
+ break;
+ case AF_INET6:
+ if (!pInfo->IPv6Address.s.Lo && !pInfo->IPv6Address.s.Hi)
+ {
+ memcpy(pInfo->IPv6Address.au8,
+ ((struct sockaddr_in6 *)addresses[RTAX_IFA])->sin6_addr.__u6_addr.__u6_addr8,
+ sizeof(pInfo->IPv6Address));
+ memcpy(pInfo->IPv6NetMask.au8,
+ ((struct sockaddr_in6 *)addresses[RTAX_NETMASK])->sin6_addr.__u6_addr.__u6_addr8,
+ sizeof(pInfo->IPv6NetMask));
+ }
+ break;
+ default:
+ Log(("NetIfList: Unsupported address family: %u\n", addresses[RTAX_IFA]->sa_family));
+ break;
+ }
+}
+
+static int getDefaultIfaceIndex(unsigned short *pu16Index)
+{
+ size_t cbNeeded;
+ char *pBuf, *pNext;
+ int aiMib[6];
+ struct sockaddr *addresses[RTAX_MAX];
+
+ aiMib[0] = CTL_NET;
+ aiMib[1] = PF_ROUTE;
+ aiMib[2] = 0;
+ aiMib[3] = PF_INET; /* address family */
+ aiMib[4] = NET_RT_DUMP;
+ aiMib[5] = 0;
+
+ if (sysctl(aiMib, 6, NULL, &cbNeeded, NULL, 0) < 0)
+ {
+ Log(("getDefaultIfaceIndex: Failed to get estimate for list size (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+ if ((pBuf = (char *)RTMemAlloc(cbNeeded)) == NULL)
+ return VERR_NO_MEMORY;
+ if (sysctl(aiMib, 6, pBuf, &cbNeeded, NULL, 0) < 0)
+ {
+ RTMemFree(pBuf);
+ Log(("getDefaultIfaceIndex: Failed to retrieve interface table (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+
+ char *pEnd = pBuf + cbNeeded;
+ struct rt_msghdr *pRtMsg;
+ for (pNext = pBuf; pNext < pEnd; pNext += pRtMsg->rtm_msglen)
+ {
+ pRtMsg = (struct rt_msghdr *)pNext;
+
+ if (pRtMsg->rtm_type != RTM_GET)
+ {
+ Log(("getDefaultIfaceIndex: Got message %u while expecting %u.\n",
+ pRtMsg->rtm_type, RTM_GET));
+ //vrc = VERR_INTERNAL_ERROR;
+ continue;
+ }
+ if ((char*)(pRtMsg + 1) < pEnd)
+ {
+ /* Extract addresses from the message. */
+ extractAddresses(pRtMsg->rtm_addrs, (char *)(pRtMsg + 1),
+ pRtMsg->rtm_msglen + 1 + (char *)pRtMsg, addresses);
+ if ((pRtMsg->rtm_addrs & RTA_DST)
+ && (pRtMsg->rtm_addrs & RTA_NETMASK))
+ {
+ if (addresses[RTAX_DST]->sa_family != AF_INET)
+ continue;
+ struct sockaddr_in *addr = (struct sockaddr_in *)addresses[RTAX_DST];
+ struct sockaddr_in *mask = (struct sockaddr_in *)addresses[RTAX_NETMASK];
+ if ((addr->sin_addr.s_addr == INADDR_ANY) &&
+ mask &&
+ (ntohl(mask->sin_addr.s_addr) == 0L ||
+ mask->sin_len == 0))
+ {
+ *pu16Index = pRtMsg->rtm_index;
+ RTMemFree(pBuf);
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ }
+ RTMemFree(pBuf);
+ return 0; /* Failed to find default interface, take the first one in the list. */
+}
+
+int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list)
+{
+ int vrc = VINF_SUCCESS;
+ size_t cbNeeded;
+ char *pBuf, *pNext;
+ int aiMib[6];
+ unsigned short u16DefaultIface = 0; /* initialized to shut up gcc */
+
+ /* Get the index of the interface associated with default route. */
+ vrc = getDefaultIfaceIndex(&u16DefaultIface);
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ aiMib[0] = CTL_NET;
+ aiMib[1] = PF_ROUTE;
+ aiMib[2] = 0;
+ aiMib[3] = 0; /* address family */
+ aiMib[4] = NET_RT_IFLIST;
+ aiMib[5] = 0;
+
+ if (sysctl(aiMib, 6, NULL, &cbNeeded, NULL, 0) < 0)
+ {
+ Log(("NetIfList: Failed to get estimate for list size (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+ if ((pBuf = (char*)RTMemAlloc(cbNeeded)) == NULL)
+ return VERR_NO_MEMORY;
+ if (sysctl(aiMib, 6, pBuf, &cbNeeded, NULL, 0) < 0)
+ {
+ RTMemFree(pBuf);
+ Log(("NetIfList: Failed to retrieve interface table (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+
+ int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sock < 0)
+ {
+ RTMemFree(pBuf);
+ Log(("NetIfList: socket() -> %d\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+
+ PDARWINETHERNIC pNIC;
+ PDARWINETHERNIC pEtherNICs = DarwinGetEthernetControllers();
+
+ char *pEnd = pBuf + cbNeeded;
+ for (pNext = pBuf; pNext < pEnd;)
+ {
+ struct if_msghdr *pIfMsg = (struct if_msghdr *)pNext;
+
+ if (pIfMsg->ifm_type != RTM_IFINFO)
+ {
+ Log(("NetIfList: Got message %u while expecting %u.\n",
+ pIfMsg->ifm_type, RTM_IFINFO));
+ vrc = VERR_INTERNAL_ERROR;
+ break;
+ }
+ struct sockaddr_dl *pSdl = (struct sockaddr_dl *)(pIfMsg + 1);
+
+ size_t cbNameLen = pSdl->sdl_nlen + 1;
+ Assert(pSdl->sdl_nlen < sizeof(pNIC->szBSDName));
+ for (pNIC = pEtherNICs; pNIC; pNIC = pNIC->pNext)
+ if ( !strncmp(pSdl->sdl_data, pNIC->szBSDName, pSdl->sdl_nlen)
+ && pNIC->szBSDName[pSdl->sdl_nlen] == '\0')
+ {
+ cbNameLen = strlen(pNIC->szName) + 1;
+ break;
+ }
+ PNETIFINFO pNew = (PNETIFINFO)RTMemAllocZ(RT_UOFFSETOF_DYN(NETIFINFO, szName[cbNameLen]));
+ if (!pNew)
+ {
+ vrc = VERR_NO_MEMORY;
+ break;
+ }
+ memcpy(pNew->MACAddress.au8, LLADDR(pSdl), sizeof(pNew->MACAddress.au8));
+ pNew->enmMediumType = NETIF_T_ETHERNET;
+ Assert(sizeof(pNew->szShortName) > pSdl->sdl_nlen);
+ memcpy(pNew->szShortName, pSdl->sdl_data, RT_MIN(pSdl->sdl_nlen, sizeof(pNew->szShortName) - 1));
+
+ /*
+ * If we found the adapter in the list returned by
+ * DarwinGetEthernetControllers() copy the name and UUID from there.
+ */
+ if (pNIC)
+ {
+ memcpy(pNew->szName, pNIC->szName, cbNameLen);
+ pNew->Uuid = pNIC->Uuid;
+ pNew->fWireless = pNIC->fWireless;
+ }
+ else
+ {
+ memcpy(pNew->szName, pSdl->sdl_data, pSdl->sdl_nlen);
+ /* Generate UUID from name and MAC address. */
+ RTUUID uuid;
+ RTUuidClear(&uuid);
+ memcpy(&uuid, pNew->szShortName, RT_MIN(cbNameLen, sizeof(uuid)));
+ uuid.Gen.u8ClockSeqHiAndReserved = (uuid.Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
+ uuid.Gen.u16TimeHiAndVersion = (uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
+ memcpy(uuid.Gen.au8Node, pNew->MACAddress.au8, sizeof(uuid.Gen.au8Node));
+ pNew->Uuid = uuid;
+ }
+
+ pNext += pIfMsg->ifm_msglen;
+ while (pNext < pEnd)
+ {
+ struct ifa_msghdr *pIfAddrMsg = (struct ifa_msghdr *)pNext;
+
+ if (pIfAddrMsg->ifam_type != RTM_NEWADDR)
+ break;
+ extractAddressesToNetInfo(pIfAddrMsg->ifam_addrs,
+ (char *)(pIfAddrMsg + 1),
+ pIfAddrMsg->ifam_msglen + (char *)pIfAddrMsg,
+ pNew);
+ pNext += pIfAddrMsg->ifam_msglen;
+ }
+
+ if (pSdl->sdl_type == IFT_ETHER)
+ {
+ struct ifreq IfReq;
+ RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pNew->szShortName);
+ if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0)
+ {
+ Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno));
+ pNew->enmStatus = NETIF_S_UNKNOWN;
+ }
+ else
+ pNew->enmStatus = (IfReq.ifr_flags & IFF_UP) ? NETIF_S_UP : NETIF_S_DOWN;
+
+ HostNetworkInterfaceType_T enmType;
+ if (strncmp(pNew->szName, RT_STR_TUPLE("vboxnet")))
+ enmType = HostNetworkInterfaceType_Bridged;
+ else
+ enmType = HostNetworkInterfaceType_HostOnly;
+
+ ComObjPtr<HostNetworkInterface> IfObj;
+ IfObj.createObject();
+ if (SUCCEEDED(IfObj->init(Bstr(pNew->szName), enmType, pNew)))
+ {
+ /* Make sure the default interface gets to the beginning. */
+ if (pIfMsg->ifm_index == u16DefaultIface)
+ list.push_front(IfObj);
+ else
+ list.push_back(IfObj);
+ }
+ }
+ RTMemFree(pNew);
+ }
+ for (pNIC = pEtherNICs; pNIC;)
+ {
+ void *pvFree = pNIC;
+ pNIC = pNIC->pNext;
+ RTMemFree(pvFree);
+ }
+ close(sock);
+ RTMemFree(pBuf);
+ return vrc;
+}
+
+int NetIfGetConfigByName(PNETIFINFO pInfo)
+{
+ int vrc = VINF_SUCCESS;
+ size_t cbNeeded;
+ char *pBuf, *pNext;
+ int aiMib[6];
+
+ aiMib[0] = CTL_NET;
+ aiMib[1] = PF_ROUTE;
+ aiMib[2] = 0;
+ aiMib[3] = 0; /* address family */
+ aiMib[4] = NET_RT_IFLIST;
+ aiMib[5] = 0;
+
+ if (sysctl(aiMib, 6, NULL, &cbNeeded, NULL, 0) < 0)
+ {
+ Log(("NetIfList: Failed to get estimate for list size (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+ if ((pBuf = (char*)RTMemAlloc(cbNeeded)) == NULL)
+ return VERR_NO_MEMORY;
+ if (sysctl(aiMib, 6, pBuf, &cbNeeded, NULL, 0) < 0)
+ {
+ RTMemFree(pBuf);
+ Log(("NetIfList: Failed to retrieve interface table (errno=%d).\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+
+ int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sock < 0)
+ {
+ RTMemFree(pBuf);
+ Log(("NetIfList: socket() -> %d\n", errno));
+ return RTErrConvertFromErrno(errno);
+ }
+
+ char *pEnd = pBuf + cbNeeded;
+ for (pNext = pBuf; pNext < pEnd;)
+ {
+ struct if_msghdr *pIfMsg = (struct if_msghdr *)pNext;
+
+ if (pIfMsg->ifm_type != RTM_IFINFO)
+ {
+ Log(("NetIfList: Got message %u while expecting %u.\n",
+ pIfMsg->ifm_type, RTM_IFINFO));
+ vrc = VERR_INTERNAL_ERROR;
+ break;
+ }
+ struct sockaddr_dl *pSdl = (struct sockaddr_dl *)(pIfMsg + 1);
+
+ bool fSkip = !!strncmp(pInfo->szShortName, pSdl->sdl_data, pSdl->sdl_nlen)
+ || pInfo->szShortName[pSdl->sdl_nlen] != '\0';
+
+ pNext += pIfMsg->ifm_msglen;
+ while (pNext < pEnd)
+ {
+ struct ifa_msghdr *pIfAddrMsg = (struct ifa_msghdr *)pNext;
+
+ if (pIfAddrMsg->ifam_type != RTM_NEWADDR)
+ break;
+ if (!fSkip)
+ extractAddressesToNetInfo(pIfAddrMsg->ifam_addrs,
+ (char *)(pIfAddrMsg + 1),
+ pIfAddrMsg->ifam_msglen + (char *)pIfAddrMsg,
+ pInfo);
+ pNext += pIfAddrMsg->ifam_msglen;
+ }
+
+ if (!fSkip && pSdl->sdl_type == IFT_ETHER)
+ {
+ size_t cbNameLen = pSdl->sdl_nlen + 1;
+ memcpy(pInfo->MACAddress.au8, LLADDR(pSdl), sizeof(pInfo->MACAddress.au8));
+ pInfo->enmMediumType = NETIF_T_ETHERNET;
+ /* Generate UUID from name and MAC address. */
+ RTUUID uuid;
+ RTUuidClear(&uuid);
+ memcpy(&uuid, pInfo->szShortName, RT_MIN(cbNameLen, sizeof(uuid)));
+ uuid.Gen.u8ClockSeqHiAndReserved = (uuid.Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
+ uuid.Gen.u16TimeHiAndVersion = (uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
+ memcpy(uuid.Gen.au8Node, pInfo->MACAddress.au8, sizeof(uuid.Gen.au8Node));
+ pInfo->Uuid = uuid;
+
+ struct ifreq IfReq;
+ RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pInfo->szShortName);
+ if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0)
+ {
+ Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno));
+ pInfo->enmStatus = NETIF_S_UNKNOWN;
+ }
+ else
+ pInfo->enmStatus = (IfReq.ifr_flags & IFF_UP) ? NETIF_S_UP : NETIF_S_DOWN;
+
+ return VINF_SUCCESS;
+ }
+ }
+ close(sock);
+ RTMemFree(pBuf);
+ return vrc;
+}
+
+/**
+ * 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;
+}
+#endif
diff --git a/src/VBox/Main/src-server/darwin/PerformanceDarwin.cpp b/src/VBox/Main/src-server/darwin/PerformanceDarwin.cpp
new file mode 100644
index 00000000..9448093f
--- /dev/null
+++ b/src/VBox/Main/src-server/darwin/PerformanceDarwin.cpp
@@ -0,0 +1,191 @@
+/* $Id: PerformanceDarwin.cpp $ */
+/** @file
+ * VBox Darwin-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
+ */
+
+#include <mach/mach_error.h>
+#include <mach/mach_host.h>
+#include <mach/mach_init.h>
+#include <mach/mach_time.h>
+#include <mach/vm_statistics.h>
+#include <sys/sysctl.h>
+#include <sys/errno.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/mp.h>
+#include <iprt/param.h>
+#include <iprt/system.h>
+#include "Performance.h"
+
+/* The following declarations are missing in 10.4.x SDK */
+/** @todo Replace them with libproc.h and sys/proc_info.h when 10.4 is no longer supported */
+extern "C" int proc_pidinfo(int pid, int flavor, uint64_t arg, void *buffer, int buffersize);
+struct proc_taskinfo {
+ uint64_t pti_virtual_size; /* virtual memory size (bytes) */
+ uint64_t pti_resident_size; /* resident memory size (bytes) */
+ uint64_t pti_total_user; /* total time */
+ uint64_t pti_total_system;
+ uint64_t pti_threads_user; /* existing threads only */
+ uint64_t pti_threads_system;
+ int32_t pti_policy; /* default policy for new threads */
+ int32_t pti_faults; /* number of page faults */
+ int32_t pti_pageins; /* number of actual pageins */
+ int32_t pti_cow_faults; /* number of copy-on-write faults */
+ int32_t pti_messages_sent; /* number of messages sent */
+ int32_t pti_messages_received; /* number of messages received */
+ int32_t pti_syscalls_mach; /* number of mach system calls */
+ int32_t pti_syscalls_unix; /* number of unix system calls */
+ int32_t pti_csw; /* number of context switches */
+ int32_t pti_threadnum; /* number of threads in the task */
+ int32_t pti_numrunning; /* number of running threads */
+ int32_t pti_priority; /* task priority*/
+};
+#define PROC_PIDTASKINFO 4
+
+namespace pm {
+
+class CollectorDarwin : public CollectorHAL
+{
+public:
+ CollectorDarwin();
+ virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle);
+ virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available);
+ virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total);
+ virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used);
+private:
+ ULONG totalRAM;
+ uint32_t nCpus;
+};
+
+CollectorHAL *createHAL()
+{
+ return new CollectorDarwin();
+}
+
+CollectorDarwin::CollectorDarwin()
+{
+ uint64_t cb;
+ int vrc = RTSystemQueryTotalRam(&cb);
+ if (RT_FAILURE(vrc))
+ totalRAM = 0;
+ else
+ totalRAM = (ULONG)(cb / 1024);
+ nCpus = RTMpGetOnlineCount();
+ Assert(nCpus);
+ if (nCpus == 0)
+ {
+ /* It is rather unsual to have no CPUs, but the show must go on. */
+ nCpus = 1;
+ }
+}
+
+int CollectorDarwin::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle)
+{
+ kern_return_t krc;
+ mach_msg_type_number_t count;
+ host_cpu_load_info_data_t info;
+
+ count = HOST_CPU_LOAD_INFO_COUNT;
+
+ krc = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&info, &count);
+ if (krc != KERN_SUCCESS)
+ {
+ Log(("host_statistics() -> %s", mach_error_string(krc)));
+ return RTErrConvertFromDarwinKern(krc);
+ }
+
+ *user = (uint64_t)info.cpu_ticks[CPU_STATE_USER]
+ + info.cpu_ticks[CPU_STATE_NICE];
+ *kernel = (uint64_t)info.cpu_ticks[CPU_STATE_SYSTEM];
+ *idle = (uint64_t)info.cpu_ticks[CPU_STATE_IDLE];
+ return VINF_SUCCESS;
+}
+
+int CollectorDarwin::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;
+ cb /= 1024;
+ *available = cb < ~(ULONG)0 ? (ULONG)cb : ~(ULONG)0;
+ *used = *total - *available;
+ }
+ return vrc;
+}
+
+static int getProcessInfo(RTPROCESS process, struct proc_taskinfo *tinfo)
+{
+ Log7(("getProcessInfo() getting info for %d", process));
+ int cbRet = proc_pidinfo((pid_t)process, PROC_PIDTASKINFO, 0, tinfo, sizeof(*tinfo));
+ if (cbRet <= 0)
+ {
+ int iErrNo = errno;
+ Log(("proc_pidinfo() -> %s", strerror(iErrNo)));
+ return RTErrConvertFromDarwin(iErrNo);
+ }
+ if ((unsigned int)cbRet < sizeof(*tinfo))
+ {
+ Log(("proc_pidinfo() -> too few bytes %d", cbRet));
+ return VERR_INTERNAL_ERROR;
+ }
+ return VINF_SUCCESS;
+}
+
+int CollectorDarwin::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total)
+{
+ struct proc_taskinfo tinfo;
+
+ int vrc = getProcessInfo(process, &tinfo);
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * Adjust user and kernel values so 100% is when ALL cores are fully
+ * utilized (see @bugref{6345}).
+ */
+ *user = tinfo.pti_total_user / nCpus;
+ *kernel = tinfo.pti_total_system / nCpus;
+ *total = mach_absolute_time();
+ }
+ return vrc;
+}
+
+int CollectorDarwin::getProcessMemoryUsage(RTPROCESS process, ULONG *used)
+{
+ struct proc_taskinfo tinfo;
+
+ int vrc = getProcessInfo(process, &tinfo);
+ if (RT_SUCCESS(vrc))
+ {
+ uint64_t cKbResident = tinfo.pti_resident_size / 1024;
+ *used = cKbResident < ~(ULONG)0 ? (ULONG)cKbResident : ~(ULONG)0;
+ }
+ return vrc;
+}
+
+}
+
diff --git a/src/VBox/Main/src-server/darwin/USBProxyBackendDarwin.cpp b/src/VBox/Main/src-server/darwin/USBProxyBackendDarwin.cpp
new file mode 100644
index 00000000..81300a0f
--- /dev/null
+++ b/src/VBox/Main/src-server/darwin/USBProxyBackendDarwin.cpp
@@ -0,0 +1,195 @@
+/* $Id: USBProxyBackendDarwin.cpp $ */
+/** @file
+ * VirtualBox USB Proxy Service (in VBoxSVC), Darwin 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 "iokit.h"
+
+#include <VBox/usb.h>
+#include <VBox/usblib.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 <iprt/asm.h>
+
+
+/**
+ * Initialize data members.
+ */
+USBProxyBackendDarwin::USBProxyBackendDarwin()
+ : USBProxyBackend(), mServiceRunLoopRef(NULL), mNotifyOpaque(NULL), mWaitABitNextTime(false)
+{
+}
+
+USBProxyBackendDarwin::~USBProxyBackendDarwin()
+{
+}
+
+/**
+ * Initializes the object (called right after construction).
+ *
+ * @returns VBox status code.
+ */
+int USBProxyBackendDarwin::init(USBProxyService *pUsbProxyService, const com::Utf8Str &strId,
+ const com::Utf8Str &strAddress, bool fLoadingSettings)
+{
+ USBProxyBackend::init(pUsbProxyService, strId, strAddress, fLoadingSettings);
+
+ unconst(m_strBackend) = Utf8Str("host");
+
+ /*
+ * Start the poller thread.
+ */
+ start();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Stop all service threads and free the device chain.
+ */
+void USBProxyBackendDarwin::uninit()
+{
+ LogFlowThisFunc(("\n"));
+
+ /*
+ * Stop the service.
+ */
+ if (isActive())
+ stop();
+
+ USBProxyBackend::uninit();
+}
+
+
+int USBProxyBackendDarwin::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);
+
+ devLock.release();
+ interruptWait();
+ return VINF_SUCCESS;
+}
+
+
+int USBProxyBackendDarwin::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);
+
+ devLock.release();
+ interruptWait();
+ return VINF_SUCCESS;
+}
+
+
+bool USBProxyBackendDarwin::isFakeUpdateRequired()
+{
+ return true;
+}
+
+
+int USBProxyBackendDarwin::wait(RTMSINTERVAL aMillies)
+{
+ SInt32 rc = CFRunLoopRunInMode(CFSTR(VBOX_IOKIT_MODE_STRING),
+ mWaitABitNextTime && aMillies >= 1000
+ ? 1.0 /* seconds */
+ : aMillies >= 5000 /* Temporary measure to poll for status changes (MSD). */
+ ? 5.0 /* seconds */
+ : aMillies / 1000.0,
+ true);
+ mWaitABitNextTime = rc != kCFRunLoopRunTimedOut;
+
+ return VINF_SUCCESS;
+}
+
+
+int USBProxyBackendDarwin::interruptWait(void)
+{
+ if (mServiceRunLoopRef)
+ CFRunLoopStop(mServiceRunLoopRef);
+ return 0;
+}
+
+
+PUSBDEVICE USBProxyBackendDarwin::getDevices(void)
+{
+ /* call iokit.cpp */
+ return DarwinGetUSBDevices();
+}
+
+
+void USBProxyBackendDarwin::serviceThreadInit(void)
+{
+ mServiceRunLoopRef = CFRunLoopGetCurrent();
+ mNotifyOpaque = DarwinSubscribeUSBNotifications();
+}
+
+
+void USBProxyBackendDarwin::serviceThreadTerm(void)
+{
+ DarwinUnsubscribeUSBNotifications(mNotifyOpaque);
+ mServiceRunLoopRef = NULL;
+}
+
+
+/**
+ * Wrapper called from iokit.cpp.
+ *
+ * @param pCur The USB device to free.
+ */
+void DarwinFreeUSBDeviceFromIOKit(PUSBDEVICE pCur)
+{
+ USBProxyBackend::freeDevice(pCur);
+}
+
diff --git a/src/VBox/Main/src-server/darwin/iokit.cpp b/src/VBox/Main/src-server/darwin/iokit.cpp
new file mode 100644
index 00000000..5fd1aba1
--- /dev/null
+++ b/src/VBox/Main/src-server/darwin/iokit.cpp
@@ -0,0 +1,1993 @@
+/* $Id: iokit.cpp $ */
+/** @file
+ * Main - Darwin IOKit Routines.
+ *
+ * Because IOKit makes use of COM like interfaces, it does not mix very
+ * well with COM/XPCOM and must therefore be isolated from it using a
+ * simpler C interface.
+ */
+
+/*
+ * 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
+#ifdef STANDALONE_TESTCASE
+# define VBOX_WITH_USB
+#endif
+
+#include <mach/mach.h>
+#include <Carbon/Carbon.h>
+#include <CoreFoundation/CFBase.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
+#include <IOKit/storage/IOBlockStorageDevice.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/IOCDMedia.h>
+#include <IOKit/scsi/SCSITaskLib.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <mach/mach_error.h>
+#include <sys/param.h>
+#include <paths.h>
+#ifdef VBOX_WITH_USB
+# include <IOKit/usb/IOUSBLib.h>
+# include <IOKit/IOCFPlugIn.h>
+#endif
+
+#include <VBox/log.h>
+#include <VBox/usblib.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/process.h>
+#include <iprt/assert.h>
+#include <iprt/system.h>
+#include <iprt/thread.h>
+#include <iprt/uuid.h>
+#ifdef STANDALONE_TESTCASE
+# include <iprt/initterm.h>
+# include <iprt/stream.h>
+#endif
+
+#include "iokit.h"
+
+/* A small hack... */
+#ifdef STANDALONE_TESTCASE
+# define DarwinFreeUSBDeviceFromIOKit(a) do { } while (0)
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** An attempt at catching reference leaks. */
+#define MY_CHECK_CREFS(cRefs) do { AssertMsg(cRefs < 25, ("%ld\n", cRefs)); NOREF(cRefs); } while (0)
+
+/** Contains the pid of the current client. If 0, the kernel is the current client. */
+#define VBOXUSB_CLIENT_KEY "VBoxUSB-Client"
+/** Contains the pid of the filter owner (i.e. the VBoxSVC pid). */
+#define VBOXUSB_OWNER_KEY "VBoxUSB-Owner"
+/** The VBoxUSBDevice class name. */
+#define VBOXUSBDEVICE_CLASS_NAME "org_virtualbox_VBoxUSBDevice"
+
+/** Define the constant for the IOUSBHostDevice class name added in El Capitan. */
+#ifndef kIOUSBHostDeviceClassName
+# define kIOUSBHostDeviceClassName "IOUSBHostDevice"
+#endif
+
+/** The major darwin version indicating OS X El Captian, used to take care of the USB changes. */
+#define VBOX_OSX_EL_CAPTIAN_VER 15
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The IO Master Port. */
+static mach_port_t g_MasterPort = MACH_PORT_NULL;
+/** Major darwin version as returned by uname -r. */
+static uint32_t g_uMajorDarwin = 0;
+
+
+/**
+ * Lazily opens the master port.
+ *
+ * @returns true if the port is open, false on failure (very unlikely).
+ */
+static bool darwinOpenMasterPort(void)
+{
+ if (!g_MasterPort)
+ {
+ kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &g_MasterPort);
+ AssertReturn(krc == KERN_SUCCESS, false);
+
+ /* Get the darwin version we are running on. */
+ char szVersion[64];
+ int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, &szVersion[0], sizeof(szVersion));
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = RTStrToUInt32Ex(&szVersion[0], NULL, 10, &g_uMajorDarwin);
+ AssertLogRelMsg(vrc == VINF_SUCCESS || vrc == VWRN_TRAILING_CHARS,
+ ("Failed to convert the major part of the version string '%s' into an integer: %Rrc\n",
+ szVersion, vrc));
+ }
+ else
+ AssertLogRelMsgFailed(("Failed to query the OS release version with %Rrc\n", vrc));
+ }
+ return true;
+}
+
+
+/**
+ * Checks whether the value exists.
+ *
+ * @returns true / false accordingly.
+ * @param DictRef The dictionary.
+ * @param KeyStrRef The key name.
+ */
+static bool darwinDictIsPresent(CFDictionaryRef DictRef, CFStringRef KeyStrRef)
+{
+ return !!CFDictionaryGetValue(DictRef, KeyStrRef);
+}
+
+
+/**
+ * Gets a boolean value.
+ *
+ * @returns Success indicator (true/false).
+ * @param DictRef The dictionary.
+ * @param KeyStrRef The key name.
+ * @param pf Where to store the key value.
+ */
+static bool darwinDictGetBool(CFDictionaryRef DictRef, CFStringRef KeyStrRef, bool *pf)
+{
+ CFTypeRef BoolRef = CFDictionaryGetValue(DictRef, KeyStrRef);
+ if ( BoolRef
+ && CFGetTypeID(BoolRef) == CFBooleanGetTypeID())
+ {
+ *pf = CFBooleanGetValue((CFBooleanRef)BoolRef);
+ return true;
+ }
+ *pf = false;
+ return false;
+}
+
+
+/**
+ * Gets an unsigned 8-bit integer value.
+ *
+ * @returns Success indicator (true/false).
+ * @param DictRef The dictionary.
+ * @param KeyStrRef The key name.
+ * @param pu8 Where to store the key value.
+ */
+static bool darwinDictGetU8(CFDictionaryRef DictRef, CFStringRef KeyStrRef, uint8_t *pu8)
+{
+ CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
+ if (ValRef)
+ {
+ if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt8Type, pu8))
+ return true;
+ }
+ *pu8 = 0;
+ return false;
+}
+
+
+/**
+ * Gets an unsigned 16-bit integer value.
+ *
+ * @returns Success indicator (true/false).
+ * @param DictRef The dictionary.
+ * @param KeyStrRef The key name.
+ * @param pu16 Where to store the key value.
+ */
+static bool darwinDictGetU16(CFDictionaryRef DictRef, CFStringRef KeyStrRef, uint16_t *pu16)
+{
+ CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
+ if (ValRef)
+ {
+ if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt16Type, pu16))
+ return true;
+ }
+ *pu16 = 0;
+ return false;
+}
+
+
+/**
+ * Gets an unsigned 32-bit integer value.
+ *
+ * @returns Success indicator (true/false).
+ * @param DictRef The dictionary.
+ * @param KeyStrRef The key name.
+ * @param pu32 Where to store the key value.
+ */
+static bool darwinDictGetU32(CFDictionaryRef DictRef, CFStringRef KeyStrRef, uint32_t *pu32)
+{
+ CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
+ if (ValRef)
+ {
+ if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt32Type, pu32))
+ return true;
+ }
+ *pu32 = 0;
+ return false;
+}
+
+
+/**
+ * Gets an unsigned 64-bit integer value.
+ *
+ * @returns Success indicator (true/false).
+ * @param DictRef The dictionary.
+ * @param KeyStrRef The key name.
+ * @param pu64 Where to store the key value.
+ */
+static bool darwinDictGetU64(CFDictionaryRef DictRef, CFStringRef KeyStrRef, uint64_t *pu64)
+{
+ CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
+ if (ValRef)
+ {
+ if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt64Type, pu64))
+ return true;
+ }
+ *pu64 = 0;
+ return false;
+}
+
+
+/**
+ * Gets a RTPROCESS value.
+ *
+ * @returns Success indicator (true/false).
+ * @param DictRef The dictionary.
+ * @param KeyStrRef The key name.
+ * @param pProcess Where to store the key value.
+ */
+static bool darwinDictGetProcess(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, PRTPROCESS pProcess)
+{
+ switch (sizeof(*pProcess))
+ {
+ case sizeof(uint16_t): return darwinDictGetU16(DictRef, KeyStrRef, (uint16_t *)pProcess);
+ case sizeof(uint32_t): return darwinDictGetU32(DictRef, KeyStrRef, (uint32_t *)pProcess);
+ case sizeof(uint64_t): return darwinDictGetU64(DictRef, KeyStrRef, (uint64_t *)pProcess);
+ default:
+ AssertMsgFailedReturn(("%d\n", sizeof(*pProcess)), false);
+ }
+}
+
+
+/**
+ * Gets string value, converted to UTF-8 and put in user buffer.
+ *
+ * @returns Success indicator (true/false).
+ * @param DictRef The dictionary.
+ * @param KeyStrRef The key name.
+ * @param psz The string buffer. On failure this will be an empty string ("").
+ * @param cch The size of the buffer.
+ */
+static bool darwinDictGetString(CFDictionaryRef DictRef, CFStringRef KeyStrRef, char *psz, size_t cch)
+{
+ CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
+ if (ValRef)
+ {
+ if (CFStringGetCString((CFStringRef)ValRef, psz, (CFIndex)cch, kCFStringEncodingUTF8))
+ return true;
+ }
+ Assert(cch > 0);
+ *psz = '\0';
+ return false;
+}
+
+
+/**
+ * Gets string value, converted to UTF-8 and put in a IPRT string buffer.
+ *
+ * @returns Success indicator (true/false).
+ * @param DictRef The dictionary.
+ * @param KeyStrRef The key name.
+ * @param ppsz Where to store the key value. Free with RTStrFree. Set to NULL on failure.
+ */
+static bool darwinDictDupString(CFDictionaryRef DictRef, CFStringRef KeyStrRef, char **ppsz)
+{
+ char szBuf[512];
+ if (darwinDictGetString(DictRef, KeyStrRef, szBuf, sizeof(szBuf)))
+ {
+ USBLibPurgeEncoding(szBuf);
+ *ppsz = RTStrDup(szBuf);
+ if (*ppsz)
+ return true;
+ }
+ *ppsz = NULL;
+ return false;
+}
+
+
+/**
+ * Gets a byte string (data) of a specific size.
+ *
+ * @returns Success indicator (true/false).
+ * @param DictRef The dictionary.
+ * @param KeyStrRef The key name.
+ * @param pvBuf The buffer to store the bytes in.
+ * @param cbBuf The size of the buffer. This must exactly match the data size.
+ */
+static bool darwinDictGetData(CFDictionaryRef DictRef, CFStringRef KeyStrRef, void *pvBuf, size_t cbBuf)
+{
+ CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
+ if (ValRef)
+ {
+ CFIndex cbActual = CFDataGetLength((CFDataRef)ValRef);
+ if (cbActual >= 0 && cbBuf == (size_t)cbActual)
+ {
+ CFDataGetBytes((CFDataRef)ValRef, CFRangeMake(0, (CFIndex)cbBuf), (uint8_t *)pvBuf);
+ return true;
+ }
+ }
+ memset(pvBuf, '\0', cbBuf);
+ return false;
+}
+
+
+#if 1 && !defined(STANDALONE_TESTCASE) /* dumping disabled */
+# define DARWIN_IOKIT_LOG(a) Log(a)
+# define DARWIN_IOKIT_LOG_FLUSH() do {} while (0)
+# define DARWIN_IOKIT_DUMP_OBJ(o) do {} while (0)
+#else
+# if defined(STANDALONE_TESTCASE)
+# include <iprt/stream.h>
+# define DARWIN_IOKIT_LOG(a) RTPrintf a
+# define DARWIN_IOKIT_LOG_FLUSH() RTStrmFlush(g_pStdOut)
+# else
+# define DARWIN_IOKIT_LOG(a) RTLogPrintf a
+# define DARWIN_IOKIT_LOG_FLUSH() RTLogFlush(NULL)
+# endif
+# define DARWIN_IOKIT_DUMP_OBJ(o) darwinDumpObj(o)
+
+/**
+ * Callback for dumping a dictionary key.
+ *
+ * @param pvKey The key name.
+ * @param pvValue The key value
+ * @param pvUser The recursion depth.
+ */
+static void darwinDumpDictCallback(const void *pvKey, const void *pvValue, void *pvUser)
+{
+ /* display the key name. */
+ char *pszKey = (char *)RTMemTmpAlloc(1024);
+ if (!CFStringGetCString((CFStringRef)pvKey, pszKey, 1024, kCFStringEncodingUTF8))
+ strcpy(pszKey, "CFStringGetCString failure");
+ DARWIN_IOKIT_LOG(("%+*s%s", (int)(uintptr_t)pvUser, "", pszKey));
+ RTMemTmpFree(pszKey);
+
+ /* display the value type */
+ CFTypeID Type = CFGetTypeID(pvValue);
+ DARWIN_IOKIT_LOG((" [%d-", Type));
+
+ /* display the value */
+ if (Type == CFDictionaryGetTypeID())
+ {
+ DARWIN_IOKIT_LOG(("dictionary] =\n"
+ "%-*s{\n", (int)(uintptr_t)pvUser, ""));
+ CFDictionaryApplyFunction((CFDictionaryRef)pvValue, darwinDumpDictCallback, (void *)((uintptr_t)pvUser + 4));
+ DARWIN_IOKIT_LOG(("%-*s}\n", (int)(uintptr_t)pvUser, ""));
+ }
+ else if (Type == CFBooleanGetTypeID())
+ DARWIN_IOKIT_LOG(("bool] = %s\n", CFBooleanGetValue((CFBooleanRef)pvValue) ? "true" : "false"));
+ else if (Type == CFNumberGetTypeID())
+ {
+ union
+ {
+ SInt8 s8;
+ SInt16 s16;
+ SInt32 s32;
+ SInt64 s64;
+ Float32 rf32;
+ Float64 rd64;
+ char ch;
+ short s;
+ int i;
+ long l;
+ long long ll;
+ float rf;
+ double rd;
+ CFIndex iCF;
+ } u;
+ RT_ZERO(u);
+ CFNumberType NumType = CFNumberGetType((CFNumberRef)pvValue);
+ if (CFNumberGetValue((CFNumberRef)pvValue, NumType, &u))
+ {
+ switch (CFNumberGetType((CFNumberRef)pvValue))
+ {
+ case kCFNumberSInt8Type: DARWIN_IOKIT_LOG(("SInt8] = %RI8 (%#RX8)\n", NumType, u.s8, u.s8)); break;
+ case kCFNumberSInt16Type: DARWIN_IOKIT_LOG(("SInt16] = %RI16 (%#RX16)\n", NumType, u.s16, u.s16)); break;
+ case kCFNumberSInt32Type: DARWIN_IOKIT_LOG(("SInt32] = %RI32 (%#RX32)\n", NumType, u.s32, u.s32)); break;
+ case kCFNumberSInt64Type: DARWIN_IOKIT_LOG(("SInt64] = %RI64 (%#RX64)\n", NumType, u.s64, u.s64)); break;
+ case kCFNumberFloat32Type: DARWIN_IOKIT_LOG(("float32] = %#lx\n", NumType, u.l)); break;
+ case kCFNumberFloat64Type: DARWIN_IOKIT_LOG(("float64] = %#llx\n", NumType, u.ll)); break;
+ case kCFNumberFloatType: DARWIN_IOKIT_LOG(("float] = %#lx\n", NumType, u.l)); break;
+ case kCFNumberDoubleType: DARWIN_IOKIT_LOG(("double] = %#llx\n", NumType, u.ll)); break;
+ case kCFNumberCharType: DARWIN_IOKIT_LOG(("char] = %hhd (%hhx)\n", NumType, u.ch, u.ch)); break;
+ case kCFNumberShortType: DARWIN_IOKIT_LOG(("short] = %hd (%hx)\n", NumType, u.s, u.s)); break;
+ case kCFNumberIntType: DARWIN_IOKIT_LOG(("int] = %d (%#x)\n", NumType, u.i, u.i)); break;
+ case kCFNumberLongType: DARWIN_IOKIT_LOG(("long] = %ld (%#lx)\n", NumType, u.l, u.l)); break;
+ case kCFNumberLongLongType: DARWIN_IOKIT_LOG(("long long] = %lld (%#llx)\n", NumType, u.ll, u.ll)); break;
+ case kCFNumberCFIndexType: DARWIN_IOKIT_LOG(("CFIndex] = %lld (%#llx)\n", NumType, (long long)u.iCF, (long long)u.iCF)); break;
+ break;
+ default: DARWIN_IOKIT_LOG(("%d?] = %lld (%llx)\n", NumType, u.ll, u.ll)); break;
+ }
+ }
+ else
+ DARWIN_IOKIT_LOG(("number] = CFNumberGetValue failed\n"));
+ }
+ else if (Type == CFBooleanGetTypeID())
+ DARWIN_IOKIT_LOG(("boolean] = %RTbool\n", CFBooleanGetValue((CFBooleanRef)pvValue)));
+ else if (Type == CFStringGetTypeID())
+ {
+ DARWIN_IOKIT_LOG(("string] = "));
+ char *pszValue = (char *)RTMemTmpAlloc(16*_1K);
+ if (!CFStringGetCString((CFStringRef)pvValue, pszValue, 16*_1K, kCFStringEncodingUTF8))
+ strcpy(pszValue, "CFStringGetCString failure");
+ DARWIN_IOKIT_LOG(("\"%s\"\n", pszValue));
+ RTMemTmpFree(pszValue);
+ }
+ else if (Type == CFDataGetTypeID())
+ {
+ CFIndex cb = CFDataGetLength((CFDataRef)pvValue);
+ DARWIN_IOKIT_LOG(("%zu bytes] =", (size_t)cb));
+ void *pvData = RTMemTmpAlloc(cb + 8);
+ CFDataGetBytes((CFDataRef)pvValue, CFRangeMake(0, cb), (uint8_t *)pvData);
+ if (!cb)
+ DARWIN_IOKIT_LOG((" \n"));
+ else if (cb <= 32)
+ DARWIN_IOKIT_LOG((" %.*Rhxs\n", cb, pvData));
+ else
+ DARWIN_IOKIT_LOG(("\n%.*Rhxd\n", cb, pvData));
+ RTMemTmpFree(pvData);
+ }
+ else
+ DARWIN_IOKIT_LOG(("??] = %p\n", pvValue));
+}
+
+
+/**
+ * Dumps a dictionary to the log.
+ *
+ * @param DictRef The dictionary to dump.
+ */
+static void darwinDumpDict(CFDictionaryRef DictRef, unsigned cIndents)
+{
+ CFDictionaryApplyFunction(DictRef, darwinDumpDictCallback, (void *)(uintptr_t)cIndents);
+ DARWIN_IOKIT_LOG_FLUSH();
+}
+
+
+/**
+ * Dumps an I/O kit registry object and all it children.
+ * @param Object The object to dump.
+ * @param cIndents The number of indents to use.
+ */
+static void darwinDumpObjInt(io_object_t Object, unsigned cIndents)
+{
+ static io_string_t s_szPath;
+ kern_return_t krc = IORegistryEntryGetPath(Object, kIOServicePlane, s_szPath);
+ if (krc != KERN_SUCCESS)
+ strcpy(s_szPath, "IORegistryEntryGetPath failed");
+ DARWIN_IOKIT_LOG(("Dumping %p - %s:\n", (const void *)Object, s_szPath));
+
+ CFMutableDictionaryRef PropsRef = 0;
+ krc = IORegistryEntryCreateCFProperties(Object, &PropsRef, kCFAllocatorDefault, kNilOptions);
+ if (krc == KERN_SUCCESS)
+ {
+ darwinDumpDict(PropsRef, cIndents + 4);
+ CFRelease(PropsRef);
+ }
+
+ /*
+ * Children.
+ */
+ io_iterator_t Children;
+ krc = IORegistryEntryGetChildIterator(Object, kIOServicePlane, &Children);
+ if (krc == KERN_SUCCESS)
+ {
+ io_object_t Child;
+ while ((Child = IOIteratorNext(Children)) != IO_OBJECT_NULL)
+ {
+ darwinDumpObjInt(Child, cIndents + 4);
+ IOObjectRelease(Child);
+ }
+ IOObjectRelease(Children);
+ }
+ else
+ DARWIN_IOKIT_LOG(("IORegistryEntryGetChildIterator -> %#x\n", krc));
+}
+
+/**
+ * Dumps an I/O kit registry object and all it children.
+ * @param Object The object to dump.
+ */
+static void darwinDumpObj(io_object_t Object)
+{
+ darwinDumpObjInt(Object, 0);
+}
+
+#endif /* helpers for dumping registry dictionaries */
+
+
+#ifdef VBOX_WITH_USB
+
+/**
+ * Notification data created by DarwinSubscribeUSBNotifications, used by
+ * the callbacks and finally freed by DarwinUnsubscribeUSBNotifications.
+ */
+typedef struct DARWINUSBNOTIFY
+{
+ /** The notification port.
+ * It's shared between the notification callbacks. */
+ IONotificationPortRef NotifyPort;
+ /** The run loop source for NotifyPort. */
+ CFRunLoopSourceRef NotifyRLSrc;
+ /** The attach notification iterator. */
+ io_iterator_t AttachIterator;
+ /** The 2nd attach notification iterator. */
+ io_iterator_t AttachIterator2;
+ /** The detach notification iterator. */
+ io_iterator_t DetachIterator;
+} DARWINUSBNOTIFY, *PDARWINUSBNOTIFY;
+
+
+/**
+ * Run thru an iterator.
+ *
+ * The docs says this is necessary to start getting notifications,
+ * so this function is called in the callbacks and right after
+ * registering the notification.
+ *
+ * @param pIterator The iterator reference.
+ */
+static void darwinDrainIterator(io_iterator_t pIterator)
+{
+ io_object_t Object;
+ while ((Object = IOIteratorNext(pIterator)) != IO_OBJECT_NULL)
+ {
+ DARWIN_IOKIT_DUMP_OBJ(Object);
+ IOObjectRelease(Object);
+ }
+}
+
+
+/**
+ * Callback for the 1st attach notification.
+ *
+ * @param pvNotify Our data.
+ * @param NotifyIterator The notification iterator.
+ */
+static void darwinUSBAttachNotification1(void *pvNotify, io_iterator_t NotifyIterator)
+{
+ DARWIN_IOKIT_LOG(("USB Attach Notification1\n"));
+ NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
+ darwinDrainIterator(NotifyIterator);
+}
+
+
+/**
+ * Callback for the 2nd attach notification.
+ *
+ * @param pvNotify Our data.
+ * @param NotifyIterator The notification iterator.
+ */
+static void darwinUSBAttachNotification2(void *pvNotify, io_iterator_t NotifyIterator)
+{
+ DARWIN_IOKIT_LOG(("USB Attach Notification2\n"));
+ NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
+ darwinDrainIterator(NotifyIterator);
+}
+
+
+/**
+ * Callback for the detach notifications.
+ *
+ * @param pvNotify Our data.
+ * @param NotifyIterator The notification iterator.
+ */
+static void darwinUSBDetachNotification(void *pvNotify, io_iterator_t NotifyIterator)
+{
+ DARWIN_IOKIT_LOG(("USB Detach Notification\n"));
+ NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
+ darwinDrainIterator(NotifyIterator);
+}
+
+
+/**
+ * Subscribes the run loop to USB notification events relevant to
+ * device attach/detach.
+ *
+ * The source mode for these events is defined as VBOX_IOKIT_MODE_STRING
+ * so that the caller can listen to events from this mode only and
+ * re-evalutate the list of attached devices whenever an event arrives.
+ *
+ * @returns opaque for passing to the unsubscribe function. If NULL
+ * something unexpectedly failed during subscription.
+ */
+void *DarwinSubscribeUSBNotifications(void)
+{
+ AssertReturn(darwinOpenMasterPort(), NULL);
+
+ PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)RTMemAllocZ(sizeof(*pNotify));
+ AssertReturn(pNotify, NULL);
+
+ /*
+ * Create the notification port, bake it into a runloop source which we
+ * then add to our run loop.
+ */
+ pNotify->NotifyPort = IONotificationPortCreate(g_MasterPort);
+ Assert(pNotify->NotifyPort);
+ if (pNotify->NotifyPort)
+ {
+ pNotify->NotifyRLSrc = IONotificationPortGetRunLoopSource(pNotify->NotifyPort);
+ Assert(pNotify->NotifyRLSrc);
+ if (pNotify->NotifyRLSrc)
+ {
+ CFRunLoopRef RunLoopRef = CFRunLoopGetCurrent();
+ CFRetain(RunLoopRef); /* Workaround for crash when cleaning up the TLS / runloop((sub)mode). See @bugref{2807}. */
+ CFRunLoopAddSource(RunLoopRef, pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
+
+ /*
+ * Create the notification callbacks.
+ */
+ kern_return_t krc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
+ kIOPublishNotification,
+ IOServiceMatching(kIOUSBDeviceClassName),
+ darwinUSBAttachNotification1,
+ pNotify,
+ &pNotify->AttachIterator);
+ if (krc == KERN_SUCCESS)
+ {
+ darwinDrainIterator(pNotify->AttachIterator);
+ krc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
+ kIOMatchedNotification,
+ IOServiceMatching(kIOUSBDeviceClassName),
+ darwinUSBAttachNotification2,
+ pNotify,
+ &pNotify->AttachIterator2);
+ if (krc == KERN_SUCCESS)
+ {
+ darwinDrainIterator(pNotify->AttachIterator2);
+ krc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
+ kIOTerminatedNotification,
+ IOServiceMatching(kIOUSBDeviceClassName),
+ darwinUSBDetachNotification,
+ pNotify,
+ &pNotify->DetachIterator);
+ if (krc == KERN_SUCCESS)
+ {
+ darwinDrainIterator(pNotify->DetachIterator);
+ return pNotify;
+ }
+ IOObjectRelease(pNotify->AttachIterator2);
+ }
+ IOObjectRelease(pNotify->AttachIterator);
+ }
+ CFRunLoopRemoveSource(RunLoopRef, pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
+ }
+ IONotificationPortDestroy(pNotify->NotifyPort);
+ }
+
+ RTMemFree(pNotify);
+ return NULL;
+}
+
+
+/**
+ * Unsubscribe the run loop from USB notification subscribed to
+ * by DarwinSubscribeUSBNotifications.
+ *
+ * @param pvOpaque The return value from DarwinSubscribeUSBNotifications.
+ */
+void DarwinUnsubscribeUSBNotifications(void *pvOpaque)
+{
+ PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvOpaque;
+ if (!pNotify)
+ return;
+
+ IOObjectRelease(pNotify->AttachIterator);
+ pNotify->AttachIterator = IO_OBJECT_NULL;
+ IOObjectRelease(pNotify->AttachIterator2);
+ pNotify->AttachIterator2 = IO_OBJECT_NULL;
+ IOObjectRelease(pNotify->DetachIterator);
+ pNotify->DetachIterator = IO_OBJECT_NULL;
+
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
+ IONotificationPortDestroy(pNotify->NotifyPort);
+ pNotify->NotifyRLSrc = NULL;
+ pNotify->NotifyPort = NULL;
+
+ RTMemFree(pNotify);
+}
+
+
+/**
+ * Descends recursively into a IORegistry tree locating the first object of a given class.
+ *
+ * The search is performed depth first.
+ *
+ * @returns Object reference if found, NULL if not.
+ * @param Object The current tree root.
+ * @param pszClass The name of the class we're looking for.
+ * @param pszNameBuf A scratch buffer for query the class name in to avoid
+ * wasting 128 bytes on an io_name_t object for every recursion.
+ */
+static io_object_t darwinFindObjectByClass(io_object_t Object, const char *pszClass, io_name_t pszNameBuf)
+{
+ io_iterator_t Children;
+ kern_return_t krc = IORegistryEntryGetChildIterator(Object, kIOServicePlane, &Children);
+ if (krc != KERN_SUCCESS)
+ return IO_OBJECT_NULL;
+ io_object_t Child;
+ while ((Child = IOIteratorNext(Children)) != IO_OBJECT_NULL)
+ {
+ krc = IOObjectGetClass(Child, pszNameBuf);
+ if ( krc == KERN_SUCCESS
+ && !strcmp(pszNameBuf, pszClass))
+ break;
+
+ io_object_t GrandChild = darwinFindObjectByClass(Child, pszClass, pszNameBuf);
+ IOObjectRelease(Child);
+ if (GrandChild)
+ {
+ Child = GrandChild;
+ break;
+ }
+ }
+ IOObjectRelease(Children);
+ return Child;
+}
+
+
+/**
+ * Descends recursively into IOUSBMassStorageClass tree to check whether
+ * the MSD is mounted or not.
+ *
+ * The current heuristic is to look for the IOMedia class.
+ *
+ * @returns true if mounted, false if not.
+ * @param MSDObj The IOUSBMassStorageClass object.
+ * @param pszNameBuf A scratch buffer for query the class name in to avoid
+ * wasting 128 bytes on an io_name_t object for every recursion.
+ */
+static bool darwinIsMassStorageInterfaceInUse(io_object_t MSDObj, io_name_t pszNameBuf)
+{
+ io_object_t MediaObj = darwinFindObjectByClass(MSDObj, kIOMediaClass, pszNameBuf);
+ if (MediaObj)
+ {
+ CFMutableDictionaryRef pProperties;
+ kern_return_t krc;
+ bool fInUse = true;
+
+ krc = IORegistryEntryCreateCFProperties(MediaObj, &pProperties, kCFAllocatorDefault, kNilOptions);
+ if (krc == KERN_SUCCESS)
+ {
+ CFBooleanRef pBoolValue = (CFBooleanRef)CFDictionaryGetValue(pProperties, CFSTR(kIOMediaOpenKey));
+ if (pBoolValue)
+ fInUse = CFBooleanGetValue(pBoolValue);
+
+ CFRelease(pProperties);
+ }
+
+ /* more checks? */
+ IOObjectRelease(MediaObj);
+ return fInUse;
+ }
+
+ return false;
+}
+
+
+/**
+ * Finds the matching IOUSBHostDevice registry entry for the given legacy USB device interface (IOUSBDevice).
+ *
+ * @returns kern_return_t error code.
+ * @param USBDeviceLegacy The legacy device I/O Kit object.
+ * @param pUSBDevice Where to store the IOUSBHostDevice object on success.
+ */
+static kern_return_t darwinGetUSBHostDeviceFromLegacyDevice(io_object_t USBDeviceLegacy, io_object_t *pUSBDevice)
+{
+ kern_return_t krc = KERN_SUCCESS;
+ uint64_t uIoRegEntryId = 0;
+
+ *pUSBDevice = 0;
+
+ /* Get the registry entry ID to match against. */
+ krc = IORegistryEntryGetRegistryEntryID(USBDeviceLegacy, &uIoRegEntryId);
+ if (krc != KERN_SUCCESS)
+ return krc;
+
+ /*
+ * Create a matching dictionary for searching for USB Devices in the IOKit.
+ */
+ CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBHostDeviceClassName);
+ AssertReturn(RefMatchingDict, KERN_FAILURE);
+
+ /*
+ * Perform the search and get a collection of USB Device back.
+ */
+ io_iterator_t USBDevices = IO_OBJECT_NULL;
+ IOReturn irc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
+ AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), KERN_FAILURE);
+ RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
+
+ /*
+ * Walk the devices and check for the matching alternate registry entry ID.
+ */
+ io_object_t USBDevice;
+ while ((USBDevice = IOIteratorNext(USBDevices)) != IO_OBJECT_NULL)
+ {
+ DARWIN_IOKIT_DUMP_OBJ(USBDevice);
+
+ CFMutableDictionaryRef PropsRef = 0;
+ krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
+ if (krc == KERN_SUCCESS)
+ {
+ uint64_t uAltRegId = 0;
+ if ( darwinDictGetU64(PropsRef, CFSTR("AppleUSBAlternateServiceRegistryID"), &uAltRegId)
+ && uAltRegId == uIoRegEntryId)
+ {
+ *pUSBDevice = USBDevice;
+ CFRelease(PropsRef);
+ break;
+ }
+
+ CFRelease(PropsRef);
+ }
+ IOObjectRelease(USBDevice);
+ }
+ IOObjectRelease(USBDevices);
+
+ return krc;
+}
+
+
+static bool darwinUSBDeviceIsGrabbedDetermineState(PUSBDEVICE pCur, io_object_t USBDevice)
+{
+ /*
+ * Iterate the interfaces (among the children of the IOUSBDevice object).
+ */
+ io_iterator_t Interfaces;
+ kern_return_t krc = IORegistryEntryGetChildIterator(USBDevice, kIOServicePlane, &Interfaces);
+ if (krc != KERN_SUCCESS)
+ return false;
+
+ bool fHaveOwner = false;
+ RTPROCESS Owner = NIL_RTPROCESS;
+ bool fHaveClient = false;
+ RTPROCESS Client = NIL_RTPROCESS;
+ io_object_t Interface;
+ while ((Interface = IOIteratorNext(Interfaces)) != IO_OBJECT_NULL)
+ {
+ io_name_t szName;
+ krc = IOObjectGetClass(Interface, szName);
+ if ( krc == KERN_SUCCESS
+ && !strcmp(szName, VBOXUSBDEVICE_CLASS_NAME))
+ {
+ CFMutableDictionaryRef PropsRef = 0;
+ krc = IORegistryEntryCreateCFProperties(Interface, &PropsRef, kCFAllocatorDefault, kNilOptions);
+ if (krc == KERN_SUCCESS)
+ {
+ fHaveOwner = darwinDictGetProcess(PropsRef, CFSTR(VBOXUSB_OWNER_KEY), &Owner);
+ fHaveClient = darwinDictGetProcess(PropsRef, CFSTR(VBOXUSB_CLIENT_KEY), &Client);
+ CFRelease(PropsRef);
+ }
+ }
+
+ IOObjectRelease(Interface);
+ }
+ IOObjectRelease(Interfaces);
+
+ /*
+ * Calc the status.
+ */
+ if (fHaveOwner)
+ {
+ if (Owner == RTProcSelf())
+ pCur->enmState = !fHaveClient || Client == NIL_RTPROCESS || !Client
+ ? USBDEVICESTATE_HELD_BY_PROXY
+ : USBDEVICESTATE_USED_BY_GUEST;
+ else
+ pCur->enmState = USBDEVICESTATE_USED_BY_HOST;
+ }
+
+ return fHaveOwner;
+}
+
+
+/**
+ * Worker for determining the USB device state for devices which are not captured by the VBoxUSB driver
+ * Works for both, IOUSBDevice (legacy on release >= El Capitan) and IOUSBHostDevice (available on >= El Capitan).
+ *
+ * @param pCur The USB device data.
+ * @param USBDevice I/O Kit USB device object (either IOUSBDevice or IOUSBHostDevice).
+ */
+static void darwinDetermineUSBDeviceStateWorker(PUSBDEVICE pCur, io_object_t USBDevice)
+{
+ /*
+ * Iterate the interfaces (among the children of the IOUSBDevice object).
+ */
+ io_iterator_t Interfaces;
+ kern_return_t krc = IORegistryEntryGetChildIterator(USBDevice, kIOServicePlane, &Interfaces);
+ if (krc != KERN_SUCCESS)
+ return;
+
+ bool fUserClientOnly = true;
+ bool fConfigured = false;
+ bool fInUse = false;
+ bool fSeizable = true;
+ io_object_t Interface;
+ while ((Interface = IOIteratorNext(Interfaces)) != IO_OBJECT_NULL)
+ {
+ io_name_t szName;
+ krc = IOObjectGetClass(Interface, szName);
+ if ( krc == KERN_SUCCESS
+ && ( !strcmp(szName, "IOUSBInterface")
+ || !strcmp(szName, "IOUSBHostInterface")))
+ {
+ fConfigured = true;
+
+ /*
+ * Iterate the interface children looking for stuff other than
+ * IOUSBUserClientInit objects.
+ */
+ io_iterator_t Children1;
+ krc = IORegistryEntryGetChildIterator(Interface, kIOServicePlane, &Children1);
+ if (krc == KERN_SUCCESS)
+ {
+ io_object_t Child1;
+ while ((Child1 = IOIteratorNext(Children1)) != IO_OBJECT_NULL)
+ {
+ krc = IOObjectGetClass(Child1, szName);
+ if ( krc == KERN_SUCCESS
+ && strcmp(szName, "IOUSBUserClientInit"))
+ {
+ fUserClientOnly = false;
+
+ if ( !strcmp(szName, "IOUSBMassStorageClass")
+ || !strcmp(szName, "IOUSBMassStorageInterfaceNub"))
+ {
+ /* Only permit capturing MSDs that aren't mounted, at least
+ until the GUI starts poping up warnings about data loss
+ and such when capturing a busy device. */
+ fSeizable = false;
+ fInUse |= darwinIsMassStorageInterfaceInUse(Child1, szName);
+ }
+ else if (!strcmp(szName, "IOUSBHIDDriver")
+ || !strcmp(szName, "AppleHIDMouse")
+ /** @todo more? */)
+ {
+ /* For now, just assume that all HID devices are inaccessible
+ because of the greedy HID service. */
+ fSeizable = false;
+ fInUse = true;
+ }
+ else
+ fInUse = true;
+ }
+ IOObjectRelease(Child1);
+ }
+ IOObjectRelease(Children1);
+ }
+ }
+
+ IOObjectRelease(Interface);
+ }
+ IOObjectRelease(Interfaces);
+
+ /*
+ * Calc the status.
+ */
+ if (!fInUse)
+ pCur->enmState = USBDEVICESTATE_UNUSED;
+ else
+ pCur->enmState = fSeizable
+ ? USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
+ : USBDEVICESTATE_USED_BY_HOST;
+}
+
+
+/**
+ * Worker function for DarwinGetUSBDevices() that tries to figure out
+ * what state the device is in and set enmState.
+ *
+ * This is mostly a matter of distinguishing between devices that nobody
+ * uses, devices that can be seized and devices that cannot be grabbed.
+ *
+ * @param pCur The USB device data.
+ * @param USBDevice The USB device object.
+ * @param PropsRef The USB device properties.
+ */
+static void darwinDeterminUSBDeviceState(PUSBDEVICE pCur, io_object_t USBDevice, CFMutableDictionaryRef /* PropsRef */)
+{
+
+ if (!darwinUSBDeviceIsGrabbedDetermineState(pCur, USBDevice))
+ {
+ /*
+ * The USB stack was completely reworked on El Capitan and the IOUSBDevice and IOUSBInterface
+ * are deprecated and don't return the information required for the additional checks below.
+ * We also can't directly make use of the new classes (IOUSBHostDevice and IOUSBHostInterface)
+ * because VBoxUSB only exposes the legacy interfaces. Trying to use the new classes results in errors
+ * because the I/O Kit USB library wants to use the new interfaces. The result is us losing the device
+ * form the list when VBoxUSB has attached to the USB device.
+ *
+ * To make the checks below work we have to get hold of the IOUSBHostDevice and IOUSBHostInterface
+ * instances for the current device. Fortunately the IOUSBHostDevice instance contains a
+ * "AppleUSBAlternateServiceRegistryID" which points to the legacy class instance for the same device.
+ * So just iterate over the list of IOUSBHostDevice instances and check whether the
+ * AppleUSBAlternateServiceRegistryID property matches with the legacy instance.
+ *
+ * The upside is that we can keep VBoxUSB untouched and still compatible with older OS X releases.
+ */
+ if (g_uMajorDarwin >= VBOX_OSX_EL_CAPTIAN_VER)
+ {
+ io_object_t IOUSBDeviceNew = IO_OBJECT_NULL;
+ kern_return_t krc = darwinGetUSBHostDeviceFromLegacyDevice(USBDevice, &IOUSBDeviceNew);
+ if ( krc == KERN_SUCCESS
+ && IOUSBDeviceNew != IO_OBJECT_NULL)
+ {
+ darwinDetermineUSBDeviceStateWorker(pCur, IOUSBDeviceNew);
+ IOObjectRelease(IOUSBDeviceNew);
+ }
+ }
+ else
+ darwinDetermineUSBDeviceStateWorker(pCur, USBDevice);
+ }
+}
+
+
+/**
+ * Enumerate the USB devices returning a FIFO of them.
+ *
+ * @returns Pointer to the head.
+ * USBProxyService::freeDevice is expected to free each of the list elements.
+ */
+PUSBDEVICE DarwinGetUSBDevices(void)
+{
+ AssertReturn(darwinOpenMasterPort(), NULL);
+ //DARWIN_IOKIT_LOG(("DarwinGetUSBDevices\n"));
+
+ /*
+ * Create a matching dictionary for searching for USB Devices in the IOKit.
+ */
+ CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
+ AssertReturn(RefMatchingDict, NULL);
+
+ /*
+ * Perform the search and get a collection of USB Device back.
+ */
+ io_iterator_t USBDevices = IO_OBJECT_NULL;
+ IOReturn irc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
+ AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), NULL);
+ RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
+
+ /*
+ * Enumerate the USB Devices.
+ */
+ PUSBDEVICE pHead = NULL;
+ PUSBDEVICE pTail = NULL;
+ unsigned i = 0;
+ io_object_t USBDevice;
+ while ((USBDevice = IOIteratorNext(USBDevices)) != IO_OBJECT_NULL)
+ {
+ DARWIN_IOKIT_DUMP_OBJ(USBDevice);
+
+ /*
+ * Query the device properties from the registry.
+ *
+ * We could alternatively use the device and such, but that will be
+ * slower and we would have to resort to the registry for the three
+ * string anyway.
+ */
+ CFMutableDictionaryRef PropsRef = 0;
+ kern_return_t krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
+ if (krc == KERN_SUCCESS)
+ {
+ bool fOk = false;
+ PUSBDEVICE pCur = (PUSBDEVICE)RTMemAllocZ(sizeof(*pCur));
+ do /* loop for breaking out of on failure. */
+ {
+ AssertBreak(pCur);
+
+ /*
+ * Mandatory
+ */
+ pCur->bcdUSB = 0; /* we've no idea. */
+ pCur->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; /* just a default, we'll try harder in a bit. */
+
+ /* Skip hubs. On 10.11 beta 3, the root hub simulations does not have a USBDeviceClass property, so
+ simply ignore failures to retrieve it. */
+ if (!darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceClass), &pCur->bDeviceClass))
+ {
+#ifdef VBOX_STRICT
+ char szTmp[80];
+ Assert( darwinDictGetString(PropsRef, CFSTR("IOClassNameOverride"), szTmp, sizeof(szTmp))
+ && strcmp(szTmp, "IOUSBRootHubDevice") == 0);
+#endif
+ break;
+ }
+ if (pCur->bDeviceClass == 0x09 /* hub, find a define! */)
+ break;
+ AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceSubClass), &pCur->bDeviceSubClass));
+ AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceProtocol), &pCur->bDeviceProtocol));
+ AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBVendorID), &pCur->idVendor));
+ AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBProductID), &pCur->idProduct));
+ AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBDeviceReleaseNumber), &pCur->bcdDevice));
+ uint32_t u32LocationId;
+ AssertBreak(darwinDictGetU32(PropsRef, CFSTR(kUSBDevicePropertyLocationID), &u32LocationId));
+ uint64_t u64SessionId;
+ AssertBreak(darwinDictGetU64(PropsRef, CFSTR("sessionID"), &u64SessionId));
+ char szAddress[64];
+ RTStrPrintf(szAddress, sizeof(szAddress), "p=0x%04RX16;v=0x%04RX16;s=0x%016RX64;l=0x%08RX32",
+ pCur->idProduct, pCur->idVendor, u64SessionId, u32LocationId);
+ pCur->pszAddress = RTStrDup(szAddress);
+ AssertBreak(pCur->pszAddress);
+ pCur->bBus = u32LocationId >> 24;
+ darwinDictGetU8(PropsRef, CFSTR("PortNum"), &pCur->bPort); /* Not present in 10.11 beta 3, so ignore failure. (Is set to zero.) */
+ uint8_t bSpeed;
+ AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDevicePropertySpeed), &bSpeed));
+ Assert(bSpeed <= 4);
+ pCur->enmSpeed = bSpeed == 4 ? USBDEVICESPEED_SUPER
+ : bSpeed == 3 ? USBDEVICESPEED_SUPER
+ : bSpeed == 2 ? USBDEVICESPEED_HIGH
+ : bSpeed == 1 ? USBDEVICESPEED_FULL
+ : bSpeed == 0 ? USBDEVICESPEED_LOW
+ : USBDEVICESPEED_UNKNOWN;
+
+ /*
+ * Optional.
+ * There are some nameless device in the iMac, apply names to them.
+ */
+ darwinDictDupString(PropsRef, CFSTR("USB Vendor Name"), (char **)&pCur->pszManufacturer);
+ if ( !pCur->pszManufacturer
+ && pCur->idVendor == kIOUSBVendorIDAppleComputer)
+ pCur->pszManufacturer = RTStrDup("Apple Computer, Inc.");
+ darwinDictDupString(PropsRef, CFSTR("USB Product Name"), (char **)&pCur->pszProduct);
+ if ( !pCur->pszProduct
+ && pCur->bDeviceClass == 224 /* Wireless */
+ && pCur->bDeviceSubClass == 1 /* Radio Frequency */
+ && pCur->bDeviceProtocol == 1 /* Bluetooth */)
+ pCur->pszProduct = RTStrDup("Bluetooth");
+ darwinDictDupString(PropsRef, CFSTR("USB Serial Number"), (char **)&pCur->pszSerialNumber);
+
+ pCur->pszBackend = RTStrDup("host");
+ AssertBreak(pCur->pszBackend);
+
+#if 0 /* leave the remainder as zero for now. */
+ /*
+ * Create a plugin interface for the service and query its USB Device interface.
+ */
+ SInt32 Score = 0;
+ IOCFPlugInInterface **ppPlugInInterface = NULL;
+ irc = IOCreatePlugInInterfaceForService(USBDevice, kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
+ if (irc == kIOReturnSuccess)
+ {
+ IOUSBDeviceInterface245 **ppUSBDevI = NULL;
+ HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
+ (LPVOID *)&ppUSBDevI);
+ irc = IODestroyPlugInInterface(ppPlugInInterface); Assert(irc == kIOReturnSuccess);
+ ppPlugInInterface = NULL;
+ if (hrc == S_OK)
+ {
+ /** @todo enumerate configurations and interfaces if we actually need them. */
+ //IOReturn (*GetNumberOfConfigurations)(void *self, UInt8 *numConfig);
+ //IOReturn (*GetConfigurationDescriptorPtr)(void *self, UInt8 configIndex, IOUSBConfigurationDescriptorPtr *desc);
+ //IOReturn (*CreateInterfaceIterator)(void *self, IOUSBFindInterfaceRequest *req, io_iterator_t *iter);
+ }
+ long cReft = (*ppUSBDeviceInterface)->Release(ppUSBDeviceInterface); MY_CHECK_CREFS(cRefs);
+ }
+#endif
+ /*
+ * Try determine the state.
+ */
+ darwinDeterminUSBDeviceState(pCur, USBDevice, PropsRef);
+
+ /*
+ * We're good. Link the device.
+ */
+ pCur->pPrev = pTail;
+ if (pTail)
+ pTail = pTail->pNext = pCur;
+ else
+ pTail = pHead = pCur;
+ fOk = true;
+ } while (0);
+
+ /* cleanup on failure / skipped device. */
+ if (!fOk && pCur)
+ DarwinFreeUSBDeviceFromIOKit(pCur);
+
+ CFRelease(PropsRef);
+ }
+ else
+ AssertMsgFailed(("krc=%#x\n", krc));
+
+ IOObjectRelease(USBDevice);
+ i++;
+ }
+
+ IOObjectRelease(USBDevices);
+ //DARWIN_IOKIT_LOG_FLUSH();
+
+ /*
+ * Some post processing. There are a couple of things we have to
+ * make 100% sure about, and that is that the (Apple) keyboard
+ * and mouse most likely to be in use by the user aren't available
+ * for capturing. If there is no Apple mouse or keyboard we'll
+ * take the first one from another vendor.
+ */
+ /* As it turns out, the HID service will take all keyboards and mice
+ and we're not currently able to seize them. */
+ PUSBDEVICE pMouse = NULL;
+ PUSBDEVICE pKeyboard = NULL;
+ for (PUSBDEVICE pCur = pHead; pCur; pCur = pCur->pNext)
+ if (pCur->idVendor == kIOUSBVendorIDAppleComputer)
+ {
+ /*
+ * This test is a bit rough, should check device class/protocol but
+ * we don't have interface info yet so that might be a bit tricky.
+ */
+ if ( ( !pKeyboard
+ || pKeyboard->idVendor != kIOUSBVendorIDAppleComputer)
+ && pCur->pszProduct
+ && strstr(pCur->pszProduct, " Keyboard"))
+ pKeyboard = pCur;
+ else if ( ( !pMouse
+ || pMouse->idVendor != kIOUSBVendorIDAppleComputer)
+ && pCur->pszProduct
+ && strstr(pCur->pszProduct, " Mouse")
+ )
+ pMouse = pCur;
+ }
+ else if (!pKeyboard || !pMouse)
+ {
+ if ( pCur->bDeviceClass == 3 /* HID */
+ && pCur->bDeviceProtocol == 1 /* Keyboard */)
+ pKeyboard = pCur;
+ else if ( pCur->bDeviceClass == 3 /* HID */
+ && pCur->bDeviceProtocol == 2 /* Mouse */)
+ pMouse = pCur;
+ /** @todo examin interfaces */
+ }
+
+ if (pKeyboard)
+ pKeyboard->enmState = USBDEVICESTATE_USED_BY_HOST;
+ if (pMouse)
+ pMouse->enmState = USBDEVICESTATE_USED_BY_HOST;
+
+ return pHead;
+}
+
+#endif /* VBOX_WITH_USB */
+
+
+/**
+ * Enumerate the CD, DVD and BlueRay drives returning a FIFO of device name strings.
+ *
+ * @returns Pointer to the head.
+ * The caller is responsible for calling RTMemFree() on each of the nodes.
+ */
+PDARWINDVD DarwinGetDVDDrives(void)
+{
+ AssertReturn(darwinOpenMasterPort(), NULL);
+
+ /*
+ * Create a matching dictionary for searching for CD, DVD and BlueRay services in the IOKit.
+ *
+ * The idea is to find all the devices which are of class IOCDBlockStorageDevice.
+ * CD devices are represented by IOCDBlockStorageDevice class itself, while DVD and BlueRay ones
+ * have it as a parent class.
+ */
+ CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOCDBlockStorageDevice");
+ AssertReturn(RefMatchingDict, NULL);
+
+ /*
+ * Perform the search and get a collection of DVD services.
+ */
+ io_iterator_t DVDServices = IO_OBJECT_NULL;
+ IOReturn irc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &DVDServices);
+ AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), NULL);
+ RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
+
+ /*
+ * Enumerate the matching services.
+ * (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
+ */
+ PDARWINDVD pHead = NULL;
+ PDARWINDVD pTail = NULL;
+ unsigned i = 0;
+ io_object_t DVDService;
+ while ((DVDService = IOIteratorNext(DVDServices)) != IO_OBJECT_NULL)
+ {
+ DARWIN_IOKIT_DUMP_OBJ(DVDService);
+
+ /*
+ * Get the properties we use to identify the DVD drive.
+ *
+ * While there is a (weird 12 byte) GUID, it isn't persistent
+ * across boots. So, we have to use a combination of the
+ * vendor name and product name properties with an optional
+ * sequence number for identification.
+ */
+ CFMutableDictionaryRef PropsRef = 0;
+ kern_return_t krc = IORegistryEntryCreateCFProperties(DVDService, &PropsRef, kCFAllocatorDefault, kNilOptions);
+ if (krc == KERN_SUCCESS)
+ {
+ /* Get the Device Characteristics dictionary. */
+ CFDictionaryRef DevCharRef = (CFDictionaryRef)CFDictionaryGetValue(PropsRef, CFSTR(kIOPropertyDeviceCharacteristicsKey));
+ if (DevCharRef)
+ {
+ /* The vendor name. */
+ char szVendor[128];
+ char *pszVendor = &szVendor[0];
+ CFTypeRef ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyVendorNameKey));
+ if ( ValueRef
+ && CFGetTypeID(ValueRef) == CFStringGetTypeID()
+ && CFStringGetCString((CFStringRef)ValueRef, szVendor, sizeof(szVendor), kCFStringEncodingUTF8))
+ pszVendor = RTStrStrip(szVendor);
+ else
+ *pszVendor = '\0';
+
+ /* The product name. */
+ char szProduct[128];
+ char *pszProduct = &szProduct[0];
+ ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyProductNameKey));
+ if ( ValueRef
+ && CFGetTypeID(ValueRef) == CFStringGetTypeID()
+ && CFStringGetCString((CFStringRef)ValueRef, szProduct, sizeof(szProduct), kCFStringEncodingUTF8))
+ pszProduct = RTStrStrip(szProduct);
+ else
+ *pszProduct = '\0';
+
+ /* Construct the name and check for duplicates. */
+ char szName[256 + 32];
+ if (*pszVendor || *pszProduct)
+ {
+ if (*pszVendor && *pszProduct)
+ RTStrPrintf(szName, sizeof(szName), "%s %s", pszVendor, pszProduct);
+ else
+ strcpy(szName, *pszVendor ? pszVendor : pszProduct);
+
+ for (PDARWINDVD pCur = pHead; pCur; pCur = pCur->pNext)
+ {
+ if (!strcmp(szName, pCur->szName))
+ {
+ if (*pszVendor && *pszProduct)
+ RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", pszVendor, pszProduct, i);
+ else
+ RTStrPrintf(szName, sizeof(szName), "%s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
+ break;
+ }
+ }
+ }
+ else
+ RTStrPrintf(szName, sizeof(szName), "(#%u)", i);
+
+ /* Create the device. */
+ size_t cbName = strlen(szName) + 1;
+ PDARWINDVD pNew = (PDARWINDVD)RTMemAlloc(RT_UOFFSETOF_DYN(DARWINDVD, szName[cbName]));
+ if (pNew)
+ {
+ pNew->pNext = NULL;
+ memcpy(pNew->szName, szName, cbName);
+ if (pTail)
+ pTail = pTail->pNext = pNew;
+ else
+ pTail = pHead = pNew;
+ }
+ }
+ CFRelease(PropsRef);
+ }
+ else
+ AssertMsgFailed(("krc=%#x\n", krc));
+
+ IOObjectRelease(DVDService);
+ i++;
+ }
+
+ IOObjectRelease(DVDServices);
+
+ return pHead;
+}
+
+
+/**
+ * Enumerate the fixed drives (HDDs, SSD, ++) returning a FIFO of device paths
+ * strings and model strings separated by ':'.
+ *
+ * @returns Pointer to the head.
+ * The caller is responsible for calling RTMemFree() on each of the nodes.
+ */
+PDARWINFIXEDDRIVE DarwinGetFixedDrives(void)
+{
+ AssertReturn(darwinOpenMasterPort(), NULL);
+
+ /*
+ * Create a matching dictionary for searching drives in the IOKit.
+ *
+ * The idea is to find all the IOMedia objects with "Whole"="True" which identify the disks but
+ * not partitions.
+ */
+ CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOMedia");
+ AssertReturn(RefMatchingDict, NULL);
+ CFDictionaryAddValue(RefMatchingDict, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
+
+ /*
+ * Perform the search and get a collection of IOMedia objects.
+ */
+ io_iterator_t MediaServices = IO_OBJECT_NULL;
+ IOReturn irc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &MediaServices);
+ AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), NULL);
+ RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
+
+ /*
+ * Enumerate the matching services.
+ * (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
+ */
+ PDARWINFIXEDDRIVE pHead = NULL;
+ PDARWINFIXEDDRIVE pTail = NULL;
+ unsigned i = 0;
+ io_object_t MediaService;
+ while ((MediaService = IOIteratorNext(MediaServices)) != IO_OBJECT_NULL)
+ {
+ DARWIN_IOKIT_DUMP_OBJ(MediaService);
+
+ /*
+ * Find the IOMedia parents having the IOBlockStorageDevice type and check they have "device-type" = "Generic".
+ * If the IOMedia object hasn't IOBlockStorageDevices with such device-type in parents the one is not general
+ * disk but either CDROM-like device or some another device which has no interest for the function.
+ */
+
+ /*
+ * Just avoid parents enumeration if the IOMedia is IOCDMedia, i.e. CDROM-like disk
+ */
+ if (IOObjectConformsTo(MediaService, kIOCDMediaClass))
+ {
+ IOObjectRelease(MediaService);
+ continue;
+ }
+
+ bool fIsGenericStorage = false;
+ io_registry_entry_t ChildEntry = MediaService;
+ io_registry_entry_t ParentEntry = IO_OBJECT_NULL;
+ kern_return_t krc = KERN_SUCCESS;
+ while ( !fIsGenericStorage
+ && (krc = IORegistryEntryGetParentEntry(ChildEntry, kIOServicePlane, &ParentEntry)) == KERN_SUCCESS)
+ {
+ if (!IOObjectIsEqualTo(ChildEntry, MediaService))
+ IOObjectRelease(ChildEntry);
+
+ DARWIN_IOKIT_DUMP_OBJ(ParentEntry);
+ if (IOObjectConformsTo(ParentEntry, kIOBlockStorageDeviceClass))
+ {
+ CFTypeRef DeviceTypeValueRef = IORegistryEntryCreateCFProperty(ParentEntry,
+ CFSTR("device-type"),
+ kCFAllocatorDefault, 0);
+ if ( DeviceTypeValueRef
+ && CFGetTypeID(DeviceTypeValueRef) == CFStringGetTypeID()
+ && CFStringCompare((CFStringRef)DeviceTypeValueRef, CFSTR("Generic"),
+ kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+ fIsGenericStorage = true;
+
+ if (DeviceTypeValueRef != NULL)
+ CFRelease(DeviceTypeValueRef);
+ }
+ ChildEntry = ParentEntry;
+ }
+ if (ChildEntry != IO_OBJECT_NULL && !IOObjectIsEqualTo(ChildEntry, MediaService))
+ IOObjectRelease(ChildEntry);
+
+ if (!fIsGenericStorage)
+ {
+ IOObjectRelease(MediaService);
+ continue;
+ }
+
+ CFTypeRef DeviceName;
+ DeviceName = IORegistryEntryCreateCFProperty(MediaService,
+ CFSTR(kIOBSDNameKey),
+ kCFAllocatorDefault,0);
+ if (DeviceName)
+ {
+ char szDeviceFilePath[MAXPATHLEN];
+ strcpy(szDeviceFilePath, _PATH_DEV);
+ size_t cchPathSize = strlen(szDeviceFilePath);
+ if (CFStringGetCString((CFStringRef)DeviceName,
+ &szDeviceFilePath[cchPathSize],
+ (CFIndex)(sizeof(szDeviceFilePath) - cchPathSize),
+ kCFStringEncodingUTF8))
+ {
+ PDARWINFIXEDDRIVE pDuplicate = pHead;
+ while (pDuplicate && strcmp(szDeviceFilePath, pDuplicate->szName) != 0)
+ pDuplicate = pDuplicate->pNext;
+ if (pDuplicate == NULL)
+ {
+ /* Get model for the IOMedia object.
+ *
+ * Due to vendor and product property names are different and
+ * depend on interface and device type, the best way to get a drive
+ * model is get IORegistry name for the IOMedia object. Usually,
+ * it takes "<vendor> <product> <revision> Media" form. Noticed,
+ * such naming are used by only IOMedia objects having
+ * "Whole" = True and "BSDName" properties set.
+ */
+ io_name_t szEntryName = { 0 };
+ if ((krc = IORegistryEntryGetName(MediaService, szEntryName)) == KERN_SUCCESS)
+ {
+ /* remove " Media" from the end of the name */
+ char *pszMedia = strrchr(szEntryName, ' ');
+ if ( pszMedia != NULL
+ && (uintptr_t)pszMedia < (uintptr_t)&szEntryName[sizeof(szEntryName)]
+ && strcmp(pszMedia, " Media") == 0)
+ {
+ *pszMedia = '\0';
+ RTStrPurgeEncoding(szEntryName);
+ }
+ }
+ /* Create the device path and model name in form "/device/path:model". */
+ cchPathSize = strlen(szDeviceFilePath);
+ size_t const cchModelSize = strlen(szEntryName);
+ size_t const cbExtra = cchPathSize + 1 + cchModelSize + !!cchModelSize;
+ PDARWINFIXEDDRIVE pNew = (PDARWINFIXEDDRIVE)RTMemAlloc(RT_UOFFSETOF_DYN(DARWINFIXEDDRIVE, szName[cbExtra]));
+ if (pNew)
+ {
+ pNew->pNext = NULL;
+ memcpy(pNew->szName, szDeviceFilePath, cchPathSize + 1);
+ pNew->pszModel = NULL;
+ if (cchModelSize)
+ pNew->pszModel = (const char *)memcpy(&pNew->szName[cchPathSize + 1], szEntryName, cchModelSize + 1);
+
+ if (pTail)
+ pTail = pTail->pNext = pNew;
+ else
+ pTail = pHead = pNew;
+ }
+ }
+ }
+ CFRelease(DeviceName);
+ }
+ IOObjectRelease(MediaService);
+ i++;
+ }
+ IOObjectRelease(MediaServices);
+
+ return pHead;
+}
+
+
+/**
+ * Enumerate the ethernet capable network devices returning a FIFO of them.
+ *
+ * @returns Pointer to the head.
+ */
+PDARWINETHERNIC DarwinGetEthernetControllers(void)
+{
+ AssertReturn(darwinOpenMasterPort(), NULL);
+
+ /*
+ * Create a matching dictionary for searching for ethernet controller
+ * services in the IOKit.
+ *
+ * For some really stupid reason I don't get all the controllers if I look for
+ * objects that are instances of IOEthernetController or its descendants (only
+ * get the AirPort on my mac pro). But fortunately using IOEthernetInterface
+ * seems to work. Weird s**t!
+ */
+ //CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOEthernetController"); - this doesn't work :-(
+ CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOEthernetInterface");
+ AssertReturn(RefMatchingDict, NULL);
+
+ /*
+ * Perform the search and get a collection of ethernet controller services.
+ */
+ io_iterator_t EtherIfServices = IO_OBJECT_NULL;
+ IOReturn irc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &EtherIfServices);
+ AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), NULL);
+ RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
+
+ /*
+ * Get a copy of the current network interfaces from the system configuration service.
+ * We'll use this for looking up the proper interface names.
+ */
+ CFArrayRef IfsRef = SCNetworkInterfaceCopyAll();
+ CFIndex cIfs = IfsRef ? CFArrayGetCount(IfsRef) : 0;
+
+ /*
+ * Get the current preferences and make a copy of the network services so we
+ * can look up the right interface names. The IfsRef is just for fallback.
+ */
+ CFArrayRef ServicesRef = NULL;
+ CFIndex cServices = 0;
+ SCPreferencesRef PrefsRef = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("org.virtualbox.VBoxSVC"), NULL);
+ if (PrefsRef)
+ {
+ SCNetworkSetRef SetRef = SCNetworkSetCopyCurrent(PrefsRef);
+ CFRelease(PrefsRef);
+ if (SetRef)
+ {
+ ServicesRef = SCNetworkSetCopyServices(SetRef);
+ CFRelease(SetRef);
+ cServices = ServicesRef ? CFArrayGetCount(ServicesRef) : 0;
+ }
+ }
+
+ /*
+ * Enumerate the ethernet controller services.
+ */
+ PDARWINETHERNIC pHead = NULL;
+ PDARWINETHERNIC pTail = NULL;
+ io_object_t EtherIfService;
+ while ((EtherIfService = IOIteratorNext(EtherIfServices)) != IO_OBJECT_NULL)
+ {
+ /*
+ * Dig up the parent, meaning the IOEthernetController.
+ */
+ io_object_t EtherNICService;
+ kern_return_t krc = IORegistryEntryGetParentEntry(EtherIfService, kIOServicePlane, &EtherNICService);
+ /*krc = IORegistryEntryGetChildEntry(EtherNICService, kIOServicePlane, &EtherIfService); */
+ if (krc == KERN_SUCCESS)
+ {
+ DARWIN_IOKIT_DUMP_OBJ(EtherNICService);
+ /*
+ * Get the properties we use to identify and name the Ethernet NIC.
+ * We need the both the IOEthernetController and it's IONetworkInterface child.
+ */
+ CFMutableDictionaryRef PropsRef = 0;
+ krc = IORegistryEntryCreateCFProperties(EtherNICService, &PropsRef, kCFAllocatorDefault, kNilOptions);
+ if (krc == KERN_SUCCESS)
+ {
+ CFMutableDictionaryRef IfPropsRef = 0;
+ krc = IORegistryEntryCreateCFProperties(EtherIfService, &IfPropsRef, kCFAllocatorDefault, kNilOptions);
+ if (krc == KERN_SUCCESS)
+ {
+ /*
+ * Gather the required data.
+ * We'll create a UUID from the MAC address and the BSD name.
+ */
+ char szTmp[256];
+ do
+ {
+ /* Check if airport (a bit heuristical - it's com.apple.driver.AirPortBrcm43xx here). */
+ darwinDictGetString(PropsRef, CFSTR("CFBundleIdentifier"), szTmp, sizeof(szTmp));
+ bool fWireless;
+ bool fAirPort = fWireless = strstr(szTmp, ".AirPort") != NULL;
+
+ /* Check if it's USB. */
+ darwinDictGetString(PropsRef, CFSTR("IOProviderClass"), szTmp, sizeof(szTmp));
+ bool fUSB = strstr(szTmp, "USB") != NULL;
+
+
+ /* Is it builtin? */
+ bool fBuiltin;
+ darwinDictGetBool(IfPropsRef, CFSTR("IOBuiltin"), &fBuiltin);
+
+ /* Is it the primary interface */
+ bool fPrimaryIf;
+ darwinDictGetBool(IfPropsRef, CFSTR("IOPrimaryInterface"), &fPrimaryIf);
+
+ /* Get the MAC address. */
+ RTMAC Mac;
+ AssertBreak(darwinDictGetData(PropsRef, CFSTR("IOMACAddress"), &Mac, sizeof(Mac)));
+
+ /* The BSD Name from the interface dictionary. No assert here as the belkin USB-C gadget
+ does not always end up with a BSD name, typically requiring replugging. */
+ char szBSDName[RT_SIZEOFMEMB(DARWINETHERNIC, szBSDName)];
+ if (RT_UNLIKELY(!darwinDictGetString(IfPropsRef, CFSTR("BSD Name"), szBSDName, sizeof(szBSDName))))
+ {
+ LogRelMax(32, ("DarwinGetEthernetControllers: Warning! Failed to get 'BSD Name'; provider class %s\n", szTmp));
+ break;
+ }
+
+ /* Check if it's really wireless. */
+ if ( darwinDictIsPresent(IfPropsRef, CFSTR("IO80211CountryCode"))
+ || darwinDictIsPresent(IfPropsRef, CFSTR("IO80211DriverVersion"))
+ || darwinDictIsPresent(IfPropsRef, CFSTR("IO80211HardwareVersion"))
+ || darwinDictIsPresent(IfPropsRef, CFSTR("IO80211Locale")))
+ fWireless = true;
+ else
+ fAirPort = fWireless = false;
+
+ /** @todo IOPacketFilters / IONetworkFilterGroup? */
+ /*
+ * Create the interface name.
+ *
+ * Note! The ConsoleImpl2.cpp code ASSUMES things about the name. It is also
+ * stored in the VM config files. (really bright idea)
+ */
+ strcpy(szTmp, szBSDName);
+ char *psz = strchr(szTmp, '\0');
+ *psz++ = ':';
+ *psz++ = ' ';
+ size_t cchLeft = sizeof(szTmp) - (size_t)(psz - &szTmp[0]) - (sizeof(" (Wireless)") - 1);
+ bool fFound = false;
+ CFIndex i;
+
+ /* look it up among the current services */
+ for (i = 0; i < cServices; i++)
+ {
+ SCNetworkServiceRef ServiceRef = (SCNetworkServiceRef)CFArrayGetValueAtIndex(ServicesRef, i);
+ SCNetworkInterfaceRef IfRef = SCNetworkServiceGetInterface(ServiceRef);
+ if (IfRef)
+ {
+ CFStringRef BSDNameRef = SCNetworkInterfaceGetBSDName(IfRef);
+ if ( BSDNameRef
+ && CFStringGetCString(BSDNameRef, psz, (CFIndex)cchLeft, kCFStringEncodingUTF8)
+ && !strcmp(psz, szBSDName))
+ {
+ CFStringRef ServiceNameRef = SCNetworkServiceGetName(ServiceRef);
+ if ( ServiceNameRef
+ && CFStringGetCString(ServiceNameRef, psz, (CFIndex)cchLeft, kCFStringEncodingUTF8))
+ {
+ fFound = true;
+ break;
+ }
+ }
+ }
+ }
+ /* Look it up in the interface list. */
+ if (!fFound)
+ for (i = 0; i < cIfs; i++)
+ {
+ SCNetworkInterfaceRef IfRef = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(IfsRef, i);
+ CFStringRef BSDNameRef = SCNetworkInterfaceGetBSDName(IfRef);
+ if ( BSDNameRef
+ && CFStringGetCString(BSDNameRef, psz, (CFIndex)cchLeft, kCFStringEncodingUTF8)
+ && !strcmp(psz, szBSDName))
+ {
+ CFStringRef DisplayNameRef = SCNetworkInterfaceGetLocalizedDisplayName(IfRef);
+ if ( DisplayNameRef
+ && CFStringGetCString(DisplayNameRef, psz, (CFIndex)cchLeft, kCFStringEncodingUTF8))
+ {
+ fFound = true;
+ break;
+ }
+ }
+ }
+ /* Generate a half plausible name if we for some silly reason didn't find the interface. */
+ if (!fFound)
+ RTStrPrintf(szTmp, sizeof(szTmp), "%s: %s%s(?)",
+ szBSDName,
+ fUSB ? "USB " : "",
+ fWireless ? fAirPort ? "AirPort " : "Wireless" : "Ethernet");
+ /* If we did find it and it's wireless but without "AirPort" or "Wireless", fix it */
+ else if ( fWireless
+ && !strstr(psz, "AirPort")
+ && !strstr(psz, "Wireless"))
+ strcat(szTmp, fAirPort ? " (AirPort)" : " (Wireless)");
+
+ /*
+ * Create the list entry.
+ */
+ DARWIN_IOKIT_LOG(("Found: if=%s mac=%.6Rhxs fWireless=%RTbool fAirPort=%RTbool fBuiltin=%RTbool fPrimaryIf=%RTbool fUSB=%RTbool\n",
+ szBSDName, &Mac, fWireless, fAirPort, fBuiltin, fPrimaryIf, fUSB));
+
+ size_t cchName = strlen(szTmp);
+ PDARWINETHERNIC pNew = (PDARWINETHERNIC)RTMemAlloc(RT_UOFFSETOF_DYN(DARWINETHERNIC, szName[cchName + 1]));
+ if (pNew)
+ {
+ strncpy(pNew->szBSDName, szBSDName, sizeof(pNew->szBSDName)); /* the '\0' padding is intentional! */
+
+ RTUuidClear(&pNew->Uuid);
+ memcpy(&pNew->Uuid, pNew->szBSDName, RT_MIN(sizeof(pNew->szBSDName), sizeof(pNew->Uuid)));
+ pNew->Uuid.Gen.u8ClockSeqHiAndReserved = (pNew->Uuid.Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
+ pNew->Uuid.Gen.u16TimeHiAndVersion = (pNew->Uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
+ pNew->Uuid.Gen.au8Node[0] = Mac.au8[0];
+ pNew->Uuid.Gen.au8Node[1] = Mac.au8[1];
+ pNew->Uuid.Gen.au8Node[2] = Mac.au8[2];
+ pNew->Uuid.Gen.au8Node[3] = Mac.au8[3];
+ pNew->Uuid.Gen.au8Node[4] = Mac.au8[4];
+ pNew->Uuid.Gen.au8Node[5] = Mac.au8[5];
+
+ pNew->Mac = Mac;
+ pNew->fWireless = fWireless;
+ pNew->fAirPort = fAirPort;
+ pNew->fBuiltin = fBuiltin;
+ pNew->fUSB = fUSB;
+ pNew->fPrimaryIf = fPrimaryIf;
+ memcpy(pNew->szName, szTmp, cchName + 1);
+
+ /*
+ * Link it into the list, keep the list sorted by fPrimaryIf and the BSD name.
+ */
+ if (pTail)
+ {
+ PDARWINETHERNIC pPrev = pTail;
+ if (strcmp(pNew->szBSDName, pPrev->szBSDName) < 0)
+ {
+ pPrev = NULL;
+ for (PDARWINETHERNIC pCur = pHead; pCur; pPrev = pCur, pCur = pCur->pNext)
+ if ( (int)pNew->fPrimaryIf - (int)pCur->fPrimaryIf > 0
+ || ( (int)pNew->fPrimaryIf - (int)pCur->fPrimaryIf == 0
+ && strcmp(pNew->szBSDName, pCur->szBSDName) >= 0))
+ break;
+ }
+ if (pPrev)
+ {
+ /* tail or in list. */
+ pNew->pNext = pPrev->pNext;
+ pPrev->pNext = pNew;
+ if (pPrev == pTail)
+ pTail = pNew;
+ }
+ else
+ {
+ /* head */
+ pNew->pNext = pHead;
+ pHead = pNew;
+ }
+ }
+ else
+ {
+ /* empty list */
+ pNew->pNext = NULL;
+ pTail = pHead = pNew;
+ }
+ }
+ } while (0);
+
+ CFRelease(IfPropsRef);
+ }
+ CFRelease(PropsRef);
+ }
+ IOObjectRelease(EtherNICService);
+ }
+ else
+ AssertMsgFailed(("krc=%#x\n", krc));
+ IOObjectRelease(EtherIfService);
+ }
+
+ IOObjectRelease(EtherIfServices);
+ if (ServicesRef)
+ CFRelease(ServicesRef);
+ if (IfsRef)
+ CFRelease(IfsRef);
+ return pHead;
+}
+
+#ifdef STANDALONE_TESTCASE
+/**
+ * This file can optionally be compiled into a testcase, this is the main function.
+ * To build:
+ * g++ -I ../../../../include -D IN_RING3 iokit.cpp ../../../../out/darwin.x86/debug/lib/RuntimeR3.a ../../../../out/darwin.x86/debug/lib/SUPR3.a ../../../../out/darwin.x86/debug/lib/RuntimeR3.a ../../../../out/darwin.x86/debug/lib/VBox-kStuff.a ../../../../out/darwin.x86/debug/lib/RuntimeR3.a -framework CoreFoundation -framework IOKit -framework SystemConfiguration -liconv -D STANDALONE_TESTCASE -o iokit -g && ./iokit
+ */
+int main(int argc, char **argv)
+{
+ RTR3InitExe(argc, &argv, 0);
+
+ if (1)
+ {
+ /*
+ * Network preferences.
+ */
+ RTPrintf("Preferences: Network Services\n");
+ SCPreferencesRef PrefsRef = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("org.virtualbox.VBoxSVC"), NULL);
+ if (PrefsRef)
+ {
+ CFDictionaryRef NetworkServiceRef = (CFDictionaryRef)SCPreferencesGetValue(PrefsRef, kSCPrefNetworkServices);
+ darwinDumpDict(NetworkServiceRef, 4);
+ CFRelease(PrefsRef);
+ }
+ }
+
+ if (1)
+ {
+ /*
+ * Network services interfaces in the current config.
+ */
+ RTPrintf("Preferences: Network Service Interfaces\n");
+ SCPreferencesRef PrefsRef = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("org.virtualbox.VBoxSVC"), NULL);
+ if (PrefsRef)
+ {
+ SCNetworkSetRef SetRef = SCNetworkSetCopyCurrent(PrefsRef);
+ if (SetRef)
+ {
+ CFArrayRef ServicesRef = SCNetworkSetCopyServices(SetRef);
+ CFIndex cServices = CFArrayGetCount(ServicesRef);
+ for (CFIndex i = 0; i < cServices; i++)
+ {
+ SCNetworkServiceRef ServiceRef = (SCNetworkServiceRef)CFArrayGetValueAtIndex(ServicesRef, i);
+ char szServiceName[128] = {0};
+ CFStringGetCString(SCNetworkServiceGetName(ServiceRef), szServiceName, sizeof(szServiceName), kCFStringEncodingUTF8);
+
+ SCNetworkInterfaceRef IfRef = SCNetworkServiceGetInterface(ServiceRef);
+ char szBSDName[16] = {0};
+ if (SCNetworkInterfaceGetBSDName(IfRef))
+ CFStringGetCString(SCNetworkInterfaceGetBSDName(IfRef), szBSDName, sizeof(szBSDName), kCFStringEncodingUTF8);
+ char szDisplayName[128] = {0};
+ if (SCNetworkInterfaceGetLocalizedDisplayName(IfRef))
+ CFStringGetCString(SCNetworkInterfaceGetLocalizedDisplayName(IfRef), szDisplayName, sizeof(szDisplayName), kCFStringEncodingUTF8);
+
+ RTPrintf(" #%u ServiceName=\"%s\" IfBSDName=\"%s\" IfDisplayName=\"%s\"\n",
+ i, szServiceName, szBSDName, szDisplayName);
+ }
+
+ CFRelease(ServicesRef);
+ CFRelease(SetRef);
+ }
+
+ CFRelease(PrefsRef);
+ }
+ }
+
+ if (1)
+ {
+ /*
+ * Network interfaces.
+ */
+ RTPrintf("Preferences: Network Interfaces\n");
+ CFArrayRef IfsRef = SCNetworkInterfaceCopyAll();
+ if (IfsRef)
+ {
+ CFIndex cIfs = CFArrayGetCount(IfsRef);
+ for (CFIndex i = 0; i < cIfs; i++)
+ {
+ SCNetworkInterfaceRef IfRef = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(IfsRef, i);
+ char szBSDName[16] = {0};
+ if (SCNetworkInterfaceGetBSDName(IfRef))
+ CFStringGetCString(SCNetworkInterfaceGetBSDName(IfRef), szBSDName, sizeof(szBSDName), kCFStringEncodingUTF8);
+ char szDisplayName[128] = {0};
+ if (SCNetworkInterfaceGetLocalizedDisplayName(IfRef))
+ CFStringGetCString(SCNetworkInterfaceGetLocalizedDisplayName(IfRef), szDisplayName, sizeof(szDisplayName), kCFStringEncodingUTF8);
+ RTPrintf(" #%u BSDName=\"%s\" DisplayName=\"%s\"\n",
+ i, szBSDName, szDisplayName);
+ }
+
+ CFRelease(IfsRef);
+ }
+ }
+
+ if (1)
+ {
+ /*
+ * Get and display the ethernet controllers.
+ */
+ RTPrintf("Ethernet controllers:\n");
+ PDARWINETHERNIC pEtherNICs = DarwinGetEthernetControllers();
+ for (PDARWINETHERNIC pCur = pEtherNICs; pCur; pCur = pCur->pNext)
+ {
+ RTPrintf("%s\n", pCur->szName);
+ RTPrintf(" szBSDName=%s\n", pCur->szBSDName);
+ RTPrintf(" UUID=%RTuuid\n", &pCur->Uuid);
+ RTPrintf(" Mac=%.6Rhxs\n", &pCur->Mac);
+ RTPrintf(" fWireless=%RTbool\n", pCur->fWireless);
+ RTPrintf(" fAirPort=%RTbool\n", pCur->fAirPort);
+ RTPrintf(" fBuiltin=%RTbool\n", pCur->fBuiltin);
+ RTPrintf(" fUSB=%RTbool\n", pCur->fUSB);
+ RTPrintf(" fPrimaryIf=%RTbool\n", pCur->fPrimaryIf);
+ }
+ }
+
+
+ return 0;
+}
+#endif
diff --git a/src/VBox/Main/src-server/darwin/iokit.h b/src/VBox/Main/src-server/darwin/iokit.h
new file mode 100644
index 00000000..67c8d3c6
--- /dev/null
+++ b/src/VBox/Main/src-server/darwin/iokit.h
@@ -0,0 +1,117 @@
+/* $Id: iokit.h $ */
+/** @file
+ * Main - Darwin IOKit 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_darwin_iokit_h
+#define MAIN_INCLUDED_SRC_src_server_darwin_iokit_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/cpp/ministring.h>
+#ifdef VBOX_WITH_USB
+# include <VBox/usb.h>
+#endif
+
+/**
+ * Darwin DVD descriptor as returned by DarwinGetDVDDrives().
+ */
+typedef struct DARWINDVD
+{
+ /** Pointer to the next DVD. */
+ struct DARWINDVD *pNext;
+ /** Variable length name / identifier. */
+ char szName[1];
+} DARWINDVD;
+/** Pointer to a Darwin DVD descriptor. */
+typedef DARWINDVD *PDARWINDVD;
+
+/** Darwin fixed drive (SSD, HDD, ++) descriptor as returned by
+ * DarwinGetFixedDrives(). */
+typedef struct DARWINFIXEDDRIVE
+{
+ /** Pointer to the next DVD. */
+ struct DARWINFIXEDDRIVE *pNext;
+ /** Pointer to the model name, NULL if none.
+ * This points after szName and needs not be freed separately. */
+ const char *pszModel;
+ /** Variable length name / identifier. */
+ char szName[1];
+} DARWINFIXEDDRIVE;
+/** Pointer to a Darwin fixed drive. */
+typedef DARWINFIXEDDRIVE *PDARWINFIXEDDRIVE;
+
+
+/**
+ * Darwin ethernet controller descriptor as returned by DarwinGetEthernetControllers().
+ */
+typedef struct DARWINETHERNIC
+{
+ /** Pointer to the next NIC. */
+ struct DARWINETHERNIC *pNext;
+ /** The BSD name. (like en0)*/
+ char szBSDName[16];
+ /** The fake unique identifier. */
+ RTUUID Uuid;
+ /** The MAC address. */
+ RTMAC Mac;
+ /** Whether it's wireless (true) or wired (false). */
+ bool fWireless;
+ /** Whether it is an AirPort device. */
+ bool fAirPort;
+ /** Whether it's built in or not. */
+ bool fBuiltin;
+ /** Whether it's a USB device or not. */
+ bool fUSB;
+ /** Whether it's the primary interface. */
+ bool fPrimaryIf;
+ /** A variable length descriptive name if possible. */
+ char szName[1];
+} DARWINETHERNIC;
+/** Pointer to a Darwin ethernet controller descriptor. */
+typedef DARWINETHERNIC *PDARWINETHERNIC;
+
+
+/** The run loop mode string used by iokit.cpp when it registers
+ * notifications events. */
+#define VBOX_IOKIT_MODE_STRING "VBoxIOKitMode"
+
+RT_C_DECLS_BEGIN
+#ifdef VBOX_WITH_USB
+void * DarwinSubscribeUSBNotifications(void);
+void DarwinUnsubscribeUSBNotifications(void *pvOpaque);
+PUSBDEVICE DarwinGetUSBDevices(void);
+void DarwinFreeUSBDeviceFromIOKit(PUSBDEVICE pCur);
+int DarwinReEnumerateUSBDevice(PCUSBDEVICE pCur);
+#endif /* VBOX_WITH_USB */
+PDARWINDVD DarwinGetDVDDrives(void);
+PDARWINFIXEDDRIVE DarwinGetFixedDrives(void);
+PDARWINETHERNIC DarwinGetEthernetControllers(void);
+RT_C_DECLS_END
+
+#endif /* !MAIN_INCLUDED_SRC_src_server_darwin_iokit_h */