Libudev USB mount, unmount and pthread_cond part 2
Here is part 2 of the previous explanation app.
#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: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.
Add new comment