将物理 USB 插槽与逻辑磁盘设备关联

将物理 USB 插槽与逻辑磁盘设备关联

我有一系列 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:host3DEVICE=+scsi:3:0:0:0

我还有其他可用的策略吗?

答案1

根据@EduardoBissi 的提示,我找到了一种方法来做到这一点。

我连接到netlink套接字只是为了udev获得我需要的信息(比我在中找到的更好klog)。

我写了两个小classes:

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})')

我对结果非常满意,但几天内我不会接受我自己的答案,看看是否有人能想出更好的解决方案。

相关内容