简体   繁体   English

在 Linux 中编写 USB 设备挂载脚本的更好方法

[英]Better way to script USB device mount in Linux

I'm writing a python module for a device that interacts with a user supplied USB memory stick.我正在为与用户提供的 USB 记忆棒交互的设备编写 python 模块。 The user can insert a USB memory stick in the device USB slot, and the device will dump data onto the memory stick without user intervention.用户可以将 USB 记忆棒插入设备 USB 插槽中,设备会将数据转储到记忆棒上,无需用户干预。 If the device is running when the user inserts the USB stick, I have hooked into D-Bus and have an auto mount routine all worked out.如果用户插入 USB 记忆棒时设备正在运行,我已经连接到 D-Bus 并完成了一个自动挂载程序。 The new issue is, what if the stick is inserted while the device is powered off?新的问题是,如果在设备断电的情况下插入棒怎么办? I get no D-Bus insertion event, or any the associated nuggets of information about the memory stick after the device is powered on.在设备通电后,我没有收到 D-Bus 插入事件,或任何有关记忆棒的相关信息块。

I have worked out a way to derive the device node ( /dev/sd? ) from scanning the USB devices in /proc, by calling:我已经找到了一种方法来从扫描 /proc 中的 USB 设备来派生设备节点( /dev/sd? ),方法是调用:

ls /proc/scsi/usb-storage

this gives the scsi device info if you cat each of the files in that folder.如果您对该文件夹中的每个文件进行分类,这将提供 scsi 设备信息。

I then take the Vendor, Product, and Serial Number fields from the usb-storage records, generate an identifier string that I then use in然后我从 USB 存储记录中获取供应商、产品和序列号字段,生成一个标识符字符串,然后在

ll /dev/disc/by-id/usb_[vendor] _[product] _[serial_number]-0:0

So I can parse through the result to get the relative path所以我可以通过结果解析得到相对路径

../../sdc

Then, I can mount the USB stick.然后,我可以安装 U 盘。

This is a cumbersome procedure, pretty much all text based, and ready for bugs when someone introduces a weird character, or non-standard serial number string.这是一个繁琐的过程,几乎都是基于文本的,并且在有人引入奇怪的字符或非标准序列号字符串时准备好应对错误。 It works with all 2 of the USB memory sticks I own.它适用于我拥有的所有 2 个 USB 记忆棒。 I have tried to map output from /var/log/messages but that ends up being text comparisons as well.我试图从 /var/log/messages 映射输出,但最终也是文本比较。 Output from lsusb, fdisk, udevinfo, lsmod, and others only show half of the required data. lsusb、fdisk、udevinfo、lsmod 等的输出仅显示所需数据的一半。

My question: how do I determine, in the absence of a D-Bus message, the /dev device assigned to a USB memory stick without user intervention, or knowing in advance the specifics of the inserted device?我的问题:在没有 D-Bus 消息的情况下,我如何确定分配给 USB 记忆棒的 /dev 设备而无需用户干预,或者事先知道插入设备的细节?

Thanks, sorry about the novel.谢谢,对不起小说。

This seems to work combining /proc/partitions and the /sys/class/block approach ephimient took.这似乎可以结合/proc/partitions和 ephimient 采用的/sys/class/block方法。

#!/usr/bin/python
import os
partitionsFile = open("/proc/partitions")
lines = partitionsFile.readlines()[2:]#Skips the header lines
for line in lines:
    words = [x.strip() for x in line.split()]
    minorNumber = int(words[1])
    deviceName = words[3]
    if minorNumber % 16 == 0:
        path = "/sys/class/block/" + deviceName
        if os.path.islink(path):
            if os.path.realpath(path).find("/usb") > 0:
                print "/dev/%s" % deviceName

I'm not sure how portable or reliable this is, but it works for my USB stick.我不确定这有多便携或可靠,但它适用于我的 USB 记忆棒。 Of course find("/usb") could be made into a more rigorous regular expression.当然find("/usb")可以做成更严谨的正则表达式。 Doing mod 16 may also not be the best approach to find the disk itself and filter out the partitions, but it works for me so far.执行 mod 16 也可能不是找到磁盘本身并过滤掉分区的最佳方法,但到目前为止它对我有用。

I'm not entirely certain how portable this is.我不完全确定这有多便携。 Also, this information would presumably also be available over D-Bus from udisks or HAL but neither of those is present on my system so I can't try.此外,这些信息大概也可以从udisksHAL通过 D-Bus 获得,但我的系统中不存在这些信息,所以我无法尝试。 It seems to be reasonably accurate here regardless:无论如何,它在这里似乎相当准确:

