在研究属性服务setprop和getprop时,发现这两个可执行文件链接的是toolbox,由此分析下toolbox源码
bp文件定义
/system/core/toolbox/Android.bp
cc_defaults {
name: "toolbox_binary_defaults",
defaults: ["toolbox_defaults"],
#包含的源文件
srcs: [
"toolbox.c",
"getevent.c",
"getprop.cpp",
"modprobe.cpp",
"setprop.cpp",
"start.cpp",
],
generated_headers: [
"toolbox_input_labels",
],
shared_libs: [
"libbase",
],
static_libs: [
"libmodprobe",
"libpropertyinfoparser",
],
#创建的符号链接,这些都会链接向toolbox
symlinks: [
"getevent",
"getprop",
"modprobe",
"setprop",
"start",
"stop",
],
}
#编译成可执行文件toolbox
cc_binary {
name: "toolbox",
defaults: ["toolbox_binary_defaults"],
recovery_available: true,
}
toolbox包含6个源文件,创建了6个符号链接。
当使用上面的这些命令的时候,就会调用到toolbox中的main函数
/system/core/toolbox/toolbox.c
static struct {
const char* name;
int (*func)(int, char**);
} tools[] = {
#define TOOL(name) { #name, name##_main },
#include "tools.h"
#undef TOOL
{ 0, 0 },
};
int main(int argc, char** argv) {
// Let's assume that none of this code handles broken pipes. At least ls,
// ps, and top were broken (though I'd previously added this fix locally
// to top). We exit rather than use SIG_IGN because tools like top will
// just keep on writing to nowhere forever if we don't stop them.
signal(SIGPIPE, SIGPIPE_handler);
char* cmd = strrchr(argv[0], '/');
char* name = cmd ? (cmd + 1) : argv[0];
for (size_t i = 0; tools[i].name; i++) {
if (!strcmp(tools[i].name, name)) {
return tools[i].func(argc, argv);
}
}
printf("%s: no such tool\n", argv[0]);
return 127;
}
main函数就是遍历tools这个数组,这个数组存入的就是各个命令名及命令名加上_main对应的函数。
TOOL是一个宏定义,用于拼接命令名_mian,构造对应的处理函数。
然后通过tools.h头文件导入对应关系
/system/core/toolbox/tools.h
TOOL(getevent)
TOOL(getprop)
TOOL(modprobe)
TOOL(setprop)
TOOL(start)
TOOL(stop)
TOOL(toolbox)
以setprop为例,当使用setprop时,会走到toolbox中的main函数,根据tools这个数组,获得对应的函数并执行,setprop对应的函数为setprop_main函数。
/system/core/toolbox/setprop.cpp
extern "C" int setprop_main(int argc, char** argv) {
if (argc != 3) {
std::cout << "usage: setprop NAME VALUE\n"
"\n"
"Sets an Android system property."
<< std::endl;
return EXIT_FAILURE;
}
auto name = std::string{argv[1]};
auto value = std::string{argv[2]};
// SetProperty() doesn't tell us why it failed, and actually can't recognize most failures, so
// we duplicate some of init's checks here to help the user.
if (name.front() == '.' || name.back() == '.') {
std::cerr << "Property names must not start or end with a '.'" << std::endl;
return EXIT_FAILURE;
}
if (name.find("..") != std::string::npos) {
std::cerr << "'..' is not allowed in a property name" << std::endl;
return EXIT_FAILURE;
}
for (const auto& c : name) {
if (!isalnum(c) && !strchr(":@_.-", c)) {
std::cerr << "Invalid character '" << c << "' in name '" << name << "'" << std::endl;
return EXIT_FAILURE;
}
}
if (value.size() >= PROP_VALUE_MAX && !StartsWith(value, "ro.")) {
std::cerr << "Value '" << value << "' is too long, " << value.size()
<< " bytes vs a max of " << PROP_VALUE_MAX << std::endl;
return EXIT_FAILURE;
}
if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
std::cerr << "Value '" << value << "' is not a UTF8 encoded string" << std::endl;
return EXIT_FAILURE;
}
if (!SetProperty(name, value)) {
std::cerr << "Failed to set property '" << name << "' to '" << value
<< "'.\nSee dmesg for error reason." << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
判断合法性,调用SetProperty设置属性