android6.0 ueventd

ueventd的主要功能是接受uevent来创建和删除设备中的dev目录下的设备节点。uevent进程和init进程不是一个进程,但是他们的二进制文件时相同的,只不过通过启动参数不一样导致程序的执行流程不一样,ueventd在init.rc中定义开启。

ueventd的功能和linux的udev类似,都是监控uevent,创建删除设备节点。


一、初始化

下面我们先来看下ueventd的代码:

int ueventd_main(int argc, char **argv)
{
    umask(000);//不屏蔽任何动作
    signal(SIGCHLD, SIG_IGN);//忽略子进程挂掉

    open_devnull_stdio();//标准输入等重定向到空设备
    klog_init();
    klog_set_level(KLOG_NOTICE_LEVEL);

    NOTICE("ueventd started!\n");

    selinux_callback cb;
    cb.func_log = selinux_klog_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);

    char hardware[PROP_VALUE_MAX];
    property_get("ro.hardware", hardware);

    ueventd_parse_config_file("/ueventd.rc");//解析uevent相关配置文件,后续再介绍
    ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware).c_str());

    device_init();

    pollfd ufd;
    ufd.events = POLLIN;
    ufd.fd = get_device_fd();

    while (true) {
        ufd.revents = 0;
        int nr = poll(&ufd, 1, -1);
        if (nr <= 0) {
            continue;
        }
        if (ufd.revents & POLLIN) {
            handle_device_fd();//处理数据
        }
    }

    return 0;
}

open_devnull_stdio函数主要是将几个标准输入输出等重定向到空设备

void open_devnull_stdio(void)
{
    // Try to avoid the mknod() call if we can. Since SELinux makes
    // a /dev/null replacement available for free, let's use it.
    int fd = open("/sys/fs/selinux/null", O_RDWR);
    if (fd == -1) {
        // OOPS, /sys/fs/selinux/null isn't available, likely because
        // /sys/fs/selinux isn't mounted. Fall back to mknod.
        static const char *name = "/dev/__null__";
        if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
            fd = open(name, O_RDWR);
            unlink(name);
        }
        if (fd == -1) {
            exit(1);
        }
    }

    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);
    if (fd > 2) {
        close(fd);
    }
}
我们再来看看device_init函数,这个函数就是创建了一个netlink socket接受kernel的uevent事件,最后对这个socket循环调用poll函数,等待POLLIN事件,一有事件就调用handle_device_fd函数处理。

void device_init() {
    sehandle = NULL;
    if (is_selinux_enabled() > 0) {
        sehandle = selinux_android_file_context_handle();
        selinux_status_open(true);
    }

    /* is 256K enough? udev uses 16MB! */
    device_fd = uevent_open_socket(256*1024, true);//创建netlink socket,并且获取到fd
    if (device_fd == -1) {
        return;
    }
    fcntl(device_fd, F_SETFL, O_NONBLOCK);//设置非阻塞

    if (access(COLDBOOT_DONE, F_OK) == 0) {
        NOTICE("Skipping coldboot, already done!\n");
        return;
    }

    Timer t;
    coldboot("/sys/class");//对这几个目录下的uevent,让kernel重新通过netlink socket发送数据,ueventd处理
    coldboot("/sys/block");
    coldboot("/sys/devices");
    close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
    NOTICE("Coldboot took %.2fs.\n", t.duration());
}
uevent_open_socket函数就是创建一个netlink socket来接受kernel的uevent事件

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;//netlink协议
    addr.nl_pid = getpid();
    addr.nl_groups = 0xffffffff;

    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);//NETLINK_KOBJECT_UEVENT代表接受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;
}


二、启动后的uevent事件处理

Android在创建设备节点有两种,一种是进程启动时创建已存在的驱动设备节点,二是启动后热插拔的设备节点文件。

启动后的创建的热插拔设备节点文件需要调用coldboot函书,

static void coldboot(const char *path)
{
    DIR *d = opendir(path);
    if(d) {
        do_coldboot(d);
        closedir(d);
    }
}

然后调用do_coldboot函书,这个函数是一个递归函数

