Linux kobject_uevent_env通知应用层事件方法

文章讲述了Linux内核中kobject的创建、初始化和添加过程,以及如何使用kobject_uevent_env上报自定义UEVENT消息。应用层通过打开网络套接字监听并处理UEVENT消息,示例代码展示了接收和解析UEVENT消息的方法。内核向应用层发送的消息包括ACTION、DEVPATH、SUBSYSTEM等环境变量,应用层接收到的信息如ACTION=online,DEVPATH=/xxx/xxx_notify,USBEVENT=xxx_notify等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux内核层:

kset_create_and_add //内核创建和注册kset

kobject_init //kobject初始化

kobject_add //初始化完成的 kobject 添加到内核中

kobject_uevent_env(obj, action, env) //上报给应用层

kobject_uevent默认会发送”ACTION=xxx”,”DEVPATH=xxx”,”SUBSYSTEM=xxx”这三个uevent环境变量,其中env可用如下方法赋值。

 char*env[] = {NULL,NULL};
 strcpy(buf,"USBEVENT=xxx_notify");
 env[0] = buf;
  

其中action为enum kobject_action中的枚举值,在内核kobject.h中有定义

enum kobject_action {
        KOBJ_ADD,
        KOBJ_REMOVE,
        KOBJ_CHANGE,
        KOBJ_MOVE,
        KOBJ_ONLINE,
        KOBJ_OFFLINE,
        KOBJ_BIND,
        KOBJ_UNBIND,
};

应用层接收uevent消息代码示例:

#include <stdio.h>

#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#include <linux/netlink.h>

#define UEVENT_MSG_LEN  1024
int uevent_open_socket(int buf_sz, bool passcred)
{
    struct sockaddr_nl addr;
    int on = passcred;
    int s;

    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid();
    addr.nl_groups = 0xffffffff;

    s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if(s < 0)
        return -1;

    setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz));
    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));

    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
    {
        close(s);
        return -1;
    }

    return s;
}


ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer,
        size_t length, uid_t *user)
{
    struct iovec iov = { buffer, length };
    struct sockaddr_nl addr;
        int len = sizeof(struct ucred);
    char control[CMSG_SPACE(len)];
    struct msghdr hdr =
    {
        &addr,
        sizeof(addr),
        &iov,
        1,
        control,
        sizeof(control),
        0,
    };

    *user = -1;
    ssize_t n = recvmsg(socket, &hdr, 0);
    if (n <= 0)
    {
        return n;
    }

    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
    if (cmsg == NULL || cmsg->cmsg_type != 0x02) //SCM_CREDENTIALS = 0x02 //03_14
    {
        /* ignoring netlink message with no sender credentials */
        goto out;
    }

    struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
    *user = cred->uid;
    if (cred->uid != 0)
    {
        /* ignoring netlink message from non-root user */
        goto out;
    }

    if (addr.nl_groups == 0 || addr.nl_pid != 0)
    {
        /* ignoring non-kernel or unicast netlink message */
        goto out;
    }

    return n;

out:
    /* clear residual potentially malicious data */
    bzero(buffer, length);
    errno = EIO;
    return -1;
}

ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length)
{
    uid_t user = -1;
    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &user);
}

int main(int argc, char *argv[])
{
    int device_fd;
    char msg[UEVENT_MSG_LEN+2];
    int n;
    int i;

    device_fd = uevent_open_socket(64*1024, true);
    if(device_fd < 0)
        return -1;

    while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
        msg[n] = '\0';
        msg[n+1] = '\0';

        for (i = 0; i < n; i++)
            if (msg[i] == '\0')
                msg[i] = ' ';

        printf("%s\n", msg);
    }

    return 0;
}
 

编译成可执行应用程序后,运行,然后Linux底层驱动进行事件上报:

应用层收到信息如下:

online@/xxx/xxx_notify ACTION=online DEVPATH=/xxx/xxx_notify SUBSYSTEM=xxx USBEVENT=xxx_notify SEQNUM=633

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a2591748032-随心所记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值