summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Cherry <tomcherry@google.com>2018-08-03 13:36:18 -0700
committerTom Cherry <tomcherry@google.com>2018-08-03 13:40:17 -0700
commit44aceed01649353af82bbc5367098b55cf3b5d69 (patch)
treebb936682d23b6a4b76dfa29ad3608272f427789d
parent132615b8031411aa731fe400ba5a8b21da834560 (diff)
downloadsystem_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.bp2
-rw-r--r--init/init.cpp158
-rw-r--r--init/init_first_stage.cpp166
-rw-r--r--init/reboot.cpp63
-rw-r--r--init/reboot.h17
-rw-r--r--init/reboot_utils.cpp113
-rw-r--r--init/reboot_utils.h32
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