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:
parent
6333cd0938
commit
eeab491efd
10 changed files with 69 additions and 9 deletions
|
@ -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());
|
||||
|
|
|
@ -104,6 +104,7 @@ class ActionManager {
|
|||
void ExecuteOneCommand();
|
||||
bool HasMoreCommands() const;
|
||||
void DumpState() const;
|
||||
void ClearQueue();
|
||||
|
||||
private:
|
||||
ActionManager(ActionManager const&) = delete;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -44,6 +44,8 @@ bool start_waiting_for_property(const char *name, const char *value);
|
|||
|
||||
void DumpState();
|
||||
|
||||
void ResetWaitForProp();
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue