从网链套接字请求连接设备的列表

Request list of attached devices from netlink socket

本文关键字:接设备 列表 连接 请求 套接字      更新时间:2023-10-16

我正在为嵌入式设备开发一个C++应用程序,该应用程序通过网络链接插座侦听USB热插拔事件。检测事件可以完美无缺,但另外我想在程序开始时查询已经连接的设备。我能够为网络接口存档相同的功能,但似乎USB是一个完全不同的故事。所以我的问题是:

  • 是否可以使用网络链接插座列出已连接的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(): %sn", 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(): %sn", 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: %sn", 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: %sn", 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(): %sn", 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: %sn", 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 receivedn");
}
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,其中 Linux 部分似乎使用 libudev 并在 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 enumeraten");
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 listn");
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=%sn", udev_device_get_devnode(device));
printf("KERNEL=%sn", udev_device_get_sysname(device));
printf("DEVPATH=%sn", udev_device_get_devpath(device));
printf("DEVTYPE=%snn", 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 monitorn");
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 descriptorsn");
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 descriptorn");
break;
}
// Check which signal was caught
switch (signal_info.ssi_signo) {
case SIGINT:
printf("SIGINT receivedn");
break;
case SIGTERM:
printf("SIGTERM receivedn");
break;
default:
printf("Unknown signal receivedn");
}
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 workn");
continue;
}
// Print device information
printf("DEVNODE=%sn", udev_device_get_devnode(device));
printf("KERNEL=%sn", udev_device_get_sysname(device));
printf("DEVPATH=%sn", udev_device_get_devpath(device));
printf("DEVTYPE=%snn", 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(): %sn", 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(): %sn", 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解决我的主要问题的可能性,我将不胜感激。