부제 : on script 안의 실행 순서
안드로이드에서 부팅시 init daemon이 호출하는 init.rc 를 파싱하는 부분을 분석하여 보았습니다.
ex) init.rc
on emmc-fs
chmod 0666 /dev/block/mmcblk0p14
mount ext4 /dev/block/mmcblk0p16 /cache nosuid nodev barrier=0
1. 아래와 같을때 실행 순서는 어떻게 될까?
on fs << 이런걸 section이라고 합니다.
내용1 << command라고 합니다.
내용2
on fs
내용3
내용4
init_parser.c
init함수에서 parse_config를 호출하게 됩니다.
- static void parse_config(const char *fn, char *s)
- for (;;) {
- switch (next_token(&state)) {
- case T_EOF:
- state.parse_line(&state, 0, 0);
- goto parser_done;
- case T_NEWLINE:
- state.line++;
- if (nargs) {
- int kw = lookup_keyword(args[0]);
- if (kw_is(kw, SECTION)) {
- state.parse_line(&state, 0, 0);
- parse_new_section(&state, kw, nargs, args);
- } else {
- state.parse_line(&state, nargs, args);
- }
- nargs = 0;
- }
- break;
- case T_TEXT:
- if (nargs < INIT_PARSER_MAXARGS) {
- args[nargs++] = state.text;
- }
- break;
- }
on 을 파싱할면 K_on 토큰을 리턴하게 됩니다.
그리고 on 이 붙게되면 new section을 만들게 되면서 parse_new_section 함수를 호출합니다. 그리고 라인 파서는 parse_line_action 함수로 바뀌게 됩니다.
- case 'o':
- if (!strcmp(s, "n")) return K_on;
그리고 그것을 parser_action함수를 통해서 목록에 넣습니다.
- 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;
- case K_on:
- state->context = parse_action(state, nargs, args);
- if (state->context) {
- state->parse_line = parse_line_action;
- return;
- }
- break;
action_list에 목록을 넣게 됩니다.
- static void *parse_action(struct parse_state *state, int nargs, char **args)
- list_init(&act->commands);
- list_add_tail(&action_list, &act->alist);
- void action_for_each_trigger(const char *trigger,
- void (*func)(struct action *act))
- {
- struct listnode *node;
- struct action *act;
- list_for_each(node, &action_list) {
- act = node_to_item(node, struct action, alist);
- if (!strcmp(act->name, trigger)) {
- func(act);
- }
- }
- }
chmod 의경우 K_chmod를 리턴합니다.
- 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, "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;
인자를 검사해서 command와 인자를 list에 넣습니다.
이때 lookup_keyword로 인자가 어떤 명령인지 검사합니다.
static void parse_line_action(struct parse_state* state, int nargs, char **args)
{
struct command *cmd;
struct action *act = state->context;
int (*func)(int nargs, char **args);
int kw, n;
if (nargs == 0) {
return;
}
kw = lookup_keyword(args[0]);
if (!kw_is(kw, COMMAND)) {
parse_error(state, "invalid command '%s'\n", args[0]);
return;
}
n = kw_nargs(kw);
if (nargs < n) {
parse_error(state, "%s requires %d %s\n", args[0], n - 1,
n > 2 ? "arguments" : "argument");
return;
}
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);
}
keyward.h 에보면 어떤 명령이 section으로 동작하는지 나타나있고
command에 따른 함수 리스트도 있습니다.
- KEYWORD(mkdir, COMMAND, 1, do_mkdir)
- KEYWORD(mount_all, COMMAND, 1, do_mount_all)
- KEYWORD(mount, COMMAND, 3, do_mount)
- KEYWORD(on, SECTION, 0, 0)
parse.h
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;
};
/init/init.c
파일에 아래 내용이 있습니다.
action_for_each_trigger("init", action_add_queue_tail);
drain_action_queue();
parser.c 에 아래와 같은 함수가 있습니다.
앞에서 section 별로 모아진 list가 존재하고
action_for_each_trigger함수뒤에 section명을 적으면 해당 section이름을 가진 command가 queue에 모이게 되고 drain_action_queue 에 의해서 실행된다.
void action_for_each_trigger(const char *trigger,
void (*func)(struct action *act))
{
struct listnode *node;
struct action *act;
list_for_each(node, &action_list) {
act = node_to_item(node, struct action, alist);
if (!strcmp(act->name, trigger)) {
func(act);
}
}
}
static void drain_action_queue(void)
{
struct listnode *node;
struct command *cmd;
struct action *act;
int ret;
while ((act = action_remove_queue_head())) {
INFO("processing action %p (%s)\n", act, act->name);
list_for_each(node, &act->commands) {
cmd = node_to_item(node, struct command, clist);
ret = cmd->func(cmd->nargs, cmd->args);
INFO("command '%s' r=%d\n", cmd->args[0], ret);
}
}
}
대부분의 동작은 queue와 list로 관리하게 됩니다.
따라서 section을 획득한 순서대도 실행하게됩니다.