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
(cherry picked from commit eeab491efd8f456324f88e444f228b1016712e45)
diff --git a/init/action.cpp b/init/action.cpp
index 21abe02..41bd061 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -325,6 +325,13 @@
}
}
+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());
diff --git a/init/action.h b/init/action.h
index d006c50..c04076a 100644
--- a/init/action.h
+++ b/init/action.h
@@ -101,6 +101,7 @@
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 bdd86ed..fcc6092 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -117,7 +117,7 @@
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 9e238d4..403269f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -92,6 +92,7 @@
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();
@@ -156,21 +157,31 @@
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();
}
}
}
@@ -1157,7 +1168,7 @@
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 6725a70..4024cfe 100644
--- a/init/init.h
+++ b/init/init.h
@@ -39,4 +39,6 @@
void DumpState();
+void ResetWaitForProp();
+
#endif /* _INIT_INIT_H */
diff --git a/init/reboot.cpp b/init/reboot.cpp
index cdfc698..99b4922 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -48,6 +48,7 @@
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>
+#include "init.h"
#include "property_service.h"
#include "service.h"
@@ -457,6 +458,9 @@
}
} 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;
}
@@ -465,6 +469,25 @@
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;
}
diff --git a/init/service.cpp b/init/service.cpp
index 1a6474b..20ca9fe 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -1133,6 +1133,15 @@
}
}
+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) {
diff --git a/init/service.h b/init/service.h
index b9c270a..4505131 100644
--- a/init/service.h
+++ b/init/service.h
@@ -89,6 +89,7 @@
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 ServiceManager {
-public:
+ public:
static ServiceManager& GetInstance();
// Exposed for testing
@@ -208,8 +209,9 @@
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 75f81b9..dfdfeb4 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -367,6 +367,7 @@
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 c32d41b..8380c52 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -678,6 +678,10 @@
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