android6.0 init进程main之prop

18 篇文章 1 订阅
2 篇文章 0 订阅

android6.0 init进程main之prop

     对应代码 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

加载 default.prop 文件

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(&current->children, memory_order_relaxed);
        if (children_offset != 0) {
            root = to_prop_bt(&current->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(&current->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(&current->prop, memory_order_relaxed);
    if (prop_offset != 0) {
        return to_prop_info(&current->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(&current->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(&current->left, memory_order_relaxed);
            if (left_offset != 0) {
                current = to_prop_bt(&current->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(&current->left, new_offset, memory_order_release);
                }
                return new_bt;
            }
        } else {
            uint_least32_t right_offset = atomic_load_explicit(&current->right, memory_order_relaxed);
            if (right_offset != 0) {
                current = to_prop_bt(&current->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(&current->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.<service name>
     *  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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值