Android init.rc详解

文章出处:http://blog.csdn.net/shift_wwx/article/details/39232763

请转载的朋友表明出处~~


前言:之前一篇博文《Android 的init过程详解》小结了一下init的流程,这一篇小结一下init.rc。网上的资料很多也很详细,我这里也是结合自己的想法做个小结。


init.rc 文件并不是普通的配置文件,而是由一种被称为“Android初始化语言”(Android Init Language,这里简称为AIL)的脚本写成的文件。在了解init如何解析init.rc文件之前,先了解AIL非常必要,否则机械地分析 init.c及其相关文件的源代码毫无意义。

为了学习AIL,读者可以到自己Android手机的根目录寻找init.rc文件,最好下载到本地以便查看,如果有编译好的Android源代码, 在out/target/product/generic/root目录也可找到init.rc文件。

一、AIL组成

1.  动作(Actions)

2.  命令(Commands)

3. 服务(Services)

4.  选项(Options)

二、AIL命令书写规则

1、上面说的AIL4部分都是面向行的代码,也就是说用回车换行符作为每一条语句的分隔符。而每一行的代码由多个符号(Tokens)表示。可以使用反斜杠转义符在 Token中插入空格。双引号可以将多个由空格分隔的Tokens合成一个Tokens。如果一行写不下,可以在行尾加上反斜杠,来连接下一行。也就是 说,可以用反斜杠将多行代码连接成一行代码。

2、AIL的注释与很多Shell脚本一行,以#开头。

3、AIL在编写时需要分成多个部分(Section),而每一部分的开头需要指定Actions或Services。也就是说,每一个Actions或 Services确定一个Section。而所有的Commands和Options只能属于最近定义的Section。如果Commands和 Options在第一个Section之前被定义,它们将被忽略。

4、Actions和Services的名称必须唯一。如果有两个或多个Action或Service拥有同样的名称,那么init在执行它们时将抛出错误,并忽略这些Action和Service。

三、四个组成的语法配置

语法中用到的关键字,可以参考system/core/init/keywords.h

    KEYWORD(capability,  OPTION,  0, 0)
    KEYWORD(chdir,       COMMAND, 1, do_chdir)
    KEYWORD(chroot,      COMMAND, 1, do_chroot)
    KEYWORD(class,       OPTION,  0, 0)
    KEYWORD(class_start, COMMAND, 1, do_class_start)
    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
    KEYWORD(class_reset, COMMAND, 1, do_class_reset)
    KEYWORD(console,     OPTION,  0, 0)
    KEYWORD(critical,    OPTION,  0, 0)
    KEYWORD(dalvik_recache,    OPTION,  0, 0)
    KEYWORD(disabled,    OPTION,  0, 0)
    KEYWORD(domainname,  COMMAND, 1, do_domainname)
    KEYWORD(exec,        COMMAND, 1, do_exec)
    KEYWORD(export,      COMMAND, 2, do_export)
    KEYWORD(group,       OPTION,  0, 0)
    KEYWORD(hostname,    COMMAND, 1, do_hostname)
    KEYWORD(ifup,        COMMAND, 1, do_ifup)
    KEYWORD(insmod,      COMMAND, 1, do_insmod)
    KEYWORD(import,      SECTION, 1, 0)
    KEYWORD(keycodes,    OPTION,  0, 0)
    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)
    KEYWORD(oneshot,     OPTION,  0, 0)
    KEYWORD(onrestart,   OPTION,  0, 0)
    KEYWORD(powerctl,    COMMAND, 1, do_powerctl)
    KEYWORD(restart,     COMMAND, 1, do_restart)
    KEYWORD(restorecon,  COMMAND, 1, do_restorecon)
    KEYWORD(rm,          COMMAND, 1, do_rm)
    KEYWORD(rmdir,       COMMAND, 1, do_rmdir)
    KEYWORD(seclabel,    OPTION,  0, 0)
    KEYWORD(service,     SECTION, 0, 0)
    KEYWORD(setcon,      COMMAND, 1, do_setcon)
    KEYWORD(setenforce,  COMMAND, 1, do_setenforce)
    KEYWORD(setenv,      OPTION,  2, 0)
    KEYWORD(setkey,      COMMAND, 0, do_setkey)
    KEYWORD(setprop,     COMMAND, 2, do_setprop)
    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
    KEYWORD(setsebool,   COMMAND, 2, do_setsebool)
    KEYWORD(socket,      OPTION,  0, 0)
    KEYWORD(start,       COMMAND, 1, do_start)
    KEYWORD(stop,        COMMAND, 1, do_stop)
    KEYWORD(swapon_all,  COMMAND, 1, do_swapon_all)
    KEYWORD(trigger,     COMMAND, 1, do_trigger)
    KEYWORD(symlink,     COMMAND, 1, do_symlink)
    KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)
    KEYWORD(user,        OPTION,  0, 0)
    KEYWORD(wait,        COMMAND, 1, do_wait)
    KEYWORD(write,       COMMAND, 2, do_write)
    KEYWORD(copy,        COMMAND, 2, do_copy)
    KEYWORD(chown,       COMMAND, 2, do_chown)
    KEYWORD(chmod,       COMMAND, 2, do_chmod)
    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
    KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)
    KEYWORD(ubiattach,   COMMAND, 1, do_ubiattach)
    KEYWORD(ubidetach,   COMMAND, 1, do_ubidetach)
    KEYWORD(ioprio,      OPTION,  0, 0)
    KEYWORD(e2fsck,      COMMAND, 2, do_e2fsck)
    KEYWORD(confirm_formated,      COMMAND, 3, do_confirm_formated)
    KEYWORD(display_logo,      COMMAND, 1, do_display_logo )
