init: handle sys.powerctl immediately

Currently if a process sets the sys.powerctl property, init adds this
property change into the event queue, just like any other property.
The actual logic to shutdown the device is not executed until init
gets to the action associated with the property change.

This is bad for multiple reasons, but explicitly causes deadlock in
the follow scenario:

A service is started with `exec` or `exec_start`
The same service sets sys.powerctl indicating to the system to
shutdown
The same service then waits infinitely

In this case, init doesn't process any further commands until the exec
service completes, including the command to reboot the device.

This change causes init to immediately handle sys.powerctl and reboot
the device regardless of the state of the event queue, wait for exec,
or wait for property conditions.

Bug: 37209359
Bug: 37415192

Test: Init reboots normally
Test: Update verifier can reboot the system
Change-Id: Iff2295aed970840f47e56c4bacc93001b791fa35
(cherry picked from commit 98ad32a967)
This commit is contained in:
Tom Cherry 2017-04-17 16:34:20 -07:00
parent 02012596c1
commit 5ceb7b3ebd
8 changed files with 64 additions and 60 deletions

View File

@ -365,10 +365,6 @@ Commands
_options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
a comma separated string, eg: barrier=1,noauto\_da\_alloc
`powerctl`
> Internal implementation detail used to respond to changes to the
"sys.powerctl" system property, used to implement rebooting.
`restart <service>`
> Stops and restarts a running service, does nothing if the service is currently
restarting, otherwise, it just starts the service.

View File

@ -603,54 +603,6 @@ static int do_restart(const std::vector<std::string>& args) {
return 0;
}
static int do_powerctl(const std::vector<std::string>& args) {
const std::string& command = args[1];
unsigned int cmd = 0;
std::vector<std::string> cmd_params = android::base::Split(command, ",");
std::string reason_string = cmd_params[0];
std::string reboot_target = "";
bool runFsck = false;
bool commandInvalid = false;
if (cmd_params.size() > 2) {
commandInvalid = true;
} else if (cmd_params[0] == "shutdown") {
cmd = ANDROID_RB_POWEROFF;
if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
// The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
// Run fsck once the file system is remounted in read-only mode.
runFsck = true;
reason_string = cmd_params[1];
}
} else if (cmd_params[0] == "reboot") {
cmd = ANDROID_RB_RESTART2;
if (cmd_params.size() == 2) {
reboot_target = cmd_params[1];
// When rebooting to the bootloader notify the bootloader writing
// also the BCB.
if (reboot_target == "bootloader") {
std::string err;
if (!write_reboot_bootloader(&err)) {
LOG(ERROR) << "reboot-bootloader: Error writing "
"bootloader_message: "
<< err;
}
}
}
} else if (command == "thermal-shutdown") { // no additional parameter allowed
cmd = ANDROID_RB_THERMOFF;
} else {
commandInvalid = true;
}
if (commandInvalid) {
LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
return -EINVAL;
}
DoReboot(cmd, reason_string, reboot_target, runFsck);
return 0;
}
static int do_trigger(const std::vector<std::string>& args) {
ActionManager::GetInstance().QueueEventTrigger(args[1]);
return 0;
@ -962,7 +914,6 @@ BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
{"mount_all", {1, kMax, do_mount_all}},
{"mount", {3, kMax, do_mount}},
{"umount", {1, 1, do_umount}},
{"powerctl", {1, 1, do_powerctl}},
{"restart", {1, 1, do_restart}},
{"restorecon", {1, kMax, do_restorecon}},
{"restorecon_recursive", {1, kMax, do_restorecon_recursive}},

View File

@ -64,6 +64,7 @@
#include "keychords.h"
#include "log.h"
#include "property_service.h"
#include "reboot.h"
#include "service.h"
#include "signal_handler.h"
#include "ueventd.h"
@ -149,8 +150,13 @@ bool start_waiting_for_property(const char *name, const char *value)
return true;
}
void property_changed(const char *name, const char *value)
{
void property_changed(const std::string& name, const std::string& value) {
// If the property is sys.powerctl, we bypass the event queue and immediately handle it.
// This is to ensure that init will always and immediately shutdown/reboot, regardless of
// if there are other pending events to process or if init is waiting on an exec service or
// waiting on a property.
if (name == "sys.powerctl") HandlePowerctlMessage(value);
if (property_triggers_enabled)
ActionManager::GetInstance().QueuePropertyTrigger(name, value);
if (waiting_for_prop) {

View File

@ -26,7 +26,7 @@ extern struct selabel_handle *sehandle_prop;
void handle_control_message(const std::string& msg, const std::string& arg);
void property_changed(const char *name, const char *value);
void property_changed(const std::string& name, const std::string& value);
void register_epoll_handler(int fd, void (*fn)());

View File

@ -206,7 +206,7 @@ uint32_t property_set(const std::string& name, const std::string& value) {
if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
write_persistent_property(name.c_str(), value.c_str());
}
property_changed(name.c_str(), value.c_str());
property_changed(name, value);
return PROP_SUCCESS;
}

View File

@ -425,3 +425,54 @@ void DoReboot(unsigned int cmd, const std::string& reason, const std::string& re
RebootSystem(cmd, rebootTarget);
abort();
}
bool HandlePowerctlMessage(const std::string& command) {
unsigned int cmd = 0;
std::vector<std::string> cmd_params = android::base::Split(command, ",");
std::string reason_string = cmd_params[0];
std::string reboot_target = "";
bool run_fsck = false;
bool command_invalid = false;
if (cmd_params.size() > 3) {
command_invalid = true;
} else if (cmd_params[0] == "shutdown") {
cmd = ANDROID_RB_POWEROFF;
if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
// The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
// Run fsck once the file system is remounted in read-only mode.
run_fsck = true;
reason_string = cmd_params[1];
}
} else if (cmd_params[0] == "reboot") {
cmd = ANDROID_RB_RESTART2;
if (cmd_params.size() >= 2) {
reboot_target = cmd_params[1];
// When rebooting to the bootloader notify the bootloader writing
// also the BCB.
if (reboot_target == "bootloader") {
std::string err;
if (!write_reboot_bootloader(&err)) {
LOG(ERROR) << "reboot-bootloader: Error writing "
"bootloader_message: "
<< err;
}
}
// If there is an additional bootloader parameter, pass it along
if (cmd_params.size() == 3) {
reboot_target += "," + cmd_params[2];
}
}
} else if (command == "thermal-shutdown") { // no additional parameter allowed
cmd = ANDROID_RB_THERMOFF;
} else {
command_invalid = true;
}
if (command_invalid) {
LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
return false;
}
DoReboot(cmd, reason_string, reboot_target, run_fsck);
return true;
}

View File

@ -27,4 +27,7 @@
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);
#endif

View File

@ -641,9 +641,6 @@ on property:vold.decrypt=trigger_shutdown_framework
class_reset late_start
class_reset main
on property:sys.powerctl=*
powerctl ${sys.powerctl}
on property:sys.boot_completed=1
bootchart stop