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中自然会收到该套接字相关的数据,达到交互的目的。
- android—init.rc中on property的触发
- Android init.rc on property
- init.rc的触发顺序
- Android中init.rc文件的解析
- Android中init.rc文件的解析
- Android中init.rc文件的解析
- Android中init.rc的初始化分析
- android 的init.rc
- Android的init.rc
- init.rc on property:vold.decrypt=trigger_reset_main 过程
- android—init.rc的读取
- Android init.rc 的執行順序
- Android init.rc 的生成
- android init.rc 的 service
- Android: 启动init.rc 中service的权限问题
- Android: 启动init.rc 中service的权限问题
- android中init.rc文件的解析问题
- android中init.rc文件的解析问题
- 【HDU5892 2016 ACM ICPC Asia Regional Shenyang Online A】【二维树状数组模板 区间修改】nn矩阵内子矩阵中各怪兽数量的奇偶性.cpp
- spring,struts2在普通类中获取session和request,及RequestContextHolder使用误区
- MFC CSocket类 通信原理
- xcode7.3.1升级iOS10 SDK
- Github与git联机问题!
- android—init.rc中on property的触发
- [iOS Xcode8报错]dyld: Library not loaded: /System/Library/Frameworks/UserNotifications.framework/UserN
- 自动化代码检查优化Lint
- 为jquery validate 添加验证失败回调
- lucene知识的整理
- 浮动回到顶部回到底部
- 基于html5的多文件上传核心骨架文件
- 前端架构组件化开发系列二 (基于VUE 扩展组件)
- 查看python模块及库函数