例如:关键字class_start

KEYWORD(class_start, COMMAND, 1, do_class_start)
int do_class_start(int nargs, char **args)
{
        /* Starting a class does not start services
         * which are explicitly disabled.  They must
         * be started individually.
         */
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}
可以看出是启动所有not disabled的services

1、 action

1.1 语法格式:

on   
     
     
   
也就是说Actions是以关键字on开头的,然后跟一个触发器,接下来是若干命令。

例一:

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_adj -16

    # Set the security context for the init process.
    # This should occur before anything else (e.g. ueventd) is started.
    setcon u:r:init:s0

    start ueventd
例二:

on boot
# basic network init
    ifup lo
    hostname localhost
    domainname localdomain
例三:

on property:vold.decrypt=trigger_reset_main
    class_reset main
都是以on 开头的, 然后不同的trigger触发不同的command。其他的触发器1.2中有列出。

在例一中就是在early-init时,做了三件事情,例如最后的start ueventd,就是启动ueventd的service

在例三中是在property vold.decrypt值为trigger_reset_main的时候重启main

1.2 触发器

1) boot、early-init等,还有几个之前一篇博文上有说明

2) =

就是在值等于多少的时候触发,如上面的例三

3) device-added-

当设备节点被添加的时候触发

4) device-removed-

当设备节点被移除的时候触发

5) service-exited-

当一个特定的服务退出的时候触发

1.3 action中出现的命令

1)exec [ ]*

创建和执行一个程序()。在程序完全执行前,init将会阻塞。由于它不是内置命令,应尽量避免使用exec ,它可能会引起init执行超时。

2)export

在全局环境中将 变量的值设为。(这将会被所有在这命令之后运行的进程所继承)

export EXTERNAL_STORAGE /storage/emulated/legacy

3)ifup

启动网络接口

4)hostname

设置主机名

5)domainname

设置域名

6)import

指定要解析的其他配置文件。常被用于当前配置文件的扩展

import /init.${ro.hardware}.rc

7)chdir

改变工作目录

8)chmod

改变文件的访问权限

chmod 0660 /sys/fs/cgroup/memory/tasks
9)chown

更改文件的所有者和组

chown root system /sys/fs/cgroup/memory/tasks
10)chroot

