diff options
author | Tom Cherry <tomcherry@google.com> | 2018-08-03 13:36:18 -0700 |
---|---|---|
committer | Tom Cherry <tomcherry@google.com> | 2018-08-03 13:40:17 -0700 |
commit | 44aceed01649353af82bbc5367098b55cf3b5d69 (patch) | |
tree | bb936682d23b6a4b76dfa29ad3608272f427789d | |
parent | 132615b8031411aa731fe400ba5a8b21da834560 (diff) | |
download | system_core-44aceed01649353af82bbc5367098b55cf3b5d69.tar.gz system_core-44aceed01649353af82bbc5367098b55cf3b5d69.tar.bz2 system_core-44aceed01649353af82bbc5367098b55cf3b5d69.zip |
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
-rw-r--r-- | init/Android.bp | 2 | ||||
-rw-r--r-- | init/init.cpp | 158 | ||||
-rw-r--r-- | init/init_first_stage.cpp | 166 | ||||
-rw-r--r-- | init/reboot.cpp | 63 | ||||
-rw-r--r-- | init/reboot.h | 17 | ||||
-rw-r--r-- | init/reboot_utils.cpp | 113 | ||||
-rw-r--r-- | init/reboot_utils.h | 32 |
7 files changed, 330 insertions, 221 deletions
diff --git a/init/Android.bp b/init/Android.bp index a93bb66af..d42ab8ac5 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -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", diff --git a/init/init.cpp b/init/init.cpp index e0cce0e52..ad80c98cf 100644 --- a/init/init.cpp +++ b/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 (REBOOT_BOOTLOADER_ON_PANIC) { - InstallRebootSignalHandlers(); + if (getenv("INIT_SECOND_STAGE") == nullptr) { + return first_stage_main(argc, argv); } - 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"; + if (REBOOT_BOOTLOADER_ON_PANIC) { + InstallRebootSignalHandlers(); } - // At this point we're in the second stage of init. InitKernelLogging(argv); LOG(INFO) << "init second stage started!"; diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp new file mode 100644 index 000000000..ef9ce813c --- /dev/null +++ b/init/init_first_stage.cpp @@ -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 diff --git a/init/reboot.cpp b/init/reboot.cpp index 11507f489..74018573c 100644 --- a/init/reboot.cpp +++ b/init/reboot.cpp @@ -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; diff --git a/init/reboot.h b/init/reboot.h index 1c58bd134..07dcb6e34 100644 --- a/init/reboot.h +++ b/init/reboot.h @@ -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 diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp new file mode 100644 index 000000000..961030409 --- /dev/null +++ b/init/reboot_utils.cpp @@ -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 diff --git a/init/reboot_utils.h b/init/reboot_utils.h new file mode 100644 index 000000000..073a16a68 --- /dev/null +++ b/init/reboot_utils.h @@ -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 |