对应代码 android6.0_r72,kernel 对应 linux3.18
init 进程系统属性初始化,属性导入 和 属性服务 :
property_load_boot_defaults();
start_property_service();
// system/core/init/property_service.cpp
// system/core/init/init.cpp
/system/core/init/Init.cpp
/system/core/init/signal_handler.cpp
property_load_boot_defaults 的整体调用过程如下:
// system/core/init/property_service.cpp
void property_load_boot_defaults() {
// /bionic/libc/include/sys/_system_properties.h :#define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
}
进一步调用 参数内容为: default.prop 和 NULL
// system/core/init/property_service.cpp
// filename : /default.prop
// filter : NULL
static void load_properties_from_file(const char* filename, const char* filter) {
Timer t;
std::string data;
if (read_file(filename, &data)) {
// 读取 default.prop 文件中的内容到 data 中
data.push_back('\n');
load_properties(&data[0], filter); //
}
NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
}
read_file 读取过程如下:
// system/core/init/util.cpp
// path : /default.prop
// content : data 地址
bool read_file(const char* path, std::string* content) {
content->clear();
int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC));
// TEMP_FAILURE_RETRY 宏替代开始
/*
int fd = -1;
do {
fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
} while (fd == -1 && errno == EINTR);
*/
// TEMP_FAILURE_RETRY 宏替代结束
if (fd == -1) {
return false;
}
// For security reasons, disallow world-writable
// or group-writable files.
struct stat sb;
// int fstat (int filedes,struct *buf); 将参数 filedes 所指向的文件状态复制到参数 buf 所指向的结构中(struct stat)。
if (fstat(fd, &sb) == -1) {
ERROR("fstat failed for '%s': %s\n", path, strerror(errno));
return false;
}
// IWGRP 是用户组用户写的权限 IWOTH 是非所有者或用户组用户写的权限
if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
ERROR("skipping insecure file '%s'\n", path);
return false;
}
// 将文件内容读入 content 字符串
bool okay = android::base::ReadFdToString(fd, content);
close(fd);
return okay;
}
load_properties_from_file 函数中在读取 /default.prop 文件内容成功后,就调用 load_properties 函数进行系统属性设置
// system/core/init/property_service.cpp
// data : /default.prop 文件中的内容
// filter : NULL
static void load_properties(char *data, const char *filter)
{
char *key, *value, *eol, *sol, *tmp, *fn;
size_t flen = 0;
if (filter) {
// 因为 filter 为 NULL 那么 if 条件不满足
flen = strlen(filter);
}
sol = data; // sol 指向 default.prop 文件中的内容
// char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。
while ((eol = strchr(sol, '\n'))) {
key = sol;
*eol++ = 0;
sol = eol;
while (isspace(*key)) key++;
if (*key == '#') continue;
tmp = eol - 2; // 指向 key 的最后一个字符位置
while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
// 清除掉此行字符串结尾的所有空格,此时 key 指向整行的第一个有效字符,tmp 指向整行的最后一个有效字符
// flen == 0 说明 filter 为 NULL
if (!strncmp(key, "import ", 7) && flen == 0) {
// 如果以 import 开头,说明引入了另一个 prop 定义文件,需要递归调用 load_properties_from_file 进行解析
fn = key + 7;
while (isspace(*fn)) fn++;
// 清除 key 7个字符(import 和一个空格) 之后的有效字符前的所有字符,那么实际上 fn 此时就指向一个有效的文件名
key = strchr(fn, ' '); // key 指向 fn 后第一个空格位置
if (key) {
*key++ = 0;
while (isspace(*key)) key++; // 清理多余的空格,指向空格后第一个有效的字符(包括 ‘\n’)
}
// fn 为 import 的文件名, key 为 某个参数 或者 ‘\n’
load_properties_from_file(fn, key); // 递归导入这里 import 引入的文件
} else {
value = strchr(key, '='); // 在 key 字符串中找到 “=” 后面的字符串为 value
if (!value) continue; // “=” 后面无内容,说明这个 key 没有定义, 略过这个 key
*value++ = 0; // 将 key 字符串的 “=” 位置设置为字符串结尾。此时 key 字符串便只有等号前的内容
tmp = value - 2;
while ((tmp > key) && isspace(*tmp)) *tmp-- = 0; // 清除 key 字符串尾部的无效字符
while (isspace(*value)) value++; // 略过头部的无效字符
if (flen > 0) {
if (filter[flen - 1] == '*') {
// ????
if (strncmp(key, filter, flen - 1)) continue;
} else {
if (strcmp(key, filter)) continue;
}
}
property_set(key, value); // 设置系统属性
}
}
}
属性设置函数 property_set
通过property_set()修改属性值后,init进程会检查访问权限,当权限满足要求后,则更改相应的属性值:
// system/core/init/property_service.cpp
// name : 需要设置的属性的名称
// value : 属性 name 的值
int property_set(const char* name, const char* value) {
int rc = property_set_impl(name, value);
if (rc == -1) {
ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
}
return rc;
}
调用了 property_set_impl ,property_set_impl 函数主要讲属性值写入,或者更新到共享内存中,然后当属性是 net 类型的,把 net 类型的属性名写入 net.change 属性, persist 属性写入文件,最后调用 property_changed 函数来处理, 属性改变后的触发器事件。
// system/core/init/property_service.cpp
static int property_set_impl(const char* name, const char* value) {
size_t namelen = strlen(name);
size_t valuelen = strlen(value);
if (!is_legal_property_name(name, namelen)) return -1; // 判断属性名称字符串是否符合 prop 名称规则
if (valuelen >= PROP_VALUE_MAX) return -1; // value 值字符串太长
//
if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
if (selinux_reload_policy() != 0) {
// selinux 策略导入判断
ERROR("Failed to reload policy\n");
}
} else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
if (restorecon_recursive(value) != 0) {
ERROR("Failed to restorecon_recursive %s\n", value);
}
}
prop_info* pi = (prop_info*) __system_property_find(name);
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 {
int rc = __system_property_add(name, namelen, value, valuelen);
if (rc < 0) {
return rc;
}
}
/* 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);
}
property_changed(name, value);
return 0;
}
属性名称判断 is_legal_property_name:
// is_legal_property_name 判断属性名称是否合法
// system/core/init/property_service.cpp
static bool is_legal_property_name(const char* name, size_t namelen)
{
size_t i;
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] == '.') {
// i=0 is guaranteed to never have a dot. See above.
if (name[i-1] == '.') return false;
continue;
}
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;
}
selinux_reload_policy:
// system/core/init/init.cpp
int selinux_reload_policy(void)
{
if (selinux_is_disabled()) {
// 文章《android6.0 init进程main之selinux_initialize》有作分析
return -1;
}
INFO("SELinux: Attempting to reload policy files\n");
if (selinux_android_reload_policy() == -1) {
return -1;
}
if (sehandle)
selabel_close(sehandle);
if (sehandle_prop)
selabel_close(sehandle_prop);
selinux_init_all_handles(); // 文章《android6.0 init进程main之selinux_initialize》有作分析
return 0;
}
// /external/libselinux/src/android.c
int selinux_android_reload_policy(void)
{
return selinux_android_load_policy_helper(true); // 文章《android6.0 init进程main之selinux_initialize》有作分析
}
// system/core/init/util.cpp
int restorecon_recursive(const char* pathname)
{
return selinux_android_restorecon(pathname, SELINUX_ANDROID_RESTORECON_RECURSE);
}
selinux_android_restorecon 这个函数可以让文件的上下文恢复成 file_contexts 文件里初始配置。selinux_android_restorecon 函数基于 file_context 或者 seapp_context 文件标记文件和目录。其调用 selinux_android_restorecon_common() 重新标记请求的文件和目录。
// /external/libselinux/src/android.c
int selinux_android_restorecon(const char *file, unsigned int flags)
{
return selinux_android_restorecon_common(file, NULL, -1, flags);
}
selinux_android_restorecon_common() 重新标记请求的文件和目录。
static int selinux_android_restorecon_common(const char* pathname_orig, const char *seinfo, uid_t uid, unsigned int flags)
{
bool nochange = (flags & SELINUX_ANDROID_RESTORECON_NOCHANGE) ? true : false;
bool verbose = (flags & SELINUX_ANDROID_RESTORECON_VERBOSE) ? true : false;
bool recurse = (flags & SELINUX_ANDROID_RESTORECON_RECURSE) ? true : false;
bool force = (flags & SELINUX_ANDROID_RESTORECON_FORCE) ? true : false;
bool datadata = (flags & SELINUX_ANDROID_RESTORECON_DATADATA) ? true : false;
bool issys;
bool setrestoreconlast = true;
struct stat sb;
FTS *fts;
FTSENT *ftsent;
char *pathname;
char * paths[2] = {
NULL , NULL };
int ftsflags = FTS_NOCHDIR | FTS_XDEV | FTS_PHYSICAL;
int error, sverrno;
char xattr_value[FC_DIGEST_SIZE];
ssize_t size;
if (is_selinux_enabled() <= 0)
return 0;
__selinux_once(fc_once, file_context_init);
if (!fc_sehandle)
return 0;
// convert passed-in pathname to canonical pathname
pathname = realpath(pathname_orig, NULL);
if (!pathname) {
sverrno = errno;
selinux_log(SELINUX_ERROR, "SELinux: Could not get canonical path %s restorecon: %s.\n",
pathname_orig, strerror(errno));
errno = sverrno;
error = -1;
goto cleanup;
}
paths[0] = pathname;
issys = (!strcmp(pathname, SYS_PATH)
|| !strncmp(pathname, SYS_PREFIX, sizeof(SYS_PREFIX)-1)) ? true : false;
if (!recurse) {
if (lstat(pathname, &sb) < 0) {
error = -1;
goto cleanup;
}
error = restorecon_sb(pathname, &sb, nochange, verbose, seinfo, uid);
goto cleanup;
}
/*
* Ignore restorecon_last on /data/data or /data/user
* since their labeling is based on seapp_contexts and seinfo
* assignments rather than file_contexts and is managed by
* installd rather than init.
*/
if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||
!strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
!fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME))
setrestoreconlast = false;
/* Also ignore on /sys since it is regenerated on each boot regardless. */
if (issys)
setrestoreconlast = false;
if (setrestoreconlast) {
size = getxattr(pathname, RESTORECON_LAST, xattr_value, sizeof fc_digest);
if (!force && size == sizeof fc_digest && memcmp(fc_digest, xattr_value, sizeof fc_digest) == 0) {
selinux_log(SELINUX_INFO,
"SELinux: Skipping restorecon_recursive(%s)\n",
pathname);
error = 0;
goto cleanup;
}
}
fts = fts_open(paths, ftsflags, NULL);
if (!fts) {
error = -1;
goto cleanup;
}
error = 0;
while ((ftsent = fts_read(fts)) != NULL) {
switch (ftsent->fts_info) {
case FTS_DC:
selinux_log(SELINUX_ERROR,
"SELinux: Directory cycle on %s.\n", ftsent->fts_path);
errno = ELOOP;
error = -1;
goto out;
case FTS_DP:
continue;
case FTS_DNR:
selinux_log(SELINUX_ERROR,
"SELinux: Could not read %s: %s.\n", ftsent->fts_path, strerror(errno));
fts_set(fts, ftsent, FTS_SKIP);
continue;
case FTS_NS:
selinux_log(SELINUX_ERROR,
"SELinux: Could not stat %s: %s.\n", ftsent->fts_path, strerror(errno));
fts_set(fts, ftsent, FTS_SKIP);
continue;
case FTS_ERR:
selinux_log(SELINUX_ERROR,
"SELinux: Error on %s: %s.\n", ftsent->fts_path, strerror(errno));
fts_set(fts, ftsent, FTS_SKIP);
continue;
case FTS_D:
if (issys && !selabel_partial_match(fc_sehandle, ftsent->fts_path)) {
fts_set(fts, ftsent, FTS_SKIP);
continue;
}
if (!datadata &&
(!strcmp(ftsent->fts_path, DATA_DATA_PATH) ||
!strncmp(ftsent->fts_path, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
!fnmatch(EXPAND_USER_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME))) {
// Don't label anything below this directory.
fts_set(fts, ftsent, FTS_SKIP);
// but fall through and make sure we label the directory itself
}
/* fall through */
default:
error |= restorecon_sb(ftsent->fts_path, ftsent->fts_statp, nochange, verbose, seinfo, uid);
break;
}
}
// Labeling successful. Mark the top level directory as completed.
if (setrestoreconlast && !nochange && !error)
setxattr(pathname, RESTORECON_LAST, fc_digest, sizeof fc_digest, 0);
out:
sverrno = errno;
(void) fts_close(fts);
errno = sverrno;
cleanup:
free(pathname);
return error;
}
// bionic/libc/bionic/system_properties.cpp
const prop_info *__system_property_find(const char *name)
{
if (__predict_false(compat_mode)) {
return __system_property_find_compat(name);
}
return find_property(root_node(), name, strlen(name), NULL, 0, false);
}
__predict_false 定义如下:
// system/core/include/log/log.h
#ifndef __predict_false
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
#endif
其中 :
__builtin_expect() 是 GCC (version >= 2.96) 提供给程序员使用的, 目的是将“分支转移”的信息提供给编译器, 这样编译器可以对代码进行优化, 以减少指令跳转带来的性能下降。
__builtin_expect((x),1)表示 x 的值为真的可能性更大;
__builtin_expect((x),0)表示 x 的值为假的可能性更大。
也就是说,使用likely(),执行 if 后面的语句的机会更大,使用 unlikely(),执行 else 后面的语句的机会更大。通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。
// bionic/libc/bionic/system_properties.cpp
__LIBC_HIDDEN__ const prop_info *__system_property_find_compat(const char *name)
{
prop_area_compat *pa = (prop_area_compat *)__system_property_area__;
unsigned count = pa->count;
unsigned *toc = pa->toc;
unsigned len = strlen(name);
prop_info_compat *pi;
if (len >= PROP_NAME_MAX)
return 0;
if (len < 1)
return 0;
while(count--) {
unsigned entry = *toc++;
if(TOC_NAME_LEN(entry) != len) continue;
pi = TOC_TO_INFO(pa, entry);
if(memcmp(name, pi->name, len)) continue;
return (const prop_info *)pi;
}
return 0;
}
// bionic/libc/bionic/system_properties.cpp
static inline prop_bt *root_node()
{
return reinterpret_cast<prop_bt*>(to_prop_obj(0));
}
// bionic/libc/bionic/system_properties.cpp
static void *to_prop_obj(uint_least32_t off)
{
if (off > pa_data_size)
return NULL;
if (!__system_property_area__)
return NULL;
return (__system_property_area__->data + off);
}
// bionic/libc/bionic/system_properties.cpp
static const prop_info *find_property(prop_bt *const trie, const char *name,
uint8_t namelen, const char *value, uint8_t valuelen,
bool alloc_if_needed)
{
if (!trie) return NULL;
const char *remaining_name = name;
prop_bt* current = trie;
while (true) {
const char *sep = strchr(remaining_name, '.');
const bool want_subtree = (sep != NULL);
const uint8_t substr_size = (want_subtree) ?
sep - remaining_name : strlen(remaining_name);
if (!substr_size) {
return NULL;
}
prop_bt* root = NULL;
uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed);
if (children_offset != 0) {
root = to_prop_bt(¤t->children);
} else if (alloc_if_needed) {
uint_least32_t new_offset;
root = new_prop_bt(remaining_name, substr_size, &new_offset);
if (root) {
atomic_store_explicit(¤t->children, new_offset, memory_order_release);
}
}
if (!root) {
return NULL;
}
current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
if (!current) {
return NULL;
}
if (!want_subtree)
break;
remaining_name = sep + 1;
}
uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed);
if (prop_offset != 0) {
return to_prop_info(¤t->prop);
} else if (alloc_if_needed) {
uint_least32_t new_offset;
prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset);
if (new_info) {
atomic_store_explicit(¤t->prop, new_offset, memory_order_release);
}
return new_info;
} else {
return NULL;
}
}
// bionic/libc/bionic/system_properties.cpp
static inline prop_bt *to_prop_bt(atomic_uint_least32_t* off_p) {
uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
return reinterpret_cast<prop_bt*>(to_prop_obj(off));
}
// bionic/libc/bionic/system_properties.cpp
static prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off)
{
uint_least32_t new_offset;
void *const p = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset);
if (p != NULL) {
prop_bt* bt = new(p) prop_bt(name, namelen);
*off = new_offset;
return bt;
}
return NULL;
}
// bionic/libc/bionic/system_properties.cpp
static void *allocate_obj(const size_t size, uint_least32_t *const off)
{
prop_area *pa = __system_property_area__;
const size_t aligned = BIONIC_ALIGN(size, sizeof(uint_least32_t));
if (pa->bytes_used + aligned > pa_data_size) {
return NULL;
}
*off = pa->bytes_used;
pa->bytes_used += aligned;
return pa->data + *off;
}
// bionic/libc/bionic/system_properties.cpp
static prop_bt *find_prop_bt(prop_bt *const bt, const char *name,
uint8_t namelen, bool alloc_if_needed)
{
prop_bt* current = bt;
while (true) {
if (!current) {
return NULL;
}
const int ret = cmp_prop_name(name, namelen, current->name, current->namelen);
if (ret == 0) {
return current;
}
if (ret < 0) {
uint_least32_t left_offset = atomic_load_explicit(¤t->left, memory_order_relaxed);
if (left_offset != 0) {
current = to_prop_bt(¤t->left);
} else {
if (!alloc_if_needed) {
return NULL;
}
uint_least32_t new_offset;
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
if (new_bt) {
atomic_store_explicit(¤t->left, new_offset, memory_order_release);
}
return new_bt;
}
} else {
uint_least32_t right_offset = atomic_load_explicit(¤t->right, memory_order_relaxed);
if (right_offset != 0) {
current = to_prop_bt(¤t->right);
} else {
if (!alloc_if_needed) {
return NULL;
}
uint_least32_t new_offset;
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
if (new_bt) {
atomic_store_explicit(¤t->right, new_offset, memory_order_release);
}
return new_bt;
}
}
}
}
// bionic/libc/bionic/system_properties.cpp
static inline prop_info *to_prop_info(atomic_uint_least32_t* off_p) {
uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
return reinterpret_cast<prop_info*>(to_prop_obj(off));
}
// bionic/libc/bionic/system_properties.cpp
static prop_info *new_prop_info(const char *name, uint8_t namelen,
const char *value, uint8_t valuelen, uint_least32_t *const off)
{
uint_least32_t new_offset;
void* const p = allocate_obj(sizeof(prop_info) + namelen + 1, &new_offset);
if (p != NULL) {
prop_info* info = new(p) prop_info(name, namelen, value, valuelen);
*off = new_offset;
return info;
}
return NULL;
}
// bionic/libc/bionic/system_properties.cpp
int __system_property_update(prop_info *pi, const char *value, unsigned int len)
{
prop_area *pa = __system_property_area__;
if (len >= PROP_VALUE_MAX)
return -1;
uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed);
serial |= 1;
atomic_store_explicit(&pi->serial, serial, memory_order_relaxed);
// The memcpy call here also races. Again pretend it
// used memory_order_relaxed atomics, and use the analogous
// counterintuitive fence.
atomic_thread_fence(memory_order_release);
memcpy(pi->value, value, len + 1);
atomic_store_explicit(
&pi->serial,
(len << 24) | ((serial + 1) & 0xffffff),
memory_order_release);
__futex_wake(&pi->serial, INT32_MAX);
atomic_store_explicit(
&pa->serial,
atomic_load_explicit(&pa->serial, memory_order_relaxed) + 1,
memory_order_release);
__futex_wake(&pa->serial, INT32_MAX);
return 0;
}
// bionic/libc/bionic/system_properties.cpp
int __system_property_add(const char *name, unsigned int namelen,
const char *value, unsigned int valuelen)
{
prop_area *pa = __system_property_area__;
const prop_info *pi;
if (namelen >= PROP_NAME_MAX)
return -1;
if (valuelen >= PROP_VALUE_MAX)
return -1;
if (namelen < 1)
return -1;
pi = find_property(root_node(), name, namelen, value, valuelen, true);
if (!pi)
return -1;
// There is only a single mutator, but we want to make sure that
// updates are visible to a reader waiting for the update.
atomic_store_explicit(
&pa->serial,
atomic_load_explicit(&pa->serial, memory_order_relaxed) + 1,
memory_order_release);
__futex_wake(&pa->serial, INT32_MAX);
return 0;
}
// bionic/libc/bionic/system_properties.cpp
static void write_persistent_property(const char *name, const char *value)
{
char tempPath[PATH_MAX];
char path[PATH_MAX];
int fd;
snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
fd = mkstemp(tempPath);
if (fd < 0) {
ERROR("Unable to write persistent property to temp file %s: %s\n", tempPath, strerror(errno));
return;
}
write(fd, value, strlen(value));
fsync(fd);
close(fd);
snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
if (rename(tempPath, path)) {
unlink(tempPath);
ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path);
}
}
// system/core/init/init.cpp
void property_changed(const char *name, const char *value)
{
if (property_triggers_enabled) // static int property_triggers_enabled = 0; 在 queue_property_triggers_action 函数中会设置为 1
queue_property_triggers(name, value);
}
// system/core/init/init_parser.cpp
void queue_property_triggers(const char *name, const char *value)
{
struct listnode *node, *node2;
struct action *act;
struct trigger *cur_trigger;
bool match;
int name_length;
list_for_each(node, &action_list) {
// for 循环,此括号内为循环体
act = node_to_item(node, struct action, alist);
match = !name;
list_for_each(node2, &act->triggers) {
// for 循环,此括号内为循环体
cur_trigger = node_to_item(node2, struct trigger, nlist);
if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {
const char *test = cur_trigger->name + strlen("property:");
if (!match) {
name_length = strlen(name);
if (!strncmp(name, test, name_length) &&
test[name_length] == '=' &&
(!strcmp(test + name_length + 1, value) ||
!strcmp(test + name_length + 1, "*"))) {
match = true;
continue;
}
}
const char* equals = strchr(test, '=');
if (equals) {
char prop_name[PROP_NAME_MAX + 1];
char value[PROP_VALUE_MAX];
int length = equals - test;
if (length <= PROP_NAME_MAX) {
int ret;
memcpy(prop_name, test, length);
prop_name[length] = 0;
/* does the property exist, and match the trigger value? */
ret = property_get(prop_name, value);
if (ret > 0 && (!strcmp(equals + 1, value) ||
!strcmp(equals + 1, "*"))) {
continue;
}
}
}
}
match = false;
break;
}
if (match) {
action_add_queue_tail(act);
}
}
}
// system/core/init/init_parser.cpp
void action_add_queue_tail(struct action *act)
{
if (list_empty(&act->qlist)) {
list_add_tail(&action_queue, &act->qlist); // 添加进队列
}
}
start_property_service() 会调用 epoll_ctl 设置 property fd 可读的回调函数
当某个进程A,通过property_set()修改属性值后,init进程会检查访问权限,当权限满足要求后,则更改相应的属性值,属性值一旦改变则会触发相应的触发器(即rc文件中的on开头的语句),在Android Shared Memmory(共享内存区域)中有一个_system_property_area_区域,里面记录着所有的属性值。对于进程A通过property_get()方法,获取的也是该共享内存区域的属性值。
// system/core/init/property_service.cpp
void start_property_service() {
// /bionic/libc/include/sys/_system_properties.h :#define PROP_SERVICE_NAME "property_service"
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL);
if (property_set_fd == -1) {
ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
exit(1);
}
listen(property_set_fd, 8);
// 将 property_set_fd 添加进 property_set_fd 监听,并设置回调处理函数为: handle_property_set_fd
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
// system/core/init/init.cpp
void register_epoll_handler(int fd, void (*fn)()) {
epoll_event ev;
ev.events = EPOLLIN;
ev.data.ptr = reinterpret_cast<void*>(fn);
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
ERROR("epoll_ctl failed: %s\n", strerror(errno));
}
}
回调处理函数为: handle_property_set_fd
// system/core/init/property_service.cpp
static void handle_property_set_fd()
{
prop_msg msg;
int s;
int r;
struct ucred cr;
struct sockaddr_un addr;
socklen_t addr_size = sizeof(addr);
socklen_t cr_size = sizeof(cr);
char * source_ctx = NULL;
struct pollfd ufds[1];
const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */
int nr;
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;
}
ufds[0].fd = s;
ufds[0].events = POLLIN;
ufds[0].revents = 0;
nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms)); // TEMP_FAILURE_RETRY 为 do while 循环 参数为循环体
if (nr == 0) {
ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
close(s);
return;
} else if (nr < 0) {
ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
close(s);
return;
}
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT)); // 循环读完 socket 缓存内容 // TEMP_FAILURE_RETRY 为 do while 循环 参数为循环体
if(r != sizeof(prop_msg)) {
ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
r, sizeof(prop_msg), strerror(errno));
close(s);
return;
}
// 解析 读取到的 msg 消息
switch(msg.cmd) {
case PROP_MSG_SETPROP: // 设置属性
msg.name[PROP_NAME_MAX-1] = 0;
msg.value[PROP_VALUE_MAX-1] = 0;
if (!is_legal_property_name(msg.name, strlen(msg.name))) {
// 判断属性名称字符串是否符合 prop 名称规则
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_mac_perms(msg.value, source_ctx)) {
// 判断 ctl. 开头系统属性的设置权限
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, source_ctx)) {
// 判断其他类型系统属性的设置权限
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;
}
}
// external/libselinux/src/getpeercon.c
int getpeercon(int fd, char ** context)
{
char *buf;
socklen_t size;
ssize_t ret;
size = INITCONTEXTLEN + 1;
buf = malloc(size);
if (!buf)
return -1;
memset(buf, 0, size);
ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &size);
if (ret < 0 && errno == ERANGE) {
char *newbuf;
newbuf = realloc(buf, size);
if (!newbuf)
goto out;
buf = newbuf;
memset(buf, 0, size);
ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &size);
}
out:
if (ret < 0)
free(buf);
else
*context = buf;
return ret;
}
// system/core/init/property_service.cpp
static int check_control_mac_perms(const char *name, char *sctx)
{
/*
* Create a name prefix out of ctl.
* The new prefix allows the use of the existing
* property service backend labeling while avoiding
* mislabels based on true property prefixes.
*/
char ctl_name[PROP_VALUE_MAX+4];
int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name);
if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
return 0;
return check_mac_perms(ctl_name, sctx);
}
// system/core/init/property_service.cpp
static int check_mac_perms(const char *name, char *sctx)
{
if (is_selinux_enabled() <= 0)
return 1;
char *tctx = NULL;
int result = 0;
if (!sctx)
goto err;
if (!sehandle_prop)
goto err;
if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
goto err;
if (selinux_check_access(sctx, tctx, "property_service", "set", (void*) name) == 0)
result = 1;
freecon(tctx);
err:
return result;
}
// system/core/init/init.cpp
void handle_control_message(const char *msg, const char *arg)
{
if (!strcmp(msg,"start")) {
msg_start(arg);
} else if (!strcmp(msg,"stop")) {
msg_stop(arg);
} else if (!strcmp(msg,"restart")) {
msg_restart(arg);
} else {
ERROR("unknown control msg '%s'\n", msg);
}
}
// system/core/init/property_service.cpp
static int check_perms(const char *name, char *sctx)
{
if(!strncmp(name, "ro.", 3))
name +=3;
return check_mac_perms(name, sctx);
}