init: make reboot_on_failure not apply to manually stopped services

Add a new service flag SVC_STOPPING which tracks whether a service is
being manually stopped by init, and make the "reboot_on_failure" service
setting not apply when SVC_STOPPING is set.

This is needed for devices that use FDE, because otherwise the device
reboots during the following init script fragment:

    on property:vold.decrypt=trigger_shutdown_framework
        class_reset late_start
        class_reset main
        class_reset_post_data core
        class_reset_post_data hal

... because that stops all services, including apexd which has been
marked with reboot_on_failure since
https://android-review.googlesource.com/c/platform/system/apex/+/1325212.
So init was killing apexd, then rebooting the device because apexd
"failed" due to having been killed.  Making reboot_on_failure not apply
when init stops a service itself fixes the problem.

This is one of a set of changes that is needed to get FDE working again
so that devices that launched with FDE can be upgraded to Android 12.

Bug: 186165644
Test: Tested FDE on Cuttlefish
Change-Id: I599f7ba107e6c126e8f31d0ae659f0ae672a25e4
This commit is contained in:
Eric Biggers 2021-04-28 10:15:38 -07:00
parent 362b2b7537
commit 1c51525f66
3 changed files with 10 additions and 3 deletions

View file

@ -277,6 +277,8 @@ runs the service.
CLD_EXITED or an status other than '0', reboot the system with the target specified in
_target_. _target_ takes the same format as the parameter to sys.powerctl. This is particularly
intended to be used with the `exec_start` builtin for any must-have checks during boot.
A service being stopped by init (e.g. using the `stop` or `class_reset` commands) is not
considered a failure for the purpose of this setting.
`restart_period <seconds>`
> If a non-oneshot service exits, it will be restarted at its start time plus

View file

@ -194,6 +194,8 @@ void Service::KillProcessGroup(int signal, bool report_oneshot) {
<< ") process group...";
int max_processes = 0;
int r;
flags_ |= SVC_STOPPING;
if (signal == SIGTERM) {
r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes);
} else {
@ -277,7 +279,8 @@ void Service::Reap(const siginfo_t& siginfo) {
f(siginfo);
}
if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_ &&
!(flags_ & SVC_STOPPING)) {
LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
trigger_shutdown(*on_failure_reboot_target_);
}
@ -287,7 +290,7 @@ void Service::Reap(const siginfo_t& siginfo) {
if (flags_ & SVC_TEMPORARY) return;
pid_ = 0;
flags_ &= (~SVC_RUNNING);
flags_ &= ~(SVC_RUNNING | SVC_STOPPING);
start_order_ = 0;
// Oneshot processes go into the disabled state on exit,
@ -411,7 +414,8 @@ Result<void> Service::Start() {
bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
flags_ &= (~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START |
SVC_STOPPING));
// Running processes require no additional work --- if they're in the
// process of exiting, we've ensured that they will immediately restart

View file

@ -54,6 +54,7 @@
// should not be killed during shutdown
#define SVC_TEMPORARY 0x1000 // This service was started by 'exec' and should be removed from the
// service list once it is reaped.
#define SVC_STOPPING 0x2000 // service is being stopped by init
#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups