init: make triggering shutdown from vendor_init better

Previously, we assumed that TriggerShutdown() should never be called
from vendor_init and used property service as a back up in case it
ever did.  We have since then found out that vendor_init may indeed
call TriggerShutdown() and we want to make it just as strict as it is
in init, wherein it will immediately start the shutdown sequence
without executing any further commands.

Test: init unit tests, trigger shuttdown from init and vendor_init
Change-Id: I1f44dae801a28269eb8127879a8b7d6adff6f353
This commit is contained in:
Tom Cherry 2019-11-12 16:21:20 -08:00
parent e91c76b210
commit 18278d2e9c
11 changed files with 50 additions and 24 deletions

View file

@ -140,14 +140,7 @@ static Result<void> reboot_into_recovery(const std::vector<std::string>& options
if (!write_bootloader_message(options, &err)) {
return Error() << "Failed to set bootloader message: " << err;
}
// This function should only be reached from init and not from vendor_init, and we want to
// immediately trigger reboot instead of relaying through property_service. Older devices may
// still have paths that reach here from vendor_init, so we keep the property_set as a fallback.
if (getpid() == 1) {
TriggerShutdown("reboot,recovery");
} else {
property_set("sys.powerctl", "reboot,recovery");
}
trigger_shutdown("reboot,recovery");
return {};
}
@ -554,7 +547,7 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
// support userdata remount on FDE devices, this should never been triggered. Time to
// panic!
LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?";
TriggerShutdown("reboot,requested-userdata-remount-on-fde-device");
trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
}
ActionManager::GetInstance().QueueEventTrigger("encrypt");
return {};
@ -564,7 +557,7 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
// don't support userdata remount on FDE devices, this should never been triggered.
// Time to panic!
LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?";
TriggerShutdown("reboot,requested-userdata-remount-on-fde-device");
trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "block");
@ -1148,7 +1141,7 @@ static Result<void> do_remount_userdata(const BuiltinArguments& args) {
}
// TODO(b/135984674): check that fstab contains /data.
if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
TriggerShutdown("reboot,mount-userdata-failed");
trigger_shutdown("reboot,mount-userdata-failed");
}
if (auto result = queue_fs_event(initial_mount_fstab_return_code, true); !result) {
return Error() << "queue_fs_event() failed: " << result.error();

View file

@ -35,11 +35,6 @@
namespace android {
namespace init {
// init.h
inline void TriggerShutdown(const std::string&) {
abort();
}
// property_service.h
inline bool CanReadProperty(const std::string&, const std::string&) {
return true;

View file

@ -180,7 +180,7 @@ void ResetWaitForProp() {
waiting_for_prop.reset();
}
void TriggerShutdown(const std::string& command) {
static void TriggerShutdown(const std::string& command) {
// We can't call HandlePowerctlMessage() directly in this function,
// because it modifies the contents of the action queue, which can cause the action queue
// to get into a bad state if this function is called from a command being executed by the
@ -681,6 +681,8 @@ int SecondStageMain(int argc, char** argv) {
boot_clock::time_point start_time = boot_clock::now();
trigger_shutdown = TriggerShutdown;
SetStdioToDevNull(argv);
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";

View file

@ -31,8 +31,6 @@ namespace init {
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
Parser CreateServiceOnlyParser(ServiceList& service_list);
void TriggerShutdown(const std::string& command);
bool start_waiting_for_property(const char *name, const char *value);
void DumpState();

View file

@ -731,7 +731,7 @@ static Result<void> DoUserspaceReboot() {
auto guard = android::base::make_scope_guard([] {
// Leave shutdown so that we can handle a full reboot.
LeaveShutdown();
TriggerShutdown("reboot,abort-userspace-reboot");
trigger_shutdown("reboot,abort-userspace-reboot");
});
// Triggering userspace-reboot-requested will result in a bunch of set_prop
// actions. We should make sure, that all of them are propagated before

View file

@ -43,7 +43,6 @@
#if defined(__ANDROID__)
#include <ApexProperties.sysprop.h>
#include "init.h"
#include "mount_namespace.h"
#include "property_service.h"
#else
@ -260,7 +259,7 @@ void Service::Reap(const siginfo_t& siginfo) {
if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
TriggerShutdown(*on_failure_reboot_target_);
trigger_shutdown(*on_failure_reboot_target_);
}
if (flags_ & SVC_EXEC) UnSetExec();
@ -340,7 +339,7 @@ void Service::DumpState() const {
Result<void> Service::ExecStart() {
auto reboot_on_failure = make_scope_guard([this] {
if (on_failure_reboot_target_) {
TriggerShutdown(*on_failure_reboot_target_);
trigger_shutdown(*on_failure_reboot_target_);
}
});
@ -371,7 +370,7 @@ Result<void> Service::ExecStart() {
Result<void> Service::Start() {
auto reboot_on_failure = make_scope_guard([this] {
if (on_failure_reboot_target_) {
TriggerShutdown(*on_failure_reboot_target_);
trigger_shutdown(*on_failure_reboot_target_);
}
});

View file

@ -51,6 +51,8 @@ namespace android {
namespace init {
namespace {
std::string shutdown_command;
class SubcontextProcess {
public:
SubcontextProcess(const BuiltinFunctionMap* function_map, std::string context, int init_fd)
@ -153,6 +155,11 @@ void SubcontextProcess::MainLoop() {
<< subcontext_command.command_case();
}
if (!shutdown_command.empty()) {
reply.set_trigger_shutdown(shutdown_command);
shutdown_command.clear();
}
if (auto result = SendMessage(init_fd_, reply); !result) {
LOG(FATAL) << "Failed to send message to init: " << result.error();
}
@ -174,6 +181,8 @@ int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map
return 0;
};
trigger_shutdown = [](const std::string& command) { shutdown_command = command; };
auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
subcontext_process.MainLoop();
return 0;
@ -254,6 +263,11 @@ Result<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& sub
Restart();
return Error() << "Unable to parse message from subcontext";
}
if (subcontext_reply.has_trigger_shutdown()) {
trigger_shutdown(subcontext_reply.trigger_shutdown());
}
return subcontext_reply;
}

View file

@ -38,4 +38,6 @@ message SubcontextReply {
Failure failure = 2;
ExpandArgsReply expand_args_reply = 3;
}
optional string trigger_shutdown = 4;
}

View file

@ -26,6 +26,7 @@
#include <selinux/selinux.h>
#include "builtin_arguments.h"
#include "util.h"
using namespace std::literals;
@ -142,6 +143,18 @@ TEST(subcontext, ContextString) {
});
}
TEST(subcontext, TriggerShutdown) {
static constexpr const char kTestShutdownCommand[] = "reboot,test-shutdown-command";
static std::string trigger_shutdown_command;
trigger_shutdown = [](const std::string& command) { trigger_shutdown_command = command; };
RunTest([](auto& subcontext, auto& context_string) {
auto result = subcontext.Execute(
std::vector<std::string>{"trigger_shutdown", kTestShutdownCommand});
ASSERT_TRUE(result);
});
EXPECT_EQ(kTestShutdownCommand, trigger_shutdown_command);
}
TEST(subcontext, ExpandArgs) {
RunTest([](auto& subcontext, auto& context_string) {
auto args = std::vector<std::string>{
@ -207,6 +220,11 @@ BuiltinFunctionMap BuildTestFunctionMap() {
return Error() << args.context;
};
auto do_trigger_shutdown = [](const BuiltinArguments& args) -> Result<void> {
trigger_shutdown(args[1]);
return {};
};
// clang-format off
BuiltinFunctionMap test_function_map = {
{"return_pids_as_error", {0, 0, {true, do_return_pids_as_error}}},
@ -216,6 +234,7 @@ BuiltinFunctionMap BuildTestFunctionMap() {
{"cause_log_fatal", {0, 0, {true, do_cause_log_fatal}}},
{"generate_sane_error", {0, 0, {true, do_generate_sane_error}}},
{"return_context_as_error", {0, 0, {true, do_return_context_as_error}}},
{"trigger_shutdown", {1, 1, {true, do_trigger_shutdown}}},
};
// clang-format on
return test_function_map;

View file

@ -61,6 +61,8 @@ namespace init {
const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
void (*trigger_shutdown)(const std::string& command) = nullptr;
// DecodeUid() - decodes and returns the given string, which can be either the
// numeric or name representation, into the integer uid or gid.
Result<uid_t> DecodeUid(const std::string& name) {

View file

@ -35,6 +35,8 @@ namespace init {
static const char kColdBootDoneProp[] = "ro.cold_boot_done";
extern void (*trigger_shutdown)(const std::string& command);
Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
gid_t gid, const std::string& socketcon);