【android】根據init.rc啓動action和service

一、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;
        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;
        break;
    case 'k':
        if (!strcmp(s, "eycodes")) return K_keycodes;
        break;
    case 'l':
        if (!strcmp(s, "oglevel")) return K_loglevel;
        break;
    case 'm':
        if (!strcmp(s, "kdir")) return K_mkdir;
        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 'r':
        if (!strcmp(s, "estart")) return K_restart;
        break;
    case 's':
        if (!strcmp(s, "ervice")) return K_service;
        if (!strcmp(s, "etenv")) return K_setenv;
        if (!strcmp(s, "etkey")) return K_setkey;
        if (!strcmp(s, "etprop")) return K_setprop;
        if (!strcmp(s, "etrlimit")) return K_setrlimit;
        if (!strcmp(s, "ocket")) return K_socket;
        if (!strcmp(s, "tart")) return K_start;
        if (!strcmp(s, "top")) return K_stop;
        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 'w':
        if (!strcmp(s, "rite")) return K_write;
        if (!strcmp(s, "ait")) return K_wait;
        break;
    }
    return K_UNKNOWN;
}


5.kw_is

5.1.kw_is宏定義

#define kw_is(kw, type) (keyword_info[kw].flags & (type))

5.2.keyword_info全局數組的定義

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文件

5.3.keywords.h文件內容

#ifndef KEYWORD
int do_chroot(int nargs, char **args);
int do_chdir(int nargs, char **args);
int do_class_start(int nargs, char **args);
int do_class_stop(int nargs, char **args);
int do_domainname(int nargs, char **args);
int do_exec(int nargs, char **args);
int do_export(int nargs, char **args);
int do_hostname(int nargs, char **args);
int do_ifup(int nargs, char **args);
int do_insmod(int nargs, char **args);
int do_import(int nargs, char **args);
int do_mkdir(int nargs, char **args);
int do_mount(int nargs, char **args);
int do_restart(int nargs, char **args);
int do_setkey(int nargs, char **args);
int do_setprop(int nargs, char **args);
int do_setrlimit(int nargs, char **args);
int do_start(int nargs, char **args);
int do_stop(int nargs, char **args);
int do_trigger(int nargs, char **args);
int do_symlink(int nargs, char **args);
int do_sysclktz(int nargs, char **args);
int do_write(int nargs, char **args);
int do_copy(int nargs, char **args);
int do_chown(int nargs, char **args);
int do_chmod(int nargs, char **args);
int do_loglevel(int nargs, char **args);
int do_wait(int nargs, char **args);
#define __MAKE_KEYWORD_ENUM__
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
enum {
    K_UNKNOWN,
#endif
    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(console,     OPTION,  0, 0)
    KEYWORD(critical,    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,      COMMAND, 1, do_import)
    KEYWORD(keycodes,    OPTION,  0, 0)
    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
    KEYWORD(mount,       COMMAND, 3, do_mount)
    KEYWORD(on,          SECTION, 0, 0)
    KEYWORD(oneshot,     OPTION,  0, 0)
    KEYWORD(onrestart,   OPTION,  0, 0)
    KEYWORD(restart,     COMMAND, 1, do_restart)
    KEYWORD(service,     SECTION, 0, 0)
    KEYWORD(setenv,      OPTION,  2, 0)
    KEYWORD(setkey,      COMMAND, 0, do_setkey)
    KEYWORD(setprop,     COMMAND, 2, do_setprop)
    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
    KEYWORD(socket,      OPTION,  0, 0)
    KEYWORD(start,       COMMAND, 1, do_start)
    KEYWORD(stop,        COMMAND, 1, do_stop)
    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(ioprio,      OPTION,  0, 0)
#ifdef __MAKE_KEYWORD_ENUM__
    KEYWORD_COUNT,
};
#undef __MAKE_KEYWORD_ENUM__
#undef KEYWORD
#endif

