부제 : 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을 획득한 순서대도 실행하게됩니다.