0%

linux-netlink-socket-get-hotplug-info

使用netlink与内核通信获取U盘插拔信息

Hotplug uevent

uevent is just string of some special format that is sent via netlink socket. Example:

add@/class/input/input9/mouse2\0    // message
ACTION=add\0                         // action type
DEVPATH=/class/input/input9/mouse2\0 // path in /sys
SUBSYSTEM=input\0                    // subsystem (class)
SEQNUM=1064\0                        // sequence number
PHYSDEVPATH=/devices/pci0000:00/0000:00:1d.1/usb2/2­2/2­2:1.0\0  // device path in /sys
PHYSDEVBUS=usb\0       // bus
PHYSDEVDRIVER=usbhid\0 // driver
MAJOR=13\0             // major number
MINOR=34\0",           // minor number

mdev, the udev for embedded systems

Example 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <errno.h>
#include <sys/poll.h>

static int init_hotplug_sock(void)
{
    struct sockaddr_nl snl;
    const int buffersize = 16 * 1024 * 1024;
    int retval;

    memset(&snl, 0x00, sizeof(struct sockaddr_nl));
    snl.nl_family = AF_NETLINK;
    snl.nl_pid = getpid();
    snl.nl_groups = -1;
    int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

    if (hotplug_sock == -1) {
        printf("error getting socket: %s", strerror(errno));
        return -1;
    }


    setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
    retval = bind(hotplug_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl));

    if (retval < 0) {
        printf("bind failed: %s", strerror(errno));
        close(hotplug_sock);
        hotplug_sock = -1;
        return -1;
    }
    return hotplug_sock;
}

main(void)
{
    init_hotplug_sock();
    while(1){
        char buf[UEVENT_BUFFER_SIZE*2] = {0};
        recv(hotplug_sock, &buf, sizeof(buf), 0);
        printf("\033[31m%s\n\033[0m", buf);
        printf("==============================\n");
    }
}

这种方式只可以输出uevent的第一行内容,是由于recv方式导致,完善如下

Example 2

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <errno.h>
#include <sys/poll.h>

static int init_hotplug_sock(void)
{
    struct sockaddr_nl snl;
    const int buffersize = 16 * 1024 * 1024;
    int retval;
    struct pollfd pfd;
    char buf[512];

    memset(&snl, 0x00, sizeof(struct sockaddr_nl));
    snl.nl_family = AF_NETLINK;
    snl.nl_pid = getpid();
    snl.nl_groups = -1;
    pfd.events = POLLIN;
    pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);

    if (pfd.fd == -1) {
        printf("error getting socket: %s", strerror(errno));
        return -1;
    }

    retval = bind(pfd.fd, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl));

    while (-1!=poll(&pfd, 1, -1)) {
        int i, len = recv(pfd.fd, buf, sizeof(buf), 0);
        if (len == -1) die("recv\n");
        // Print the data to stdout.
        printf("@@@@@@@@@@@@@@@@@\n");
        i = 0;
        while (i<len) {
            if(strstr(buf+i, "add@") || strstr(buf+i, "remove@")) {
                printf("\033[31m%s\n\033[0m", buf+i);
            }
            else if(strstr(buf+i, "ACTION=")) {
            }
            else if(strstr(buf+i, "SUBSYSTEM=")) {
                if(!strstr(buf+i, "usb")) {
                    len = 0;
                    break;
                }
            }
            else if(strstr(buf+i, "DEVTYPE=")) {
                if(!strstr(buf+i, "usb_device")) {
                    len = 0;
                    break;
                }
            }
            else if(strstr(buf+i, "PRODUCT=")) {
                printf("\033[31m%s\n\033[0m", buf+i);
                close(pfd.fd);
                return 0;
            }
            i += strlen(buf+i)+1;
        }
    }

    return pfd.fd;
}

Example 3

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <errno.h>
#include <sys/poll.h>

void die(char *s)
{
    write(2,s,strlen(s));
    exit(1);
}

int netlink_test(void)
{
    struct sockaddr_nl nls;
    struct pollfd pfd;
    char buf[512];

    // Open hotplug event netlink socket
    memset(&nls,0,sizeof(struct sockaddr_nl));
    nls.nl_family = AF_NETLINK;
    nls.nl_pid = getpid();
    nls.nl_groups = -1; //<--add this so can receive from kernel broadcast
    pfd.events = POLLIN;
    pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if (pfd.fd==-1)
        die("Not root\n");
    // Listen to netlink socket
    if (bind(pfd.fd, (void *)&nls, sizeof(struct sockaddr_nl)))
        die("Bind failed\n");

    while (-1!=poll(&pfd, 1, -1)) {
        int i, len = recv(pfd.fd, buf, sizeof(buf), 0);
        if (len == -1) die("recv\n");
        // Print the data to stdout.
        i = 0;
        while (i<len) {
            printf("\033[33m%s\n\033[0m", buf+i);
            i += strlen(buf+i)+1;
        }
    }
    die("poll\n");
    // Dear gcc: shut up.
    return 0;
}

Ref

  1. How to receive Kernel uevents with Netlink socket?
  2. 检查内核反馈uevent消息,并提取出USB插入事件