改变处理根目录

11)class_start

启动所有指定服务类下的未运行服务。

on charger
    class_start charger
12)class_stop

停止指定服务类下的所有已运行的服务。

13)class_reset

重启指定的服务

on property:vold.decrypt=trigger_reset_main
    class_reset main

14)insmod

加载指定的驱动模块

insmod  /system/lib/audio_data.ko

15)mkdir [mode] [owner] [group]

创建一个目录 ,可以选择性地指定mode、owner以及group。如果没有指定,默认的权限为755,并属于root用户和 root组。

    mkdir /data/misc 01771 system misc
    mkdir /data/misc/adb 02750 system shell

16)mount

[ ]*

试图在目录

挂载指定的设备。 可以是mtd@name的形式指定一个mtd块设备。包括 "ro"、"rw"、"re

on fs
    setprop ro.crypto.umount_sd false
    #mount ext4 /dev/block/system /system wait ro noatime nodiratime noauto_da_alloc
    mount ext4 /dev/block/mmcblk0p2 /system wait ro noatime nodiratime noauto_da_alloc
    #e2fsck -y /dev/block/data
    #mount ext4 /dev/block/data /data noatime nodiratime norelatime nosuid nodev noauto_da_alloc
    mount ext4 /dev/block/mmcblk0p4 /data noatime nodiratime norelatime nosuid nodev noauto_da_alloc
    #e2fsck -y /dev/block/cache
    #mount ext4 /dev/block/cache /cache noatime nodiratime norelatime nosuid nodev noauto_da_alloc
    mount ext4 /dev/block/mmcblk0p3 /cache noatime nodiratime norelatime nosuid nodev noauto_da_alloc
17)setkey

保留,暂时未用

18)setprop

将系统属性的值设为

setprop ro.crypto.umount_sd false

19)setrlimit

设置的rlimit (资源限制)

20)start

启动指定服务(如果此服务还未运行)。

start ueventd

21)stop

停止指定服务(如果此服务在运行中)。

22)symlink

创建一个指向的软连接

23)sysclktz

设置系统时钟基准(0代表时钟滴答以格林威治平均时(GMT)为准)

24)trigger

触发一个事件。用于Action排队

25)wait [ ]

等待一个文件是否存在,当文件存在时立即返回,或到指定的超时时间后返回,如果不指定,默认超时时间是5秒。

26)write [ ]*

指定的文件写入一个或多个字符串

write /sys/fs/cgroup/memory/sw/memory.swappiness 100

27)copy

将特定文件copy到指定的path

28)powerctl

从code中,可以看出是android reboot时候的各种模式,ANDROID_RB_RESTART、ANDROID_RB_POWEROFF、ANDROID_RB_RESTART2

2、 service

Services (服务)是一个程序,他在初始化时启动,并在退出时重启(可选)。

2.1 语法格式:

service   [  ]*
    
例一:

service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm
例二:

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

2.2 服务支持的格式

1)critical

表明这是一个非常重要的服务。如果该服务4分钟内退出大于4次,系统将会重启并进入 Recovery (恢复)模式。

2)disabled

   This service will not automatically start with its class.
   It must be explicitly started by name.

这个关键字的service,是不会自动start的,必须要单独指定service name去start才行

3)setenv

在进程启动时将环境变量设置为

4)socket [ [ ] ]

   Create a unix domain socketnamed /dev/socket/ and pass

   its fd to the launchedprocess.  must be"dgram", "stream" or "seqpacket".

   User and group default to0.

创建一个unix域的名为/dev/socket/ 的套接字,并传递它的文件描述符给已启动的进程。 必须是 "dgram","stream" 或"seqpacket"。用户和组默认是0。

5)user

在启动这个服务前改变该服务的用户名。此时默认为 root。

6)group [ ]*

在启动这个服务前改变该服务的组名。除了(必需的)第一个组名,附加的组名通常被用于设置进程的补充组(通过setgroups函数),档案默认是root。

7)oneshot

