android—init.rc中on property的触发

来源:互联网 发布:mac os 10.13更新内容 编辑:程序博客网 时间:2024/05/29 15:42

  • init中的处理过程
  • 设置属性时发生了什么


在前面的文章《android—init.rc的读取 》中主要介绍了init.rc文件的读取,本文主要分析init针对on property类型的属性触发的原理。

init中的处理过程

在init.c的main()中,

int main(int argc, char **argv){    //添加property相关的action    queue_builtin_action(property_service_init_action, "property_service_init");}void queue_builtin_action(int (*func)(int nargs, char **args), char *name){    struct action *act;    struct command *cmd;    act = calloc(1, sizeof(*act));    act->name = name;    list_init(&act->commands);    list_init(&act->qlist);    cmd = calloc(1, sizeof(*cmd));    cmd->func = func;    cmd->args[0] = name;    list_add_tail(&act->commands, &cmd->clist);    list_add_tail(&action_list, &act->alist);    //该action放到全局的action_queue链表中    action_add_queue_tail(act);}

那么当init在执行for循环,逐个执行action中的commands时便会执行到上面的property_service_init_action()函数,

static int property_service_init_action(int nargs, char **args){    /* read any property files on system or data and     * fire up the property service.  This must happen     * after the ro.foo properties are set above so     * that /data/local.prop cannot interfere with them.     */    start_property_service();    return 0;}void start_property_service(void){    int fd;    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);    load_override_properties();    /* Read persistent properties after all default values have been loaded. */    load_persistent_properties();    //PROP_SERVICE_NAME为 "property_service"    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);    if(fd < 0) return;    fcntl(fd, F_SETFD, FD_CLOEXEC);    fcntl(fd, F_SETFL, O_NONBLOCK);    listen(fd, 8);    property_set_fd = fd;}/* * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR * ("/dev/socket") as dictated in init.rc. This socket is inherited by the * daemon. We communicate the file descriptor's value via the environment * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo"). */int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid){    struct sockaddr_un addr;    int fd, ret;    char *secon;    fd = socket(PF_UNIX, type, 0);    if (fd < 0) {        ERROR("Failed to open socket '%s': %s\n", name, strerror(errno));        return -1;    }    memset(&addr, 0 , sizeof(addr));    addr.sun_family = AF_UNIX;    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",             name);    ret = unlink(addr.sun_path);    if (ret != 0 && errno != ENOENT) {        ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno));        goto out_close;    }    secon = NULL;    if (sehandle) {        ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK);        if (ret == 0)            setfscreatecon(secon);    }    ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));    if (ret) {        ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));        goto out_unlink;    }    setfscreatecon(NULL);    freecon(secon);    chown(addr.sun_path, uid, gid);    chmod(addr.sun_path, perm);    INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",         addr.sun_path, perm, uid, gid);    return fd;out_unlink:    unlink(addr.sun_path);out_close:    close(fd);    return -1;}

其实init就是为property创建了一个域套接字,如下所示,名字为property_service,

这里写图片描述

在创建完域套接字后,init利用poll监听了这个fd,

        if (!property_set_fd_init && get_property_set_fd() > 0) {            ufds[fd_count].fd = get_property_set_fd();            ufds[fd_count].events = POLLIN;            ufds[fd_count].revents = 0;            fd_count++;            property_set_fd_init = 1;        }
 nr = poll(ufds, fd_count, timeout);        if (nr <= 0)            continue;        for (i = 0; i < fd_count; i++) {            if (ufds[i].revents == POLLIN) {                if (ufds[i].fd == get_property_set_fd())                    //该描述符有数据时,执行handle_property_set_fd                    handle_property_set_fd();                else if (ufds[i].fd == get_keychord_fd())                    handle_keychord();                else if (ufds[i].fd == get_signal_fd())                    handle_signal();            }        }

下面是处理函数handle_property_set_fd(),基本原理就是从域套接字中读取数据,也就是属性name和value,

void handle_property_set_fd(){    prop_msg msg;    int s;    int r;    int res;    struct ucred cr;    struct sockaddr_un addr;    socklen_t addr_size = sizeof(addr);    socklen_t cr_size = sizeof(cr);    char * source_ctx = NULL;    if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {        return;    }    /* Check socket options here */    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {        close(s);        ERROR("Unable to receive socket options\n");        return;    }    r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0));    if(r != sizeof(prop_msg)) {        ERROR("sys_prop: mis-match msg size received: %d expected: %d errno: %d\n",              r, sizeof(prop_msg), errno);        close(s);        return;    }    switch(msg.cmd) {    case PROP_MSG_SETPROP:        msg.name[PROP_NAME_MAX-1] = 0;        msg.value[PROP_VALUE_MAX-1] = 0;    //property名字要合法        if (!is_legal_property_name(msg.name, strlen(msg.name))) {            ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);            close(s);            return;        }        getpeercon(s, &source_ctx);        if(memcmp(msg.name,"ctl.",4) == 0) {            // Keep the old close-socket-early behavior when handling            // ctl.* properties.            close(s);            if (check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)) {                handle_control_message((char*) msg.name + 4, (char*) msg.value);            } else {                ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",                        msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);            }        } else {            if (check_perms(msg.name, cr.uid, cr.gid, source_ctx)) {                //权限检查通过,调用property_set,                //这是init自己的property_set函数,别搞混了                property_set((char*) msg.name, (char*) msg.value);            } else {                ERROR("sys_prop: permission denied uid:%d  name:%s\n",                      cr.uid, msg.name);            }            // Note: bionic's property client code assumes that the            // property server will not close the socket until *AFTER*            // the property is written to memory.            close(s);        }        freecon(source_ctx);        break;    default:        close(s);        break;    }}//prop的名字长度不能大于32static bool is_legal_property_name(const char* name, size_t namelen){    size_t i;    bool previous_was_dot = false;    if (namelen >= PROP_NAME_MAX) return false;    if (namelen < 1) return false;    if (name[0] == '.') return false;    if (name[namelen - 1] == '.') return false;    /* Only allow alphanumeric, plus '.', '-', or '_' */    /* Don't allow ".." to appear in a property name */    for (i = 0; i < namelen; i++) {        if (name[i] == '.') {            if (previous_was_dot == true) return false;            previous_was_dot = true;            continue;        }        previous_was_dot = false;        if (name[i] == '_' || name[i] == '-') continue;        if (name[i] >= 'a' && name[i] <= 'z') continue;        if (name[i] >= 'A' && name[i] <= 'Z') continue;        if (name[i] >= '0' && name[i] <= '9') continue;        return false;    }    return true;}

接着是一些权限的检查,还有特殊的属性,例如ctrl.start以ctrl开头的,是用来start stop restart service的。接着执行property_set,这是init自己的property_set函数,别搞混了,

int property_set(const char *name, const char *value){    prop_info *pi;    int ret;    size_t namelen = strlen(name);    size_t valuelen = strlen(value);    if (!is_legal_property_name(name, namelen)) return -1;    if (valuelen >= PROP_VALUE_MAX) return -1;    pi = (prop_info*) __system_property_find(name);    //read only的是不能修改的    if(pi != 0) {        /* ro.* properties may NEVER be modified once set */        if(!strncmp(name, "ro.", 3)) return -1;        __system_property_update(pi, value, valuelen);    } else {        ret = __system_property_add(name, namelen, value, valuelen);        if (ret < 0) {            ERROR("Failed to set '%s'='%s'\n", name, value);            return ret;        }    }    //如果是设置net.开头的属性,    //如果是net.change,不做任何东西    //net.change是为了记录当前哪个net.开头的属性被设置了    //[net.change]: [net.qtaguid_enabled]    /* If name starts with "net." treat as a DNS property. */    if (strncmp("net.", name, strlen("net.")) == 0)  {        if (strcmp("net.change", name) == 0) {            return 0;        }       /*        * The 'net.change' property is a special property used track when any        * 'net.*' property name is updated. It is _ONLY_ updated here. Its value        * contains the last updated 'net.*' property.        */        property_set("net.change", name);    } else if (persistent_properties_loaded &&            strncmp("persist.", name, strlen("persist.")) == 0) {        /*         * Don't write properties to disk until after we have read all default properties         * to prevent them from being overwritten by default values.         */        write_persistent_property(name, value);    } else if (strcmp("selinux.reload_policy", name) == 0 &&               strcmp("1", value) == 0) {        selinux_reload_policy();    }    property_changed(name, value);    return 0;}void property_changed(const char *name, const char *value){    if (property_triggers_enabled)        queue_property_triggers(name, value);}void queue_property_triggers(const char *name, const char *value){    struct listnode *node;    struct action *act;    list_for_each(node, &action_list) {        act = node_to_item(node, struct action, alist);        if (!strncmp(act->name, "property:", strlen("property:"))) {            const char *test = act->name + strlen("property:");            int name_length = strlen(name);            //如果属性值相同,则将act加入到action_queue            if (!strncmp(name, test, name_length) &&                    test[name_length] == '=' &&                    (!strcmp(test + name_length + 1, value) ||                     !strcmp(test + name_length + 1, "*"))) {                action_add_queue_tail(act);            }        }    }}

从queue_property_triggers()函数中我们能看到,当属性的name和value对应上后,就会将该action添加到全局的action_queue中,等到init中for循环的执行。下面我们看看在adb在调用setprop这类命令时,发生了什么。

设置属性时发生了什么

java层或者native中调用设置属性的相关函数时,最终都会调用andorid的c库bonic中的__system_property_set()函数,

int __system_property_set(const char *key, const char *value){    int err;    prop_msg msg;    if(key == 0) return -1;    if(value == 0) value = "";    if(strlen(key) >= PROP_NAME_MAX) return -1;    if(strlen(value) >= PROP_VALUE_MAX) return -1;    memset(&msg, 0, sizeof msg);    msg.cmd = PROP_MSG_SETPROP;    strlcpy(msg.name, key, sizeof msg.name);    strlcpy(msg.value, value, sizeof msg.value);    err = send_prop_msg(&msg);    if(err < 0) {        return err;    }    return 0;}
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;//通过uniux域套接字进行交互,static int send_prop_msg(prop_msg *msg){    struct pollfd pollfds[1];    struct sockaddr_un addr;    socklen_t alen;    size_t namelen;    int s;    int r;    int result = -1;    s = socket(AF_LOCAL, SOCK_STREAM, 0);    if(s < 0) {        return result;    }    memset(&addr, 0, sizeof(addr));    namelen = strlen(property_service_socket);    strlcpy(addr.sun_path, property_service_socket, sizeof addr.sun_path);    addr.sun_family = AF_LOCAL;    alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;    if(TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen)) < 0) {        close(s);        return result;    }    r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0));    if(r == sizeof(prop_msg)) {        // We successfully wrote to the property server but now we        // wait for the property server to finish its work.  It        // acknowledges its completion by closing the socket so we        // poll here (on nothing), waiting for the socket to close.        // If you 'adb shell setprop foo bar' you'll see the POLLHUP        // once the socket closes.  Out of paranoia we cap our poll        // at 250 ms.        pollfds[0].fd = s;        pollfds[0].events = 0;        r = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */));        if (r == 1 && (pollfds[0].revents & POLLHUP) != 0) {            result = 0;        } else {            // Ignore the timeout and treat it like a success anyway.            // The init process is single-threaded and its property            // service is sometimes slow to respond (perhaps it's off            // starting a child process or something) and thus this            // times out and the caller thinks it failed, even though            // it's still getting around to it.  So we fake it here,            // mostly for ctl.* properties, but we do try and wait 250            // ms so callers who do read-after-write can reliably see            // what they've written.  Most of the time.            // TODO: fix the system properties design.            result = 0;        }    }    close(s);    return result;}

通过上面的代码,我们能看出来这里会和/dev/socket/property_service这个域套接字去send数据,send完成后,init中自然会收到该套接字相关的数据,达到交互的目的。

0 0
原创粉丝点击