1. 该文件位于 rk3399prodevicerockchipcommonueventd.rockchip.rc, rc只是一个资源文件,类似于xml,
以gps为例:
/dev/ttyS1 0660 gps gps
分别对应设备节点,设备权限,设备所有者,设备所有者所在组。
2. 在它被解析使用前,还有一个init.rc文件会用到,位于rk3399prosystemcorerootdirinit.rc(在recovery内也有一个一样的文件init.rc,而正常启动时只会使用rootdir中的init.rc,这里的是在刷机时用到)。
## Daemon processes to be run by init.
##
service ueventd /***in/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
service为Android初始化语言的四种类型声明之一(其他三种分别为action-行为, command-命令, option-选项)
service表示服务程序,在init进程中启动,一般在另外一个由init 进程fork出的子进程中运行,在启动前会检测对应的可执行文件是否存在。
它的语法格式如下:
service
[ ]*
...
因此,有上面ueventd服务程序可知,
a.该服务的名称为 ueventd (在代码中直接使用这个名字)
b.该服务的对应的程序位置为 /***in/ueventd
c.该服务无可选参数
d.该服务设置的选项有四个:
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
① 选项class core,表示指定一个服务类为core, 所有同一个类的服务可以同时启动和停止,若不通过class选项指定一个类,则默认为“default”类服务,它的语法格式为:
class
② 选项critical,表示这是一个非常重要的服务,若该服务四分钟内退出大于四次,系统将会重启并进入Recovery模式。
③选项seclabel u:r:ueventd:s0, 表示ueventd的SELinux安全上下文名
④选项shutdown critical,表示关机时,通过什么行为来杀死服务进程。若未指定,则表示关机时通过信号SIGTERM或SIGKILL来杀死服务进程。而指定为critical,则表示关机超时才去杀死服务进程,而在关机开始时不杀进程。
所有服务所支持的选项详细解释在rk3399prosystemcoreinitREADME.md中有。
3. 节点会在init进程中被解析使用,init进程位于 rk3399prosystemcoreinitinit.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() 在rk3399prosystemcoreinitueventd.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()来解析对应的设备信息, 它在文件rk3399prosystemcoreinitdevices.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 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 == "u***") {
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/u***/%03d/%03d", bus_id, device_id);
}
} else if (StartsWith(uevent.subsystem, "u***")) {
// 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& 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& 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);
}
}
2022-2-15 14:18:38
评论
举报