views:

165

answers:

2

Hi,

if I plugin a USB memory stick, I see a new folder in /sys/bus/usb/devices ... thus a new USB-device.

Also I see a new folder in /sys/block ... thus a new block-device.

My question is: How can I get a waterproof mapping between those two devices? Means: If I get a new device in /sys/bus/usb/devices, how can I programatically (f.i. by checking /sys/...) find out which block device is mapped/related to this usb-device and vice-versa?!

A: 

The information in /sys is organized in multiple ways (by driver, by bus, etc.), and there are many symbolic links to go from one hierarchy to another.

Case in point (example seen on kernel 2.6.26): starting from the block device in /sys/block/sdc, the symbolic link /sys/block/sdc/device points inside the per-device-type hierarchy. You can see that it's an USB device because the target of the link is something like

../../devices/pci0000:00/0000:00:1d.7/usb8/8-2/8-2:1.0/host9/target9:0:0/9:0:0:0

Conversely, USB devices are listed in /sys/bus/usb/devices, and we can see that 8-2:1.0 is a disk-like device because /sys/bus/usb/devices/8-2:1.0/driver links to usb-storage. To find out what the associated block device is, it seems we need to go down to the directory /sys/bus/usb/devices/8-2:1.0/host9/target9:0:0/9:0:0:0 which contains a symbolic link block:sdc whose target is /sys/block/sdc.

ADDED: Caution: the exact structure of /sys changes from kernel version to kernel version. For example, with kernel 2.6.32, /sys/block/sdc/device points directly into the /dev/bus/scsi without going through the USB hop.


A different approach is to call the udevadm info command. udevadm info -p /sys/block/sdc --query=… gives information on a device based on its /sys entry, while udevadm info -n sdc --query=… gives information on the device /dev/sdc.

The information includes bus information, for example udevadm info -p /sys/block/sdc --query=env shows

ID_BUS=usb
ID_PATH=pci-0000:00:1d.7-usb-0:2:1.0-scsi-0:0:0:0

The udev documentation may have more information of interest to you.


A final word of caution: there are all kinds of complex cases that may make whatever you do not so waterproof. How will your program deal with a single USB device that is an array of disks that are assigned multiple block devices? Conversely, how will your program deal with a RAID array assembled from multiple devices (perhaps some of them USB and some of them not)? Do you care about other removable media types such as Firewire and e-SATA? etc. You won't be able to predict all corner cases, so make sure to fail gracefully.

Gilles
Thanks for the explanation. I can follow your sample on a debian machine. But on Ubuntu f.i. /sys/block/sdb contains a "device" link to "../../../7:0:0:0". If I follow the rest of you sample, there is no "block:sdb" symlink. All device paths starting from /sys/block do not show up any hint that this is an USB device :-(So for debian it might work, but Ubuntu seems to be somehow different. The command "udevinfo" is not available with Ubuntu. But there's "udevadm" which seems to be similiar. But parsing a commandline output is maybe not the best?!
Alex
regarding the waterproof issue:My use cases don't care about raid of different types of devices. It's just about USB flash memory disk. All I need to have is the vendor-id + product-id + serialnumber + the relation to the mountpoint. That's all.
Alex
@Alex: Ok, I had a look and it seems that more more recent kernel versions have a different symlink structure under `/sys`. As far as I can tell `udevinfo` (where it exists) is exactly equivalent to `udevadm info`. It looks like `udevadm info` is the way to go (it gives you vendor and serial number information as well).
Gilles
Do you know if there's a way of programtically get this information? via an .so file or system call, rather than executing a command and parsing the output....?!
Alex
@Alex: The information has to come from the kernel somehow. As far as I know (which is not much), most of this information is only available to userland through `/sys`, which we've discovered changes with the kernel version. Udev is maintained in sync with the kernel, and has a mostly-documented API in the form of `udevadm`. `udevadm` works by reading files in `/dev/.udev/db` (created by `udevd` I presume), but these files get reorganized too (e.g. they're not the same on Debian lenny and Ubuntu lucid). I don't know of any other interface. So `udevadm` looks by far the most reliable way.
Gilles
A: 

As far as I found out, it's possible to access udev information via "libudev" library. There's also a good sample on the net available: http://www.signal11.us/oss/udev/

I was able to modify it to read out all "/dev/sd*" devices and get their Vendor-ID, Product-ID as well as Serial number. I think this solution is kernel/linux distribution independant enough. But I still have to verify this.

Alex