Split init's source files init first stage and second stage
This is a baseline for splitting init first and second stage into their own executables. Bug: 79758715 Test: sailfish boots Change-Id: I549ad4502893b3a5e4c2a9886f66850f6d31b619
This commit is contained in:
parent
132615b803
commit
44aceed016
7 changed files with 330 additions and 221 deletions
|
@ -107,6 +107,7 @@ cc_library_static {
|
|||
"first_stage_mount.cpp",
|
||||
"import_parser.cpp",
|
||||
"init.cpp",
|
||||
"init_first_stage.cpp",
|
||||
"keychords.cpp",
|
||||
"modalias_handler.cpp",
|
||||
"parser.cpp",
|
||||
|
@ -115,6 +116,7 @@ cc_library_static {
|
|||
"property_service.cpp",
|
||||
"property_type.cpp",
|
||||
"reboot.cpp",
|
||||
"reboot_utils.cpp",
|
||||
"security.cpp",
|
||||
"selinux.cpp",
|
||||
"service.cpp",
|
||||
|
|
158
init/init.cpp
158
init/init.cpp
|
@ -18,15 +18,12 @@
|
|||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <pthread.h>
|
||||
#include <seccomp_policy.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
@ -43,8 +40,6 @@
|
|||
#include <cutils/android_reboot.h>
|
||||
#include <keyutils.h>
|
||||
#include <libavb/libavb.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <selinux/android.h>
|
||||
|
||||
#include "action_parser.h"
|
||||
#include "epoll.h"
|
||||
|
@ -53,6 +48,7 @@
|
|||
#include "keychords.h"
|
||||
#include "property_service.h"
|
||||
#include "reboot.h"
|
||||
#include "reboot_utils.h"
|
||||
#include "security.h"
|
||||
#include "selinux.h"
|
||||
#include "sigchld_handler.h"
|
||||
|
@ -417,14 +413,6 @@ static Result<Success> queue_property_triggers_action(const BuiltinArguments& ar
|
|||
return Success();
|
||||
}
|
||||
|
||||
static void global_seccomp() {
|
||||
import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) {
|
||||
if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
|
||||
LOG(FATAL) << "Failed to globally enable seccomp!";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Set the UDC controller for the ConfigFS USB Gadgets.
|
||||
// Read the UDC controller in use from "/sys/class/udc".
|
||||
// In case of multiple UDC controllers select the first one.
|
||||
|
@ -441,40 +429,6 @@ static void set_usb_controller() {
|
|||
}
|
||||
}
|
||||
|
||||
static void InstallRebootSignalHandlers() {
|
||||
// Instead of panic'ing the kernel as is the default behavior when init crashes,
|
||||
// we prefer to reboot to bootloader on development builds, as this will prevent
|
||||
// boot looping bad configurations and allow both developers and test farms to easily
|
||||
// recover.
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
sigfillset(&action.sa_mask);
|
||||
action.sa_handler = [](int signal) {
|
||||
// These signal handlers are also caught for processes forked from init, however we do not
|
||||
// want them to trigger reboot, so we directly call _exit() for children processes here.
|
||||
if (getpid() != 1) {
|
||||
_exit(signal);
|
||||
}
|
||||
|
||||
// Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
|
||||
// RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
|
||||
// and probably good enough given this is already an error case and only enabled for
|
||||
// development builds.
|
||||
RebootSystem(ANDROID_RB_RESTART2, "bootloader");
|
||||
};
|
||||
action.sa_flags = SA_RESTART;
|
||||
sigaction(SIGABRT, &action, nullptr);
|
||||
sigaction(SIGBUS, &action, nullptr);
|
||||
sigaction(SIGFPE, &action, nullptr);
|
||||
sigaction(SIGILL, &action, nullptr);
|
||||
sigaction(SIGSEGV, &action, nullptr);
|
||||
#if defined(SIGSTKFLT)
|
||||
sigaction(SIGSTKFLT, &action, nullptr);
|
||||
#endif
|
||||
sigaction(SIGSYS, &action, nullptr);
|
||||
sigaction(SIGTRAP, &action, nullptr);
|
||||
}
|
||||
|
||||
static void HandleSigtermSignal(const signalfd_siginfo& siginfo) {
|
||||
if (siginfo.ssi_pid != 0) {
|
||||
// Drop any userspace SIGTERM requests.
|
||||
|
@ -611,6 +565,8 @@ static void InitKernelLogging(char* argv[]) {
|
|||
android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
|
||||
}
|
||||
|
||||
int first_stage_main(int argc, char** argv);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (!strcmp(basename(argv[0]), "ueventd")) {
|
||||
return ueventd_main(argc, argv);
|
||||
|
@ -622,114 +578,14 @@ int main(int argc, char** argv) {
|
|||
return SubcontextMain(argc, argv, &function_map);
|
||||
}
|
||||
|
||||
if (getenv("INIT_SECOND_STAGE") == nullptr) {
|
||||
return first_stage_main(argc, argv);
|
||||
}
|
||||
|
||||
if (REBOOT_BOOTLOADER_ON_PANIC) {
|
||||
InstallRebootSignalHandlers();
|
||||
}
|
||||
|
||||
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
|
||||
|
||||
if (is_first_stage) {
|
||||
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.
|
||||
umask(0);
|
||||
|
||||
CHECKCALL(clearenv());
|
||||
CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
|
||||
// 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.
|
||||
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
|
||||
CHECKCALL(mkdir("/dev/pts", 0755));
|
||||
CHECKCALL(mkdir("/dev/socket", 0755));
|
||||
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
|
||||
#define MAKE_STR(x) __STRING(x)
|
||||
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
|
||||
#undef MAKE_STR
|
||||
// Don't expose the raw commandline to unprivileged processes.
|
||||
CHECKCALL(chmod("/proc/cmdline", 0440));
|
||||
gid_t groups[] = { AID_READPROC };
|
||||
CHECKCALL(setgroups(arraysize(groups), groups));
|
||||
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)));
|
||||
|
||||
// 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));
|
||||
|
||||
#undef CHECKCALL
|
||||
|
||||
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
|
||||
// talk to the outside world...
|
||||
InitKernelLogging(argv);
|
||||
|
||||
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!";
|
||||
|
||||
if (!DoFirstStageMount()) {
|
||||
LOG(FATAL) << "Failed to mount required partitions early ...";
|
||||
}
|
||||
|
||||
SetInitAvbVersionInRecovery();
|
||||
|
||||
// Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
|
||||
global_seccomp();
|
||||
|
||||
// Set up SELinux, loading the SELinux policy.
|
||||
SelinuxSetupKernelLogging();
|
||||
SelinuxInitialize();
|
||||
|
||||
// We're in the kernel domain, so re-exec init to transition to the init domain now
|
||||
// that the SELinux policy has been loaded.
|
||||
if (selinux_android_restorecon("/init", 0) == -1) {
|
||||
PLOG(FATAL) << "restorecon failed of /init failed";
|
||||
}
|
||||
|
||||
setenv("INIT_SECOND_STAGE", "true", 1);
|
||||
|
||||
static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
|
||||
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
|
||||
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
|
||||
|
||||
char* path = argv[0];
|
||||
char* args[] = { path, nullptr };
|
||||
execv(path, args);
|
||||
|
||||
// execv() only returns if an error happened, in which case we
|
||||
// panic and never fall through this conditional.
|
||||
PLOG(FATAL) << "execv(\"" << path << "\") failed";
|
||||
}
|
||||
|
||||
// At this point we're in the second stage of init.
|
||||
InitKernelLogging(argv);
|
||||
LOG(INFO) << "init second stage started!";
|
||||
|
||||
|
|
166
init/init_first_stage.cpp
Normal file
166
init/init_first_stage.cpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <paths.h>
|
||||
#include <seccomp_policy.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/chrono_utils.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <cutils/android_reboot.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <selinux/android.h>
|
||||
|
||||
#include "first_stage_mount.h"
|
||||
#include "reboot_utils.h"
|
||||
#include "selinux.h"
|
||||
#include "util.h"
|
||||
|
||||
using android::base::boot_clock;
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
static void GlobalSeccomp() {
|
||||
import_kernel_cmdline(false, [](const std::string& key, const std::string& value,
|
||||
bool in_qemu) {
|
||||
if (key == "androidboot.seccomp" && value == "global" && !set_global_seccomp_filter()) {
|
||||
LOG(FATAL) << "Failed to globally enable seccomp!";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int first_stage_main(int argc, char** argv) {
|
||||
if (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.
|
||||
umask(0);
|
||||
|
||||
CHECKCALL(clearenv());
|
||||
CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
|
||||
// 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.
|
||||
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
|
||||
CHECKCALL(mkdir("/dev/pts", 0755));
|
||||
CHECKCALL(mkdir("/dev/socket", 0755));
|
||||
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
|
||||
#define MAKE_STR(x) __STRING(x)
|
||||
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
|
||||
#undef MAKE_STR
|
||||
// Don't expose the raw commandline to unprivileged processes.
|
||||
CHECKCALL(chmod("/proc/cmdline", 0440));
|
||||
gid_t groups[] = {AID_READPROC};
|
||||
CHECKCALL(setgroups(arraysize(groups), groups));
|
||||
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)));
|
||||
|
||||
// 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));
|
||||
|
||||
#undef CHECKCALL
|
||||
|
||||
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
|
||||
// talk to the outside world...
|
||||
android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
|
||||
RebootSystem(ANDROID_RB_RESTART2, "bootloader");
|
||||
});
|
||||
|
||||
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!";
|
||||
|
||||
if (!DoFirstStageMount()) {
|
||||
LOG(FATAL) << "Failed to mount required partitions early ...";
|
||||
}
|
||||
|
||||
SetInitAvbVersionInRecovery();
|
||||
|
||||
// Does this need to be done in first stage init or can it be done later?
|
||||
// Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
|
||||
GlobalSeccomp();
|
||||
|
||||
// Set up SELinux, loading the SELinux policy.
|
||||
SelinuxSetupKernelLogging();
|
||||
SelinuxInitialize();
|
||||
|
||||
// Unneeded? It's an ext4 file system so shouldn't it have the right domain already?
|
||||
// We're in the kernel domain, so re-exec init to transition to the init domain now
|
||||
// that the SELinux policy has been loaded.
|
||||
if (selinux_android_restorecon("/init", 0) == -1) {
|
||||
PLOG(FATAL) << "restorecon failed of /init failed";
|
||||
}
|
||||
|
||||
setenv("INIT_SECOND_STAGE", "true", 1);
|
||||
|
||||
static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
|
||||
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
|
||||
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
|
||||
|
||||
const char* path = argv[0];
|
||||
const char* args[] = {path, nullptr};
|
||||
execv(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;
|
||||
}
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
|
@ -20,11 +20,9 @@
|
|||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <mntent.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -51,9 +49,9 @@
|
|||
#include <selinux/selinux.h>
|
||||
|
||||
#include "action_manager.h"
|
||||
#include "capabilities.h"
|
||||
#include "init.h"
|
||||
#include "property_service.h"
|
||||
#include "reboot_utils.h"
|
||||
#include "service.h"
|
||||
#include "sigchld_handler.h"
|
||||
|
||||
|
@ -159,54 +157,6 @@ static void LogShutdownTime(UmountStat stat, Timer* t) {
|
|||
<< stat;
|
||||
}
|
||||
|
||||
bool IsRebootCapable() {
|
||||
if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
|
||||
PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
|
||||
return true;
|
||||
}
|
||||
|
||||
ScopedCaps caps(cap_get_proc());
|
||||
if (!caps) {
|
||||
PLOG(WARNING) << "cap_get_proc() failed";
|
||||
return true;
|
||||
}
|
||||
|
||||
cap_flag_value_t value = CAP_SET;
|
||||
if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
|
||||
PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
|
||||
return true;
|
||||
}
|
||||
return value == CAP_SET;
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
|
||||
LOG(INFO) << "Reboot ending, jumping to kernel";
|
||||
|
||||
if (!IsRebootCapable()) {
|
||||
// On systems where init does not have the capability of rebooting the
|
||||
// device, just exit cleanly.
|
||||
exit(0);
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case ANDROID_RB_POWEROFF:
|
||||
reboot(RB_POWER_OFF);
|
||||
break;
|
||||
|
||||
case ANDROID_RB_RESTART2:
|
||||
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
|
||||
LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
|
||||
break;
|
||||
|
||||
case ANDROID_RB_THERMOFF:
|
||||
reboot(RB_POWER_OFF);
|
||||
break;
|
||||
}
|
||||
// In normal case, reboot should not return.
|
||||
PLOG(ERROR) << "reboot call returned";
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Find all read+write block devices and emulated devices in /proc/mounts
|
||||
* and add them to correpsponding list.
|
||||
*/
|
||||
|
@ -329,8 +279,15 @@ static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeo
|
|||
return stat;
|
||||
}
|
||||
|
||||
void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
|
||||
bool runFsck) {
|
||||
//* Reboot / shutdown the system.
|
||||
// cmd ANDROID_RB_* as defined in android_reboot.h
|
||||
// reason Reason string like "reboot", "shutdown,userrequested"
|
||||
// rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
|
||||
// empty string.
|
||||
// runFsck Whether to run fsck after umount is done.
|
||||
//
|
||||
static void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
|
||||
bool runFsck) {
|
||||
Timer t;
|
||||
LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
|
||||
|
||||
|
|
|
@ -22,26 +22,9 @@
|
|||
namespace android {
|
||||
namespace init {
|
||||
|
||||
// This is a wrapper around the actual reboot calls. DoReboot() should be preferred in most cases.
|
||||
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget);
|
||||
|
||||
/* Reboot / shutdown the system.
|
||||
* cmd ANDROID_RB_* as defined in android_reboot.h
|
||||
* reason Reason string like "reboot", "shutdown,userrequested"
|
||||
* rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
|
||||
* empty string.
|
||||
* runFsck Whether to run fsck after umount is done.
|
||||
*/
|
||||
void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
|
||||
bool runFsck) __attribute__((__noreturn__));
|
||||
|
||||
// Parses and handles a setprop sys.powerctl message.
|
||||
bool HandlePowerctlMessage(const std::string& command);
|
||||
|
||||
// Determines whether the system is capable of rebooting. This is conservative,
|
||||
// so if any of the attempts to determine this fail, it will still return true.
|
||||
bool IsRebootCapable();
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
||||
|
|
113
init/reboot_utils.cpp
Normal file
113
init/reboot_utils.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sys/capability.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <cutils/android_reboot.h>
|
||||
|
||||
#include "capabilities.h"
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
bool IsRebootCapable() {
|
||||
if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
|
||||
PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
|
||||
return true;
|
||||
}
|
||||
|
||||
ScopedCaps caps(cap_get_proc());
|
||||
if (!caps) {
|
||||
PLOG(WARNING) << "cap_get_proc() failed";
|
||||
return true;
|
||||
}
|
||||
|
||||
cap_flag_value_t value = CAP_SET;
|
||||
if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
|
||||
PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
|
||||
return true;
|
||||
}
|
||||
return value == CAP_SET;
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
|
||||
LOG(INFO) << "Reboot ending, jumping to kernel";
|
||||
|
||||
if (!IsRebootCapable()) {
|
||||
// On systems where init does not have the capability of rebooting the
|
||||
// device, just exit cleanly.
|
||||
exit(0);
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case ANDROID_RB_POWEROFF:
|
||||
reboot(RB_POWER_OFF);
|
||||
break;
|
||||
|
||||
case ANDROID_RB_RESTART2:
|
||||
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
|
||||
LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
|
||||
break;
|
||||
|
||||
case ANDROID_RB_THERMOFF:
|
||||
reboot(RB_POWER_OFF);
|
||||
break;
|
||||
}
|
||||
// In normal case, reboot should not return.
|
||||
PLOG(ERROR) << "reboot call returned";
|
||||
abort();
|
||||
}
|
||||
|
||||
void InstallRebootSignalHandlers() {
|
||||
// Instead of panic'ing the kernel as is the default behavior when init crashes,
|
||||
// we prefer to reboot to bootloader on development builds, as this will prevent
|
||||
// boot looping bad configurations and allow both developers and test farms to easily
|
||||
// recover.
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
sigfillset(&action.sa_mask);
|
||||
action.sa_handler = [](int signal) {
|
||||
// These signal handlers are also caught for processes forked from init, however we do not
|
||||
// want them to trigger reboot, so we directly call _exit() for children processes here.
|
||||
if (getpid() != 1) {
|
||||
_exit(signal);
|
||||
}
|
||||
|
||||
// Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
|
||||
// RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
|
||||
// and probably good enough given this is already an error case and only enabled for
|
||||
// development builds.
|
||||
RebootSystem(ANDROID_RB_RESTART2, "bootloader");
|
||||
};
|
||||
action.sa_flags = SA_RESTART;
|
||||
sigaction(SIGABRT, &action, nullptr);
|
||||
sigaction(SIGBUS, &action, nullptr);
|
||||
sigaction(SIGFPE, &action, nullptr);
|
||||
sigaction(SIGILL, &action, nullptr);
|
||||
sigaction(SIGSEGV, &action, nullptr);
|
||||
#if defined(SIGSTKFLT)
|
||||
sigaction(SIGSTKFLT, &action, nullptr);
|
||||
#endif
|
||||
sigaction(SIGSYS, &action, nullptr);
|
||||
sigaction(SIGTRAP, &action, nullptr);
|
||||
}
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
32
init/reboot_utils.h
Normal file
32
init/reboot_utils.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
// Determines whether the system is capable of rebooting. This is conservative,
|
||||
// so if any of the attempts to determine this fail, it will still return true.
|
||||
bool IsRebootCapable();
|
||||
// This is a wrapper around the actual reboot calls.
|
||||
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target);
|
||||
void InstallRebootSignalHandlers();
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
Loading…
Reference in a new issue