一、init.rc语法规则
1.init.rc文件的内容主要分类
动作(Action)
命令(Commands)
服务(Services)
选项(Options)
触发(trigger)
2.动作和命令一起使用
on <trigger>
<command>
<command>
<command>
2.1.trigger是触发条件,为真执行命令
trigger有以下几种类型
boot
/init.conf加载完毕时触发
<name>=<value>
当<name>被设置为<value>时触发
device-added-<path>
device-removed-<path>
设备<path>被添加移除时触发
service-exited-<name>
当服务<name>退出时触发
3.服务和选项一起使用
service <name> <pathname> [ <argument> ]*
<option>
<option>
3.1.option选项
critical
disabled
setenv <name> <value>
socket <name> <type> <perm> [ <user> [ <group> ] ]
user <username>
group <groupname> [ <groupname> ]*
oneshot
class <name>
onrestart
二、init.rc文件的解析
1.init_parse_config_file
在system/core/init/init.c文件的main函数中调用
init_parse_config_file("/init.rc");
函数将init.rc作为参数读取进来
int init_parse_config_file(const char *fn)
{
char *data;
data = read_file(fn, 0); //读取init.rc文件到data中
if (!data) return -1;
parse_config(fn, data); //解析配置
DUMP();
return 0;
}
2.解析配置parse_config
static void parse_config(const char *fn, char *s)
{
struct parse_state state;
char *args[INIT_PARSER_MAXARGS];
int nargs;
nargs = 0;
state.filename = fn;
state.line = 1;
state.ptr = s; //指向init.rc的数据
state.nexttoken = 0;
state.parse_line = parse_line_no_op; //空操作
for (;;) {
switch (next_token(&state)) { //-->3.next_token跳过注释等,筛选需要解析的行
case T_EOF: //文件末尾
state.parse_line(&state, 0, 0); //后面参数为 0, 0所以直接返回
return;
case T_NEWLINE: //新行解析
if (nargs) { //有文本参数
int kw = lookup_keyword(args[0]); //解析关键词-->4.lookup_keyword
if (kw_is(kw, SECTION)) { //判断是否section类-->5.kw_is
state.parse_line(&state, 0, 0); //后面参数为 0, 0所以直接返回
parse_new_section(&state, kw, nargs, args); //6.parse_new_section
}
else {
state.parse_line(&state, nargs, args); //解析前一行parse_line_service或parse_line_action
}
nargs = 0; //nargs参数个数清0
}
break;
case T_TEXT: //文本
if (nargs < INIT_PARSER_MAXARGS) {
args[nargs++] = state.text; //保存文本参数,nargs++
}
break;
}
}
}
T_TEXT分支记录参数信息 例如
on early-init
symlink /initlogo.rle.bak /initlogo.rle
则会记录成
args[0]=on, args[1]=early-init -->换行
args[0]=symlink, args[1]=/initlogo.rle.bak, args[2]=/initlogo.rle -->换行
记录完后换新行会进入T_NEWLINE分支,分支会解析上一行的args[0],提取关键字,判断关键字类型做处理
如果是on和service关键词会调用parse_new_section处理,如果不是则调用state.parse_line函数处理,
parse_line函数可以是parse_line_service【service】或parse_line_action【on】
该parse_line函数主要是解析on/service后面带的command/options
正如例子中,on early-init 调用parse_new_section设置parse_line函数为parse_line_action
接着处理【on的command】symlink /initlogo.rle.bak /initlogo.rle的时候则调用parse_line函数parse_line_action
等到所有都处理完了则进入T_EOF分支,T_EOF分支return,跳出循环体
3.next_token
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': //换行
state->line++; //行数++
x++;
state->ptr = x;
return T_NEWLINE;
case ' ': //空格
case '\t': //tab制表
case '\r': //回车
x++;
continue;
case '#': //注释
while (*x && (*x != '\n')) //跳过注释直到换行
x++;
state->line++; //行数++
state->ptr = x;
return T_NEWLINE;
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': //tab制表
case '\r': //回车
x++;
goto textdone;
case '\n': //换行
state->nexttoken = T_NEWLINE;
x++;
goto textdone;
case '"': //引号括起
x++;
for (;;) {
switch (*x) {
case 0:
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
*s++ = *x++;
}
}
return T_EOF;
}
4.lookup_keyword
int lookup_keyword(const char *s)
{
switch (*s++) {
case 'c':
if (!strcmp(s, "opy")) return K_copy;
if (!strcmp(s, "apability")) return K_capability;
if (!strcmp(s, "hdir")) return K_chdir;
if (!strcmp(s, "hroot")) return K_chroot;
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, "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, "xec")) return K_exec;