// Configure logging based on ANDROID_LOG_TAGS environment variable.
// We need to parse a string that looks like
//
// *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
//
// The tag (or '*' for the global level) comes first, followed by a colon and a
// letter indicating the minimum priority level we're expected to log. This can
// be used to reveal or conceal logs with specific tags.
#ifdef __ANDROID__
#define INIT_LOGGING_DEFAULT_LOGGER LogdLogger()
#else
#define INIT_LOGGING_DEFAULT_LOGGER StderrLogger
#endif
void InitLogging(char* argv[],LogFunction&& logger = INIT_LOGGING_DEFAULT_LOGGER,AbortFunction&& aborter = DefaultAborter);
#undef INIT_LOGGING_DEFAULT_LOGGER
3.1.4 參考:SubcontextMain
return SubcontextMain(argc, argv, &function_map);
3.1.5 參考:selinux_setup
if (!strcmp(argv[1], "selinux_setup")) {return SetupSelinux(argv);
}
int FirstStageMain(int argc, char** argv) {// 參考: 3.2.1// init崩潰時(shí)候重啟系統(tǒng):只有userdebug 和 eng 版本的固件中, REBOOT_BOOTLOADER_ON_PANIC才會(huì)等于1if (REBOOT_BOOTLOADER_ON_PANIC) {InstallRebootSignalHandlers();}boot_clock::time_point start_time = boot_clock::now();std::vector<std::pair<std::string, int>> errors;
#define CHECKCALL(x) \if ((x) != 0) errors.emplace_back(#x " failed", errno);// Clear the umask.// 參考: 3.2.2 // 權(quán)限掩碼清0,創(chuàng)建文件(含目錄等特殊文件)時(shí),將使用默認(rèn)權(quán)限umask(0);// 清除環(huán)境變量設(shè)定CHECKCALL(clearenv());// 參考:3.2.3 設(shè)置環(huán)境變量PATHCHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));// 關(guān)鍵部分:從initramdisk中,獲取基礎(chǔ)的文件系統(tǒng)配置,然后讓rc文件解決剩下的問(wèn)題 // Get the basic filesystem setup we need put together in the initramdisk// on / and then we'll let the rc file figure out the rest.// 掛載一個(gè)基于內(nèi)存的分區(qū),掛載目錄為 /dev, 并開(kāi)始在/dev下創(chuàng)建一系列文件,包括設(shè)備節(jié)點(diǎn),CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755")); CHECKCALL(mkdir("/dev/pts", 0755));CHECKCALL(mkdir("/dev/socket", 0755));CHECKCALL(mkdir("/dev/dm-user", 0755));// 掛載devpts遠(yuǎn)程虛擬終端文件設(shè)備,文件夾里面一般是一些字符設(shè)備文件CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));// 掛載進(jìn)程文件系統(tǒng)
#define MAKE_STR(x) __STRING(x)CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR// 參考:3.2.4// 讀取內(nèi)核的配置參數(shù)// Don't expose the raw commandline to unprivileged processes.CHECKCALL(chmod("/proc/cmdline", 0440));std::string cmdline;android::base::ReadFileToString("/proc/cmdline", &cmdline);// Don't expose the raw bootconfig to unprivileged processes.// 參考:3.2.5// 讀取Android 用戶空間的配置參數(shù)chmod("/proc/bootconfig", 0440);std::string bootconfig;android::base::ReadFileToString("/proc/bootconfig", &bootconfig);// 將當(dāng)前進(jìn)程添加到 AID_READPROC 進(jìn)程組,從而擁有讀取進(jìn)程文件系統(tǒng)的權(quán)限gid_t groups[] = {AID_READPROC};CHECKCALL(setgroups(arraysize(groups), groups));// 下面繼續(xù)掛載所需的fs,創(chuàng)建所需的節(jié)點(diǎn)和目錄CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));if constexpr (WORLD_WRITABLE_KMSG) {CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));}CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));// This is needed for log wrapper, which gets called before ueventd runs.CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));// These below mounts are done in first stage init so that first stage mount can mount// subdirectories of /mnt/{vendor,product}/. Other mounts, not required by first stage mount,// should be done in rc files.// Mount staging areas for devices managed by vold// See storage config details at http://source.android.com/devices/storage/CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,"mode=0755,uid=0,gid=1000"));// /mnt/vendor is used to mount vendor-specific partitions that can not be// part of the vendor partition, e.g. because they are mounted read-write.CHECKCALL(mkdir("/mnt/vendor", 0755));// /mnt/product is used to mount product-specific partitions that can not be// part of the product partition, e.g. because they are mounted read-write.CHECKCALL(mkdir("/mnt/product", 0755));// /debug_ramdisk is used to preserve additional files from the debug ramdiskCHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,"mode=0755,uid=0,gid=0"));// /second_stage_resources is used to preserve files from first to second// stage initCHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,"mode=0755,uid=0,gid=0"))
#undef CHECKCALL// 參考:3.2.6// 將標(biāo)準(zhǔn)輸入、輸出、錯(cuò)誤重定向到/dev/null SetStdioToDevNull(argv);// 從此處開(kāi)始,就可以看到打印信息了// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually// talk to the outside world...InitKernelLogging(argv);// 先檢查下之前的過(guò)程中,是否有錯(cuò)誤發(fā)生,如有錯(cuò)誤則打印相關(guān)錯(cuò)誤日志if (!errors.empty()) {for (const auto& [error_string, error_errno] : errors) {LOG(ERROR) << error_string << " " << strerror(error_errno);}LOG(FATAL) << "Init encountered errors starting first stage, aborting";}LOG(INFO) << "init first stage started!";auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};if (!old_root_dir) {PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";}struct stat old_root_info;if (stat("/", &old_root_info) != 0) {PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";old_root_dir.reset();}auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;boot_clock::time_point module_start_time = boot_clock::now();int module_count = 0;if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,module_count)) {if (want_console != FirstStageConsoleParam::DISABLED) {LOG(ERROR) << "Failed to load kernel modules, starting console";} else {LOG(FATAL) << "Failed to load kernel modules";}}if (module_count > 0) {auto module_elapse_time = std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - module_start_time);setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1);LOG(INFO) << "Loaded " << module_count << " kernel modules took "<< module_elapse_time.count() << " ms";}bool created_devices = false;if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {if (!IsRecoveryMode()) {created_devices = DoCreateDevices();if (!created_devices){LOG(ERROR) << "Failed to create device nodes early";}}StartConsole(cmdline);}if (access(kBootImageRamdiskProp, F_OK) == 0) {std::string dest = GetRamdiskPropForSecondStage();std::string dir = android::base::Dirname(dest);std::error_code ec;if (!fs::create_directories(dir, ec) && !!ec) {LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();}if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) {LOG(FATAL) << "Can't copy " << kBootImageRamdiskProp << " to " << dest << ": "<< ec.message();}LOG(INFO) << "Copied ramdisk prop to " << dest;}// If "/force_debuggable" is present, the second-stage init will use a userdebug// sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked.if (access("/force_debuggable", F_OK) == 0) {std::error_code ec; // to invoke the overloaded copy_file() that won't throw.if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||!fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {LOG(ERROR) << "Failed to setup debug ramdisk";} else {// setenv for second-stage init to read above kDebugRamdisk* files.setenv("INIT_FORCE_DEBUGGABLE", "true", 1);}}if (ForceNormalBoot(cmdline, bootconfig)) {mkdir("/first_stage_ramdisk", 0755);// SwitchRoot() must be called with a mount point as the target, so we bind mount the// target directory to itself here.if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";}SwitchRoot("/first_stage_ramdisk");}if (!DoFirstStageMount(!created_devices)) {LOG(FATAL) << "Failed to mount required partitions early ...";}struct stat new_root_info;if (stat("/", &new_root_info) != 0) {PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";old_root_dir.reset();}if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);}SetInitAvbVersionInRecovery();setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),1);const char* path = "/system/bin/init";const char* args[] = {path, "selinux_setup", nullptr};auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);close(fd);// 以selinux_setup為參數(shù),啟動(dòng)1號(hào)進(jìn)程// 即:/system/bin/init selinux_setupexecv(path, const_cast<char**>(args));// execv() only returns if an error happened, in which case we// panic and never fall through this conditional.PLOG(FATAL) << "execv(\"" << path << "\") failed";return 1;
}
// The kernel opens /dev/console and uses that fd for stdin/stdout/stderr if there is a serial
// console enabled and no initramfs, otherwise it does not provide any fds for stdin/stdout/stderr.
// SetStdioToDevNull() is used to close these existing fds if they exist and replace them with
// /dev/null regardless.
//
// In the case that these fds are provided by the kernel, the exec of second stage init causes an
// SELinux denial as it does not have access to /dev/console. In the case that they are not
// provided, exec of any further process is potentially dangerous as the first fd's opened by that
// process will take the stdin/stdout/stderr fileno's, which can cause issues if printf(), etc is
// then used by that process.
//
// Lastly, simply calling SetStdioToDevNull() in first stage init is not enough, since first
// stage init still runs in kernel context, future child processes will not have permissions to
// access any fds that it opens, including the one opened below for /dev/null. Therefore,
// SetStdioToDevNull() must be called again in second stage init.
void SetStdioToDevNull(char** argv) {// Make stdin/stdout/stderr all point to /dev/null.int fd = open("/dev/null", O_RDWR); // NOLINT(android-cloexec-open)if (fd == -1) {int saved_errno = errno;android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);errno = saved_errno;PLOG(FATAL) << "Couldn't open /dev/null";}dup2(fd, STDIN_FILENO);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);if (fd > STDERR_FILENO) close(fd);
}
(2) dup2
intdup2(int old_fd,int new_fd){// If old_fd is equal to new_fd and a valid file descriptor, dup2 returns// old_fd without closing it. This is not true of dup3, so we have to// handle this case ourselves.if(old_fd == new_fd){if(fcntl(old_fd, F_GETFD)==-1){return-1;}return old_fd;}returnFDTRACK_CREATE(__dup3(old_fd, new_fd,0));}