static void do_coldboot(DIR *d)
{
    struct dirent *de;
    int dfd, fd;

    dfd = dirfd(d);

    fd = openat(dfd, "uevent", O_WRONLY);//目录下是否有uevent文件
    if(fd >= 0) {
        write(fd, "add\n", 4);//写入add值后kernel会重新发送uevent事件到ueventd
        close(fd);
        handle_device_fd();//这个时候就需要处理uevent事件
    }

    while((de = readdir(d))) {
        DIR *d2;

        if(de->d_type != DT_DIR || de->d_name[0] == '.')
            continue;

        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);//遍历目录下的各个目录
        if(fd < 0)
            continue;

        d2 = fdopendir(fd);
        if(d2 == 0)
            close(fd);
        else {
            do_coldboot(d2);//递归调用,直到找到所有目录下uevent文件
            closedir(d2);
        }
    }
}

这里主要用了一个openat函数,这个函数介绍在这篇博客中点击打开链接。上面函数就是看sys/class sys/block sys/devices下所有的uevent事件,重新让kernel上报到ueventd处理。


三、处理uevent事件

下面我们再来看handle_device_fd函数处理uevent事件

#define UEVENT_MSG_LEN  2048
void handle_device_fd()
{
    char msg[UEVENT_MSG_LEN+2];
    int n;
    while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {//这个函数就不分析了就是从netlink socket拿数据
        if(n >= UEVENT_MSG_LEN)   /* overflow -- discard */
            continue;

        msg[n] = '\0';
        msg[n+1] = '\0';

        struct uevent uevent;
        parse_event(msg, &uevent);//把消息解析成uevent

        if (sehandle && selinux_status_updated() > 0) {
            struct selabel_handle *sehandle2;
            sehandle2 = selinux_android_file_context_handle();
            if (sehandle2) {
                selabel_close(sehandle);
                sehandle = sehandle2;
            }
        }

        handle_device_event(&uevent);//处理设备相关uevent
        handle_firmware_event(&uevent);//处理fireware相关uevent
    }
}

解析消息,就是把解析出来的内容封装在uevent中。

static void parse_event(const char *msg, struct uevent *uevent)
{
    uevent->action = "";
    uevent->path = "";
    uevent->subsystem = "";
    uevent->firmware = "";
    uevent->major = -1;
    uevent->minor = -1;
    uevent->partition_name = NULL;
    uevent->partition_num = -1;
    uevent->device_name = NULL;

        /* currently ignoring SEQNUM */
    while(*msg) {
        if(!strncmp(msg, "ACTION=", 7)) {
            msg += 7;
            uevent->action = msg;
        } else if(!strncmp(msg, "DEVPATH=", 8)) {
            msg += 8;
            uevent->path = msg;
        } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
            msg += 10;
            uevent->subsystem = msg;
        } else if(!strncmp(msg, "FIRMWARE=", 9)) {
            msg += 9;
            uevent->firmware = msg;
        } else if(!strncmp(msg, "MAJOR=", 6)) {
            msg += 6;
            uevent->major = atoi(msg);
        } else if(!strncmp(msg, "MINOR=", 6)) {
            msg += 6;
            uevent->minor = atoi(msg);
        } else if(!strncmp(msg, "PARTN=", 6)) {
            msg += 6;
            uevent->partition_num = atoi(msg);
        } else if(!strncmp(msg, "PARTNAME=", 9)) {
            msg += 9;
            uevent->partition_name = msg;
        } else if(!strncmp(msg, "DEVNAME=", 8)) {
            msg += 8;
            uevent->device_name = msg;
        }

        /* advance to after the next \0 */
        while(*msg++)
            ;
    }

    if (LOG_UEVENTS) {
        INFO("event { '%s', '%s', '%s', '%s', %d, %d }\n",
             uevent->action, uevent->path, uevent->subsystem,
             uevent->firmware, uevent->major, uevent->minor);
    }
}

我们在这里分析下device的uevent事件

static void handle_device_event(struct uevent *uevent)
{
    if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online"))
        fixup_sys_perms(uevent->path);//主要配置一些参数

    if (!strncmp(uevent->subsystem, "block", 5)) {//block
        handle_block_device_event(uevent);
    } else if (!strncmp(uevent->subsystem, "platform", 8)) {//platform
        handle_platform_device_event(uevent);
    } else {
        handle_generic_device_event(uevent);//其他input usb等
    }
}

我们先来看fixup_sys_perms函数,主要是从sys_perms得到的一些参数,然后设置sys下面的主要是调用chown chmod。而sys_perms这个列表又是从之前的uevent.rc配置文件解析出来的。

