我正在为嵌入式设备开发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,如果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/

    10-11 04:20