表示该服务只启动一次,而如果没有oneshot选项,这个可执行程序将一直存在——如果可执行程序被杀死,则会重新启动

8)class

指定一个服务类。所有同一类的服务可以同时启动和停止。如果不通过class选项指定一个类,则默认为"default"类服务

9)onrestart

当服务重启,执行一个命令(下详)

其余的关键字,可以参考keywords.h,或者是system/core/init下的readme.txt


四、init.rc加载过程

在上一篇博文《Android 的init过程详解》中,简单的说了一下init.rc的加载

在init.c中的main函数中:

    if (!strcmp(bootmode,"factory"))
       init_parse_config_file("/init.factorytest.rc");
    else if (!strcmp(bootmode,"factory2"))
       init_parse_config_file("/init.factorytest2.rc");
    else
       init_parse_config_file("/init.rc");

int init_parse_config_file(const char *fn)
{
    char *data;
    data = read_file(fn, 0);
    if (!data) return -1;

    parse_config(fn, data);
    DUMP();
    return 0;
}
显示读文件读出来,然后执行关键函数parse_config。
static void parse_config(const char *fn, char *s)
{
    struct parse_state state;
    struct listnode import_list;
    struct listnode *node;
    char *args[INIT_PARSER_MAXARGS];
    int nargs;

    nargs = 0;
    state.filename = fn;
    state.line = 0;
    state.ptr = s;
    state.nexttoken = 0;
    state.parse_line = parse_line_no_op;

    list_init(&import_list);
    state.priv = &import_list;

    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;
        }
    }

parser_done:
    list_for_each(node, &import_list) {
         struct import *import = node_to_item(node, struct import, list);
         int ret;

         INFO("importing '%s'", import->filename);
         ret = init_parse_config_file(import->filename);
         if (ret)
             ERROR("could not import file '%s' from '%s'\n",
                   import->filename, fn);
    }
}
如果仔细研读,发现code的其实很简单的,首先是读出import的文件,然后再对每个文件进行递归解析。

首先,code最开始是初始化一个链表,用于存储import的文件。

    list_init(&import_list);
    state.priv = &import_list;
for循环主要关键是next_token函数,根据函数的返回值判断各种状态,主要由三种,即T_EOF(文件结束)、T_NEWLINE(新的一行)、T_TEXT(读到了非空格等字符的token)。

根据code中,会将读出来的字符串保存到args中:

args[nargs++] = state.text;
刚开始的时候,我有点疑问,这里保存的是字符指针,那么如果没有碰到字符串结束符,应该是指针以后的所有字符,后来,在next_token函数中注意到了一句话:

textdone:
    state->ptr = x;
    *s = 0;
    return T_TEXT;
在解析text 结束后,会将最后的一个字符置0,这就是字符串结束符。那也就是说之前指针数组args存放的是到空格、退格符、制表符为止的一个字符串,这就是token的真正意义了。

根据code:

        case T_TEXT:
            if (nargs < INIT_PARSER_MAXARGS) {
                args[nargs++] = state.text;
            }
            break;
        }
可以看出会一直解析这样的token,当然个数是有限制的,最大是64,也就是说init.rc中命令行关键字及option加起来个数最多是64。

循环解析,一直到了一行结束。

next_token函数中可以看出,在换行的时候

        case '\n':
            state->nexttoken = T_NEWLINE;
            x++;
            goto textdone;
    if (state->nexttoken) {
        int t = state->nexttoken;
        state->nexttoken = 0;
        return t;
    }
这样就是解析这一行的时候:

        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;
lookup_keyword函数就是为了解析这一行的第一个关键字类型。source code应该很简单。

主要是这个判断很关键:

#define kw_is(kw, type) (keyword_info[kw].flags & (type))
struct {
    const char *name;
    int (*func)(int nargs, char **args);
    unsigned char nargs;
    unsigned char flags;
} keyword_info[KEYWORD_COUNT] = {
    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
#include "keywords.h"
};
这里初始化挺好玩的,将keywords.h中的定义include进来了,第一次碰见这样玩的,学习了。
type是SECTION的关键字有:

