【 国产信创系统】udev监测USB事件
前言
本文从linux运维的角度来实现检测USB事件(插入/拔出)的,主要是通过udev的rules规则,以及系统D-bus服务,加上python脚本进行实时监测。
关联文章导读
Linux服务器端自动挂载存储设备(U盘、移动硬盘) 分享了存储设备通过.rules文件挂载到linux上
【Linux C/C++开发】udev监测USB事件 分享了libudev库,开发的监测程序
详细操作
1、生成.rules文件
sudo cp 99-usb-monitor.rules /etc/udev/rules.d/ -rf
99-usb-monitor.rules配置文件内容如下:
# 99-usb-monitor.rules
SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", \RUN+="/usr/bin/dbus-send --system --type=signal /com/Monitor/USBMonitor com.Monitor.USBMonitor.DeviceChanged string:'added' string:'$env{ID_VENDOR_ID}:$env{ID_MODEL_ID}' string:'$env{DEVNAME}' string:'$env{ID_VENDOR}' string:'$env{ID_MODEL}'"SUBSYSTEM=="usb", ACTION=="remove", ENV{DEVTYPE}=="usb_device", \RUN+="/usr/bin/dbus-send --system --type=signal /com/Monitor/USBMonitor com.Monitor.USBMonitor.DeviceChanged string:'removed' string:'$env{ID_VENDOR_ID}:$env{ID_MODEL_ID}' string:'$env{DEVNAME}' string:'$env{ID_VENDOR}' string:'$env{ID_MODEL}'"
以上规则仅监测add和remove两个事件,其他事件不处理
2、重新加载.rules
sudo udevadm control --reload-rules && sudo udevadm trigger
3、创建USB的D-bus服务
sudo cp com.Monitor.USBMonitor.conf /etc/dbus-1/system.d/ -rf
com.Monitor.USBMonitor.conf配置文件内容如下:
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig><policy user="usr"><allow own="com.Monitor.USBMonitor"/><allow send_destination="com.Monitor.USBMonitor"/><allow receive_sender="com.Monitor.USBMonitor"/></policy>
</busconfig>
send_destination:允许用户 usr 向 com.example.USBMonitor 服务发送消息
receive_sender:用户 usr 可以监听该服务广播的事件通知
注意,要把 <policy user="usr">中的”usr“改成系统的用户名称
4、重启D-Bus服务
sudo systemctl reload dbus
5、运行监测脚本
python3 usb_monitor.py
udev只能传递设备路径和事件名称,我在脚本中加了获取vid和pid的逻辑代码,脚本代码如下:
# -*- coding: utf-8 -*-
import dbus
import dbus.mainloop.glib
import logging
import time
import os
from datetime import datetime
from gi.repository import GLiblogging.basicConfig(level=logging.INFO)
logger = logging.getLogger('USBClient')dist_vidpid = {}class USBMonitorClient:def __init__(self):dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)self.bus = dbus.SystemBus()self.connected_devices = {}def listen_for_events(self):try:self.bus.add_signal_receiver(self.on_device_changed,dbus_interface='com.Monitor.USBMonitor',signal_name='DeviceChanged')logger.info("正在监听USB设备事件...")logger.info("按 Ctrl+C 退出")loop = GLib.MainLoop()loop.run()except KeyboardInterrupt:logger.info("客户端已停止")except Exception as e:logger.error(f"客户端错误: {e}")def get_device_vid_pid(self, device_path):try:cmd = f"udevadm info {device_path}"with os.popen(cmd) as pipe:result = pipe.read()if result:vid = '未知'pid = '未知'for line in result.split('\n'):if line.startswith('E: ID_VENDOR_ID='):vid = line.split('=')[1]elif line.startswith('E: ID_MODEL_ID='):pid = line.split('=')[1]print(vid, pid)return vid, pidelse:logger.warning("udevadm命令执行失败,无返回结果")return '未知', '未知'except Exception as e:logger.error(f"获取设备VID/PID失败: {e}")return '未知', '未知'def on_device_changed(self, action, vendor_product, device_path, vendor_name, product_name):clean_action = action.strip("'\"")#传过来的action带了单引号vid = '未知'pid = '未知'timestamp = datetime.now().strftime("%H:%M:%S")status_icon = "🔌" if clean_action == "added" else "❌"if clean_action == "added":vid, pid = self.get_device_vid_pid(device_path)dist_vidpid[device_path] = vid + ":" + pidelse:vidpid = dist_vidpid[device_path].split(':')vid = vidpid[0]pid = vidpid[1]if vid != '未知' and pid != '未知':logger.info(f"{timestamp} {status_icon} USB设备{action}: {vendor_name} {product_name} (VID: {vid}, PID: {pid})")else:logger.info(f"{timestamp} {status_icon} USB设备{action}: {vendor_name} {product_name} (路径: {device_path})")if __name__ == "__main__":client = USBMonitorClient()client.listen_for_events()
6、执行效果