void fixup_sys_perms(const char *upath)
{
    char buf[512];
    struct listnode *node;
    struct perms_ *dp;

    /* upaths omit the "/sys" that paths in this list
     * contain, so we add 4 when comparing...
     */
    list_for_each(node, &sys_perms) {
        dp = &(node_to_item(node, struct perm_node, plist))->dp;
        if (dp->prefix) {
            if (strncmp(upath, dp->name + 4, strlen(dp->name + 4)))
                continue;
        } else if (dp->wildcard) {
            if (fnmatch(dp->name + 4, upath, FNM_PATHNAME) != 0)
                continue;
        } else {
            if (strcmp(upath, dp->name + 4))
                continue;
        }

        if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf))
            break;

        snprintf(buf, sizeof(buf), "/sys%s/%s", upath, dp->attr);
        INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm);
        chown(buf, dp->uid, dp->gid);
        chmod(buf, dp->perm);
    }

    // Now fixup SELinux file labels
    int len = snprintf(buf, sizeof(buf), "/sys%s", upath);
    if ((len < 0) || ((size_t) len >= sizeof(buf))) {
        // Overflow
        return;
    }
    if (access(buf, F_OK) == 0) {
        INFO("restorecon_recursive: %s\n", buf);
        restorecon_recursive(buf);
    }
}

我们来看处理block的函数

static void handle_block_device_event(struct uevent *uevent)
{
    const char *base = "/dev/block/";
    const char *name;
    char devpath[96];
    char **links = NULL;

    name = parse_device_name(uevent, 64);
    if (!name)
        return;

    snprintf(devpath, sizeof(devpath), "%s%s", base, name);
    make_dir(base, 0755);//创建目录

    if (!strncmp(uevent->path, "/devices/", 9))
        links = get_block_device_symlinks(uevent);

    handle_device(uevent->action, devpath, uevent->path, 1,
            uevent->major, uevent->minor, links);
}

再来看handle_device函数,这个函数主要调用了make_device

static void make_device(const char *path,
                        const char */*upath*/,
                        int block, int major, int minor,
                        const char **links)
{
    unsigned uid;
    unsigned gid;
    mode_t mode;
    dev_t dev;
    char *secontext = NULL;

    mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);//获取device参数

    if (sehandle) {
        selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
        setfscreatecon(secontext);
    }

    dev = makedev(major, minor);
    /* Temporarily change egid to avoid race condition setting the gid of the
     * device node. Unforunately changing the euid would prevent creation of
     * some device nodes, so the uid has to be set with chown() and is still
     * racy. Fixing the gid race at least fixed the issue with system_server
     * opening dynamic input devices under the AID_INPUT gid. */
    setegid(gid);
    mknod(path, mode, dev);//创建节点
    chown(path, uid, -1);
    setegid(AID_ROOT);

    if (secontext) {
        freecon(secontext);
        setfscreatecon(NULL);
    }
}

其中device参数也从解析uevent.rc获取的,因此一些存储设备的device会在dev/block下面创建,我们可以对比手机插上sd卡和没有插sd卡的dev/block目录

有sd卡:

bootdevice
loop0
loop1
loop2
loop3
loop4
loop5
loop6
loop7
mmcblk0
mmcblk0boot0
mmcblk0boot1
mmcblk0p1
mmcblk0p10
mmcblk0p11
mmcblk0p12
mmcblk0p13
mmcblk0p14
mmcblk0p15
mmcblk0p16
mmcblk0p17
mmcblk0p18
mmcblk0p19
mmcblk0p2
mmcblk0p20
mmcblk0p3
mmcblk0p4
mmcblk0p5
mmcblk0p6
mmcblk0p7
mmcblk0p8
mmcblk0p9
mmcblk0rpmb
mmcblk1
mmcblk1p1
platform
vold
zram0

没有sd卡

loop0
loop1
loop2
loop3
loop4
loop5
loop6
loop7
mmcblk0
mmcblk0boot0
mmcblk0boot1
mmcblk0p1
mmcblk0p10
mmcblk0p11
mmcblk0p12
mmcblk0p13
mmcblk0p14
mmcblk0p15
mmcblk0p16
mmcblk0p17
mmcblk0p18
mmcblk0p19
mmcblk0p2
mmcblk0p20
mmcblk0p3
mmcblk0p4
mmcblk0p5
mmcblk0p6
mmcblk0p7
mmcblk0p8
mmcblk0p9
mmcblk0rpmb
platform
vold
zram0

对比我们发现新增了mmcblk1 mmcblk1p1,最后我们把mmcblk1p1这个device mount到一个目录,也能挂载sd卡。

我们再来看下处理其他的uevent事件

