开发者

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. 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. 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. 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.

I have worked out a way to derive the device node ( /dev/sd? ) from scanning the USB devices in /proc, by calling:

ls /proc/scsi/usb-storage

this gives the scsi device info if you cat each of the files in that folder.

I then take the Vendor, Product, and Serial Number fields from the usb-storage records, generate an identifier string that I then use in

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.

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. I have tried to map output from /var/log/messages but that ends up being text comparisons as well. Output from lsusb, fdisk, udevinfo, lsmod, and others only show half of the required data.

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?

Thanks, sorry about the novel.


This seems to work combining /proc/partitions and the /sys/class/block approach ephimient took.

#!/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. Of course find("/usb") could be made into a more rigorous regular expression. 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.


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. 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.

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.

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. (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

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? i had to deal with a similar situation, and my solution was to create a file in /etc/udev/rules.d containing following rule:

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. it has however the advantage that i know in advance where the stick will be mounted (/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...


I think the easiest way is to use lsblk:

lsblk -d -o NAME,TRAN | grep usb
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