Libudev USB storage polling example in C part1

Here is a simple libudev example that performs the following:

  • Enumerates static plugged in USB storage devices
  • Using epoll, polls for USB and block device changes

To compile:

gcc -Wall -g -o udev_example usbtest2.c -ludev

The code:

#include <libudev.h>
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define NUM_OF_EVENTS 5
int main(void)
{
        struct udev *udev;
        struct udev_enumerate *enumerate;
        struct udev_list_entry *devices;
        struct udev_device *dev;

        int fd_udev = -1;
        struct epoll_event ep_udev = { 0 };

        struct udev_monitor *mon;
        int fd_ep = -1;

        udev = udev_new();
        if (!udev) {
                printf("Can't create udev\n");
                exit(1);
        }

        /// What devices do we have plugged in at this moment in time?
       
        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);

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

        /// enumerate through any that are installed
        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 *usb = udev_device_get_parent_with_subsystem_devtype(scsi, "usb", "usb_device");

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

                udev_device_unref(scsi);
        }

        udev_enumerate_unref(enumerate);
       
        /// Begin active polling for USB input and output

        mon = udev_monitor_new_from_netlink(udev, "udev");
        udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", "usb_device");
        udev_monitor_filter_add_match_subsystem_devtype(mon, "block", NULL);
        udev_monitor_enable_receiving(mon);

        /// Setup epoll
        fd_ep = epoll_create1(0);
        if (fd_ep < 0) {
                fprintf(stderr, "error creating epoll\n");
                return 1;
        }

        fd_udev = udev_monitor_get_fd(mon);
        ep_udev.events = EPOLLIN;
        ep_udev.data.fd = fd_udev;
        if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
                fprintf(stderr, "fail to add fd to epoll\n");
                return 2;
        }

        /// Polling loop for devices
        while (1) {
                int fdcount;
                struct epoll_event ev[NUM_OF_EVENTS];
                int i = 0;

                fdcount = epoll_wait(fd_ep, ev, NUM_OF_EVENTS, -1);
                if (fdcount < 0) {
                        if (errno != EINTR)
                                fprintf(stderr, "error receiving uevent message: %m\n");
                        continue;
                }

                for (i = 0; i < fdcount; i++) {
                        if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {

                                dev = udev_monitor_receive_device(mon);
                                if (dev == NULL)
                                        continue;

                                printf("\n   Action: %s\n",udev_device_get_action(dev)); // add or remove events
                                printf("   Node: %s\n", udev_device_get_devnode(dev));
                                printf("   Subsystem: %s\n", udev_device_get_subsystem(dev));
                                printf("   Devtype: %s\n", udev_device_get_devtype(dev));
                                udev_unref(udev);
                        }
                }
        }

        return 0;
}

Output from plugging in a sandisk USB storage device

sudo ./udev_example
usb = 0781:5406, scsi = SanDisk
usb = 0781:5406, scsi = SanDisk

   Action: remove
   Node: /dev/sdb
   Subsystem: block
   Devtype: disk

   Action: remove
   Node: /dev/sr1
   Subsystem: block
   Devtype: disk

   Action: remove
   Node: /dev/bus/usb/001/018
   Subsystem: usb
   Devtype: usb_device

   Action: add
   Node: /dev/bus/usb/004/028
   Subsystem: usb
   Devtype: usb_device

   Action: add
   Node: /dev/sr1
   Subsystem: block
   Devtype: disk

   Action: change
   Node: /dev/sr1
   Subsystem: block
   Devtype: disk

   Action: add
   Node: /dev/sdb
   Subsystem: block
   Devtype: disk

Blog tags: 

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <python> <c>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.