init: Support custom shutdown actions

We have been seeing panics and errors during shutdown sequence in
some vendor's platform, and it is required to disable error handling
during shutdown.

This CL separates the shutdown request to execute another "shutdown"
trigger at the beginning of shutdown stage. And vendor can use this
trigger to add custom commands needed for shutting down gracefully.

Bug: 38203024
Bug: 62084631
Test: device reboot/shutdown
Change-Id: I3fac4ed59f06667d86e477ee55ed391cf113717f
This commit is contained in:
Wei Wang 2017-06-27 22:08:45 -07:00
parent 6333cd0938
commit eeab491efd
10 changed files with 69 additions and 9 deletions

View file

@ -326,6 +326,13 @@ void ActionManager::DumpState() const {
}
}
void ActionManager::ClearQueue() {
// We are shutting down so don't claim the oneshot builtin actions back
current_executing_actions_ = {};
event_queue_ = {};
current_command_ = 0;
}
bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) {
std::vector<std::string> triggers(args.begin() + 1, args.end());

View file

@ -104,6 +104,7 @@ class ActionManager {
void ExecuteOneCommand();
bool HasMoreCommands() const;
void DumpState() const;
void ClearQueue();
private:
ActionManager(ActionManager const&) = delete;

View file

@ -119,7 +119,7 @@ static int reboot_into_recovery(const std::vector<std::string>& options) {
LOG(ERROR) << "failed to set bootloader message: " << err;
return -1;
}
DoReboot(ANDROID_RB_RESTART2, "reboot", "recovery", false);
property_set("sys.powerctl", "reboot,recovery");
return 0;
}

View file

@ -94,6 +94,7 @@ static int epoll_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
static std::string wait_prop_name;
static std::string wait_prop_value;
static bool shutting_down;
void DumpState() {
ServiceManager::GetInstance().DumpState();
@ -158,21 +159,31 @@ bool start_waiting_for_property(const char *name, const char *value)
return true;
}
void ResetWaitForProp() {
wait_prop_name.clear();
wait_prop_value.clear();
waiting_for_prop.reset();
}
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);
// In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
// commands to be executed.
if (name == "sys.powerctl") {
if (HandlePowerctlMessage(value)) {
shutting_down = true;
}
}
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
if (waiting_for_prop) {
if (wait_prop_name == name && wait_prop_value == value) {
wait_prop_name.clear();
wait_prop_value.clear();
LOG(INFO) << "Wait for property took " << *waiting_for_prop;
waiting_for_prop.reset();
ResetWaitForProp();
}
}
}
@ -1135,7 +1146,7 @@ int main(int argc, char** argv) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || sm.IsWaitingForExec())) {
restart_processes();
if (!shutting_down) restart_processes();
// If there's a process that needs restarting, wake up in time for that.
if (process_needs_restart_at != 0) {

View file

@ -44,6 +44,8 @@ bool start_waiting_for_property(const char *name, const char *value);
void DumpState();
void ResetWaitForProp();
} // namespace init
} // namespace android

View file

@ -49,6 +49,7 @@
#include <private/android_filesystem_config.h>
#include "capabilities.h"
#include "init.h"
#include "property_service.h"
#include "service.h"
@ -490,6 +491,9 @@ bool HandlePowerctlMessage(const std::string& command) {
}
} else if (command == "thermal-shutdown") { // no additional parameter allowed
cmd = ANDROID_RB_THERMOFF;
// Do not queue "shutdown" trigger since we want to shutdown immediately
DoReboot(cmd, command, reboot_target, run_fsck);
return true;
} else {
command_invalid = true;
}
@ -498,7 +502,26 @@ bool HandlePowerctlMessage(const std::string& command) {
return false;
}
DoReboot(cmd, command, reboot_target, run_fsck);
LOG(INFO) << "Clear action queue and start shutdown trigger";
ActionManager::GetInstance().ClearQueue();
// Queue shutdown trigger first
ActionManager::GetInstance().QueueEventTrigger("shutdown");
// Queue built-in shutdown_done
auto shutdown_handler = [cmd, command, reboot_target,
run_fsck](const std::vector<std::string>&) {
DoReboot(cmd, command, reboot_target, run_fsck);
return 0;
};
ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
// Skip wait for prop if it is in progress
ResetWaitForProp();
// Skip wait for exec if it is in progress
if (ServiceManager::GetInstance().IsWaitingForExec()) {
ServiceManager::GetInstance().ClearExecWait();
}
return true;
}

View file

@ -1143,6 +1143,15 @@ void ServiceManager::ReapAnyOutstandingChildren() {
}
}
void ServiceManager::ClearExecWait() {
// Clear EXEC flag if there is one pending
// And clear the wait flag
for (const auto& s : services_) {
s->UnSetExec();
}
exec_waiter_.reset();
}
bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) {
if (args.size() < 3) {

View file

@ -89,6 +89,7 @@ class Service {
void DumpState() const;
void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
void UnSetExec() { flags_ &= ~SVC_EXEC; }
const std::string& name() const { return name_; }
const std::set<std::string>& classnames() const { return classnames_; }
@ -186,7 +187,7 @@ class Service {
};
class ServiceManager {
public:
public:
static ServiceManager& GetInstance();
// Exposed for testing
@ -208,8 +209,9 @@ public:
void ReapAnyOutstandingChildren();
void RemoveService(const Service& svc);
void DumpState() const;
void ClearExecWait();
private:
private:
// Cleans up a child process that exited.
// Returns true iff a children was cleaned up.
bool ReapOneProcess();

View file

@ -370,6 +370,7 @@ bool expand_props(const std::string& src, std::string* dst) {
void panic() {
LOG(ERROR) << "panic: rebooting to bootloader";
// Do not queue "shutdown" trigger since we want to shutdown immediately
DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
}

View file

@ -681,6 +681,10 @@ on property:security.perf_harden=0
on property:security.perf_harden=1
write /proc/sys/kernel/perf_event_paranoid 3
# on shutdown
# In device's init.rc, this trigger can be used to do device-specific actions
# before shutdown. e.g disable watchdog and mask error handling
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd