C++ – how to list USB mass storage devices programatically using libudev in Linux

clinuxlinux-kerneluser interface

I am doing a project with mass storage devices in linux. I am trying to write an application which will list all the connected usb mass storage devices and will give notification when a new mass storage device is plugged in. I am using libudev for this purpose. I have used the code found in "http://www.signal11.us/oss/udev/" . I have done the modification here

  /* Create a list of the devices in the 'block' subsystem. */
  enumerate = udev_enumerate_new(udev);
  udev_enumerate_add_match_subsystem(enumerate, "block");
  udev_enumerate_scan_devices(enumerate);
  devices = udev_enumerate_get_list_entry(enumerate);

The problem now is that it list all the block devices. I want only the usb mass storage devices. How to get this list. One more problem is that how to get the label of usb storage devices using libudev.

Best Answer

One solution is to match devices with the following criteria:

  • SUBSYSTEM == "scsi", DEVTYPE == "scsi_device"
  • child device exists with SUBSYSTEM == "block"
  • child device exists with SUBSYSTEM == "scsi_disk"
  • parent device exists with SUBSYSTEM == "usb", DEVTYPE == "usb_device"

Here is example program (also available on github):

#include <libudev.h>
#include <stdio.h>

static struct udev_device*
get_child(
     struct udev* udev, struct udev_device* parent, const char* subsystem)
{
  struct udev_device* child = NULL;
  struct udev_enumerate *enumerate = udev_enumerate_new(udev);

  udev_enumerate_add_match_parent(enumerate, parent);
  udev_enumerate_add_match_subsystem(enumerate, subsystem);
  udev_enumerate_scan_devices(enumerate);

  struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
  struct udev_list_entry *entry;

  udev_list_entry_foreach(entry, devices) {
    const char *path = udev_list_entry_get_name(entry);
    child = udev_device_new_from_syspath(udev, path);
    break;
  }

  udev_enumerate_unref(enumerate);
  return child;
}

static void enumerate_usb_mass_storage(struct udev* udev)
{
  struct udev_enumerate* enumerate = udev_enumerate_new(udev);

  udev_enumerate_add_match_subsystem(enumerate, "scsi");
  udev_enumerate_add_match_property(enumerate, "DEVTYPE", "scsi_device");
  udev_enumerate_scan_devices(enumerate);

  struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
  struct udev_list_entry *entry;

  udev_list_entry_foreach(entry, devices) {
    const char* path = udev_list_entry_get_name(entry);
    struct udev_device* scsi = udev_device_new_from_syspath(udev, path);

    struct udev_device* block = get_child(udev, scsi, "block");
    struct udev_device* scsi_disk = get_child(udev, scsi, "scsi_disk");

    struct udev_device* usb
      = udev_device_get_parent_with_subsystem_devtype(
          scsi, "usb", "usb_device");

    if (block && scsi_disk && usb) {
        printf("block = %s, usb = %s:%s, scsi = %s\n",
          udev_device_get_devnode(block),
          udev_device_get_sysattr_value(usb, "idVendor"),
          udev_device_get_sysattr_value(usb, "idProduct"),
          udev_device_get_sysattr_value(scsi, "vendor"));
    }

    if (block)
      udev_device_unref(block);

    if (scsi_disk)
      udev_device_unref(scsi_disk);

    udev_device_unref(scsi);
  }

  udev_enumerate_unref(enumerate);
}

int main()
{
  struct udev* udev = udev_new();

  enumerate_usb_mass_storage(udev);

  udev_unref(udev);
  return 0;
}

And it's output for my external disk:

block = /dev/sdb, usb = 0bc2:ab20, scsi = Seagate
Related Topic