设备监控
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.cache/clangd/index/DeviceEvent.h.86CA2B2EF71C68E1.idx
Normal file
BIN
.cache/clangd/index/DeviceEvent.h.86CA2B2EF71C68E1.idx
Normal file
Binary file not shown.
Binary file not shown.
BIN
.cache/clangd/index/DeviceMonitorService.h.2C037D82AC481AAB.idx
Normal file
BIN
.cache/clangd/index/DeviceMonitorService.h.2C037D82AC481AAB.idx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.cache/clangd/index/IDeviceMonitor.h.35FD4D7CC78F8E5E.idx
Normal file
BIN
.cache/clangd/index/IDeviceMonitor.h.35FD4D7CC78F8E5E.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/IDeviceRegistry.h.255744FF99F84B26.idx
Normal file
BIN
.cache/clangd/index/IDeviceRegistry.h.255744FF99F84B26.idx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.cache/clangd/index/NetlinkDeviceMonitor.h.D31493ABAC6FE7FD.idx
Normal file
BIN
.cache/clangd/index/NetlinkDeviceMonitor.h.D31493ABAC6FE7FD.idx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.cache/clangd/index/UdevDeviceMonitor.cpp.8FD4DE713A589E66.idx
Normal file
BIN
.cache/clangd/index/UdevDeviceMonitor.cpp.8FD4DE713A589E66.idx
Normal file
Binary file not shown.
BIN
.cache/clangd/index/UdevDeviceMonitor.h.0028E083196B240B.idx
Normal file
BIN
.cache/clangd/index/UdevDeviceMonitor.h.0028E083196B240B.idx
Normal file
Binary file not shown.
Binary file not shown.
BIN
.cache/clangd/index/WindowsDeviceMonitor.h.00AD1ABB43F51CCC.idx
Normal file
BIN
.cache/clangd/index/WindowsDeviceMonitor.h.00AD1ABB43F51CCC.idx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -31,6 +31,7 @@ set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
# 查找Qt6(守护进程只需要 Core/DBus/Network/SerialPort/Sql)
|
||||
find_package(Qt6 6.5 REQUIRED COMPONENTS Core SerialPort Network Sql DBus)
|
||||
include(CheckIncludeFileCXX)
|
||||
|
||||
# 检测编译平台
|
||||
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
|
||||
@@ -77,8 +78,19 @@ qt_add_executable(softbus_daemon
|
||||
# device bus: tree & capabilities
|
||||
src/device_bus/tree/DeviceTreeModel.h
|
||||
src/device_bus/tree/DeviceTreeModel.cpp
|
||||
src/device_bus/capabilities/DriverCapabilityProvider.h
|
||||
src/device_bus/capabilities/DriverCapabilityProvider.cpp
|
||||
|
||||
# device bus: monitor
|
||||
src/device_bus/monitor/DeviceEvent.h
|
||||
src/device_bus/monitor/IDeviceMonitor.h
|
||||
src/device_bus/monitor/DeviceMonitorService.h
|
||||
src/device_bus/monitor/DeviceMonitorService.cpp
|
||||
src/device_bus/monitor/UdevDeviceMonitor.h
|
||||
src/device_bus/monitor/UdevDeviceMonitor.cpp
|
||||
src/device_bus/monitor/NetlinkDeviceMonitor.h
|
||||
src/device_bus/monitor/NetlinkDeviceMonitor.cpp
|
||||
src/device_bus/monitor/WindowsDeviceMonitor.h
|
||||
src/device_bus/monitor/WindowsDeviceMonitor.cpp
|
||||
|
||||
|
||||
# device bus: facade & manager
|
||||
src/device_bus/DeviceBus.h
|
||||
@@ -114,6 +126,27 @@ target_link_libraries(softbus_daemon
|
||||
Qt6::Network
|
||||
)
|
||||
|
||||
# Platform-specific deps
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PkgConfig_FOUND)
|
||||
pkg_check_modules(UDEV QUIET libudev)
|
||||
endif()
|
||||
|
||||
if(UDEV_FOUND)
|
||||
target_include_directories(softbus_daemon PRIVATE ${UDEV_INCLUDE_DIRS})
|
||||
target_link_directories(softbus_daemon PRIVATE ${UDEV_LIBRARY_DIRS})
|
||||
target_link_libraries(softbus_daemon PRIVATE ${UDEV_LIBRARIES})
|
||||
else()
|
||||
# Fallback: rely on system linker search path
|
||||
target_link_libraries(softbus_daemon PRIVATE udev)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(softbus_daemon PRIVATE setupapi iphlpapi user32 advapi32)
|
||||
endif()
|
||||
|
||||
add_subdirectory(plugins)
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
@@ -6,6 +6,7 @@
|
||||
#include <QSaveFile>
|
||||
|
||||
#include "device_bus/manager/DeviceBusManager.h"
|
||||
#include "device_bus/monitor/DeviceMonitorService.h"
|
||||
#include "device_bus/tree/DeviceTreeModel.h"
|
||||
#include "utils/logs/logging.h"
|
||||
|
||||
@@ -14,6 +15,7 @@ namespace softbus::device_bus {
|
||||
class DeviceBus::Impl {
|
||||
public:
|
||||
DeviceBusManager manager;
|
||||
std::unique_ptr<monitor::DeviceMonitorService> monitorService;
|
||||
std::shared_ptr<DeviceTreeModel> treeModel;
|
||||
QString configPath;
|
||||
bool deviceTreeDirty{false};
|
||||
@@ -32,6 +34,13 @@ DeviceBus::~DeviceBus() {
|
||||
}
|
||||
|
||||
bool DeviceBus::initialize(const QJsonObject &rootConfig) {
|
||||
if (!d->monitorService) {
|
||||
d->monitorService = std::make_unique<monitor::DeviceMonitorService>(nullptr);
|
||||
if (!d->monitorService->start()) {
|
||||
LOG_WARNING() << "DeviceBus: device monitor service start failed";
|
||||
}
|
||||
}
|
||||
|
||||
if (!d->treeModel) {
|
||||
d->treeModel = std::make_shared<DeviceTreeModel>();
|
||||
}
|
||||
@@ -40,6 +49,11 @@ bool DeviceBus::initialize(const QJsonObject &rootConfig) {
|
||||
}
|
||||
|
||||
void DeviceBus::shutdown() {
|
||||
if (d->monitorService) {
|
||||
d->monitorService->stop();
|
||||
d->monitorService.reset();
|
||||
}
|
||||
|
||||
d->manager.shutdown();
|
||||
|
||||
if (!d->deviceTreeDirty) {
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
#include "device_bus/capabilities/DriverCapabilityProvider.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include "utils/logs/logging.h"
|
||||
|
||||
bool DriverCapabilityProvider::loadFromFile(const QString& jsonPath, QString* errorMessage)
|
||||
{
|
||||
m_defaults = QJsonObject();
|
||||
m_enums = QJsonObject();
|
||||
|
||||
QFile f(jsonPath);
|
||||
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
if (errorMessage) {
|
||||
*errorMessage = QStringLiteral("Failed to open capability file: %1").arg(jsonPath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const QByteArray data = f.readAll();
|
||||
f.close();
|
||||
|
||||
QJsonParseError pe;
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(data, &pe);
|
||||
if (pe.error != QJsonParseError::NoError || !doc.isObject()) {
|
||||
if (errorMessage) {
|
||||
*errorMessage = QStringLiteral("Invalid JSON in capability file %1: %2")
|
||||
.arg(jsonPath, pe.errorString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const QJsonObject root = doc.object();
|
||||
m_defaults = root.value(QStringLiteral("defaults")).toObject();
|
||||
m_enums = root.value(QStringLiteral("enums")).toObject();
|
||||
|
||||
LOG_INFO() << "DriverCapabilityProvider loaded from" << jsonPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonObject DriverCapabilityProvider::mergeAndValidateParams(const QJsonObject& nodeParams) const
|
||||
{
|
||||
// 1. 从 defaults 拷贝一份出来
|
||||
QJsonObject result = m_defaults;
|
||||
|
||||
// 2. 覆盖节点里的显式参数
|
||||
for (auto it = nodeParams.constBegin(); it != nodeParams.constEnd(); ++it) {
|
||||
const QString key = it.key();
|
||||
const QJsonValue value = it.value();
|
||||
|
||||
// 如果存在枚举限制,则先校验
|
||||
const QJsonValue enumVal = m_enums.value(key);
|
||||
if (enumVal.isArray()) {
|
||||
const QJsonArray arr = enumVal.toArray();
|
||||
bool ok = false;
|
||||
for (const QJsonValue& candidate : arr) {
|
||||
if (candidate == value) {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
LOG_WARNING() << "DriverCapabilityProvider: param" << key
|
||||
<< "value" << value.toVariant()
|
||||
<< "not in allowed enum, keep default";
|
||||
continue; // 不覆盖默认值
|
||||
}
|
||||
}
|
||||
|
||||
result.insert(key, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
|
||||
// 负责加载 drivers 下的 parameters.json,并对节点 params 做默认值合并与简单枚举校验
|
||||
|
||||
class DriverCapabilityProvider
|
||||
{
|
||||
public:
|
||||
// 从给定路径加载能力描述(如 "src/hardware/drivers/serial/parameters.json")
|
||||
bool loadFromFile(const QString& jsonPath, QString* errorMessage = nullptr);
|
||||
|
||||
// 将节点 params 与 defaults 合并,并按 enums 做简单校验
|
||||
QJsonObject mergeAndValidateParams(const QJsonObject& nodeParams) const;
|
||||
|
||||
private:
|
||||
QJsonObject m_defaults;
|
||||
QJsonObject m_enums;
|
||||
};
|
||||
|
||||
74
src/device_bus/monitor/DeviceEvent.h
Normal file
74
src/device_bus/monitor/DeviceEvent.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QDateTime>
|
||||
#include <QMap>
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
|
||||
enum class DeviceEventType {
|
||||
Serial,
|
||||
Usb,
|
||||
Storage,
|
||||
NetInterface,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
enum class DeviceEventAction {
|
||||
Add,
|
||||
Remove,
|
||||
Change,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
struct DeviceEvent
|
||||
{
|
||||
DeviceEventType type{DeviceEventType::Unknown};
|
||||
DeviceEventAction action{DeviceEventAction::Unknown};
|
||||
QDateTime timestamp{QDateTime::currentDateTimeUtc()};
|
||||
|
||||
// Common identifiers
|
||||
QString subsystem; // e.g. "tty", "usb", "block", "net"
|
||||
QString devnode; // e.g. /dev/ttyUSB0
|
||||
QString devicePath; // platform-native path (Windows device interface path)
|
||||
|
||||
// USB-ish fields
|
||||
QString vid;
|
||||
QString pid;
|
||||
QString serial;
|
||||
QString model;
|
||||
QString vendor;
|
||||
|
||||
// Network fields
|
||||
QString ifname;
|
||||
int ifindex{-1};
|
||||
bool linkUp{false};
|
||||
|
||||
// Extra properties (best-effort)
|
||||
QMap<QString, QString> props;
|
||||
};
|
||||
|
||||
inline QString toString(DeviceEventType t)
|
||||
{
|
||||
switch (t) {
|
||||
case DeviceEventType::Serial: return QStringLiteral("Serial");
|
||||
case DeviceEventType::Usb: return QStringLiteral("Usb");
|
||||
case DeviceEventType::Storage: return QStringLiteral("Storage");
|
||||
case DeviceEventType::NetInterface: return QStringLiteral("NetInterface");
|
||||
default: return QStringLiteral("Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
inline QString toString(DeviceEventAction a)
|
||||
{
|
||||
switch (a) {
|
||||
case DeviceEventAction::Add: return QStringLiteral("add");
|
||||
case DeviceEventAction::Remove: return QStringLiteral("remove");
|
||||
case DeviceEventAction::Change: return QStringLiteral("change");
|
||||
default: return QStringLiteral("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
97
src/device_bus/monitor/DeviceMonitorService.cpp
Normal file
97
src/device_bus/monitor/DeviceMonitorService.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "device_bus/monitor/DeviceMonitorService.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include "utils/logs/logging.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include "device_bus/monitor/UdevDeviceMonitor.h"
|
||||
#include "device_bus/monitor/NetlinkDeviceMonitor.h"
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "device_bus/monitor/WindowsDeviceMonitor.h"
|
||||
#endif
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
|
||||
DeviceMonitorService::DeviceMonitorService(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
m_monitors.emplace_back(std::make_unique<UdevDeviceMonitor>(this));
|
||||
m_monitors.emplace_back(std::make_unique<NetlinkDeviceMonitor>(this));
|
||||
#elif defined(_WIN32)
|
||||
m_monitors.emplace_back(std::make_unique<WindowsDeviceMonitor>(this));
|
||||
#endif
|
||||
|
||||
for (auto& m : m_monitors) {
|
||||
m->setCallback([this](const DeviceEvent& ev) { onEvent(ev); });
|
||||
}
|
||||
}
|
||||
|
||||
DeviceMonitorService::~DeviceMonitorService()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
bool DeviceMonitorService::start()
|
||||
{
|
||||
if (m_started) return true;
|
||||
|
||||
bool ok = true;
|
||||
for (auto& m : m_monitors) {
|
||||
ok = m->start() && ok;
|
||||
}
|
||||
m_started = ok;
|
||||
return ok;
|
||||
}
|
||||
|
||||
void DeviceMonitorService::stop()
|
||||
{
|
||||
if (!m_started) return;
|
||||
for (auto& m : m_monitors) {
|
||||
m->stop();
|
||||
}
|
||||
m_started = false;
|
||||
}
|
||||
|
||||
void DeviceMonitorService::onEvent(const DeviceEvent& ev)
|
||||
{
|
||||
// De-dup (short window): suppress identical events bursts
|
||||
const QString key = QStringLiteral("%1|%2|%3|%4|%5|%6|%7|%8|%9")
|
||||
.arg(toString(ev.type),
|
||||
toString(ev.action),
|
||||
ev.subsystem,
|
||||
ev.devnode,
|
||||
ev.props.value(QStringLiteral("SYSPATH")),
|
||||
ev.ifname,
|
||||
QString::number(ev.ifindex),
|
||||
ev.vid + ev.pid,
|
||||
ev.serial);
|
||||
|
||||
const qint64 nowMs = QDateTime::currentMSecsSinceEpoch();
|
||||
const qint64 lastMs = m_lastEventMsByKey.value(key, -1);
|
||||
if (lastMs >= 0 && (nowMs - lastMs) < 800) {
|
||||
return;
|
||||
}
|
||||
m_lastEventMsByKey.insert(key, nowMs);
|
||||
|
||||
// 第一版:仅日志
|
||||
LOG_INFO() << "DeviceMonitor event"
|
||||
<< "type=" << toString(ev.type)
|
||||
<< "action=" << toString(ev.action)
|
||||
<< "subsystem=" << ev.subsystem
|
||||
<< "devnode=" << ev.devnode
|
||||
<< "ifname=" << ev.ifname
|
||||
<< "ifindex=" << ev.ifindex
|
||||
<< "linkUp=" << ev.linkUp
|
||||
<< "vid=" << ev.vid
|
||||
<< "pid=" << ev.pid
|
||||
<< "serial=" << ev.serial
|
||||
<< "path=" << ev.devicePath;
|
||||
}
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
36
src/device_bus/monitor/DeviceMonitorService.h
Normal file
36
src/device_bus/monitor/DeviceMonitorService.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <QHash>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "device_bus/monitor/IDeviceMonitor.h"
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
|
||||
class DeviceMonitorService : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DeviceMonitorService(QObject* parent = nullptr);
|
||||
~DeviceMonitorService() override;
|
||||
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
void onEvent(const DeviceEvent& ev);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<IDeviceMonitor>> m_monitors;
|
||||
bool m_started{false};
|
||||
|
||||
// Lightweight de-dup: key -> last timestamp (ms)
|
||||
QHash<QString, qint64> m_lastEventMsByKey;
|
||||
};
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
41
src/device_bus/monitor/IDeviceMonitor.h
Normal file
41
src/device_bus/monitor/IDeviceMonitor.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "device_bus/monitor/DeviceEvent.h"
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
|
||||
class IDeviceMonitor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using EventCallback = std::function<void(const DeviceEvent&)>;
|
||||
|
||||
explicit IDeviceMonitor(QObject* parent = nullptr) : QObject(parent) {}
|
||||
~IDeviceMonitor() override = default;
|
||||
|
||||
virtual bool start() = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
void setCallback(EventCallback cb) { m_cb = std::move(cb); }
|
||||
|
||||
protected:
|
||||
void emitEvent(const DeviceEvent& ev)
|
||||
{
|
||||
if (m_cb) m_cb(ev);
|
||||
emit event(ev);
|
||||
}
|
||||
|
||||
signals:
|
||||
void event(const softbus::device_bus::monitor::DeviceEvent& ev);
|
||||
|
||||
private:
|
||||
EventCallback m_cb;
|
||||
};
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
155
src/device_bus/monitor/NetlinkDeviceMonitor.cpp
Normal file
155
src/device_bus/monitor/NetlinkDeviceMonitor.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "device_bus/monitor/NetlinkDeviceMonitor.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include "utils/logs/logging.h"
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
|
||||
NetlinkDeviceMonitor::NetlinkDeviceMonitor(QObject* parent)
|
||||
: IDeviceMonitor(parent)
|
||||
{
|
||||
}
|
||||
|
||||
NetlinkDeviceMonitor::~NetlinkDeviceMonitor()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
bool NetlinkDeviceMonitor::start()
|
||||
{
|
||||
if (m_sock >= 0) return true;
|
||||
|
||||
m_sock = ::socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (m_sock < 0) {
|
||||
LOG_ERROR() << "NetlinkDeviceMonitor: socket failed errno=" << errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
sockaddr_nl addr{};
|
||||
addr.nl_family = AF_NETLINK;
|
||||
addr.nl_groups = RTMGRP_LINK;
|
||||
if (::bind(m_sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
|
||||
LOG_ERROR() << "NetlinkDeviceMonitor: bind failed errno=" << errno;
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_notifier = new QSocketNotifier(m_sock, QSocketNotifier::Read, this);
|
||||
connect(m_notifier, &QSocketNotifier::activated, this, [this]() { onReadable(); });
|
||||
|
||||
LOG_INFO() << "NetlinkDeviceMonitor started sock=" << m_sock;
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetlinkDeviceMonitor::stop()
|
||||
{
|
||||
if (m_notifier) {
|
||||
m_notifier->setEnabled(false);
|
||||
m_notifier->deleteLater();
|
||||
m_notifier = nullptr;
|
||||
}
|
||||
if (m_sock >= 0) {
|
||||
::close(m_sock);
|
||||
m_sock = -1;
|
||||
}
|
||||
m_seenIfIndex.clear();
|
||||
m_lastIfNameByIndex.clear();
|
||||
m_lastLinkUpByIndex.clear();
|
||||
}
|
||||
|
||||
void NetlinkDeviceMonitor::onReadable()
|
||||
{
|
||||
if (m_sock < 0) return;
|
||||
|
||||
alignas(nlmsghdr) char buf[8192];
|
||||
const ssize_t len = ::recv(m_sock, buf, sizeof(buf), 0);
|
||||
if (len <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int remaining = static_cast<int>(len);
|
||||
for (nlmsghdr* nh = reinterpret_cast<nlmsghdr*>(buf);
|
||||
NLMSG_OK(nh, static_cast<unsigned int>(remaining));
|
||||
nh = NLMSG_NEXT(nh, remaining)) {
|
||||
|
||||
if (nh->nlmsg_type == NLMSG_DONE || nh->nlmsg_type == NLMSG_ERROR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nh->nlmsg_type != RTM_NEWLINK && nh->nlmsg_type != RTM_DELLINK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* ifi = reinterpret_cast<ifinfomsg*>(NLMSG_DATA(nh));
|
||||
const int ifindex = ifi ? ifi->ifi_index : -1;
|
||||
|
||||
QString ifname;
|
||||
bool linkUp = false;
|
||||
|
||||
// Parse attributes
|
||||
int attrLen = static_cast<int>(nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)));
|
||||
for (rtattr* rta = IFLA_RTA(ifi); RTA_OK(rta, attrLen); rta = RTA_NEXT(rta, attrLen)) {
|
||||
if (rta->rta_type == IFLA_IFNAME) {
|
||||
ifname = QString::fromUtf8(reinterpret_cast<const char*>(RTA_DATA(rta)));
|
||||
}
|
||||
}
|
||||
|
||||
// Link state (best-effort)
|
||||
if (ifi) {
|
||||
linkUp = (ifi->ifi_flags & IFF_RUNNING) != 0;
|
||||
}
|
||||
|
||||
DeviceEvent ev;
|
||||
ev.timestamp = QDateTime::currentDateTimeUtc();
|
||||
ev.type = DeviceEventType::NetInterface;
|
||||
ev.subsystem = QStringLiteral("net");
|
||||
ev.ifindex = ifindex;
|
||||
ev.ifname = ifname;
|
||||
ev.linkUp = linkUp;
|
||||
|
||||
if (nh->nlmsg_type == RTM_DELLINK) {
|
||||
ev.action = DeviceEventAction::Remove;
|
||||
m_seenIfIndex.remove(ifindex);
|
||||
m_lastIfNameByIndex.remove(ifindex);
|
||||
m_lastLinkUpByIndex.remove(ifindex);
|
||||
} else {
|
||||
// NEWLINK can be add or change; treat first time as add
|
||||
if (!m_seenIfIndex.contains(ifindex)) {
|
||||
ev.action = DeviceEventAction::Add;
|
||||
m_seenIfIndex.insert(ifindex);
|
||||
m_lastIfNameByIndex.insert(ifindex, ifname);
|
||||
m_lastLinkUpByIndex.insert(ifindex, linkUp);
|
||||
} else {
|
||||
const QString lastName = m_lastIfNameByIndex.value(ifindex);
|
||||
const bool lastLink = m_lastLinkUpByIndex.value(ifindex, linkUp);
|
||||
|
||||
// Drop pure spam: only emit change if name or link state changes
|
||||
if (lastName == ifname && lastLink == linkUp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ev.action = DeviceEventAction::Change;
|
||||
m_lastIfNameByIndex.insert(ifindex, ifname);
|
||||
m_lastLinkUpByIndex.insert(ifindex, linkUp);
|
||||
}
|
||||
}
|
||||
|
||||
emitEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
38
src/device_bus/monitor/NetlinkDeviceMonitor.h
Normal file
38
src/device_bus/monitor/NetlinkDeviceMonitor.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "device_bus/monitor/IDeviceMonitor.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#include <QSocketNotifier>
|
||||
#include <QSet>
|
||||
#include <QHash>
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
|
||||
class NetlinkDeviceMonitor : public IDeviceMonitor
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NetlinkDeviceMonitor(QObject* parent = nullptr);
|
||||
~NetlinkDeviceMonitor() override;
|
||||
|
||||
bool start() override;
|
||||
void stop() override;
|
||||
|
||||
private:
|
||||
void onReadable();
|
||||
|
||||
private:
|
||||
int m_sock{-1};
|
||||
QSocketNotifier* m_notifier{nullptr};
|
||||
QSet<int> m_seenIfIndex;
|
||||
QHash<int, QString> m_lastIfNameByIndex;
|
||||
QHash<int, bool> m_lastLinkUpByIndex;
|
||||
};
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
222
src/device_bus/monitor/UdevDeviceMonitor.cpp
Normal file
222
src/device_bus/monitor/UdevDeviceMonitor.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
#include "device_bus/monitor/UdevDeviceMonitor.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "utils/logs/logging.h"
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
|
||||
UdevDeviceMonitor::UdevDeviceMonitor(QObject* parent)
|
||||
: IDeviceMonitor(parent)
|
||||
{
|
||||
}
|
||||
|
||||
UdevDeviceMonitor::~UdevDeviceMonitor()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
bool UdevDeviceMonitor::start()
|
||||
{
|
||||
if (m_mon) return true;
|
||||
|
||||
m_udev = udev_new();
|
||||
if (!m_udev) {
|
||||
LOG_ERROR() << "UdevDeviceMonitor: udev_new failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_mon = udev_monitor_new_from_netlink(m_udev, "udev");
|
||||
if (!m_mon) {
|
||||
LOG_ERROR() << "UdevDeviceMonitor: udev_monitor_new_from_netlink failed";
|
||||
udev_unref(m_udev);
|
||||
m_udev = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter subsystems
|
||||
udev_monitor_filter_add_match_subsystem_devtype(m_mon, "tty", nullptr);
|
||||
udev_monitor_filter_add_match_subsystem_devtype(m_mon, "usb", nullptr);
|
||||
udev_monitor_filter_add_match_subsystem_devtype(m_mon, "block", nullptr);
|
||||
|
||||
if (udev_monitor_enable_receiving(m_mon) != 0) {
|
||||
LOG_ERROR() << "UdevDeviceMonitor: enable_receiving failed";
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_fd = udev_monitor_get_fd(m_mon);
|
||||
if (m_fd < 0) {
|
||||
LOG_ERROR() << "UdevDeviceMonitor: monitor fd invalid";
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
|
||||
connect(m_notifier, &QSocketNotifier::activated, this, [this]() { onReadable(); });
|
||||
|
||||
LOG_INFO() << "UdevDeviceMonitor started fd=" << m_fd;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UdevDeviceMonitor::stop()
|
||||
{
|
||||
if (m_notifier) {
|
||||
m_notifier->setEnabled(false);
|
||||
m_notifier->deleteLater();
|
||||
m_notifier = nullptr;
|
||||
}
|
||||
m_fd = -1;
|
||||
|
||||
if (m_mon) {
|
||||
udev_monitor_unref(m_mon);
|
||||
m_mon = nullptr;
|
||||
}
|
||||
if (m_udev) {
|
||||
udev_unref(m_udev);
|
||||
m_udev = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void UdevDeviceMonitor::onReadable()
|
||||
{
|
||||
if (!m_mon) return;
|
||||
|
||||
while (true) {
|
||||
struct udev_device* dev = udev_monitor_receive_device(m_mon);
|
||||
if (!dev) break;
|
||||
|
||||
const char* action = udev_device_get_action(dev);
|
||||
DeviceEvent ev = toEvent(dev, action);
|
||||
|
||||
// Drop unknown action (usually noise / incomplete events)
|
||||
if (ev.action == DeviceEventAction::Unknown) {
|
||||
udev_device_unref(dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
// USB: only keep usb_device (drop interface-level noise)
|
||||
if (ev.subsystem == QStringLiteral("usb")) {
|
||||
const QString devtype = ev.props.value(QStringLiteral("DEVTYPE"));
|
||||
if (!devtype.isEmpty() && devtype != QStringLiteral("usb_device")) {
|
||||
udev_device_unref(dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Drop most "change" spam for usb (keep add/remove)
|
||||
if (ev.action == DeviceEventAction::Change) {
|
||||
udev_device_unref(dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it carries no stable identifiers, it's not useful
|
||||
if (ev.devnode.isEmpty() && ev.vid.isEmpty() && ev.pid.isEmpty() && ev.serial.isEmpty()) {
|
||||
udev_device_unref(dev);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Storage: keep add/remove; drop change spam
|
||||
if (ev.type == DeviceEventType::Storage && ev.action == DeviceEventAction::Change) {
|
||||
udev_device_unref(dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
emitEvent(ev);
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
}
|
||||
|
||||
QString UdevDeviceMonitor::getProp(struct udev_device* dev, const char* key)
|
||||
{
|
||||
if (!dev || !key) return {};
|
||||
const char* v = udev_device_get_property_value(dev, key);
|
||||
return v ? QString::fromUtf8(v) : QString();
|
||||
}
|
||||
|
||||
DeviceEvent UdevDeviceMonitor::toEvent(struct udev_device* dev, const char* action)
|
||||
{
|
||||
DeviceEvent ev;
|
||||
ev.timestamp = QDateTime::currentDateTimeUtc();
|
||||
|
||||
const char* subsys = udev_device_get_subsystem(dev);
|
||||
if (subsys) ev.subsystem = QString::fromUtf8(subsys);
|
||||
|
||||
const char* devtype = udev_device_get_devtype(dev);
|
||||
if (devtype) ev.props.insert(QStringLiteral("DEVTYPE"), QString::fromUtf8(devtype));
|
||||
|
||||
const char* syspath = udev_device_get_syspath(dev);
|
||||
if (syspath) ev.props.insert(QStringLiteral("SYSPATH"), QString::fromUtf8(syspath));
|
||||
|
||||
const char* devnode = udev_device_get_devnode(dev);
|
||||
if (devnode) ev.devnode = QString::fromUtf8(devnode);
|
||||
|
||||
const QString act = action ? QString::fromUtf8(action) : QString();
|
||||
if (act == QStringLiteral("add")) ev.action = DeviceEventAction::Add;
|
||||
else if (act == QStringLiteral("remove")) ev.action = DeviceEventAction::Remove;
|
||||
else if (act == QStringLiteral("change")) ev.action = DeviceEventAction::Change;
|
||||
else ev.action = DeviceEventAction::Unknown;
|
||||
|
||||
if (ev.subsystem == QStringLiteral("tty")) {
|
||||
ev.type = DeviceEventType::Serial;
|
||||
// devnode like /dev/ttyUSB0
|
||||
} else if (ev.subsystem == QStringLiteral("usb")) {
|
||||
ev.type = DeviceEventType::Usb;
|
||||
} else if (ev.subsystem == QStringLiteral("block")) {
|
||||
// Prefer only usb-backed block devices
|
||||
const QString idBus = getProp(dev, "ID_BUS");
|
||||
if (idBus == QStringLiteral("usb")) {
|
||||
ev.type = DeviceEventType::Storage;
|
||||
} else {
|
||||
// check parent chain for usb
|
||||
struct udev_device* p = udev_device_get_parent_with_subsystem_devtype(dev, "usb", nullptr);
|
||||
if (p) {
|
||||
ev.type = DeviceEventType::Storage;
|
||||
} else {
|
||||
ev.type = DeviceEventType::Unknown;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ev.type = DeviceEventType::Unknown;
|
||||
}
|
||||
|
||||
// Common USB-ish props (best-effort)
|
||||
ev.vid = getProp(dev, "ID_VENDOR_ID");
|
||||
ev.pid = getProp(dev, "ID_MODEL_ID");
|
||||
ev.serial = getProp(dev, "ID_SERIAL_SHORT");
|
||||
ev.model = getProp(dev, "ID_MODEL");
|
||||
ev.vendor = getProp(dev, "ID_VENDOR");
|
||||
|
||||
// Fallback to sysfs attributes for usb subsystem
|
||||
if (ev.vid.isEmpty() && ev.subsystem == QStringLiteral("usb")) {
|
||||
const char* a = udev_device_get_sysattr_value(dev, "idVendor");
|
||||
if (a) ev.vid = QString::fromUtf8(a);
|
||||
}
|
||||
if (ev.pid.isEmpty() && ev.subsystem == QStringLiteral("usb")) {
|
||||
const char* a = udev_device_get_sysattr_value(dev, "idProduct");
|
||||
if (a) ev.pid = QString::fromUtf8(a);
|
||||
}
|
||||
if (ev.serial.isEmpty() && ev.subsystem == QStringLiteral("usb")) {
|
||||
const char* a = udev_device_get_sysattr_value(dev, "serial");
|
||||
if (a) ev.serial = QString::fromUtf8(a);
|
||||
}
|
||||
|
||||
if (!ev.devnode.isEmpty()) {
|
||||
ev.props.insert(QStringLiteral("DEVNAME"), ev.devnode);
|
||||
}
|
||||
if (!ev.subsystem.isEmpty()) {
|
||||
ev.props.insert(QStringLiteral("SUBSYSTEM"), ev.subsystem);
|
||||
}
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
39
src/device_bus/monitor/UdevDeviceMonitor.h
Normal file
39
src/device_bus/monitor/UdevDeviceMonitor.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "device_bus/monitor/IDeviceMonitor.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#include <libudev.h>
|
||||
|
||||
#include <QSocketNotifier>
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
|
||||
class UdevDeviceMonitor : public IDeviceMonitor
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UdevDeviceMonitor(QObject* parent = nullptr);
|
||||
~UdevDeviceMonitor() override;
|
||||
|
||||
bool start() override;
|
||||
void stop() override;
|
||||
|
||||
private:
|
||||
void onReadable();
|
||||
DeviceEvent toEvent(struct udev_device* dev, const char* action);
|
||||
static QString getProp(struct udev_device* dev, const char* key);
|
||||
|
||||
private:
|
||||
struct udev* m_udev{nullptr};
|
||||
struct udev_monitor* m_mon{nullptr};
|
||||
int m_fd{-1};
|
||||
QSocketNotifier* m_notifier{nullptr};
|
||||
};
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
42
src/device_bus/monitor/WindowsDeviceMonitor.cpp
Normal file
42
src/device_bus/monitor/WindowsDeviceMonitor.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "device_bus/monitor/WindowsDeviceMonitor.h"
|
||||
|
||||
#include "utils/logs/logging.h"
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
|
||||
WindowsDeviceMonitor::WindowsDeviceMonitor(QObject* parent)
|
||||
: IDeviceMonitor(parent)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
// real impl allocated in start()
|
||||
#endif
|
||||
}
|
||||
|
||||
WindowsDeviceMonitor::~WindowsDeviceMonitor()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
bool WindowsDeviceMonitor::start()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
// TODO: Implement WM_DEVICECHANGE + SetupAPI + NotifyIpInterfaceChange.
|
||||
// Keep as stub for now to allow Linux builds; Windows build will need completing.
|
||||
LOG_INFO() << "WindowsDeviceMonitor started (stub)";
|
||||
return true;
|
||||
#else
|
||||
LOG_WARNING() << "WindowsDeviceMonitor is not available on this platform";
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void WindowsDeviceMonitor::stop()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
LOG_INFO() << "WindowsDeviceMonitor stopped (stub)";
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
27
src/device_bus/monitor/WindowsDeviceMonitor.h
Normal file
27
src/device_bus/monitor/WindowsDeviceMonitor.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "device_bus/monitor/IDeviceMonitor.h"
|
||||
|
||||
namespace softbus::device_bus::monitor
|
||||
{
|
||||
|
||||
// Windows 实现:仅在 _WIN32 下启用;在其他平台上会是一个空实现以保证可编译。
|
||||
class WindowsDeviceMonitor : public IDeviceMonitor
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WindowsDeviceMonitor(QObject* parent = nullptr);
|
||||
~WindowsDeviceMonitor() override;
|
||||
|
||||
bool start() override;
|
||||
void stop() override;
|
||||
|
||||
private:
|
||||
#if defined(_WIN32)
|
||||
struct Impl;
|
||||
Impl* d{nullptr};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace softbus::device_bus::monitor
|
||||
|
||||
Reference in New Issue
Block a user