c++ - 从 netlink 套接字请求连接设备列表

标签 c++ linux linux-kernel embedded-linux netlink

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

  • 是否可以使用 netlink 套接字列出已连接的 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 , 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 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/

    相关文章:

    c++ - 错误 C2144 和错误 C4430 在同一行

    c++ - 在 C++ 中通过常量引用传递指向对象的指针

    linux - 在 Linux 中以 root 身份登录后如何授予其他用户权限?

    c - 如何在嵌入式 Linux 上找到显示库

    c++ - 如何在OpenGL中将汽车朝轮胎方向移动

    c++ - 管理 InputIterator 和 OutputIterator 的 `operator*` 返回值的常量性的规则是什么?

    Linux 脚本 : Reinterpret environment variable

    linux - 运行程序时会发生什么?

    android - 如何解决 W/art : Could not create image space with image file '/system/framework/boot.art' while trying to port Android Lollipop on Arndale Board?

    linux - 如何将 Linux 内核取消优化并使用 -O0 进行编译?