1. 該文件位於 rk3399pro\device\rockchip\common\ueventd.rockchip.rc, rc只是一個資源文件,類似於xml,
以gps爲例:
/dev/ttyS1 0660 gps gps
分別對應設備節點,設備權限,設備所有者,設備所有者所在組。
2. 在它被解析使用前,還有一個init.rc文件會用到,位於rk3399pro\system\core\rootdir\init.rc(在recovery內也有一個一樣的文件init.rc,而正常啓動時只會使用rootdir中的init.rc,這裏的是在刷機時用到)。
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
service爲Android初始化語言的四種類型聲明之一(其他三種分別爲action-行爲, command-命令, option-選項)
service表示服務程序,在init進程中啓動,一般在另外一個由init 進程fork出的子進程中運行,在啓動前會檢測對應的可執行文件是否存在。
它的語法格式如下:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
因此,有上面ueventd服務程序可知,
a.該服務的名稱爲 ueventd (在代碼中直接使用這個名字)
b.該服務的對應的程序位置爲 /sbin/ueventd
c.該服務無可選參數
d.該服務設置的選項有四個:
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
① 選項class core,表示指定一個服務類爲core, 所有同一個類的服務可以同時啓動和停止,若不通過class選項指定一個類,則默認爲“default”類服務,它的語法格式爲:
class <name>
② 選項critical,表示這是一個非常重要的服務,若該服務四分鐘內退出大於四次,系統將會重啓並進入Recovery模式。
③選項seclabel u:r:ueventd:s0, 表示ueventd的SELinux安全上下文名
④選項shutdown critical,表示關機時,通過什麼行爲來殺死服務進程。若未指定,則表示關機時通過信號SIGTERM或SIGKILL來殺死服務進程。而指定爲critical,則表示關機超時纔去殺死服務進程,而在關機開始時不殺進程。
所有服務所支持的選項詳細解釋在rk3399pro\system\core\init\README.md中有。
3. 節點會在init進程中被解析使用,init進程位於 rk3399pro\system\core\init\init.cpp:
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
if (argc > 1 && !strcmp(argv[1], "subcontext")) {
InitKernelLogging(argv);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
...
}
其中的 !strcmp(basename(argv[0]), "ueventd") 就表示找到ueventd服務並執行ueventd_main()。這裏主要工作是根據接收到的uevent來創建或刪除設備(比如/dev/ttyS1),它最終也是調用到mknod。
4. 函數ueventd_main() 在rk3399pro\system\core\init\ueventd.cpp中定義:
int ueventd_main(int argc, char** argv) {
/*
* init sets the umask to 077 for forked processes. We need to
* create files with exact permissions, without modification by
* the umask.
*/
umask(000);
InitKernelLogging(argv);
LOG(INFO) << "ueventd started!";
SelinuxSetupKernelLogging();
SelabelInitialize();
DeviceHandler device_handler = CreateDeviceHandler();
UeventListener uevent_listener;
if (access(COLDBOOT_DONE, F_OK) != 0) {
ColdBoot cold_boot(uevent_listener, device_handler);
cold_boot.Run();
}
// We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.
signal(SIGCHLD, SIG_IGN);
// Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
// for SIGCHLD above.
while (waitpid(-1, nullptr, WNOHANG) > 0) {
}
uevent_listener.Poll([&device_handler](const Uevent& uevent) {
HandleFirmwareEvent(uevent);
device_handler.HandleDeviceEvent(uevent);
return ListenerAction::kContinue;
});
return 0;
}
5. 通過Poll監聽uevent,調用HandleDeviceEvent()來解析對應的設備信息, 它在文件rk3399pro\system\core\init\devices.cpp中:
void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
FixupSysPermissions(uevent.path, uevent.subsystem);
}
// if it's not a /dev device, nothing to do
if (uevent.major < 0 || uevent.minor < 0) return;
std::string devpath;
std::vector<std::string> links;
bool block = false;
if (uevent.subsystem == "block") {
block = true;
devpath = "/dev/block/" + Basename(uevent.path);
if (StartsWith(uevent.path, "/devices")) {
links = GetBlockDeviceSymlinks(uevent);
}
} else if (const auto subsystem =
std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
subsystem != subsystems_.cend()) {
devpath = subsystem->ParseDevPath(uevent);
} else if (uevent.subsystem == "usb") {
if (!uevent.device_name.empty()) {
devpath = "/dev/" + uevent.device_name;
} else {
// This imitates the file system that would be created
// if we were using devfs instead.
// Minors are broken up into groups of 128, starting at "001"
int bus_id = uevent.minor / 128 + 1;
int device_id = uevent.minor % 128 + 1;
devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
}
} else if (StartsWith(uevent.subsystem, "usb")) {
// ignore other USB events
return;
} else {
devpath = "/dev/" + Basename(uevent.path);
}
mkdir_recursive(Dirname(devpath), 0755);
HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
}
6. 再通過HandleDevice傳入解析完的uevent信息來MakeDevice,最終是用到了mknod。
add行爲觸發設備創建動作,remove行爲觸發設備刪除動作。
HandleDevice:
void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
int major, int minor, const std::vector<std::string>& links) const {
if (action == "add") {
MakeDevice(devpath, block, major, minor, links);
for (const auto& link : links) {
if (!mkdir_recursive(Dirname(link), 0755)) {
PLOG(ERROR) << "Failed to create directory " << Dirname(link);
}
if (symlink(devpath.c_str(), link.c_str())) {
if (errno != EEXIST) {
PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
} else if (std::string link_path;
Readlink(link, &link_path) && link_path != devpath) {
PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link
<< ", which already links to: " << link_path;
}
}
}
}
if (action == "remove") {
for (const auto& link : links) {
std::string link_path;
if (Readlink(link, &link_path) && link_path == devpath) {
unlink(link.c_str());
}
}
unlink(devpath.c_str());
}
}
MakeDevice --> mknod:
void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor,
const std::vector<std::string>& links) const {
auto[mode, uid, gid] = GetDevicePermissions(path, links);
mode |= (block ? S_IFBLK : S_IFCHR);
std::string secontext;
if (!SelabelLookupFileContextBestMatch(path, links, mode, &secontext)) {
PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
return;
}
if (!secontext.empty()) {
setfscreatecon(secontext.c_str());
}
dev_t dev = makedev(major, minor);
/* Temporarily change egid to avoid race condition setting the gid of the
* device node. Unforunately changing the euid would prevent creation of
* some device nodes, so the uid has to be set with chown() and is still
* racy. Fixing the gid race at least fixed the issue with system_server
* opening dynamic input devices under the AID_INPUT gid. */
if (setegid(gid)) {
PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
goto out;
}
/* If the node already exists update its SELinux label to handle cases when
* it was created with the wrong context during coldboot procedure. */
if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
char* fcon = nullptr;
int rc = lgetfilecon(path.c_str(), &fcon);
if (rc < 0) {
PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
goto out;
}
bool different = fcon != secontext;
freecon(fcon);
if (different && lsetfilecon(path.c_str(), secontext.c_str())) {
PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
<< "' device";
}
}
out:
chown(path.c_str(), uid, -1);
if (setegid(AID_ROOT)) {
PLOG(FATAL) << "setegid(AID_ROOT) failed";
}
if (!secontext.empty()) {
setfscreatecon(nullptr);
}
}