如何使用C程序捕获kobject_uevent?

时间:2018-02-13 12:06:59

标签: events linux-kernel

我已在目标中为gps连接创建了/ dev / ttyACM0端口。由于某种原因,当目标运行时,此端口正在断开连接。每当断开连接时,我都会得到kobject-uevent,而不是我正在使用netlinl示例程序捕获此事件,如下所示。

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <linux/netlink.h>
    #include <unistd.h>
    #include <errno.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/time.h>
    #include <sys/socket.h>
    #include <linux/netlink.h>

#define NETLINK_TEST    17
#define MYGROUP         1

#define MAX_PAYLOAD 1024  /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;

int main()
{
    int ret, fd;
    char *temp;

    fd = open("/dev/ttyACM1", O_RDWR);
    if (fd < 0) {
        printf("Unable to open port\n");
        return -1;
    }
    printf("Creating socket\n");
    sock_fd=socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if(sock_fd<0) {
        printf("Socket creating failed\n");
        return -1;
    }

    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid();  /* self pid */
    src_addr.nl_groups = MYGROUP;
    /* interested in group 1<<0 */
    ret = bind(sock_fd, (struct sockaddr*)&src_addr,
            sizeof(src_addr));
    if (ret < 0) {
        printf("Bind Failed\n");
        perror("bind:");
        return -1;
    }

    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;   /* For Linux Kernel */
    dest_addr.nl_groups = 0; /* unicast */

memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0;   /* For Linux Kernel */
dest_addr.nl_groups = 0; /* unicast */

    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    nlh->nlmsg_flags = 0;
    iov.iov_base = (void *)nlh;
    iov.iov_len = nlh->nlmsg_len;
    msg.msg_name = (void *)&dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    printf("Waiting for message from kernel\n");
    /* Read message from kernel */
    while (1) {
        recvmsg(sock_fd, &msg, 0);
        temp = strstr(NLMSG_DATA(nlh), "tty/ttyACM1");
        printf("Received message payload: %s\n", temp);
    }
    close(sock_fd);
    return 0;
}

使用这个程序我的输出低于输出。

Received message payload: 0000:00/0000:00:15.0/usb1/1-4/1-4:1.0/tty/ttyACM1

这是发生断开连接的设备的路径。但是,当我检查/lib/kobject_uevent.c代码时,我看到它正在发送带有设备路径的 action_string ,在我的情况下 remove

如果我想捕获此action_string我该如何捕获它?

1 个答案:

答案 0 :(得分:1)

嗨由于Prabhakar在评论中的一些研究和建议,我开始知道我们必须使用libudev.so库来获得用户空间中的正确uevent。

但是出于测试目的,我浏览了libudev源代码,发现libudev也使用netlink捕获uevent。只有libudev代码的不同之处在于它使用的是union {},其中包含&#39; struct nlmsghdr&#39;和&#39; char buf [SIZE]&#39;。因此,我们可以使用buf打印nlmsghdr结构中的数据。

因此,作为一个黑客,我使用了联合而不是&#34; struct nlmsghdr * nlh&#34;并在该联合中创建了一个原始缓冲区以及此结构,如下所示

union {
    struct nlmsghdr nlh;
    char raw[8192];
} buf;

此更改后的完整工作程序如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <linux/netlink.h>

#define NETLINK_TEST    17
#define MYGROUP         1

#define MAX_PAYLOAD 1024  /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
union {
    struct nlmsghdr nlh;
    char raw[8192];
} buf;
struct iovec iov;
int sock_fd;
struct msghdr msg;

int main() 
{
    int ret, fd, i;
    char *event_buf;
    char action[50];
#if 1
    fd = open("/dev/ttyACM1", O_RDWR);
    if (fd < 0) {
        printf("Unable to open port\n");
        return -1;
    }
#endif
    printf("Creating socket\n");
    sock_fd=socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if(sock_fd<0) {
        printf("Socket creating failed\n");
        return -1;
    }

    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid();  /* self pid */
    src_addr.nl_groups = MYGROUP;
    /* interested in group 1<<0 */
    ret = bind(sock_fd, (struct sockaddr*)&src_addr,
            sizeof(src_addr));
    if (ret < 0) {
        printf("Bind Failed\n");
        perror("bind:");
        return -1;
    }

    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;   /* For Linux Kernel */
    dest_addr.nl_groups = 0; /* unicast */

    memset(&buf, 0, sizeof(buf));
    iov.iov_base = &buf;
    iov.iov_len = sizeof(buf);
    msg.msg_name = (void *)&dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
#if 1
    /* Read message from kernel */
    while (1) {
        recvmsg(sock_fd, &msg, 0);
        if (strstr(buf.raw, "/tty/ttyACM1") != NULL) {
            event_buf = buf.raw;
            for (i = 0; event_buf[i] != '@'; i++)
                action[i] = event_buf[i];
            action[i] = '\0';
            printf("ACTION = %s\n", action);
            if ((strcmp(action, "remove")) == 0) {
                close(fd);
                printf("ACM port closed\n");
            }
        }
    }
end:
    close(sock_fd);
#endif
    return 0;
}

在buf.raw中,我正在使用action_string获取uevent消息。例如:

remove@/devices/pci0000:00/0000:00:15.0/usb1/1-3/1-3:1.0/tty/ttyACM2

其中&#34;删除&#34;是&#39; @&#39;之后的动作字符串和其余字符串是该事件的设备路径。