我正在为嵌入式设备开发C++应用程序,该应用程序通过netlink套接字侦听USB热插拔事件。检测事件可以完美地进行,但是我想在程序开始时查询已经连接的设备。我能够为网络接口(interface)存档相同的功能,但是USB似乎完全不同。所以我的问题是:
MWE用于接收热插拔事件:
#include <sys/signalfd.h>
#include <csignal>
#include <linux/netlink.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <poll.h>
int main() {
struct sockaddr_nl addr = {0};
char buffer[4096];
sigset_t signal_set;
struct signalfd_siginfo signal_info;
struct pollfd pfd[2];
int ret_poll;
ssize_t n;
// Set signals we want to catch
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGTERM);
sigaddset(&signal_set, SIGINT);
// Change the signal mask and check
if (sigprocmask(SIG_BLOCK, &signal_set, nullptr) < 0) {
fprintf(stderr, "Error while sigprocmask(): %s\n", strerror(errno));
return EXIT_FAILURE;
}
// Get a signal file descriptor
pfd[0].fd = signalfd(-1, &signal_set, 0);
// Check the signal file descriptor
if (pfd[0].fd < 0) {
fprintf(stderr, "Error while signalfd(): %s\n", strerror(errno));
return EXIT_FAILURE;
}
// Create a netlink socket
pfd[1].fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
if (pfd[1].fd < 0) {
fprintf(stderr, "Netlink socket create failed: %s\n", strerror(errno));
return EXIT_FAILURE;
}
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = 2;
if (bind(pfd[1].fd, (struct sockaddr *) &addr, sizeof(addr))) {
fprintf(stderr, "Netlink socket bind() failed: %s\n", strerror(errno));
return EXIT_FAILURE;
}
pfd[0].events = POLLIN;
pfd[1].events = POLLIN;
while (true) {
// Wait for events without time limit
ret_poll = poll(pfd, 2, -1);
if (ret_poll < 0) {
fprintf(stderr, "SystemMaster::execute() -> "
"Error while poll(): %s\n", strerror(errno));
return EXIT_FAILURE;
}
// True, if a signal from the operating system was sent to this process
if (pfd[0].revents & POLLIN) {
// 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) {
fprintf(stderr, "Error while read() on signal pipe: %s\n", strerror(errno));
}
// Check, if we are really interested in the caught signal
if ((signal_info.ssi_signo == SIGTERM) || (signal_info.ssi_signo == SIGINT)) {
printf("Signal received\n");
}
break;
}
// True, if a netlink message is available
if (pfd[1].revents & POLLIN) {
n = recv(pfd[1].fd, &buffer, sizeof(buffer), 0);
for (int i = 0; i < n; ++i) {
if (buffer[i] == 0) printf("\n");
else if (buffer[i] > 33 && buffer[i] < 126) printf("%c", buffer[i]);
}
}
}
// Close both file descriptors
close(pfd[0].fd);
close(pfd[1].fd);
return 0;
}
预先感谢您的任何回复!
最佳答案
按照@UlrichEckhardt的建议,我检查了lsusb及其来源。它使用libusb,如果libudev不可用,则Linux部分似乎使用libudev并退回到netlink套接字进行热插拔检测。似乎仅使用netlink时,无法扫描设备。但是,没有任何保证,因为我没有深入研究所有内容。
无论如何,我决定使用libudev进行扫描和热插拔,因为我喜欢统一的解决方案。该代码如下所示:
#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);
}
我真的很想对所有内容都使用netlink,因为我完整程序的其他部分也使用了它。如果有人知道将netlink用于我的主要问题的可能性,我将非常感谢您的分享。
关于c++ - 从netlink套接字请求连接的设备列表,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62389036/