5.4.結合KEYWORD宏拆開全局keyword_info數組可得

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 		},
[ K_capability 	] = { "capability", 	0,				0, 		OPTION	},
[ K_chdir 		] = { "chdir",    		do_chdir, 		1,   	COMMAND	},
[ K_chroot 		] = { "chroot",   		do_chroot, 		1, 		COMMAND },
[ K_class 		] = { "class",     		0, 				0,   	OPTION, },
[ K_class_start ] = { "class_start",	do_class_start, 1, 		COMMAND	},
[ K_class_stop 	] = { "class_stop", 	do_class_stop, 	1,		COMMAND },
[ K_console 	] = { "console",  		0,  			0, 		OPTION 	},
[ K_critical 	] = { "critical",  		0,  			0,		OPTION 	},
[ K_disabled 	] = { "disabled",   	0,  			0,  	OPTION	},
[ K_domainname 	] = { "domainname", 	do_domainname, 	1,		COMMAND },
[ K_exec 		] = { "exec",   		do_exec, 		1,     	COMMAND	},
[ K_export 		] = { "export",			do_export, 		2,    	COMMAND },
[ K_group 		] = { "group",   		0,  			0,   	OPTION	},
[ K_hostname 	] = { "hostname", 		do_hostname, 	1,  	COMMAND },
[ K_ifup 		] = { "ifup", 	 		do_ifup, 		1    	COMMAND	},
[ K_insmod 		] = { "insmod", 		do_insmod, 		1,   	COMMAND },
[ K_import 		] = { "import",  		do_import, 		1,   	COMMAND },
[ K_keycodes 	] = { "keycodes", 		0,  			0,   	OPTION 	},
[ K_mkdir 		] = { "mkdir", 			do_mkdir, 		1,  	COMMAND },
[ K_mount 		] = { "mount",  		do_mount, 		3,  	COMMAND },
[ K_on 			] = { "on",      		0, 				0,  	SECTION },///////////////////////
[ K_oneshot 	] = { "oneshot",  		0,  			0, 		OPTION 	},
[ K_onrestart 	] = { "onrestart", 		0,  			0, 		OPTION	},
[ K_restart 	] = { "restart", 		do_restart, 	1,   	COMMAND },
[ K_service 	] = { "service",  		0, 				0, 		SECTION },///////////////////////
[ K_setenv 		] = { "setenv",  		0,  			2, 		OPTION 	},
[ K_setkey 		] = { "setkey",   		do_setkey, 		0,  	COMMAND },
[ K_setprop 	] = { "setprop", 		do_setprop, 	2,   	COMMAND	},
[ K_setrlimit 	] = { "setrlimit", 		do_setrlimit , 	3, 		COMMAND },
[ K_socket 		] = { "socket",  		0,  			0,   	OPTION 	},
[ K_start 		] = { "start", 			do_start, 		1,     	COMMAND	},
[ K_stop 		] = { "stop",   		do_stop, 		1,     	COMMAND	},
[ K_trigger 	] = { "trigger",		do_trigger, 	1,   	COMMAND },
[ K_symlink 	] = { "symlink", 		do_symlink, 	1,   	COMMAND },
[ K_sysclktz 	] = { "sysclktz", 		do_sysclktz, 	1,  	COMMAND },
[ K_user 		] = { "user",  			0,  			0,   	OPTION 	},
[ K_wait 		] = { "wait",			do_wait, 		1,      COMMAND	},
[ K_write 		] = { "write",			do_write, 		2,     	COMMAND },
[ K_copy 		] = { "copy",  			do_copy, 		2,   	COMMAND },
[ K_chown 		] = { "chown",  		do_chown, 		2,  	COMMAND },
[ K_chmod 		] = { "chmod",  		do_chmod, 		2,   	COMMAND },,
[ K_loglevel 	] = { "loglevel",  		do_loglevel,	1, 		COMMAND	},
[ K_ioprio 		] = { "ioprio",     	0, 				0  		OPTION 	},
}

5.5.同理可以理解

#define kw_name(kw) (keyword_info[kw].name)		//根據關鍵詞獲取名字
#define kw_func(kw) (keyword_info[kw].func)		//根據關鍵詞獲取處理函數指針
#define kw_nargs(kw) (keyword_info[kw].nargs)	//根據關鍵詞獲取參數個數

6.parse_new_section

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:	//service類型-->三、service的處理
        state->context = parse_service(state, nargs, args);	//解析service
        if (state->context) {
            state->parse_line = parse_line_service;	//設置parse_line函數,處理options
            return;
        }
        break;
    case K_on:		//on類型     -->四、action的處理
        state->context = parse_action(state, nargs, args);	//解析action
        if (state->context) {
            state->parse_line = parse_line_action;	//設置parse_line函數,處理command
            return;
        }
        break;
    }
    state->parse_line = parse_line_no_op;
}

三、service的處理

1.相關結構體

struct service {
    struct listnode slist;
    const char *name;
    const char *classname;
    unsigned flags;
    pid_t pid;
    time_t time_started;
    time_t time_crashed;
    int nr_crashed;
    uid_t uid;
    gid_t gid;
    gid_t supp_gids[NR_SVC_SUPP_GIDS];
    size_t nr_supp_gids;
    struct socketinfo *sockets;
    struct svcenvinfo *envvars;
    struct action onrestart; 
    int *keycodes;
    int nkeycodes;
    int keychord_id;
    int ioprio_class;
    int ioprio_pri;
    int nargs;
    char *args[1];
}

