The init.rc script is parsed in the init. CPP entry method main, and the actual work is done by calling the init_parse_config_file method in init_parser. CPP.

system/core/init/init.cpp

.int main(int argc, char** argv) {...// Parse the init.rc script
    init_parse_config_file("/init.rc"); .return 0;
}
Copy the code

Below we to resolve system/core/rootdir/init zygote32. Rc, for example, specific init. Zygote32. The meaning of the rc script can refer to the Android source code read init. Zygote32. Rc file “section.

Init.zygote32. rc is imported to init.rc via the import statement.

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
Copy the code

system/core/rootdir/init.rc

. import /init.${ro.zygote}.rc ......Copy the code

The init_parse_config_file method is declared in the init_parser.h header and implemented in the corresponding CPP file. The init_parser.h file also declares action and service structures. These two structures are defined in 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
Copy the code

The init_parse_config_file method first reads the contents of the init.rc text file from the path path and then calls the parse_config function to do the actual parsing.

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(a);NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
    return 0;
}
Copy the code

The parse_config function processes the init.rc script line by line, doing the actual parsing. Listnode is used to implement bidirectional linked lists, and parse_state is a parse state structure. The parse_line_no_op function is an empty implementation that does not parse any rows. The service we are parsing is implicitly declared as a new Section. All commands or options belong to the most recently declared Section. So the following flow goes into the parse_new_section function to parse the service defined in init.zygote32.rc. Of course, since the service definition is in init.zygote32.rc, it needs to be handled first in the parser_done tag.

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;
    // Initialize the import list
    list_init(&import_list);
    state.priv = &import_list;

    for (;;) {
        // Next_token is used to get the next token, as described below
        switch (next_token(&state)) {
        // End of file case
        case T_EOF:
            state.parse_line(&state, 0.0);
            goto parser_done;
        // New text line case
        case T_NEWLINE:
            state.line++;
            if (nargs) {
                // Find the keyword
                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 {
                    // Parse other lines, such as those below the service definition
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
            break;
        / / text case
        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break; }}/ / import_list processing
parser_done:
    list_for_each(node, &import_list) {
         // Convert listNode in import_list to import structure
         struct import *import = node_to_item(node, struct import, list);
         int ret;
         // Parse the file corresponding to the import statement
         ret = init_parse_config_file(import->filename);
         if (ret)
             ERROR("could not import file '%s' from '%s'\n".import->filename, fn); }}Copy the code

Take a look at the parser.h header. This header defines T_EOF, T_TEXT, and T_NEWLINE, as well as the parse_state structure, which declares the next_token function.

Tips:

EOF is a computer term, short for End Of File, used in an operating system to indicate that a data source has no more data to read. Data sources are often referred to as files or streams. This character usually exists at the end of the text to indicate the end of the data.

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_ */
Copy the code

The next_token function isn’t much fun. Its main job is to find the next token (end-of-file, newline, text), and of course it skips comments (starting with #).

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;
        // Ignore comments
        case The '#':
            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;
}
Copy the code

Look again at the lookup_keyword function. Matching the string passed in returns K_xxx, with each K_xxx associated with a do_xxx function.

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 is here
        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;
}
Copy the code

Now you can focus on the parse_new_section function and go through the corresponding parsing process, depending on the KW type. Parse_new_section eventually calls the parse_service method to resolve the service. We assign the parse_state structure member context to the parse_state structure member parse_line to parse_line_service, which is a function pointer. Parse_line_service is then called in the parse_config function to parse the rest of the configuration under the service (corresponding to other lines under the 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; }Copy the code

Parse_service parses the input string into a service structure.

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 is an action structure
    list_init(&svc->onrestart.triggers);
    cur_trigger->name = "onrestart";
    list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
    list_init(&svc->onrestart.commands);
    // Add service to the end of the service_list.
    list_add_tail(&service_list, &svc->slist);
    return svc;
}
Copy the code

Below is the action structure.

system/core/init/init.h

struct action {
        /* Nodes in the action list */
    struct listnode alist;
        /* Nodes in the queue */
    struct listnode qlist;
        /* Nodes in the action list of the trigger */
    struct listnode tlist;

    unsigned hash;

        /* Triggers the command action list */
    struct listnode triggers;
    struct listnode commands;
    struct command *current;
};
Copy the code

Parse_line_service resolves the service class named main, next resolves the corresponding socketinfo structure, then resolves the command to be executed when the service is restarted, and finally, the writepid command.

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) {
    / / parse the 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[n2 -] = 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;
    // Parse the commands executed when the service is restarted
    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);
        // the do_xxx function pointer is assigned to the command structure func member
        cmd->func = kw_func(kw);
        cmd->nargs = nargs;
        memcpy(cmd->args, args, sizeof(char*) * nargs);
        // Add the command to the end of the list
        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];
        // Connect to the socket list
        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;
    // Parse the writepid command
    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]); }}Copy the code

Finally, look at the Service structure.

system/core/init/init.h

struct service {
    void NotifyStateChange(const char* new_state);

        /* Connect to node */ for service double-linked list
    struct listnode slist;

    char *name;/ / service name
    const char *classname;/ / the name of the class

    unsigned flags;
    pid_t pid;
    time_t time_started;    /* Last start time */
    time_t time_crashed;    /* For the first time crash */ occurred in the check window
    int nr_crashed;         /* The number of crashes in the window */

    uid_t uid;
    gid_t gid;
    gid_t supp_gids[NR_SVC_SUPP_GIDS];/ / added group
    size_t nr_supp_gids;// Add the number of groups

    const char* seclabel;// Security related

    struct socketinfo *sockets;// socket single-linked table header
    struct svcenvinfo *envvars;// Environment variable single linked list header

    struct action onrestart;  /* Action to execute on restart. * /

    std::vector<std::string>* writepid_files_;// File path used by the writepid command

    /* Trigger the key code for this service via /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' must end in structure! * /
Copy the code

Service parsing is complete as follows: