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
diff --git a/init/action.cpp b/init/action.cpp
index 6900391..4a3ab48 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -326,6 +326,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 5cb50a7..ad15f3f 100644
--- a/init/action.h
+++ b/init/action.h
@@ -104,6 +104,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 00ffbc3..75a8f19 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -119,7 +119,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 0562dad..d23e1a3 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -94,6 +94,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();
@@ -158,21 +159,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();
}
}
}
@@ -1135,7 +1146,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 479b771..aaab523 100644
--- a/init/init.h
+++ b/init/init.h
@@ -44,6 +44,8 @@
void DumpState();
+void ResetWaitForProp();
+
} // namespace init
} // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 34c98a7..ec1ddd6 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -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 @@
}
} 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 @@
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 f2e5d22..f14606e 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -1143,6 +1143,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 3c7dc74..0cc1683 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 4b1894f..479179e 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -370,6 +370,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 ebbec35..ae86834 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -681,6 +681,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