static void handle_generic_device_event(struct uevent *uevent)
{
    const char *base;
    const char *name;
    char devpath[DEVPATH_LEN] = {0};
    char **links = NULL;

    name = parse_device_name(uevent, 64);
    if (!name)
        return;

    struct ueventd_subsystem *subsystem =
            ueventd_subsystem_find_by_name(uevent->subsystem);//这个也是之前解析uevent.rc

    if (subsystem) {
        const char *devname;

        switch (subsystem->devname_src) {
        case DEVNAME_UEVENT_DEVNAME:
            devname = uevent->device_name;
            break;

        case DEVNAME_UEVENT_DEVPATH:
            devname = name;
            break;

        default:
            ERROR("%s subsystem's devpath option is not set; ignoring event\n",
                    uevent->subsystem);
            return;
        }

        if (!assemble_devpath(devpath, subsystem->dirname, devname))
            return;
        mkdir_recursive_for_devpath(devpath);
    } else if (!strncmp(uevent->subsystem, "usb", 3)) {
         if (!strcmp(uevent->subsystem, "usb")) {
            if (uevent->device_name) {
                if (!assemble_devpath(devpath, "/dev", uevent->device_name))
                    return;
                mkdir_recursive_for_devpath(devpath);
             }
             else {
                 /* This imitates the file system that would be created
                  * if we were using devfs instead.
                  * Minors are broken up into groups of 128, starting at "001"
                  */
                 int bus_id = uevent->minor / 128 + 1;
                 int device_id = uevent->minor % 128 + 1;
                 /* build directories */
                 make_dir("/dev/bus", 0755);
                 make_dir("/dev/bus/usb", 0755);
                 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
                 make_dir(devpath, 0755);
                 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
             }
         } else {
             /* ignore other USB events */
             return;
         }
     } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
         base = "/dev/graphics/";
         make_dir(base, 0755);
     } else if (!strncmp(uevent->subsystem, "drm", 3)) {
         base = "/dev/dri/";
         make_dir(base, 0755);
     } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
         base = "/dev/oncrpc/";
         make_dir(base, 0755);
     } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
         base = "/dev/adsp/";
         make_dir(base, 0755);
     } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
         base = "/dev/msm_camera/";
         make_dir(base, 0755);
     } else if(!strncmp(uevent->subsystem, "input", 5)) {
         base = "/dev/input/";
         make_dir(base, 0755);
     } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
         base = "/dev/mtd/";
         make_dir(base, 0755);
     } else if(!strncmp(uevent->subsystem, "sound", 5)) {
         base = "/dev/snd/";
         make_dir(base, 0755);
     } else if(!strncmp(uevent->subsystem, "misc", 4) &&
                 !strncmp(name, "log_", 4)) {
         INFO("kernel logger is deprecated\n");
         base = "/dev/log/";
         make_dir(base, 0755);
         name += 4;
     } else
         base = "/dev/";
     links = get_character_device_symlinks(uevent);

     if (!devpath[0])
         snprintf(devpath, sizeof(devpath), "%s%s", base, name);

     handle_device(uevent->action, devpath, uevent->path, 0,
             uevent->major, uevent->minor, links);
}

同样各个不同的event都是先创建目录,然后调用handle_device来mknod device。


三、解析uevent.rc


我们再来看下解析uevent.rc uevent.hareware.rc这个hardware是个属性

int ueventd_parse_config_file(const char *fn)
{
    std::string data;
    if (!read_file(fn, &data)) {
        return -1;
    }

    data.push_back('\n'); // TODO: fix parse_config.
    parse_config(fn, data);
    dump_parser_state();
    return 0;
}

主要是通过parse_config函数来解析uevent.rc配置文件的

static void parse_config(const char *fn, const std::string& data)
{
    char *args[UEVENTD_PARSER_MAXARGS];

    int nargs = 0;
    parse_state state;
    state.filename = fn;
    state.line = 1;
    state.ptr = strdup(data.c_str());  // TODO: fix this code!
    state.nexttoken = 0;
    state.parse_line = parse_line_no_op;
    for (;;) {
        int token = next_token(&state);
        switch (token) {
        case T_EOF:
            parse_line(&state, args, nargs);
            return;
        case T_NEWLINE:
            if (nargs) {
                parse_line(&state, args, nargs);
                nargs = 0;
            }
            state.line++;
            break;
        case T_TEXT:
            if (nargs < UEVENTD_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
    }
}
最后在parse_line分别解析了uevent.rc中,把最后解析出来的数据放在sys_perms dev_perms subsystem_list



你可能感兴趣的:(Android,Framework)