diff options
author | Bertrand SIMONNET <bsimonnet@google.com> | 2015-12-18 11:39:59 -0800 |
---|---|---|
committer | Bertrand SIMONNET <bsimonnet@google.com> | 2016-01-08 11:12:15 -0800 |
commit | b7e03e82b89a30b09fea88eaf2a5638df1017cf6 (patch) | |
tree | 4a82e891ea154a1ac42c736e120a1b8e985c5a06 /init/builtins.cpp | |
parent | a649a7df15617e1e54cc89c2f682a5e6472bf304 (diff) |
init: Allows shutting down cleanly.
When ro.build.shutdown_timeout is set, init will send a SIGTERM signal to
all services on reboot. The normal shutdown process will continue once
all services have exited or after the shutdown timeout
(ro.build.shutdown_timeout).
If ro.build.shutdown_timeout is not set, we assume a 0s timeout.
Bug: 26216447
Test: manual: Ask to reboot. All services exit cleanly.
Change-Id: If921f6e8d87211e500ac9fa86f3e1eabe02d18cf
Diffstat (limited to 'init/builtins.cpp')
-rw-r--r-- | init/builtins.cpp | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/init/builtins.cpp b/init/builtins.cpp index 10f9d8171..d2291bb50 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -39,6 +39,7 @@ #include <selinux/label.h> #include <fs_mgr.h> +#include <android-base/parseint.h> #include <android-base/stringprintf.h> #include <cutils/partition_utils.h> #include <cutils/android_reboot.h> @@ -53,6 +54,7 @@ #include "log.h" #include "property_service.h" #include "service.h" +#include "signal_handler.h" #include "util.h" #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW @@ -62,6 +64,8 @@ // System call provided by bionic but not in any header file. extern "C" int init_module(void *, unsigned long, const char *); +static const int kTerminateServiceDelayMicroSeconds = 50000; + static int insmod(const char *filename, const char *options) { std::string module; if (!read_file(filename, &module)) { @@ -608,6 +612,42 @@ static int do_powerctl(const std::vector<std::string>& args) { return -EINVAL; } + std::string timeout = property_get("ro.build.shutdown_timeout"); + unsigned int delay = 0; + + if (android::base::ParseUint(timeout.c_str(), &delay) && delay > 0) { + Timer t; + // Ask all services to terminate. + ServiceManager::GetInstance().ForEachService( + [] (Service* s) { s->Terminate(); }); + + while (t.duration() < delay) { + ServiceManager::GetInstance().ReapAnyOutstandingChildren(); + + int service_count = 0; + ServiceManager::GetInstance().ForEachService( + [&service_count] (Service* s) { + // Count the number of services running. + // Exclude the console as it will ignore the SIGTERM signal + // and not exit. + // Note: SVC_CONSOLE actually means "requires console" but + // it is only used by the shell. + if (s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) { + service_count++; + } + }); + + if (service_count == 0) { + // All terminable services terminated. We can exit early. + break; + } + + // Wait a bit before recounting the number or running services. + usleep(kTerminateServiceDelayMicroSeconds); + } + NOTICE("Terminating running services took %.02f seconds", t.duration()); + } + return android_reboot_with_callback(cmd, 0, reboot_target, callback_on_ro_remount); } |