diff --git a/plugins/network/CMakeLists.txt b/plugins/network/CMakeLists.txt index 625d5dbdd90d9a57b8450a512f57f5ea677c3b42..259699dac4ba5e3b94fd709f0ab39db65318ba48 100644 --- a/plugins/network/CMakeLists.txt +++ b/plugins/network/CMakeLists.txt @@ -26,5 +26,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") target_link_libraries(ksystemstats_plugin_network PRIVATE ${NL_LIBRARIES} Qt::Network) target_include_directories(ksystemstats_plugin_network PRIVATE ${NL_INCLUDE_DIRS}) endif() - +if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + target_sources(ksystemstats_plugin_network PRIVATE SysctlBackend.cpp) +endif() install(TARGETS ksystemstats_plugin_network DESTINATION ${KSYSTEMSTATS_PLUGIN_INSTALL_DIR}) diff --git a/plugins/network/NetworkPlugin.cpp b/plugins/network/NetworkPlugin.cpp index 6919d93be2e5fcee3216e0e2ebf34931ac714c94..7907673e46c184eaf4797299222f4e2ffc156034 100644 --- a/plugins/network/NetworkPlugin.cpp +++ b/plugins/network/NetworkPlugin.cpp @@ -23,7 +23,9 @@ #ifdef Q_OS_LINUX #include "RtNetlinkBackend.h" #endif - +#ifdef Q_OS_FREEBSD +#include "SysctlBackend.h" +#endif class NetworkPrivate { public: @@ -51,6 +53,9 @@ NetworkPlugin::NetworkPlugin(QObject *parent, const QVariantList &args) #endif #ifdef Q_OS_LINUX backendFunctions.emplace_back([](NetworkPlugin *parent) -> NetworkBackend* {return new RtNetlinkBackend(parent);}); +#endif +#ifdef Q_OS_FREEBSD + backendFunctions.emplace_back([](NetworkPlugin *parent) -> NetworkBackend* {return new SysctlBackend(parent);}); #endif for (auto func : backendFunctions) { auto backend = func(this); diff --git a/plugins/network/SysctlBackend.cpp b/plugins/network/SysctlBackend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4cd363cf72bb1888ba032b731e39a38bb72365d8 --- /dev/null +++ b/plugins/network/SysctlBackend.cpp @@ -0,0 +1,160 @@ +/* + * SPDX-FileCopyrightText: 2022 Jesper Schmitz Mouridsen + * + * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + */ + +#include "SysctlBackend.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SysctlNetDevice::SysctlNetDevice(const QString &id, const QString &name, int ifnumber) + : NetworkDevice(id, name) +{ + m_sysctlName = {CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, ifnumber, IFDATA_GENERAL}; + // Even though we have no sensor, we need to have a name for the grouped text on the front page + // of plasma-systemmonitor + m_networkSensor->setValue(id); + std::array statisticSensors = + {m_downloadSensor, m_downloadBitsSensor, m_totalDownloadSensor, m_uploadSensor, m_uploadBitsSensor, m_totalUploadSensor}; + auto resetStatistics = [this, statisticSensors]() { + if (std::none_of(statisticSensors.begin(), statisticSensors.end(), [](auto property) { + return property->isSubscribed(); + })) { + m_totalDownloadSensor->setValue(0); + m_totalUploadSensor->setValue(0); + m_statisticsTimer->stop(); + } else if (!m_statisticsTimer->isActive()) { + m_statisticsTimer->start(); + } + }; + for (auto property : statisticSensors) { + connect(property, &KSysGuard::SensorProperty::subscribedChanged, this, resetStatistics); + } + m_statisticsTimer = std::make_unique(); + m_statisticsTimer->setInterval(UpdateRate); + connect(m_statisticsTimer.get(), &QTimer::timeout, this, [this]() { + ifmibdata ifmd{}; + size_t len = sizeof(ifmd); + if (sysctl(m_sysctlName.data(), 6, &ifmd, &len, NULL, 0) == 0) { + const qulonglong newDownload = ifmd.ifmd_data.ifi_ibytes; + const qulonglong previousDownload = m_totalDownloadSensor->value().toULongLong(); + const qulonglong deltaDownload = newDownload - previousDownload; + if (previousDownload > 0) { + m_downloadSensor->setValue(deltaDownload * 1000 / UpdateRate); + m_downloadBitsSensor->setValue((deltaDownload * 1000 / UpdateRate) * 8); + } + m_totalDownloadSensor->setValue(newDownload); + + const qulonglong newUpload = ifmd.ifmd_data.ifi_obytes; + const qulonglong previousUpload = m_totalUploadSensor->value().toULongLong(); + const qulonglong deltaUpload = newUpload - previousUpload; + if (previousUpload > 0) { + m_uploadSensor->setValue(deltaUpload * 1000 / UpdateRate); + m_uploadBitsSensor->setValue((deltaUpload * 1000 / UpdateRate) * 8); + } + m_totalUploadSensor->setValue(newUpload); + } + }); +} +void SysctlNetDevice::update() +{ + char addr_buf[NI_MAXHOST]; + QStringList ipv4_addrs; + QStringList ipv6_addrs; + struct sockaddr_in *sin; + + ipv4_addrs.clear(); + ipv6_addrs.clear(); + ifaddrs *ifap; + bzero(&ifap, sizeof(ifap)); + for (getifaddrs(&ifap); ifap != nullptr; ifap = ifap->ifa_next) { + if (name() == QString::fromLatin1(ifap->ifa_name)) { + sin = reinterpret_cast(ifap->ifa_addr); + if (sin == NULL) + return; + + if(getnameinfo(ifap->ifa_addr, sin->sin_len, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST)==0) { + + if (sin->sin_family == AF_INET) { + ipv4_addrs << QString::fromLatin1(addr_buf); + } + if (sin->sin_family == AF_INET6) { + ipv6_addrs << QString::fromLatin1(addr_buf); + } + } + + } + } + m_ipv4Sensor->setValue(ipv4_addrs.join("\n")); + m_ipv6Sensor->setValue(ipv6_addrs.join("\n")); + freeifaddrs(ifap); +} + +SysctlNetDevice::~SysctlNetDevice() +{ +} +SysctlBackend::SysctlBackend(QObject *parent) + : NetworkBackend(parent) +{ +} + +SysctlBackend::~SysctlBackend() +{ + qDeleteAll(m_devices); +} + +bool SysctlBackend::isSupported() +{ + return true; +} + +void SysctlBackend::start() +{ + int count, i; + size_t len; + int ifcnt_name[] = {CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_SYSTEM, IFMIB_IFCOUNT}; + len = sizeof(int); + if (sysctl(ifcnt_name, 5, &count, &len, NULL, 0) < 0) { + return; + } + + for (i = 1; i <= count; i++) { + struct ifmibdata ifmd { + }; + size_t len = sizeof(ifmd); + int sname[6] = {CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, i, IFDATA_GENERAL}; + + if (sysctl(sname, 6, &ifmd, &len, NULL, 0) < 0) { + continue; + } + + if ((ifmd.ifmd_data.ifi_type == IFT_ETHER) || (ifmd.ifmd_data.ifi_type == IFT_IEEE80211)) { + auto device = new SysctlNetDevice(QString::fromLatin1(ifmd.ifmd_name), QString::fromLatin1(ifmd.ifmd_name), i); + + m_devices.insert(QByteArray(ifmd.ifmd_name), device); + Q_EMIT deviceAdded(device); + } + } +} + +void SysctlBackend::stop() +{ +} + +void SysctlBackend::update() +{ + QHashIterator iter(m_devices); + while (iter.hasNext()) { + iter.next(); + iter.value()->update(); + } +} diff --git a/plugins/network/SysctlBackend.h b/plugins/network/SysctlBackend.h new file mode 100644 index 0000000000000000000000000000000000000000..a6d52fb502620c42f6f234929e5421c6d70a1195 --- /dev/null +++ b/plugins/network/SysctlBackend.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2022 Jesper Schmitz Mouridsen + * + * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + */ + +#pragma once + +#include "NetworkBackend.h" +#include "NetworkDevice.h" +#include +#include +#include + +static const int UpdateRate = 500; +class SysctlNetDevice : public NetworkDevice +{ + Q_OBJECT + +public: + SysctlNetDevice(const QString &id, const QString &name,int ifnumber); + ~SysctlNetDevice() override; + std::array m_sysctlName; + void update(); + bool isConnected() const; + std::unique_ptr m_statisticsTimer; + QList m_ifaddrs; +}; + +class SysctlBackend : public NetworkBackend +{ +public: + SysctlBackend(QObject *parent); + ~SysctlBackend() override; + bool isSupported() override; + void start() override; + void stop() override; + void update() override; + +private: + QHash m_devices; +};