在Android Init进程源码分析中讲到init进程会依次执行被加入到待执行队列action_queue中的Action,在init.rc中我们有这么一段配置:
- 11 on early-init
- 12 # Set init and its forked children's oom_adj.
- 13 write /proc/1/oom_adj -16
- 14
- 15 start ueventd
- 16
- 17 # create mountpoints
- 18 mkdir /mnt 0775 root system
11 on early-init
12 # Set init and its forked children's oom_adj.
13 write /proc/1/oom_adj -16
14
15 start ueventd
16
17 # create mountpoints
18 mkdir /mnt 0775 root system
对于early-init 这个section 在
Android Init进程源码分析一文中已经介绍了在解析完init.rc文件后被添加到了action_queue队列
- action_for_each_trigger("early-init", action_add_queue_tail);
action_for_each_trigger("early-init", action_add_queue_tail);
因此Init进程会首先启动ueventd进程,关键字start用于启动一个服务进程,该关键字对应的执行函数为:
- KEYWORD(start, COMMAND, 1, do_start)
KEYWORD(start, COMMAND, 1, do_start)
在解析命令行时,被添加到Action的commands链表中:
- cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
- cmd->func = kw_func(kw);
- cmd->nargs = nargs;
- memcpy(cmd->args, args, sizeof(char*) * nargs);
- list_add_tail(&act->commands, &cmd->clist);
cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
cmd->func = kw_func(kw);
cmd->nargs = nargs;
memcpy(cmd->args, args, sizeof(char*) * nargs);
list_add_tail(&act->commands, &cmd->clist);
ueventd进程的执行代码和Init进程的执行代码被编译到了同一个可执行程序Init中了,通过查看Android.mk文件即可以得到证实:
- LOCAL_SRC_FILES:= \
- builtins.c \
- init.c \
- devices.c \
- property_service.c \
- util.c \
- parser.c \
- logo.c \
- keychords.c \
- signal_handler.c \
- init_parser.c \
- ueventd.c \
- ueventd_parser.c
- ifeq ($(strip $(INIT_BOOTCHART)),true)
- LOCAL_SRC_FILES += bootchart.c
- LOCAL_CFLAGS += -DBOOTCHART=1
- endif
- ifeq ($(BOARD_HAVE_BLUETOOTH_BCM),true)
- LOCAL_CFLAGS += \
- -DBOARD_HAVE_BLUETOOTH_BCM
- endif
- ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
- LOCAL_CFLAGS += -DALLOW_LOCAL_PROP_OVERRIDE=1
- endif
- LOCAL_MODULE:= init
- LOCAL_FORCE_STATIC_EXECUTABLE := true
- LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
- LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
- LOCAL_STATIC_LIBRARIES := libfs_mgr libcutils libc
- ifeq ($(HAVE_SELINUX),true)
- LOCAL_STATIC_LIBRARIES += libselinux
- LOCAL_C_INCLUDES += external/libselinux/include
- LOCAL_CFLAGS += -DHAVE_SELINUX
- endif
- include $(BUILD_EXECUTABLE)
- # Make a symlink from /sbin/ueventd to /init
- SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd
- $(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE)
- $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
- @echo "Symlink: $@ -> ../$(INIT_BINARY)"
- @mkdir -p $(dir $@)
- @rm -rf $@
- $(hide) ln -sf ../$(INIT_BINARY) $@
- ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
LOCAL_SRC_FILES:= \
builtins.c \
init.c \
devices.c \
property_service.c \
util.c \
parser.c \
logo.c \
keychords.c \
signal_handler.c \
init_parser.c \
ueventd.c \
ueventd_parser.c
ifeq ($(strip $(INIT_BOOTCHART)),true)
LOCAL_SRC_FILES += bootchart.c
LOCAL_CFLAGS += -DBOOTCHART=1
endif
ifeq ($(BOARD_HAVE_BLUETOOTH_BCM),true)
LOCAL_CFLAGS += \
-DBOARD_HAVE_BLUETOOTH_BCM
endif
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
LOCAL_CFLAGS += -DALLOW_LOCAL_PROP_OVERRIDE=1
endif
LOCAL_MODULE:= init
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
LOCAL_STATIC_LIBRARIES := libfs_mgr libcutils libc
ifeq ($(HAVE_SELINUX),true)
LOCAL_STATIC_LIBRARIES += libselinux
LOCAL_C_INCLUDES += external/libselinux/include
LOCAL_CFLAGS += -DHAVE_SELINUX
endif
include $(BUILD_EXECUTABLE)
# Make a symlink from /sbin/ueventd to /init
SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd
$(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE)
$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
@echo "Symlink: $@ -> ../$(INIT_BINARY)"
@mkdir -p $(dir $@)
@rm -rf $@
$(hide) ln -sf ../$(INIT_BINARY) $@
ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)
uevent以软链接的方式链接到了Init可执行程序,通过查看手机中的uevent程序可知:
因为ueventd 和 Init 在同一个可执行文件下,因此在启动ueventd进程时,进程入口函数依然是system\core\init\init.c文件中的main函数:
- int main(int argc, char **argv)
- {
- int fd_count = 0;
- struct pollfd ufds[4];
- char *tmpdev;
- char* debuggable;
- char tmp[32];
- int property_set_fd_init = 0;
- int signal_fd_init = 0;
- int keychord_fd_init = 0;
- bool is_charger = false;
- if (!strcmp(basename(argv[0]), "ueventd"))
- return ueventd_main(argc, argv);
- .....
- }
int main(int argc, char **argv)
{
int fd_count = 0;
struct pollfd ufds[4];
char *tmpdev;
char* debuggable;
char tmp[32];
int property_set_fd_init = 0;
int signal_fd_init = 0;
int keychord_fd_init = 0;
bool is_charger = false;
if (!strcmp(basename(argv[0]), "ueventd"))
return ueventd_main(argc, argv);
.....
}
basename 函数从启动的程序路径下截取应用名称,如果启动的应用为ueventd进程,则跳转到ueventd_main函数作为应用的入口函数:
- int ueventd_main(int argc, char **argv)
- {
- struct pollfd ufd;
- int nr;
- char tmp[32];
- /*
- * init sets the umask to 077 for forked processes. We need to
- * create files with exact permissions, without modification by
- * the umask.
- */
- umask(000);
- /* Prevent fire-and-forget children from becoming zombies.
- * If we should need to wait() for some children in the future
- * (as opposed to none right now), double-forking here instead
- * of ignoring SIGCHLD may be the better solution.
- */
- signal(SIGCHLD, SIG_IGN);
- open_devnull_stdio();
- klog_init();
- INFO("starting ueventd\n");
- /* Respect hardware passed in through the kernel cmd line. Here we will look
- * for androidboot.hardware param in kernel cmdline, and save its value in
- * hardware[]. */
- import_kernel_cmdline(0, import_kernel_nv);
- get_hardware_name(hardware, &revision);
- //解析ueventd.rc配置文件
- ueventd_parse_config_file("/ueventd.rc");
- //解析ueventd.xxx.rc 配置文件
- snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);
- ueventd_parse_config_file(tmp);
- //设备节点初始化
- device_init();
- ufd.events = POLLIN;
- ufd.fd = get_device_fd();
- //进入闭环监控模式
- while(1) {
- ufd.revents = 0;
- //监控uevent socket的连接,参数为-1表示无限超时,在没有事件发生时函数阻塞监控
- nr = poll(&ufd, 1, -1);
- if (nr <= 0)
- continue;
- if (ufd.revents == POLLIN)
- //设备事件处理
- handle_device_fd();
- }
- }
int ueventd_main(int argc, char **argv)
{
struct pollfd ufd;
int nr;
char tmp[32];
/*
* init sets the umask to 077 for forked processes. We need to
* create files with exact permissions, without modification by
* the umask.
*/
umask(000);
/* Prevent fire-and-forget children from becoming zombies.
* If we should need to wait() for some children in the future
* (as opposed to none right now), double-forking here instead
* of ignoring SIGCHLD may be the better solution.
*/
signal(SIGCHLD, SIG_IGN);
open_devnull_stdio();
klog_init();
INFO("starting ueventd\n");
/* Respect hardware passed in through the kernel cmd line. Here we will look
* for androidboot.hardware param in kernel cmdline, and save its value in
* hardware[]. */
import_kernel_cmdline(0, import_kernel_nv);
get_hardware_name(hardware, &revision);
//解析ueventd.rc配置文件
ueventd_parse_config_file("/ueventd.rc");
//解析ueventd.xxx.rc 配置文件
snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);
ueventd_parse_config_file(tmp);
//设备节点初始化
device_init();
ufd.events = POLLIN;
ufd.fd = get_device_fd();
//进入闭环监控模式
while(1) {
ufd.revents = 0;
//监控uevent socket的连接,参数为-1表示无限超时,在没有事件发生时函数阻塞监控
nr = poll(&ufd, 1, -1);
if (nr <= 0)
continue;
if (ufd.revents == POLLIN)
//设备事件处理
handle_device_fd();
}
}
该函数前面几个步骤和init进程基本相同,这里就不在详细介绍了,可参考
Android Init进程源码分析,在初始化完标准输入输出、log、导入命令行参数后,将解析uevent.rc文件和一个同硬件特定硬件相关的ueventd.XXX.rc文件,ueventd.rc文件内容如下所示:
- /dev/null 0666 root root
- /dev/zero 0666 root root
- /dev/full 0666 root root
- /dev/ptmx 0666 root root
- /dev/tty 0666 root root
- /dev/random 0666 root root
- /dev/urandom 0666 root root
- /dev/ashmem 0666 root root
- /dev/binder 0666 root root
- # Anyone can read the logs, but if they're not in the "logs"
- # group, then they'll only see log entries for their UID.
- /dev/log/* 0666 root log
- # the msm hw3d client device node is world writable/readable.
- /dev/msm_hw3dc 0666 root root
- # gpu driver for adreno200 is globally accessible
- /dev/kgsl 0666 root root
- # these should not be world writable
- /dev/diag 0660 radio radio
- /dev/diag_arm9 0660 radio radio
- /dev/android_adb 0660 adb adb
- /dev/android_adb_enable 0660 adb adb
- /dev/ttyMSM0 0600 bluetooth bluetooth
- /dev/uinput 0660 system bluetooth
/dev/null 0666 root root
/dev/zero 0666 root root
/dev/full 0666 root root
/dev/ptmx 0666 root root
/dev/tty 0666 root root
/dev/random 0666 root root
/dev/urandom 0666 root root
/dev/ashmem 0666 root root
/dev/binder 0666 root root
# Anyone can read the logs, but if they're not in the "logs"
# group, then they'll only see log entries for their UID.
/dev/log/* 0666 root log
# the msm hw3d client device node is world writable/readable.
/dev/msm_hw3dc 0666 root root
# gpu driver for adreno200 is globally accessible
/dev/kgsl 0666 root root
# these should not be world writable
/dev/diag 0660 radio radio
/dev/diag_arm9 0660 radio radio
/dev/android_adb 0660 adb adb
/dev/android_adb_enable 0660 adb adb
/dev/ttyMSM0 0600 bluetooth bluetooth
/dev/uinput 0660 system bluetooth
在system\core\init\ueventd.c文件中,使用ueventd_parse_config_file函数来对ueventd.rc进行解析,接下来详细分析整个解析过程:
- int ueventd_parse_config_file(const char *fn)
- {
- char *data;
- //读取文件内容
- data = read_file(fn, 0);
- if (!data) return -1;
- //解析整个rc文件内容
- parse_config(fn, data);
- DUMP();
- return 0;
- }
int ueventd_parse_config_file(const char *fn)
{
char *data;
//读取文件内容
data = read_file(fn, 0);
if (!data) return -1;
//解析整个rc文件内容
parse_config(fn, data);
DUMP();
return 0;
}
以上步骤和Init进程解析init.rc文件的步骤相同,不过这里调用的parse_config函数不同,该函数是专门用于解析ueventd.rc文件的,具体解析过程如下:
- static void parse_config(const char *fn, char *s)
- {
- struct parse_state state;
- char *args[UEVENTD_PARSER_MAXARGS];
- int nargs;
- nargs = 0;
- state.filename = fn; //设置解析文件的路径
- state.line = 1;
- state.ptr = s;//文件内容
- state.nexttoken = 0;
- state.parse_line = parse_line_device; //设置每行解析回调函数
- for (;;) {
- //从文件内容中查找token,与init.rc文件类似
- int token = next_token(&state);
- switch (token) {
- //文件结束
- case T_EOF:
- state.parse_line(&state, 0, 0);
- return;
- //新的一行
- case T_NEWLINE:
- if (nargs) {
- //调用行解析函数解析每一行
- state.parse_line(&state, nargs, args);
- nargs = 0;
- }
- break;
- case T_TEXT:
- if (nargs < UEVENTD_PARSER_MAXARGS) {
- args[nargs++] = state.text;
- }
- break;
- }
- }
- }
static void parse_config(const char *fn, char *s)
{
struct parse_state state;
char *args[UEVENTD_PARSER_MAXARGS];
int nargs;
nargs = 0;
state.filename = fn; //设置解析文件的路径
state.line = 1;
state.ptr = s;//文件内容
state.nexttoken = 0;
state.parse_line = parse_line_device; //设置每行解析回调函数
for (;;) {
//从文件内容中查找token,与init.rc文件类似
int token = next_token(&state);
switch (token) {
//文件结束
case T_EOF:
state.parse_line(&state, 0, 0);
return;
//新的一行
case T_NEWLINE:
if (nargs) {
//调用行解析函数解析每一行
state.parse_line(&state, nargs, args);
nargs = 0;
}
break;
case T_TEXT:
if (nargs < UEVENTD_PARSER_MAXARGS) {
args[nargs++] = state.text;
}
break;
}
}
}
函数首先查找指定的token,然后对不同的token做不同的处理,对于发现新行时,调用parse_line_device函数对每一行进行详细解析,该函数实现如下:
- static void parse_line_device(struct parse_state* state, int nargs, char **args)
- {
- set_device_permission(nargs, args);
- }
static void parse_line_device(struct parse_state* state, int nargs, char **args)
{
set_device_permission(nargs, args);
}
函数直接调用set_device_permission来实现,ueventd.rc文件每一行的书写规则为:
非sysfs 设备文件:
|name| |permission| |user| |group|
/dev/cam 0660 root ca
sysfs 设备文件属性:
/sys/devices/virtual/input/input* enable 0660 root input
- void set_device_permission(int nargs, char **args)
- {
- char *name;
- char *attr = 0;
- mode_t perm;
- uid_t uid;
- gid_t gid;
- int prefix = 0;
- char *endptr;
- int ret;
- char *tmp = 0;
- if (nargs == 0)
- return;
- if (args[0][0] == '#')
- return;
- /* |name| |permission| |user| |group| */
- name = args[0];
- if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
- INFO("/sys/ rule %s %s\n",args[0],args[1]);
- attr = args[1];
- args++;
- nargs--;
- }
- //参数检查
- if (nargs != 4) {
- ERROR("invalid line ueventd.rc line for '%s'\n", args[0]);
- return;
- }
- /* If path starts with mtd@ lookup the mount number. */
- if (!strncmp(name, "mtd@", 4)) {
- int n = mtd_name_to_number(name + 4);
- if (n >= 0)
- asprintf(&tmp, "/dev/mtd/mtd%d", n);
- name = tmp;
- } else {
- int len = strlen(name);
- if (name[len - 1] == '*') {
- prefix = 1;
- name[len - 1] = '\0';
- }
- }
- //权限检查
- perm = strtol(args[1], &endptr, 8);
- if (!endptr || *endptr != '\0') {
- ERROR("invalid mode '%s'\n", args[1]);
- free(tmp);
- return;
- }
- //从android_ids数组中查找uid
- ret = get_android_id(args[2]);
- if (ret < 0) {
- ERROR("invalid uid '%s'\n", args[2]);
- free(tmp);
- return;
- }
- uid = ret;
- //从android_ids数组中查找gid
- ret = get_android_id(args[3]);
- if (ret < 0) {
- ERROR("invalid gid '%s'\n", args[3]);
- free(tmp);
- return;
- }
- gid = ret;
- //为设备文件添加权限
- add_dev_perms(name, attr, perm, uid, gid, prefix);
- free(tmp);
- }
void set_device_permission(int nargs, char **args)
{
char *name;
char *attr = 0;
mode_t perm;
uid_t uid;
gid_t gid;
int prefix = 0;
char *endptr;
int ret;
char *tmp = 0;
if (nargs == 0)
return;
if (args[0][0] == '#')
return;
/* |name| |permission| |user| |group| */
name = args[0];
if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
INFO("/sys/ rule %s %s\n",args[0],args[1]);
attr = args[1];
args++;
nargs--;
}
//参数检查
if (nargs != 4) {
ERROR("invalid line ueventd.rc line for '%s'\n", args[0]);
return;
}
/* If path starts with mtd@ lookup the mount number. */
if (!strncmp(name, "mtd@", 4)) {
int n = mtd_name_to_number(name + 4);
if (n >= 0)
asprintf(&tmp, "/dev/mtd/mtd%d", n);
name = tmp;
} else {
int len = strlen(name);
if (name[len - 1] == '*') {
prefix = 1;
name[len - 1] = '\0';
}
}
//权限检查
perm = strtol(args[1], &endptr, 8);
if (!endptr || *endptr != '\0') {
ERROR("invalid mode '%s'\n", args[1]);
free(tmp);
return;
}
//从android_ids数组中查找uid
ret = get_android_id(args[2]);
if (ret < 0) {
ERROR("invalid uid '%s'\n", args[2]);
free(tmp);
return;
}
uid = ret;
//从android_ids数组中查找gid
ret = get_android_id(args[3]);
if (ret < 0) {
ERROR("invalid gid '%s'\n", args[3]);
free(tmp);
return;
}
gid = ret;
//为设备文件添加权限
add_dev_perms(name, attr, perm, uid, gid, prefix);
free(tmp);
}
首先检查参数的合法性,并根据参数查找uid、gid,对不同的用户和组的uid、gid已经事先配置在数组android_ids中了,如下:
- static const struct android_id_info android_ids[] = {
- { "root", AID_ROOT, },
- { "system", AID_SYSTEM, },
- { "radio", AID_RADIO, },
- { "bluetooth", AID_BLUETOOTH, },
- { "graphics", AID_GRAPHICS, },
- { "input", AID_INPUT, },
- { "audio", AID_AUDIO, },
- { "camera", AID_CAMERA, },
- { "log", AID_LOG, },
- { "compass", AID_COMPASS, },
- { "mount", AID_MOUNT, },
- { "wifi", AID_WIFI, },
- { "dhcp", AID_DHCP, },
- { "adb", AID_ADB, },
- { "install", AID_INSTALL, },
- { "media", AID_MEDIA, },
- { "drm", AID_DRM, },
- { "mdnsr", AID_MDNSR, },
- { "nfc", AID_NFC, },
- { "drmrpc", AID_DRMRPC, },
- { "shell", AID_SHELL, },
- { "cache", AID_CACHE, },
- { "diag", AID_DIAG, },
- { "net_bt_admin", AID_NET_BT_ADMIN, },
- { "net_bt", AID_NET_BT, },
- { "sdcard_r", AID_SDCARD_R, },
- { "sdcard_rw", AID_SDCARD_RW, },
- { "media_rw", AID_MEDIA_RW, },
- { "vpn", AID_VPN, },
- { "keystore", AID_KEYSTORE, },
- { "usb", AID_USB, },
- { "mtp", AID_MTP, },
- { "gps", AID_GPS, },
- { "inet", AID_INET, },
- { "net_raw", AID_NET_RAW, },
- { "net_admin", AID_NET_ADMIN, },
- { "net_bw_stats", AID_NET_BW_STATS, },
- { "net_bw_acct", AID_NET_BW_ACCT, },
- { "misc", AID_MISC, },
- { "nobody", AID_NOBODY, },
- };
static const struct android_id_info android_ids[] = {
{ "root", AID_ROOT, },
{ "system", AID_SYSTEM, },
{ "radio", AID_RADIO, },
{ "bluetooth", AID_BLUETOOTH, },
{ "graphics", AID_GRAPHICS, },
{ "input", AID_INPUT, },
{ "audio", AID_AUDIO, },
{ "camera", AID_CAMERA, },
{ "log", AID_LOG, },
{ "compass", AID_COMPASS, },
{ "mount", AID_MOUNT, },
{ "wifi", AID_WIFI, },
{ "dhcp", AID_DHCP, },
{ "adb", AID_ADB, },
{ "install", AID_INSTALL, },
{ "media", AID_MEDIA, },
{ "drm", AID_DRM, },
{ "mdnsr", AID_MDNSR, },
{ "nfc", AID_NFC, },
{ "drmrpc", AID_DRMRPC, },
{ "shell", AID_SHELL, },
{ "cache", AID_CACHE, },
{ "diag", AID_DIAG, },
{ "net_bt_admin", AID_NET_BT_ADMIN, },
{ "net_bt", AID_NET_BT, },
{ "sdcard_r", AID_SDCARD_R, },
{ "sdcard_rw", AID_SDCARD_RW, },
{ "media_rw", AID_MEDIA_RW, },
{ "vpn", AID_VPN, },
{ "keystore", AID_KEYSTORE, },
{ "usb", AID_USB, },
{ "mtp", AID_MTP, },
{ "gps", AID_GPS, },
{ "inet", AID_INET, },
{ "net_raw", AID_NET_RAW, },
{ "net_admin", AID_NET_ADMIN, },
{ "net_bw_stats", AID_NET_BW_STATS, },
{ "net_bw_acct", AID_NET_BW_ACCT, },
{ "misc", AID_MISC, },
{ "nobody", AID_NOBODY, },
};
这些uid、gid都是以宏的形式被定义:
- #define AID_ROOT 0 /* traditional unix root user */
- #define AID_SYSTEM 1000 /* system server */
- #define AID_RADIO 1001 /* telephony subsystem, RIL */
- #define AID_BLUETOOTH 1002 /* bluetooth subsystem */
#define AID_ROOT 0 /* traditional unix root user */
#define AID_SYSTEM 1000 /* system server */
#define AID_RADIO 1001 /* telephony subsystem, RIL */
#define AID_BLUETOOTH 1002 /* bluetooth subsystem */
通过调用get_android_id函数在数组android_ids中查找对应的uid、gid
- static int get_android_id(const char *id)
- {
- unsigned int i;
- for (i = 0; i < ARRAY_SIZE(android_ids); i++)
- if (!strcmp(id, android_ids[i].name))
- return android_ids[i].aid;
- return 0;
- }
static int get_android_id(const char *id)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(android_ids); i++)
if (!strcmp(id, android_ids[i].name))
return android_ids[i].aid;
return 0;
}
函数实现比较简单,通过遍历数组,并匹配数组元素的name属性来查找指定name的uid或gid。
最后通过add_dev_perms函数来设置设备文件的操作权限,该函数定义在system\core\init\devices.c文件中,在该文件中声明了三个链表:
- static list_declare(sys_perms);
- static list_declare(dev_perms);
- static list_declare(platform_names);
static list_declare(sys_perms);
static list_declare(dev_perms);
static list_declare(platform_names);
add_dev_perms函数就是将解析得到的设备及设备属性,添加到指定的链表中,
使用解析得到的内容来创建一个perm_node变量,并根据条件添加到sys_perms或dev_perms链表中。
- int add_dev_perms(const char *name, const char *attr,
- mode_t perm, unsigned int uid, unsigned int gid,
- unsigned short prefix) {
- //创建perm_node
- struct perm_node *node = calloc(1, sizeof(*node));
- if (!node)
- return -ENOMEM;
- node->dp.name = strdup(name);
- if (!node->dp.name)
- return -ENOMEM;
- if (attr) {
- node->dp.attr = strdup(attr);
- if (!node->dp.attr)
- return -ENOMEM;
- }
- //设置perm_node的成员属性
- node->dp.perm = perm;
- node->dp.uid = uid;
- node->dp.gid = gid;
- node->dp.prefix = prefix;
- //根据attr 来选择添加到sys_perms或dev_perms链表中
- if (attr)
- list_add_tail(&sys_perms, &node->plist);
- else
- list_add_tail(&dev_perms, &node->plist);
- return 0;
- }
int add_dev_perms(const char *name, const char *attr,
mode_t perm, unsigned int uid, unsigned int gid,
unsigned short prefix) {
//创建perm_node
struct perm_node *node = calloc(1, sizeof(*node));
if (!node)
return -ENOMEM;
node->dp.name = strdup(name);
if (!node->dp.name)
return -ENOMEM;
if (attr) {
node->dp.attr = strdup(attr);
if (!node->dp.attr)
return -ENOMEM;
}
//设置perm_node的成员属性
node->dp.perm = perm;
node->dp.uid = uid;
node->dp.gid = gid;
node->dp.prefix = prefix;
//根据attr 来选择添加到sys_perms或dev_perms链表中
if (attr)
list_add_tail(&sys_perms, &node->plist);
else
list_add_tail(&dev_perms, &node->plist);
return 0;
}
至此ueventd.rc文件的解析工作完成了,uevent进程接下来将调用device_init()函数来初始化设备文件
- void device_init(void)
- {
- suseconds_t t0, t1;
- struct stat info;
- int fd;
- #ifdef HAVE_SELINUX
- struct selinux_opt seopts[] = {
- { SELABEL_OPT_PATH, "/file_contexts" }
- };
- if (is_selinux_enabled() > 0)
- sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
- #endif
- /* is 64K enough? udev uses 16MB! */
- //创建NETLINK socket,用于监听内核发送过来的uevent消息
- device_fd = uevent_open_socket(64*1024, true);
- if(device_fd < 0)
- return;
- //设置socket相关属性
- fcntl(device_fd, F_SETFD, FD_CLOEXEC);
- fcntl(device_fd, F_SETFL, O_NONBLOCK);
- //查看"/dev/.coldboot_done" 文件信息
- if (stat(coldboot_done, &info) < 0) {
- t0 = get_usecs();
- coldboot("/sys/class");
- coldboot("/sys/block");
- coldboot("/sys/devices");
- t1 = get_usecs();
- fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
- close(fd);
- log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
- } else {
- log_event_print("skipping coldboot, already done\n");
- }
- }
void device_init(void)
{
suseconds_t t0, t1;
struct stat info;
int fd;
#ifdef HAVE_SELINUX
struct selinux_opt seopts[] = {
{ SELABEL_OPT_PATH, "/file_contexts" }
};
if (is_selinux_enabled() > 0)
sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
#endif
/* is 64K enough? udev uses 16MB! */
//创建NETLINK socket,用于监听内核发送过来的uevent消息
device_fd = uevent_open_socket(64*1024, true);
if(device_fd < 0)
return;
//设置socket相关属性
fcntl(device_fd, F_SETFD, FD_CLOEXEC);
fcntl(device_fd, F_SETFL, O_NONBLOCK);
//查看"/dev/.coldboot_done" 文件信息
if (stat(coldboot_done, &info) < 0) {
t0 = get_usecs();
coldboot("/sys/class");
coldboot("/sys/block");
coldboot("/sys/devices");
t1 = get_usecs();
fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
close(fd);
log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
} else {
log_event_print("skipping coldboot, already done\n");
}
}
函数首先调用uevent_open_socket 来创建PF_NETLINK socket 并绑定到指定地址上:
- 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;
- //创建socket
- s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
- if(s < 0)
- return -1;
- //设置该socket属性
- setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz));
- setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
- //绑定该socket
- if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(s);
- return -1;
- }
- return s;
- }
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;
//创建socket
s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if(s < 0)
return -1;
//设置该socket属性
setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz));
setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
//绑定该socket
if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(s);
return -1;
}
return s;
}
ueventd进程接下来将通过系统调用poll函数来监控该socket,如下所示:
- ufd.events = POLLIN;
- ufd.fd = get_device_fd();
- while(1) {
- ufd.revents = 0;
- nr = poll(&ufd, 1, -1);
- if (nr <= 0)
- continue;
- if (ufd.revents == POLLIN)
- handle_device_fd();
- }
ufd.events = POLLIN;
ufd.fd = get_device_fd();
while(1) {
ufd.revents = 0;
nr = poll(&ufd, 1, -1);
if (nr <= 0)
continue;
if (ufd.revents == POLLIN)
handle_device_fd();
}
函数get_device_fd()返回创建的socket句柄值,并设置到ufd中,最后ueventd进程进入闭环监控模式,使用poll函数监控ufd,同时将第三个参数设置为-1,表示只有在监控的socket上有事件发生时,该函数才能返回。当热插入某一设备时,Linux内核将通过NETLINKsocket 发送uevent事件,此时poll函数得以返回,并调用handle_device_fd()函数来出来设备变化事件:
- void handle_device_fd()
- {
- char msg[UEVENT_MSG_LEN+2];
- int n;
- //从socket中读取消息内容
- while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
- //如果读取的内容长度大于1024,继续读取
- if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
- continue;
- msg[n] = '\0';
- msg[n+1] = '\0';
- //将uevent消息解析成uevent类型的事件
- struct uevent uevent;
- parse_event(msg, &uevent);
- //处理uevent事件
- handle_device_event(&uevent);
- handle_firmware_event(&uevent);
- }
- }
void handle_device_fd()
{
char msg[UEVENT_MSG_LEN+2];
int n;
//从socket中读取消息内容
while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
//如果读取的内容长度大于1024,继续读取
if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
continue;
msg[n] = '\0';
msg[n+1] = '\0';
//将uevent消息解析成uevent类型的事件
struct uevent uevent;
parse_event(msg, &uevent);
//处理uevent事件
handle_device_event(&uevent);
handle_firmware_event(&uevent);
}
}
当有设备事件发生时,poll函数返回,并从socket中读取内核发送过来的消息内容,并将该消息解析成uevent事件,同时调用handle_device_event函数和handle_firmware_event函数来分别处理设备事件或firmware事件
- static void handle_device_event(struct uevent *uevent)
- {
- //如果是设备添加事件
- if (!strcmp(uevent->action,"add"))
- fixup_sys_perms(uevent->path);
- //块设备事件
- if (!strncmp(uevent->subsystem, "block", 5)) {
- handle_block_device_event(uevent);
- //平台设备事件
- } else if (!strncmp(uevent->subsystem, "platform", 8)) {
- handle_platform_device_event(uevent);
- //通用设备事件
- } else {
- handle_generic_device_event(uevent);
- }
- }
static void handle_device_event(struct uevent *uevent)
{
//如果是设备添加事件
if (!strcmp(uevent->action,"add"))
fixup_sys_perms(uevent->path);
//块设备事件
if (!strncmp(uevent->subsystem, "block", 5)) {
handle_block_device_event(uevent);
//平台设备事件
} else if (!strncmp(uevent->subsystem, "platform", 8)) {
handle_platform_device_event(uevent);
//通用设备事件
} else {
handle_generic_device_event(uevent);
}
}
- static void handle_firmware_event(struct uevent *uevent)
- {
- pid_t pid;
- int ret;
- if(strcmp(uevent->subsystem, "firmware"))
- return;
- if(strcmp(uevent->action, "add"))
- return;
- //创建一个线程来专门执行firmware事件
- /* we fork, to avoid making large memory allocations in init proper */
- pid = fork();
- if (!pid) {
- process_firmware_event(uevent);
- exit(EXIT_SUCCESS);
- }
- }
static void handle_firmware_event(struct uevent *uevent)
{
pid_t pid;
int ret;
if(strcmp(uevent->subsystem, "firmware"))
return;
if(strcmp(uevent->action, "add"))
return;
//创建一个线程来专门执行firmware事件
/* we fork, to avoid making large memory allocations in init proper */
pid = fork();
if (!pid) {
process_firmware_event(uevent);
exit(EXIT_SUCCESS);
}
}
具体的处理过程这里不在详细分析,读者有兴趣请自行分析!至此就介绍完了整个ueventd进程的工作,