$ for i in /sys/class/block/*; do
>     /sbin/udevadm info -a -p $i | grep -qx '    SUBSYSTEMS=="usb"' &&
>     echo ${i##*/}
> done
sde
sdf
sdg
sdh
sdi
sdj
sdj1
$ cd /sys/class/block/
$ for i in *; do [[ $(cd $i; pwd -P) = */usb*/* ]] && echo $i; done
sde
sdf
sdg
sdh
sdi
sdj
sdj1

After looking at this thread about doing what ubuntu does with nautilus, i found a few recommendations and decided to go with accessing udisks through shell commands.查看了有关如何使用 nautilus 执行 ubuntu 操作的线程后,我找到了一些建议,并决定通过 shell 命令访问 udisk。

The Mass storage device class is what you want.大容量存储设备类正是您想要的。 Just give it the device file.只要给它设备文件。 ie: /dev/sdb you can then do d.mount() and d.mount_point to get where it has been mounted.即: /dev/sdb 然后您可以执行 d.mount() 和 d.mount_point 以获取已安装的位置。

After that is also a class for finding many identical USB devices to control mounting, un-mounting and ejecting a large list of devices that all have the same label.之后也是一个类,用于查找许多相同的 USB 设备来控制安装、卸载和弹出大量具有相同标签的设备。 (if you run is with no argument, it will apply this to all SD devices. Could be handy for a "just auto mount everything" script (如果您不带参数运行,它将将此应用于所有 SD 设备。对于“仅自动安装所有内容”脚本可能很方便

import re
import subprocess

#used as a quick way to handle shell commands
def getFromShell_raw(command):
    p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return p.stdout.readlines()

def getFromShell(command):
    result = getFromShell_raw(command)
    for i in range(len(result)):       
        result[i] = result[i].strip() # strip out white space
    return result



class Mass_storage_device(object):
    def __init__(self, device_file):
       self.device_file = device_file
       self.mount_point = None

    def as_string(self):
        return "%s -> %s" % (self.device_file, self.mount_point)

    """ check if we are already mounted"""
    def is_mounted(self):
        result = getFromShell('mount | grep %s' % self.device_file)
        if result:
            dev, on, self.mount_point, null = result[0].split(' ', 3)
            return True
        return False

    """ If not mounted, attempt to mount """
    def mount(self):
        if not self.is_mounted():
            result = getFromShell('udisks --mount %s' % self.device_file)[0] #print result
            if re.match('^Mounted',result): 
                mounted, dev, at, self.mount_point = result.split(' ')

        return self.mount_point

    def unmount(self):
        if self.is_mounted():
            result = getFromShell('udisks --unmount %s' % self.device_file) #print result
            self.mount_point=None

    def eject(self):
        if self.is_mounted():
            self.unmount()
        result = getFromShell('udisks --eject %s' % self.device_file) #print result
        self.mount_point=None


class Mass_storage_management(object):
    def __init__(self, label=None):
        self.label = label
        self.devices = [] 
        self.devices_with_label(label=label)

    def refresh(self):
        self.devices_with_label(self.label)

    """ Uses udisks to retrieve a raw list of all the /dev/sd* devices """
    def get_sd_list(self):
        devices = []
        for d in getFromShell('udisks --enumerate-device-files'):
            if re.match('^/dev/sd.$',d): 
                devices.append(Mass_storage_device(device_file=d))
        return devices


    """ takes a list of devices and uses udisks --show-info 
    to find their labels, then returns a filtered list"""
    def devices_with_label(self, label=None):
        self.devices = []
        for d in self.get_sd_list():
            if label is None:
                self.devices.append(d)
            else:
                match_string = 'label:\s+%s' % (label)
                for info in getFromShell('udisks --show-info %s' % d.device_file):
                    if re.match(match_string,info): self.devices.append(d)
        return self

    def as_string(self):
        string = ""
        for d in self.devices:
            string+=d.as_string()+'\n'
        return string

    def mount_all(self): 
        for d in self.devices: d.mount()

    def unmount_all(self): 
        for d in self.devices: d.unmount()

    def eject_all(self): 
        for d in self.devices: d.eject()
        self.devices = []



if __name__ == '__main__':
    name = 'my devices'
    m = Mass_storage_management(name)
    print m.as_string()

    print "mounting"
    m.mount_all()
    print m.as_string()

    print "un mounting"
    m.unmount_all()
    print m.as_string()

    print "ejecting"
    m.eject_all()
    print m.as_string()

why don't you simply use an udev rule?为什么不简单地使用 udev 规则? i had to deal with a similar situation, and my solution was to create a file in /etc/udev/rules.d containing following rule:我不得不处理类似的情况,我的解决方案是在 /etc/udev/rules.d 中创建一个包含以下规则的文件:

SUBSYSTEMS=="scsi", KERNEL=="sd[b-h]1", RUN+="/bin/mount -o umask=000 /dev/%k /media/usbdrive"

one assumption here is that nobody ever inserts more than one usb stick at time.这里的一个假设是,没有人一次插入超过一根 U 盘。 it has however the advantage that i know in advance where the stick will be mounted (/media/usbdrive).然而,它的优点是我事先知道将安装棒的位置(/media/usbdrive)。

you can quite surely elaborate it a bit to make it smarter, but personally i never had to change it and it still works on several computers.您可以肯定地详细说明它以使其更智能,但就我个人而言,我从来不必更改它,它仍然可以在多台计算机上运行。

however, as i understand, you want to be alerted somehow when a stick is inserted, and perhaps this strategy gives you some trouble on that side, i don't know, didn't investigate...然而,据我所知,当插入一根棍子时,你想以某种方式得到提醒,也许这种策略在那方面给你带来了一些麻烦,我不知道,没有调查......

我认为最简单的方法是使用 lsblk:

lsblk -d -o NAME,TRAN | grep usb

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM