【问题标题】:How to receive Kernel uevents with Netlink socket?如何使用 Netlink 套接字接收内核 uevents?
【发布时间】:2017-04-08 20:40:29
【问题描述】:

当 USB 设备(断开)连接时,我正在尝试通过 netlink 套接字从内核接收 uevent。我有一个 python 脚本可以完成相同的工作并且它可以工作,但我需要 C 语言中的相同功能。到目前为止,我有这个:

#include <sys/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define NL_MAX_PAYLOAD 8192

int main () {
    int nl_socket;
    struct sockaddr_nl src_addr, dest_addr;
    struct nlmsghdr* nlh;
    struct msghdr msg;
    struct iovec iov;

    // Prepare source address
    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid();

    // Prepare destination address
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;
    dest_addr.nl_groups = 0;

    // Prepare netlink message
    nlh = (struct nlmsghdr*) malloc(NLMSG_SPACE(NL_MAX_PAYLOAD));
    memset(nlh, 0, NLMSG_SPACE(NL_MAX_PAYLOAD));
    nlh->nlmsg_len = NLMSG_SPACE(NL_MAX_PAYLOAD);
    nlh->nlmsg_pid = getpid();
    nlh->nlmsg_flags = 0;
    iov.iov_base = (void *) nlh;
    iov.iov_len = nlh->nlmsg_len;
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    nl_socket = socket(AF_NETLINK, SOCK_DGRAM,      NETLINK_KOBJECT_UEVENT);
    if (nl_socket < 0) {
        printf("Failed to create socket for DeviceFinder");
        exit(1);
    }

    bind(nl_socket, (struct sockaddr*) &src_addr, sizeof(src_addr));

    while (1) {
        int r = recvmsg(nl_socket, &msg, 0);
        printf("%i\n", r);
        if (r < 0) {
            perror("");
        }
    }
}

本文断章取义,创建了一个小的工作示例。

问题是我什么也没收到。我没有收到任何错误,当我(取消)插入设备时,recvmsg 的调用永远不会返回。为什么?

【问题讨论】:

    标签: c sockets linux-kernel netlink


    【解决方案1】:

    接收部分不正确,而不是使用recvmmsg 我正在使用recv,我对程序进行了一些修改,以下是打印uevents的代码:

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <linux/netlink.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    #define NL_MAX_PAYLOAD 8192
    
    int main () {
        int nl_socket;
        struct sockaddr_nl src_addr;
        char msg[NL_MAX_PAYLOAD];
        int ret;
    
        // Prepare source address
        memset(&src_addr, 0, sizeof(src_addr));
        src_addr.nl_family = AF_NETLINK;
        src_addr.nl_pid = getpid();
        src_addr.nl_groups = -1;
    
        nl_socket = socket(AF_NETLINK, (SOCK_DGRAM | SOCK_CLOEXEC),      NETLINK_KOBJECT_UEVENT);
        if (nl_socket < 0) {
            printf("Failed to create socket for DeviceFinder");
            exit(1);
        }
    
        ret = bind(nl_socket, (struct sockaddr*) &src_addr, sizeof(src_addr));
        if (ret) {
            printf("Failed to bind netlink socket..");
            close(nl_socket);
            return 1;
        }
    
        printf("Waiting for events now...\n");
        while (1) {
            int r = recv(nl_socket, msg, sizeof(msg), MSG_DONTWAIT);
            if (r == -1)
                continue;
            if (r < 0) {
                perror("");
                continue;
            }
            printf("length:%i\n msg:%s", r, msg);
    
        }
    }
    

    【讨论】:

    • 谢谢,这很好用,虽然由于某种原因很多消息都是空的。不过我需要的还可以。
    【解决方案2】:

    recvmsg 比 recv 好用(仅用于连接的链接).ref man recvmsg。 并且计划接近成功。

    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid();
    src_addr.nl_groups = -1; <--add this so can receive from kernel broadcast
    

    【讨论】:

      最近更新 更多