Libudev USB mount, unmount and pthread_cond part 2

Here is part 2 of the previous explanation app.

#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>
#include <sys/mount.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>

#define NUM_OF_EVENTS 5
#define MOUNT_POINT "/mnt/"

#define handle_error_en(en, msg) \
               do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

pthread_mutex_t mount_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t mount_cond = PTHREAD_COND_INITIALIZER;

static int unmount_drive(void);
static int mount_drive(struct udev_device *dev);
static void *sig_thread(void *arg);

static void *sig_thread(void *arg)
{
        for (;;) {
                pthread_mutex_lock(&mount_lock);
                while (pthread_cond_wait(&mount_cond, &mount_lock) == 0) {
                        printf("Signal handling thread got signal\n");
                        unmount_drive();
                }
                pthread_mutex_unlock(&mount_lock);

                usleep(10000);
        }
        pthread_exit(NULL);
}

static int unmount_drive(void)
{

        int ret = 0;
        if ((ret = umount2(MOUNT_POINT, MNT_FORCE | MNT_DETACH)) < 0) {

                if (errno == EBUSY) {
                        printf("Unable to unmount - device busy\n");
                } else if (errno == EINVAL) {
                        printf("Unable to unmount - device/mount point invalid\n");

                }
        } else {
                printf("Unmount successful\n");
        }
        return ret;
}

static int mount_drive(struct udev_device *dev)
{

        int ret = 0;
        if ((ret = mount(udev_device_get_devnode(dev), MOUNT_POINT, "vfat", MS_NOATIME, NULL)) < 0) {
                if (errno == EBUSY) {
                        printf("Mountpoint busy\n");
                } else {
                        printf("Mount error: %s\n", strerror(errno));
                }
        } else {
                printf("Mount successful\n");
        }
        sleep(1);

        return ret;
}

void epoll_loop(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");
                exit(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");
                exit(1);
        }
        /// Polling loop for devies
        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));

                                if ((strcmp("add", udev_device_get_action(dev)) == 0)
                                    && (strncmp(udev_device_get_devnode(dev), "/dev/sd", 7) == 0)
                                    && (strcmp(udev_device_get_devtype(dev), "disk") == 0)) {

                                        printf("We have a disk - lets mount it \n");
                                        mount_drive(dev);
                                        sleep(2);

                                        pthread_mutex_lock(&mount_lock);
                                        pthread_cond_broadcast(&mount_cond);
                                        pthread_mutex_unlock(&mount_lock);
                                }

                                udev_unref(udev);
                        }
                }
        }
        udev_monitor_unref(mon);
}

int main(int argc, char *argv[])
{
        pthread_t thread;
        int s = 0;

        if ((s = pthread_mutex_init(&mount_lock, NULL)) < 0) {
                handle_error_en(s, "pthread_mutex_init");
        }
        if ((s = pthread_cond_init(&mount_cond, NULL)) < 0) {
                handle_error_en(s, "pthread_cond_init");
        }

        if ((s = pthread_create(&thread, NULL, &sig_thread, NULL)) < 0) {
                handle_error_en(s, "pthread_create");
        }

        epoll_loop();

        pthread_mutex_destroy(&mount_lock);
        pthread_cond_destroy(&mount_cond);

        return (0);
}

Example compiling and output:

^<a href="mailto:Crbrash@ackbar">Crbrash@ackbar</a>:~/Desktop$ gcc -Wall -o usbtest usbtest2.c -ludev -lpthread
<a href="mailto:rbrash@ackbar">rbrash@ackbar</a>:~/Desktop$ sudo ./usbtest
usb = 0781:5406, scsi = SanDisk
usb = 0781:5406, scsi = SanDisk

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

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

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

   Action: add
   Node: /dev/bus/usb/001/044
   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/sde
   Subsystem: block
   Devtype: disk
We have a disk - lets mount it
Mount successful
Signal handling thread got signal
Unmount successful

Basically, the above code allows you to signal an unmount if you are performing work in another thread.

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.