KEYWORD(import,      SECTION, 1, 0)
KEYWORD(on,          SECTION, 0, 0)
KEYWORD(service,     SECTION, 0, 0)
而KEYWORD 定义在init_parser.c中:

#define KEYWORD(symbol, flags, nargs, func) \
    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
这样就清楚了,keyword_info[KEYWORD_COUNT]如果是完整列出来应该是:

keyword_info[KEYWORD_COUNT] = {  
        [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },  
        [K_ capability] = {" capability ", 0, 1, OPTION },  
        [K_ chdir] = {"chdir", do_chdir ,2, COMMAND}, 
        ...
        ...
根据上面分析,如果type是SECTION,就会

state.parse_line(&state, 0, 0);
parse_new_section(&state, kw, nargs, args);
可以state.parse_line在函数的最开始赋值为:

state.parse_line = parse_line_no_op;
可是函数parase_line_no_op是个空的:
void parse_line_no_op(struct parse_state *state, int nargs, char **args)
{
}
为什么会这样设计呢?等会做解释。

接下来看一下另一个函数了:

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:
    case K_e2fsck:
        state->context = parse_action(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_action;
            return;
        }
        break;
    case K_import:
        parse_import(state, nargs, args);
        break;
    }
    state->parse_line = parse_line_no_op;
}

以service为例,有两个关键点:

1、parse_service(state, nargs, args);

static void *parse_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc;
    if (nargs < 3) {
        parse_error(state, "services must have a name and a program\n");
        return 0;
    }
    if (!valid_name(args[1])) {
        parse_error(state, "invalid service name '%s'\n", args[1]);
        return 0;
    }

    svc = service_find_by_name(args[1]);
    if (svc) {
        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
        return 0;
    }

    nargs -= 2;
    svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
    if (!svc) {
        parse_error(state, "out of memory\n");
        return 0;
    }
    svc->name = args[1];
    svc->classname = "default";
    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
    svc->args[nargs] = 0;
    svc->nargs = nargs;
    svc->onrestart.name = "onrestart";
    list_init(&svc->onrestart.commands);
    list_add_tail(&service_list, &svc->slist);
    return svc;
}
1)service的命名规则可以看valid_name函数,service name不能有数字、下划线、横线
2)新建一个service struct,并保存在service_list中

2、state->parse_line = parse_line_service;

这里会将state中parse_line做了改变,记得之前赋值的另一个函数parase_line_no_op。

终于明白了为什么刚开始parase_line_no_op为空函数了吧?

到此可以看到了service所有option的定义了:

static void parse_line_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc = 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) {
    case K_capability:
        break;
    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  \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  <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 = 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 = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
        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_dalvik_recache:
        svc->flags |= SVC_DALVIK_RECACHE;
        break;
    case K_setenv: { /* name value */
        struct svcenvinfo *ei;
        if (nargs < 2) {
            parse_error(state, "setenv option requires name and value arguments\n");
            break;
        }
        ei = 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;
    }
    case K_socket: {/* name type perm [ uid gid ] */
        struct socketinfo *si;
        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;
        }
        si = 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]);
        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;

    default:
        parse_error(state, "invalid option '%s'\n", args[0]);
    }
}

Action和service的处理大致是一样的,根据code:

    case K_on:
    case K_e2fsck:
        state->context = parse_action(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_action;
            return;
        }
        break;
也就是说最后处理是在parse_line_action函数中。就不做细细分析了。


至此init.rc的解析过程就全部结束了。


总结一下解析过程:

1、init_parse_config_file(const char *fn)引入init.rc

2、read_file

3、parse_config做递归的解析*.rc

1)next_token函数解析每一个token

2)parse_new_section根据token 的type选择parse_service、parse_action、parse_import

3)state.parse_line(&state, nargs, args);



参考文献:

http://www.cnblogs.com/nokiaguy/p/3164799.html







你可能感兴趣的:(android,----,init,android,源码分析)