我有一系列 USB 存储设备(目前有 14 个,但可能会改变),可以以“随机”顺序插入/删除。
我需要编写一个监控程序,自动跟踪插入到哪里的东西以及与之关联的设备。
典型的插入会生成以下/dev/kmsg
条目:
6,2146,143457970892,-;usb 2-4.1.1: new SuperSpeed Gen 1 USB device number 61 using xhci_hcd
SUBSYSTEM=usb
DEVICE=+usb:2-4.1.1
6,2147,143457998502,-;usb 2-4.1.1: New USB device found, idVendor=05e3, idProduct=0749, bcdDevice=12.06
SUBSYSTEM=usb
DEVICE=c189:188
6,2148,143457998509,-;usb 2-4.1.1: New USB device strings: Mfr=3, Product=1, SerialNumber=5
SUBSYSTEM=usb
DEVICE=c189:188
6,2149,143457998513,-;usb 2-4.1.1: Product: USB Storage
SUBSYSTEM=usb
DEVICE=c189:188
6,2150,143457998517,-;usb 2-4.1.1: Manufacturer: SABRENT
SUBSYSTEM=usb
DEVICE=c189:188
6,2151,143457998520,-;usb 2-4.1.1: SerialNumber: 000000000015
SUBSYSTEM=usb
DEVICE=c189:188
6,2152,143458003311,-;usb-storage 2-4.1.1:1.0: USB Mass Storage device detected
SUBSYSTEM=usb
DEVICE=+usb:2-4.1.1:1.0
6,2153,143458005156,-;scsi host3: usb-storage 2-4.1.1:1.0
SUBSYSTEM=scsi
DEVICE=+scsi:host3
5,2154,143459012497,-;scsi 3:0:0:0: Direct-Access SABRENT SD 1206 PQ: 0 ANSI: 6
SUBSYSTEM=scsi
DEVICE=+scsi:3:0:0:0
5,2155,143459013727,-;sd 3:0:0:0: Attached scsi generic sg2 type 0
SUBSYSTEM=scsi
DEVICE=+scsi:3:0:0:0
5,2156,143459425631,-;sd 3:0:0:0: [sdc] 15661056 512-byte logical blocks: (8.02 GB/7.47 GiB)
SUBSYSTEM=scsi
DEVICE=+scsi:3:0:0:0
5,2157,143459426476,-;sd 3:0:0:0: [sdc] Write Protect is off
SUBSYSTEM=scsi
DEVICE=+scsi:3:0:0:0
7,2158,143459426488,-;sd 3:0:0:0: [sdc] Mode Sense: 21 00 00 00
SUBSYSTEM=scsi
DEVICE=+scsi:3:0:0:0
5,2159,143459427197,-;sd 3:0:0:0: [sdc] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
SUBSYSTEM=scsi
DEVICE=+scsi:3:0:0:0
6,2160,143459469457,-; sdc: sdc1 sdc2 sdc3 sdc4 sdc5 sdc6 sdc7 sdc8 sdc9
5,2161,143459473702,-;sd 3:0:0:0: [sdc] Attached SCSI removable disk
SUBSYSTEM=scsi
DEVICE=+scsi:3:0:0:0
而断开连接只会导致:
6,2163,143469908844,-;usb 2-4.1.1: USB disconnect, device number 61
SUBSYSTEM=usb
DEVICE=c189:188
我的问题是,即使有多个设备“同时”连接/断开,也无法可靠地将2-4.1.1
(USB 插槽在树中的位置)与(我可以用来访问存储的设备名称)关联起来(它sdc
能发生!)。
我看到的唯一链是usb 2-4.1.1: new SuperSpeed Gen 1 USB device number 61
-> scsi host3: usb-storage 2-4.1.1:1.0
-> ,但是如果<==>sd 3:0:0:0: [sdc] Attached SCSI removable disk
则有效,这似乎是合理的,但我不确定。DEVICE=+scsi:host3
DEVICE=+scsi:3:0:0:0
我还有其他可用的策略吗?
答案1
根据@EduardoBissi 的提示,我找到了一种方法来做到这一点。
我连接到netlink
套接字只是为了udev
获得我需要的信息(比我在中找到的更好klog
)。
我写了两个小class
es:
import os
import socket
NETLINK_KOBJECT_UEVENT = 15
class KernelEventMonitor(object):
def __init__(self):
self.socket = socket.socket(socket.AF_NETLINK, socket.SOCK_DGRAM, NETLINK_KOBJECT_UEVENT)
def __enter__(self):
self.socket.bind((os.getpid(), -1))
return self
def __exit__(self, exc_type, exc_value, traceback):
self.socket.close()
def __iter__(self):
while True:
data = self.socket.recv(16384)
if data.startswith(b'libudev'):
event = {}
for item in data.split(b'\x00'):
try:
k, v = item.split(b'=', 1)
event[k.decode('ascii')] = v.decode('ascii')
except ValueError:
pass
yield event
if __name__ == '__main__':
import re
r2 = re.compile(r'/devices/[^/]+/[^/]+/[^/]+/(?:[0-9-.]+/)+([0-9-.]+):[0-9.]+/[^/]+/[^/]+/[^/]+/'
r'block/(sd[a-z])/sd[a-z]1$')
with KernelEventMonitor() as monitor:
for evt in monitor:
if evt['SUBSYSTEM'] == 'block' and (m := r2.search(evt['DEVPATH'])):
print(f'Device {m.group(2)} went {evt["ACTION"]} on usb:{m.group(1)}')
注意:出现“相同”事件两次在AF_NETLINK
套接字中,一个在处理它之前,一个在udev
处理它之后。我选择使用“udev 之后”的那个,这样我就可以确保在生成事件时,任何所需的设备都已经到位。
第二个class
只是过滤和重塑事件,并且更具体到我的 USB 配置(我有几个相同的 7 端口 USB 集线器,实际上它们是几个 4 端口集线器,第二个集线器级联在第一个的端口 1 上;这显然非常具体,但我把它留在里面以展示如何处理类似的情况):
import re
import netlink
class SDMonitor(netlink.KernelEventMonitor):
r2 = re.compile(r'/devices/[^/]+/[^/]+/[^/]+/(?:[0-9-.]+/)+[0-9]+-(\d+)\.([0-9.]+):'
r'[0-9.]+/[^/]+/[^/]+/[^/]+/block/(sd[a-z])/sd[a-z]1$')
def __iter__(self):
def _get_index(t):
return
for event in super().__iter__():
if event['SUBSYSTEM'] == 'block':
m = SDMonitor.r2.search(event['DEVPATH'])
if m:
try:
act = {'add': '+', 'remove': '-'}[event["ACTION"]]
dev = m.group(3)
row = int(m.group(1))
col = {'1.1': 1, '1.2': 2, '1.3': 3, '1.4': 4, '2': 5, '3': 6, '4': 7}[m.group(2)]
evt = (act, dev, row, col)
yield evt
except (KeyError, ValueError):
print(f'** Bad entry ** {m.group(3)} went {event["ACTION"]} on usb:{m.group(1)}.{m.group(2)}')
if __name__ == '__main__':
with SDMonitor() as monitor:
for a, d, r, c in monitor:
print(f'{a} {d} --> ({r}, {c})')
我对结果非常满意,但几天内我不会接受我自己的答案,看看是否有人能想出更好的解决方案。