blob: 961030409a473c7dfbcfdb46a0f52e94095e93bf [file] [log] [blame]
Tom Cherry44aceed2018-08-03 13:36:18 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <sys/capability.h>
18#include <sys/reboot.h>
19#include <sys/syscall.h>
20#include <unistd.h>
21
22#include <android-base/logging.h>
23#include <cutils/android_reboot.h>
24
25#include "capabilities.h"
26
27namespace android {
28namespace init {
29
30bool IsRebootCapable() {
31 if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
32 PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
33 return true;
34 }
35
36 ScopedCaps caps(cap_get_proc());
37 if (!caps) {
38 PLOG(WARNING) << "cap_get_proc() failed";
39 return true;
40 }
41
42 cap_flag_value_t value = CAP_SET;
43 if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {
44 PLOG(WARNING) << "cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed";
45 return true;
46 }
47 return value == CAP_SET;
48}
49
50void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
51 LOG(INFO) << "Reboot ending, jumping to kernel";
52
53 if (!IsRebootCapable()) {
54 // On systems where init does not have the capability of rebooting the
55 // device, just exit cleanly.
56 exit(0);
57 }
58
59 switch (cmd) {
60 case ANDROID_RB_POWEROFF:
61 reboot(RB_POWER_OFF);
62 break;
63
64 case ANDROID_RB_RESTART2:
65 syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
66 LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
67 break;
68
69 case ANDROID_RB_THERMOFF:
70 reboot(RB_POWER_OFF);
71 break;
72 }
73 // In normal case, reboot should not return.
74 PLOG(ERROR) << "reboot call returned";
75 abort();
76}
77
78void InstallRebootSignalHandlers() {
79 // Instead of panic'ing the kernel as is the default behavior when init crashes,
80 // we prefer to reboot to bootloader on development builds, as this will prevent
81 // boot looping bad configurations and allow both developers and test farms to easily
82 // recover.
83 struct sigaction action;
84 memset(&action, 0, sizeof(action));
85 sigfillset(&action.sa_mask);
86 action.sa_handler = [](int signal) {
87 // These signal handlers are also caught for processes forked from init, however we do not
88 // want them to trigger reboot, so we directly call _exit() for children processes here.
89 if (getpid() != 1) {
90 _exit(signal);
91 }
92
93 // Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
94 // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
95 // and probably good enough given this is already an error case and only enabled for
96 // development builds.
97 RebootSystem(ANDROID_RB_RESTART2, "bootloader");
98 };
99 action.sa_flags = SA_RESTART;
100 sigaction(SIGABRT, &action, nullptr);
101 sigaction(SIGBUS, &action, nullptr);
102 sigaction(SIGFPE, &action, nullptr);
103 sigaction(SIGILL, &action, nullptr);
104 sigaction(SIGSEGV, &action, nullptr);
105#if defined(SIGSTKFLT)
106 sigaction(SIGSTKFLT, &action, nullptr);
107#endif
108 sigaction(SIGSYS, &action, nullptr);
109 sigaction(SIGTRAP, &action, nullptr);
110}
111
112} // namespace init
113} // namespace android