2.parse_service解析service

static void *parse_service(struct parse_state *state, int nargs, char **args)
{
    struct service *svc;
    if (nargs < 3) {	//至少要三個service 服務名 應用程序路徑
        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]);	//根據服務名找到service
    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);	//分配service和參數內存
    if (!svc) {
        parse_error(state, "out of memory\n");
        return 0;
    }
    svc->name = args[1];	//服務名
    svc->classname = "default";  //默認設置service->classname爲"default",在init.rc有一句class_start default
    memcpy(svc->args, args + 2, sizeof(char*) * nargs);	//複製參數,參數中移除argc[0]和argc[1],從argc[2]複製起
    svc->args[nargs] = 0;	//最後一個參數值爲0表示參數結束
    svc->nargs = nargs;	//設置服務的參數個數爲更新後的參數個數
    svc->onrestart.name = "onrestart";
    list_init(&svc->onrestart.commands);	//初始化命令鏈表
    list_add_tail(&service_list, &svc->slist);	//添加到全局service_list鏈表中
    return svc;
}

3.parse_line_service解析service的選項

static void parse_line_service(struct parse_state *state, int nargs, char **args)	//解析service的選項options
{
    struct service *svc = state->context;
    struct command *cmd;
    int i, kw, kw_nargs;

    if (nargs == 0) {	//參數個數爲0直接返回
        return;
    }

    svc->ioprio_class = IoSchedClass_NONE;

    kw = lookup_keyword(args[0]);	//根據參數0獲取關鍵詞(選項Options)
    switch (kw) {
    case K_capability:	//capability
        break;
    case K_class:		//class
        if (nargs != 2) {
            parse_error(state, "class option requires a classname\n");
        } else {
            svc->classname = args[1];
        }
        break;
    case K_console:		//console
        svc->flags |= SVC_CONSOLE;
        break;
    case K_disabled:	//disabled
        svc->flags |= SVC_DISABLED;
        break;
    case K_ioprio:		//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:	//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:	//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:		//oneshot
        svc->flags |= SVC_ONESHOT;
        break;
    case K_onrestart:	//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:	//critical
        svc->flags |= SVC_CRITICAL;
        break;
    case K_setenv: 		//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: 		//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:		//user
        if (nargs != 2) {
            parse_error(state, "user option requires a user id\n");
        } 
		else {
            svc->uid = decode_uid(args[1]);
        }
        break;
    default:
        parse_error(state, "invalid option '%s'\n", args[0]);
    }
}

四、action的處理
1.相關結構體

struct action {
    struct listnode alist;
    struct listnode qlist;
    struct listnode tlist;
    unsigned hash;
    const char *name;
    struct listnode commands;
    struct command *current;
};
struct command
{
    struct listnode clist;
    int (*func)(int nargs, char **args);
    int nargs;
    char *args[1];
};

2.parse_action解析action

static void *parse_action(struct parse_state *state, int nargs, char **args)
{
    struct action *act;
    if (nargs < 2) {	//on類型的參數必須爲2個 args[0]=on,args[1]=觸發條件[trigger]
        parse_error(state, "actions must have a trigger\n");
        return 0;
    }
    if (nargs > 2) {	//多了也不行
        parse_error(state, "actions may not have extra parameters\n");
        return 0;
    }
    act = calloc(1, sizeof(*act));	//分配action結構體內存
    act->name = args[1];
    list_init(&act->commands)	//初始化命令鏈表;
    list_add_tail(&action_list, &act->alist);	//添加到全局action_list鏈表中
    return act;
}

3.parse_line_action解析command

static void parse_line_action(struct parse_state* state, int nargs, char **args)	//解析command
{
    struct command *cmd;
    struct action *act = state->context;
    int (*func)(int nargs, char **args);
    int kw, n;

    if (nargs == 0) {	//參數個數爲0直接返回
        return;
    }

    kw = lookup_keyword(args[0]);	//根據參數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);	//添加到state->context鏈表
}



五、service和action的執行

前面分析了:init解析init.rc文件並篩選出service和action,分別設置其結構體成員並添加進各自的全局鏈表service_list和action_list中

接着分析下service和action的執行

在init的main函數中除了解析init.rc(init_parse_config_file("/init.rc");)

