Last active
March 1, 2021 23:16
-
-
Save tiagovignatti/8a7297b847e5be796ac2394a9f232109 to your computer and use it in GitHub Desktop.
usb monitoring on Linux
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
g++ -Wall -o usbmonitor usbmonitor.cpp -ludev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <unistd.h> | |
#include <poll.h> | |
#include <cstdio> | |
#include <cstdlib> | |
#include <cerrno> | |
#include <cstring> | |
#include <sys/signalfd.h> | |
#include <csignal> | |
#include <libudev.h> | |
void scanDevices(struct udev *udev) { | |
struct udev_device *device; | |
struct udev_enumerate *enumerate; | |
struct udev_list_entry *devices, *dev_list_entry; | |
// Create enumerate object | |
enumerate = udev_enumerate_new(udev); | |
if (!enumerate) { | |
printf("Error while creating udev enumerate\n"); | |
return; | |
} | |
// Scan devices | |
udev_enumerate_scan_devices(enumerate); | |
// Fill up device list | |
devices = udev_enumerate_get_list_entry(enumerate); | |
if (!devices) { | |
printf("Error while getting device list\n"); | |
return; | |
} | |
udev_list_entry_foreach(dev_list_entry, devices) { | |
// Get the device | |
device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(dev_list_entry)); | |
// Print device information | |
printf("DEVNODE=%s\n", udev_device_get_devnode(device)); | |
printf("KERNEL=%s\n", udev_device_get_sysname(device)); | |
printf("DEVPATH=%s\n", udev_device_get_devpath(device)); | |
printf("DEVTYPE=%s\n\n", udev_device_get_devtype(device)); | |
// Free the device | |
udev_device_unref(device); | |
} | |
// Free enumerate | |
udev_enumerate_unref(enumerate); | |
} | |
void monitorDevices(int signal_fd, struct udev *udev) { | |
udev_monitor *monitor = udev_monitor_new_from_netlink(udev, "udev"); | |
struct pollfd pfd[2]; | |
int ret_poll; | |
ssize_t n; | |
// Enable receiving hotplug events | |
udev_monitor_enable_receiving(monitor); | |
pfd[0].events = POLLIN; | |
pfd[0].fd = signal_fd; | |
pfd[1].events = POLLIN; | |
pfd[1].fd = udev_monitor_get_fd(monitor); | |
if (pfd[1].fd < 0) { | |
printf("Error while getting hotplug monitor\n"); | |
udev_monitor_unref(monitor); | |
return; | |
} | |
while (true) { | |
// Wait for events without time limit | |
ret_poll = poll(pfd, 2, -1); | |
if (ret_poll < 0) { | |
printf("Error while polling file descriptors\n"); | |
break; | |
} | |
// True, if a signal from the operating system was sent to this process | |
if (pfd[0].revents & POLLIN) { | |
struct signalfd_siginfo signal_info; | |
// Get the signal | |
n = read(pfd[0].fd, &signal_info, sizeof(signal_info)); | |
// True, if an error occurred while getting the signal | |
if (n == -1) { | |
printf("Error while read on signal file descriptor\n"); | |
break; | |
} | |
// Check which signal was caught | |
switch (signal_info.ssi_signo) { | |
case SIGINT: | |
printf("SIGINT received\n"); | |
break; | |
case SIGTERM: | |
printf("SIGTERM received\n"); | |
break; | |
default: | |
printf("Unknown signal received\n"); | |
} | |
break; | |
} | |
if (pfd[1].revents & POLLIN) { | |
// Get the device | |
struct udev_device *device = udev_monitor_receive_device(monitor); | |
if (!device) { | |
printf("Error while getting device...returning to work\n"); | |
continue; | |
} | |
// Print device information | |
printf("DEVNODE=%s\n", udev_device_get_devnode(device)); | |
printf("KERNEL=%s\n", udev_device_get_sysname(device)); | |
printf("DEVPATH=%s\n", udev_device_get_devpath(device)); | |
printf("DEVTYPE=%s\n\n", udev_device_get_devtype(device)); | |
// Free the device | |
udev_device_unref(device); | |
} | |
} | |
// Free the monitor | |
udev_monitor_unref(monitor); | |
} | |
int main() { | |
// Create a new udev object | |
struct udev *udev = udev_new(); | |
if (!udev) { | |
printf("Error while initialization!\n"); | |
return EXIT_FAILURE; | |
} | |
sigset_t mask; | |
// Set signals we want to catch | |
sigemptyset(&mask); | |
sigaddset(&mask, SIGTERM); | |
sigaddset(&mask, SIGINT); | |
// Change the signal mask and check | |
if (sigprocmask(SIG_BLOCK, &mask, nullptr) < 0) { | |
fprintf(stderr, "Error while sigprocmask(): %s\n", std::strerror(errno)); | |
return EXIT_FAILURE; | |
} | |
// Get a signal file descriptor | |
int signal_fd = signalfd(-1, &mask, 0); | |
// Check the signal file descriptor | |
if (signal_fd < 0) { | |
fprintf(stderr, "Error while signalfd(): %s\n", std::strerror(errno)); | |
return EXIT_FAILURE; | |
} | |
// First scan already attached devices | |
scanDevices(udev); | |
// Second monitor hotplug events | |
monitorDevices(signal_fd, udev); | |
// Free the udev object | |
udev_unref(udev); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
TODO
Print up Serial Number using property with
udev_device_get_property_value(dev, "ID_SERIAL_SHORT"))
https://stackoverflow.com/questions/18605701/get-unique-serial-number-of-usb-device-mounted-to-dev-folder
cpp it and clean up the code a bit
https://github.com/ElFeesho/libudevpp
https://github.com/Jinjinov/Usb.Events/blob/master/Usb.Events/UsbEventWatcher.Linux.c
https://github.com/gavv/snippets/blob/master/udev/udev_monitor_usb.c
Can we statically link libudev due its "LGPL-2.1-or-later" license?
"libudev-dev does not include static library":
https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/1358372