init.rc 脚本是在 init.cpp 入口方法 main 中开始解析的,实际工作调用了 init_parser.cpp 内的 init_parse_config_file 方法。
system/core/init/init.cpp
......
int main(int argc, char** argv) {
......
// 解析 init.rc 脚本
init_parse_config_file("/init.rc");
......
return 0;
}
下面我们以解析 system/core/rootdir/init.zygote32.rc 为例,具体 init.zygote32.rc 脚本的含义可以参考《Android 源码 读懂 init.zygote32.rc 文件》一节。
init.zygote32.rc 通过 import 语句导入到 init.rc。
system/core/rootdir/init.zygote32.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks
system/core/rootdir/init.rc
......
import /init.${ro.zygote}.rc
......
init_parse_config_file 方法声明在头文件 init_parser.h 中,实现在对应的 cpp 文件中。init_parser.h 文件中还声明了 action、service 结构体,具体这两个结构体的定义是在 init.h 文件中。
system/core/init/init_parser.h
#ifndef _INIT_INIT_PARSER_H_
#define _INIT_INIT_PARSER_H_
#define INIT_PARSER_MAXARGS 64
struct action;
struct service;
......
int init_parse_config_file(const char *fn);
......
#endif
init_parse_config_file 方法中首先从 path 路径中读取 init.rc 文本文件对应的内容,接着调用 parse_config 函数做实际的解析工作。
system/core/init/init_parser.cpp
int init_parse_config_file(const char* path) {
INFO("Parsing %s...\n", path);
Timer t;
std::string data;
if (!read_file(path, &data)) {
return -1;
}
data.push_back('\n'); // TODO: fix parse_config.
parse_config(path, data);
dump_parser_state();
NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
return 0;
}
parse_config 函数一行一行的处理 init.rc 脚本,做实际的解析工作。listnode 是用来实现双向链表的,parse_state 是一个解析状态结构体。parse_line_no_op 函数是个空实现,不解析任何行。我们要解析的 service 隐式声明为一个新的 Section。 所有命令(Command)或选项(Option)都属于最近声明的 Section。 所以下面的流程会进入 parse_new_section 函数解析 init.zygote32.rc 中定义的 service。当然因为 service 定义在 init.zygote32.rc 文件中,那么需要在 parser_done 标签处先处理。
system/core/init/init_parser.cpp
static void parse_config(const char *fn, const std::string& data)
{
struct listnode import_list;
struct listnode *node;
char *args[INIT_PARSER_MAXARGS];
int nargs = 0;
parse_state state;
state.filename = fn;
state.line = 0;
state.ptr = strdup(data.c_str()); // TODO: fix this code!
state.nexttoken = 0;
state.parse_line = parse_line_no_op;
// 初始化 import 链表
list_init(&import_list);
state.priv = &import_list;
for (;;) {
// next_token 用来获取下一个标记,具体参见下文
switch (next_token(&state)) {
// 文件结尾 case
case T_EOF:
state.parse_line(&state, 0, 0);
goto parser_done;
// 新的文本行 case
case T_NEWLINE:
state.line++;
if (nargs) {
// 查找关键字
int kw = lookup_keyword(args[0]);
if (kw_is(kw, SECTION)) {
state.parse_line(&state, 0, 0);
// 解析 section
parse_new_section(&state, kw, nargs, args);
} else {
// 解析其他行,比如 service 定义下面的行
state.parse_line(&state, nargs, args);
}
nargs = 0;
}
break;
// 文本 case
case T_TEXT:
if (nargs < INIT_PARSER_MAXARGS) {
args[nargs++] = state.text;
}
break;
}
}
// 处理 import_list
parser_done:
list_for_each(node, &import_list) {
// 将 import_list 中的 listnode 转化为 import 结构体
struct import *import = node_to_item(node, struct import, list);
int ret;
// 解析 import 语句对应的文件
ret = init_parse_config_file(import->filename);
if (ret)
ERROR("could not import file '%s' from '%s'\n",
import->filename, fn);
}
}
先来研究一下 parser.h 头文件。这个头文件定义了 T_EOF、T_TEXT 和 T_NEWLINE,以及 parse_state 结构体,声明了 next_token 函数。
tips:
EOF 是一个计算机术语,为 End Of File 的缩写,在操作系统中表示资料源无更多的资料可读取。资料源通常称为档案或串流。通常在文本的最后存在此字符表示资料结束。
system/core/init/parser.h
#ifndef PARSER_H_
#define PARSER_H_
#define T_EOF 0
#define T_TEXT 1
#define T_NEWLINE 2
struct parse_state
{
char *ptr;
char *text;
int line;
int nexttoken;
void *context;
void (*parse_line)(struct parse_state *state, int nargs, char **args);
const char *filename;
void *priv;
};
void dump_parser_state(void);
int next_token(struct parse_state *state);
void parse_error(struct parse_state *state, const char *fmt, ...);
#endif /* PARSER_H_ */
next_token 函数没什么趣味,主要工作就是寻找下一个标识(文件结束、新行和文本),当然它还会略过注释(以 # 开头)。
system/core/init/parser.cpp
int next_token(struct parse_state *state)
{
char *x = state->ptr;
char *s;
if (state->nexttoken) {
int t = state->nexttoken;
state->nexttoken = 0;
return t;
}
for (;;) {
switch (*x) {
case 0:
state->ptr = x;
return T_EOF;
case '\n':
x++;
state->ptr = x;
return T_NEWLINE;
case ' ':
case '\t':
case '\r':
x++;
continue;
// 忽略注释
case '#':
while (*x && (*x != '\n')) x++;
if (*x == '\n') {
state->ptr = x+1;
return T_NEWLINE;
} else {
state->ptr = x;
return T_EOF;
}
default:
goto text;
}
}
textdone:
state->ptr = x;
*s = 0;
return T_TEXT;
text:
state->text = s = x;
textresume:
for (;;) {
switch (*x) {
case 0:
goto textdone;
case ' ':
case '\t':
case '\r':
x++;
goto textdone;
case '\n':
state->nexttoken = T_NEWLINE;
x++;
goto textdone;
case '"':
x++;
for (;;) {
switch (*x) {
case 0:
/* unterminated quoted thing */
state->ptr = x;
return T_EOF;
case '"':
x++;
goto textresume;
default:
*s++ = *x++;
}
}
break;
case '\\':
x++;
switch (*x) {
case 0:
goto textdone;
case 'n':
*s++ = '\n';
break;
case 'r':
*s++ = '\r';
break;
case 't':
*s++ = '\t';
break;
case '\\':
*s++ = '\\';
break;
case '\r':
/* \ <cr> <lf> -> line continuation */
if (x[1] != '\n') {
x++;
continue;
}
case '\n':
/* \ <lf> -> line continuation */
state->line++;
x++;
/* eat any extra whitespace */
while((*x == ' ') || (*x == '\t')) x++;
continue;
default:
/* unknown escape -- just copy */
*s++ = *x++;
}
continue;
default:
*s++ = *x++;
}
}
return T_EOF;
}
再来研究一下 lookup_keyword 函数。根据传入的字符串去匹配返回 K_xxx ,每个 K_xxx 关联了一个 do_xxx 函数。
system/core/init/init_parser.cpp
static int lookup_keyword(const char *s)
{
switch (*s++) {
case 'b':
if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
break;
case 'c':
if (!strcmp(s, "opy")) return K_copy;
if (!strcmp(s, "lass")) return K_class;
if (!strcmp(s, "lass_start")) return K_class_start;
if (!strcmp(s, "lass_stop")) return K_class_stop;
if (!strcmp(s, "lass_reset")) return K_class_reset;
if (!strcmp(s, "onsole")) return K_console;
if (!strcmp(s, "hown")) return K_chown;
if (!strcmp(s, "hmod")) return K_chmod;
if (!strcmp(s, "ritical")) return K_critical;
break;
case 'd':
if (!strcmp(s, "isabled")) return K_disabled;
if (!strcmp(s, "omainname")) return K_domainname;
break;
case 'e':
if (!strcmp(s, "nable")) return K_enable;
if (!strcmp(s, "xec")) return K_exec;
if (!strcmp(s, "xport")) return K_export;
break;
case 'g':
if (!strcmp(s, "roup")) return K_group;
break;
case 'h':
if (!strcmp(s, "ostname")) return K_hostname;
break;
case 'i':
if (!strcmp(s, "oprio")) return K_ioprio;
if (!strcmp(s, "fup")) return K_ifup;
if (!strcmp(s, "nsmod")) return K_insmod;
if (!strcmp(s, "mport")) return K_import;
if (!strcmp(s, "nstallkey")) return K_installkey;
break;
case 'k':
if (!strcmp(s, "eycodes")) return K_keycodes;
break;
case 'l':
if (!strcmp(s, "oglevel")) return K_loglevel;
if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
if (!strcmp(s, "oad_system_props")) return K_load_system_props;
break;
case 'm':
if (!strcmp(s, "kdir")) return K_mkdir;
if (!strcmp(s, "ount_all")) return K_mount_all;
if (!strcmp(s, "ount")) return K_mount;
break;
case 'o':
if (!strcmp(s, "n")) return K_on;
if (!strcmp(s, "neshot")) return K_oneshot;
if (!strcmp(s, "nrestart")) return K_onrestart;
break;
case 'p':
if (!strcmp(s, "owerctl")) return K_powerctl;
break;
case 'r':
if (!strcmp(s, "estart")) return K_restart;
if (!strcmp(s, "estorecon")) return K_restorecon;
if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive;
if (!strcmp(s, "mdir")) return K_rmdir;
if (!strcmp(s, "m")) return K_rm;
break;
case 's':
if (!strcmp(s, "eclabel")) return K_seclabel;
// service 在这里
if (!strcmp(s, "ervice")) return K_service;
if (!strcmp(s, "etenv")) return K_setenv;
if (!strcmp(s, "etprop")) return K_setprop;
if (!strcmp(s, "etrlimit")) return K_setrlimit;
if (!strcmp(s, "etusercryptopolicies")) return K_setusercryptopolicies;
if (!strcmp(s, "ocket")) return K_socket;
if (!strcmp(s, "tart")) return K_start;
if (!strcmp(s, "top")) return K_stop;
if (!strcmp(s, "wapon_all")) return K_swapon_all;
if (!strcmp(s, "ymlink")) return K_symlink;
if (!strcmp(s, "ysclktz")) return K_sysclktz;
break;
case 't':
if (!strcmp(s, "rigger")) return K_trigger;
break;
case 'u':
if (!strcmp(s, "ser")) return K_user;
break;
case 'v':
if (!strcmp(s, "erity_load_state")) return K_verity_load_state;
if (!strcmp(s, "erity_update_state")) return K_verity_update_state;
break;
case 'w':
if (!strcmp(s, "rite")) return K_write;
if (!strcmp(s, "ritepid")) return K_writepid;
if (!strcmp(s, "ait")) return K_wait;
break;
}
return K_UNKNOWN;
}
现在可以继续关注 parse_new_section 函数了,根据不同的 kw 类型,进入相应的解析流程。parse_new_section 最终会调用 parse_service 方法解析 service。并将解析出的 service 结构体赋值给 parse_state 结构体的成员 context,下一步就会赋值 parse_state 结构体的成员 parse_line 为 parse_line_service,这是一个函数指针。然后接下来 parse_config 函数中进一步解析 service 下的其他配置(对应 service 下的其他行)调用 parse_line_service 函数进行的。
system/core/init/init_parser.cpp
static void parse_new_section(struct parse_state *state, int kw,
int nargs, char **args)
{
printf("[ %s %s ]\n", args[0],
nargs > 1 ? args[1] : "");
switch(kw) {
case K_service:
state->context = parse_service(state, nargs, args);
if (state->context) {
state->parse_line = parse_line_service;
return;
}
break;
......
}
state->parse_line = parse_line_no_op;
}
parse_service 将输入的字符串解析成 service 结构体。
system/core/init/init_parser.cpp
static void *parse_service(struct parse_state *state, int nargs, char **args)
{
......
service* svc = (service*) service_find_by_name(args[1]);
......
nargs -= 2;
svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
......
svc->name = strdup(args[1]);
svc->classname = "default";
memcpy(svc->args, args + 2, sizeof(char*) * nargs);
trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
svc->args[nargs] = 0;
svc->nargs = nargs;
// onrestart 是一个 action 结构体
list_init(&svc->onrestart.triggers);
cur_trigger->name = "onrestart";
list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
list_init(&svc->onrestart.commands);
// 将 service 添加到了 service_list 双链表尾部。
list_add_tail(&service_list, &svc->slist);
return svc;
}
下面是 action 结构体。
system/core/init/init.h
struct action {
/* action 列表中的节点 */
struct listnode alist;
/* 等待队列中的节点 */
struct listnode qlist;
/* 触发器的 action 列表中的节点 */
struct listnode tlist;
unsigned hash;
/* 触发命令的 action 列表*/
struct listnode triggers;
struct listnode commands;
struct command *current;
};
parse_line_service 解析出 service class 名为 main,下一步解析出对应的 socketinfo 结构体,接着解析解析服务重启时执行的命令,最后解析 writepid 命令。
static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
struct service *svc = (service*) state->context;
struct command *cmd;
int i, kw, kw_nargs;
if (nargs == 0) {
return;
}
svc->ioprio_class = IoSchedClass_NONE;
kw = lookup_keyword(args[0]);
switch (kw) {
// 解析 classname
case K_class:
if (nargs != 2) {
parse_error(state, "class option requires a classname\n");
} else {
svc->classname = args[1];
}
break;
case K_console:
svc->flags |= SVC_CONSOLE;
break;
case K_disabled:
svc->flags |= SVC_DISABLED;
svc->flags |= SVC_RC_DISABLED;
break;
case K_ioprio:
if (nargs != 3) {
parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
} else {
svc->ioprio_pri = strtoul(args[2], 0, 8);
if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
parse_error(state, "priority value must be range 0 - 7\n");
break;
}
if (!strcmp(args[1], "rt")) {
svc->ioprio_class = IoSchedClass_RT;
} else if (!strcmp(args[1], "be")) {
svc->ioprio_class = IoSchedClass_BE;
} else if (!strcmp(args[1], "idle")) {
svc->ioprio_class = IoSchedClass_IDLE;
} else {
parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
}
}
break;
case K_group:
if (nargs < 2) {
parse_error(state, "group option requires a group id\n");
} else if (nargs > NR_SVC_SUPP_GIDS + 2) {
parse_error(state, "group option accepts at most %d supp. groups\n",
NR_SVC_SUPP_GIDS);
} else {
int n;
svc->gid = decode_uid(args[1]);
for (n = 2; n < nargs; n++) {
svc->supp_gids[n-2] = decode_uid(args[n]);
}
svc->nr_supp_gids = n - 2;
}
break;
case K_keycodes:
if (nargs < 2) {
parse_error(state, "keycodes option requires atleast one keycode\n");
} else {
svc->keycodes = (int*) malloc((nargs - 1) * sizeof(svc->keycodes[0]));
if (!svc->keycodes) {
parse_error(state, "could not allocate keycodes\n");
} else {
svc->nkeycodes = nargs - 1;
for (i = 1; i < nargs; i++) {
svc->keycodes[i - 1] = atoi(args[i]);
}
}
}
break;
case K_oneshot:
svc->flags |= SVC_ONESHOT;
break;
// 解析服务重启时执行的命令
case K_onrestart:
nargs--;
args++;
kw = lookup_keyword(args[0]);
if (!kw_is(kw, COMMAND)) {
parse_error(state, "invalid command '%s'\n", args[0]);
break;
}
kw_nargs = kw_nargs(kw);
if (nargs < kw_nargs) {
parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
kw_nargs > 2 ? "arguments" : "argument");
break;
}
cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
// do_xxx 函数指针赋给 command 结构体 func 成员
cmd->func = kw_func(kw);
cmd->nargs = nargs;
memcpy(cmd->args, args, sizeof(char*) * nargs);
// 将命令添加到列表尾部
list_add_tail(&svc->onrestart.commands, &cmd->clist);
break;
case K_critical:
svc->flags |= SVC_CRITICAL;
break;
case K_setenv: { /* name value */
if (nargs < 3) {
parse_error(state, "setenv option requires name and value arguments\n");
break;
}
svcenvinfo* ei = (svcenvinfo*) calloc(1, sizeof(*ei));
if (!ei) {
parse_error(state, "out of memory\n");
break;
}
ei->name = args[1];
ei->value = args[2];
ei->next = svc->envvars;
svc->envvars = ei;
break;
}
// 解析 socket
case K_socket: {/* name type perm [ uid gid context ] */
if (nargs < 4) {
parse_error(state, "socket option requires name, type, perm arguments\n");
break;
}
if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
&& strcmp(args[2],"seqpacket")) {
parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
break;
}
socketinfo* si = (socketinfo*) calloc(1, sizeof(*si));
if (!si) {
parse_error(state, "out of memory\n");
break;
}
si->name = args[1];
si->type = args[2];
si->perm = strtoul(args[3], 0, 8);
if (nargs > 4)
si->uid = decode_uid(args[4]);
if (nargs > 5)
si->gid = decode_uid(args[5]);
if (nargs > 6)
si->socketcon = args[6];
// 连接到 socket 单链表
si->next = svc->sockets;
svc->sockets = si;
break;
}
case K_user:
if (nargs != 2) {
parse_error(state, "user option requires a user id\n");
} else {
svc->uid = decode_uid(args[1]);
}
break;
case K_seclabel:
if (nargs != 2) {
parse_error(state, "seclabel option requires a label string\n");
} else {
svc->seclabel = args[1];
}
break;
// 解析 writepid 命令
case K_writepid:
if (nargs < 2) {
parse_error(state, "writepid option requires at least one filename\n");
break;
}
svc->writepid_files_ = new std::vector<std::string>;
for (int i = 1; i < nargs; ++i) {
svc->writepid_files_->push_back(args[i]);
}
break;
default:
parse_error(state, "invalid option '%s'\n", args[0]);
}
}
最后来看看 service 结构体。
system/core/init/init.h
struct service {
void NotifyStateChange(const char* new_state);
/* 连接到 service 双链表用的节点 */
struct listnode slist;
char *name;//服务名
const char *classname;//类名
unsigned flags;
pid_t pid;
time_t time_started; /* 最后起动时间 */
time_t time_crashed; /* 第一次在检查窗口内发生 crash */
int nr_crashed; /* 窗口内 crash 的次数 */
uid_t uid;
gid_t gid;
gid_t supp_gids[NR_SVC_SUPP_GIDS];//补充组
size_t nr_supp_gids;//补充组数量
const char* seclabel;//安全相关
struct socketinfo *sockets;// socket 单链表表头
struct svcenvinfo *envvars;// 环境变量单链表表头
struct action onrestart; /* 重新启动时要执行的 Action。 */
std::vector<std::string>* writepid_files_;// writepid 命令使用的 file path
/* 通过 /dev/keychord 触发此服务的密钥代码 */
int *keycodes;
int nkeycodes;
int keychord_id;
IoSchedClass ioprio_class;
int ioprio_pri;
int nargs;
/* "MUST BE AT THE END OF THE STRUCT" */
char *args[1];
}; /* ^-------'args' 一定要在结构体结尾! */
service 解析结束以后如下: