From eeab491efd8f456324f88e444f228b1016712e45 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 27 Jun 2017 22:08:45 -0700 Subject: [PATCH] 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 --- init/action.cpp | 7 +++++++ init/action.h | 1 + init/builtins.cpp | 2 +- init/init.cpp | 21 ++++++++++++++++----- init/init.h | 2 ++ init/reboot.cpp | 25 ++++++++++++++++++++++++- init/service.cpp | 9 +++++++++ init/service.h | 6 ++++-- init/util.cpp | 1 + rootdir/init.rc | 4 ++++ 10 files changed, 69 insertions(+), 9 deletions(-) diff --git a/init/action.cpp b/init/action.cpp index 69003917b..4a3ab4816 100644 --- a/init/action.cpp +++ b/init/action.cpp @@ -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&& args, const std::string& filename, int line, std::string* err) { std::vector triggers(args.begin() + 1, args.end()); diff --git a/init/action.h b/init/action.h index 5cb50a741..ad15f3f42 100644 --- a/init/action.h +++ b/init/action.h @@ -104,6 +104,7 @@ class ActionManager { void ExecuteOneCommand(); bool HasMoreCommands() const; void DumpState() const; + void ClearQueue(); private: ActionManager(ActionManager const&) = delete; diff --git a/init/builtins.cpp b/init/builtins.cpp index 00ffbc3d2..75a8f1972 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -119,7 +119,7 @@ static int reboot_into_recovery(const std::vector& 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; } diff --git a/init/init.cpp b/init/init.cpp index 0562dad2b..d23e1a3c0 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -94,6 +94,7 @@ static int epoll_fd = -1; static std::unique_ptr 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) { diff --git a/init/init.h b/init/init.h index 479b77173..aaab523ee 100644 --- a/init/init.h +++ b/init/init.h @@ -44,6 +44,8 @@ bool start_waiting_for_property(const char *name, const char *value); void DumpState(); +void ResetWaitForProp(); + } // namespace init } // namespace android diff --git a/init/reboot.cpp b/init/reboot.cpp index 34c98a740..ec1ddd6f3 100644 --- a/init/reboot.cpp +++ b/init/reboot.cpp @@ -49,6 +49,7 @@ #include #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&) { + 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; } diff --git a/init/service.cpp b/init/service.cpp index f2e5d228c..f14606e02 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -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&& args, const std::string& filename, int line, std::string* err) { if (args.size() < 3) { diff --git a/init/service.h b/init/service.h index 3c7dc742c..0cc16839b 100644 --- a/init/service.h +++ b/init/service.h @@ -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& 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(); diff --git a/init/util.cpp b/init/util.cpp index 4b1894f8c..479179e41 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -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); } diff --git a/rootdir/init.rc b/rootdir/init.rc index ebbec357b..ae8683420 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -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