還解析了跟硬件相關的

 get_hardware_name(hardware, &revision);
    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
    init_parse_config_file(tmp);

解析的結果都同樣保存在全局鏈表service_list和action_list中

接着會有以下幾行函數

action_for_each_trigger("early-init", action_add_queue_tail);
queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
queue_builtin_action(property_init_action, "property_init");
queue_builtin_action(keychord_init_action, "keychord_init");
queue_builtin_action(console_init_action, "console_init");
queue_builtin_action(set_init_properties_action, "set_init_properties");
action_for_each_trigger("init", action_add_queue_tail);
action_for_each_trigger("early-fs", action_add_queue_tail);
action_for_each_trigger("fs", action_add_queue_tail);
action_for_each_trigger("post-fs", action_add_queue_tail);
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(signal_init_action, "signal_init");
queue_builtin_action(check_startup_action, "check_startup");
action_for_each_trigger("early-boot", action_add_queue_tail);
action_for_each_trigger("boot", action_add_queue_tail);
queue_builtin_action(queue_property_triggers_action, "queue_propety_triggers");

1.action_for_each_trigger

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) {		//遍歷全局action_list
        act = node_to_item(node, struct action, alist);
        if (!strcmp(act->name, trigger)) {	//比較名字和trigger觸發條件
            func(act);	//符合觸發條件,則執行action_add_queue_tail函數
        }
    }
}

1.1 action_add_queue_tail

void action_add_queue_tail(struct action *act)
{
    list_add_tail(&action_queue, &act->qlist);
}

綜合上面的action_for_each_trigger函數集,可知main函數

依次處理腳本中on標誌以 early-init init early-fs fs post-fs early-boot boot的trigger的action

執行action_add_queue_tail函數將action添加到全局action_queue隊列鏈表

2.queue_builtin_action

void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
{
    struct action *act;
    struct command *cmd;

    act = calloc(1, sizeof(*act));	//分配action內存
    act->name = name;	//設置名字
    list_init(&act->commands);	//初始化command鏈表

    cmd = calloc(1, sizeof(*cmd));	//分配command
    cmd->func = func;	//設置執行函數爲傳遞的第一個參數
    cmd->args[0] = name;	//設置command的參數0
    list_add_tail(&act->commands, &cmd->clist);	//添加command到action的commands鏈表中

    list_add_tail(&action_list, &act->alist);	//添加action到全局action_list鏈表中
    action_add_queue_tail(act);	//添加action到全局action_queue隊列鏈表
}


創建以name標記的action,並添加進全局action_list中,同時添加進action_queue隊列鏈表

所以全局action_queue隊列鏈表中的順序調整爲

early-init [wait_for_coldboot_done] [property_init] [keychord_init] [console_init] [set_init_properties]

init early-fs fs post-fs [property_service_init] [signal_init] [check_startup] early-boot boot [queue_propety_triggers]

接着進入main的for(;;)循環體

3.execute_one_command執行一個command

void execute_one_command(void)
{
    int ret;
	//第一次執行cur_action爲0,執行完某個action下的所有command後cur_command爲0,條件爲真
    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
        cur_action = action_remove_queue_head();	//獲取下一個action
        cur_command = NULL;
        if (!cur_action)
            return;
        INFO("processing action %p (%s)\n", cur_action, cur_action->name);
        cur_command = get_first_command(cur_action);	//獲取action的第一個command
    } 
	else {	//循環執行某個action下的所有command
        cur_command = get_next_command(cur_action, cur_command);	//獲取action的下一個command
    }

    if (!cur_command)	//沒有command可取了,則cur_command=0
        return;			//返回

    ret = cur_command->func(cur_command->nargs, cur_command->args);	//執行command的處理函數
    INFO("command '%s' r=%d\n", cur_command->args[0], ret);
}

因爲execute_one_command套在for(;;)中所以會循環執行action_queue隊列鏈表下的action的command

4.service_list下的服務的執行

4.1service由service_start啓動

搜索代碼遍歷調用service_start的分支:

KEYWORD(class_start, COMMAND, 1, do_class_start)
do_class_start->service_for_each_class->service_start_if_not_disabled->service_start

KEYWORD(start,       COMMAND, 1, do_start)
do_start->service_start

KEYWORD(restart,     COMMAND, 1, do_restart)
do_restart->service_start

main[for(;;)]->restart_processes->service_for_each_flags->restart_service_if_needed->service_start

main[for(;;)]->handle_property_set_fd->handle_control_message->msg_start->service_start

main[for(;;)]->handle_keychord->service_start

前三個分支是由處理command是調用的處理函數do_XXX調用的,也就是execute_one_command函數的處理過程會調用到
後三個分支是在init的main函數的循環體中調用
在init.rc中有一句class_start default
作爲class_start命令在處理時會調用到do_class_start

int do_class_start(int nargs, char **args)	//argc[1]=default
{
    service_for_each_class(args[1], service_start_if_not_disabled);
    return 0;
}

service_for_each_class根據class類名查找service

void service_for_each_class(const char *classname,void (*func)(struct service *svc))
{
    struct listnode *node;
    struct service *svc;
    list_for_each(node, &service_list) {	//遍歷全局service_list
        svc = node_to_item(node, struct service, slist);
        if (!strcmp(svc->classname, classname)) {	//在前面parse_service函數中,將svc->classname = "default",所以爲真
            func(svc);	//調用service_start_if_not_disabled
        }
    }
}

service_start_if_not_disabled啓動沒有禁止啓動的service

static void service_start_if_not_disabled(struct service *svc)
{
    if (!(svc->flags & SVC_DISABLED)) {	//若沒設置SVC_DISABLED標誌,也就是init.rc中對應的service的options沒有disabled
        service_start(svc, NULL);	//則會啓動服務
    }
}

到這裏重要的正規的service就已經啓動了

5.service_start的簡單解析

void service_start(struct service *svc, const char *dynamic_args)
{
    struct stat s;
    pid_t pid;
    int needs_console;
    int n;

    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING));	//去掉禁用和重啓標誌
    svc->time_started = 0;
    
    if (svc->flags & SVC_RUNNING) {	//若已經運行了,則直接返回
        return;
    }

    needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;	//需要console
    if (needs_console && (!have_console)) {	//需要但沒有console
        ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if (stat(svc->args[0], &s) != 0) {
        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {	//沒one-shot標誌但使用了動態參數
        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",svc->args[0]);
        svc->flags |= SVC_DISABLED;
        return;
    }

    NOTICE("starting '%s'\n", svc->name);	//打印信息

    pid = fork();	//創建進程

    if (pid == 0) {	//子進程
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        if (properties_inited()) {
            get_property_workspace(&fd, &sz);
            sprintf(tmp, "%d,%d", dup(fd), sz);
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }

        for (ei = svc->envvars; ei; ei = ei->next)
            add_environment(ei->name, ei->value);

        for (si = svc->sockets; si; si = si->next) {
            int socket_type = (!strcmp(si->type, "stream") ? SOCK_STREAM :(!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
            int s = create_socket(si->name, socket_type,si->perm, si->uid, si->gid);
            if (s >= 0) {
                publish_socket(si->name, s);
            }
        }

        if (svc->ioprio_class != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
            }
        }

        if (needs_console) {	//需要控制檯
            setsid();
            open_console();	//打開控制檯
        } else {
            zap_stdio();
        }

        setpgid(0, getpid());

        if (svc->gid) {
            setgid(svc->gid);
        }
        if (svc->nr_supp_gids) {
            setgroups(svc->nr_supp_gids, svc->supp_gids);
        }
        if (svc->uid) {
            setuid(svc->uid);
        }

        if (!dynamic_args) {
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {//service 路徑參數對應的應用程序
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }
        } 
		else {
            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
            int arg_idx = svc->nargs;
            char *tmp = strdup(dynamic_args);
            char *next = tmp;
            char *bword;

            /* Copy the static arguments */
            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));

            while((bword = strsep(&next, " "))) {
                arg_ptrs[arg_idx++] = bword;
                if (arg_idx == INIT_PARSER_MAXARGS)
                    break;
            }
            arg_ptrs[arg_idx] = '\0';
            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);	//service 路徑參數對應的應用程序
        }
        _exit(127);
    }

    if (pid < 0) {
        ERROR("failed to start '%s'\n", svc->name);
        svc->pid = 0;
        return;
    }

    svc->time_started = gettime();	//記錄service啓動時間
    svc->pid = pid;	//記錄service的pid號
    svc->flags |= SVC_RUNNING;	//添加已運行標誌SVC_RUNNING

    if (properties_inited())
        notify_service_state(svc->name, "running");
}

這裏execve(svc->args[0], (char**) arg_ptrs, (char**) ENV)中args[0]是init.rc中service <name> <pathname> [ <argument> ]*的<pathname>,

arg_ptrs指向[ <argument> ]*

例如:service servicemanager /system/bin/servicemanager就會執行execve(/system/bin/servicemanager,...........)











 

 

發佈了85 篇原創文章 · 獲贊 12 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章