Merge "init: Allows shutting down cleanly."

This commit is contained in:
Bertrand Simonnet 2016-01-08 20:48:34 +00:00 committed by Gerrit Code Review
commit f9f4ee8b72
4 changed files with 110 additions and 55 deletions

View file

@ -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);
}

View file

@ -19,6 +19,7 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
@ -531,6 +532,17 @@ void Service::Stop() {
StopOrReset(SVC_DISABLED);
}
void Service::Terminate() {
flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);
flags_ |= SVC_DISABLED;
if (pid_) {
NOTICE("Sending SIGTERM to service '%s' (pid %d)...\n", name_.c_str(),
pid_);
kill(-pid_, SIGTERM);
NotifyStateChange("stopping");
}
}
void Service::Restart() {
if (flags_ & SVC_RUNNING) {
/* Stop, wait, then start the service. */
@ -724,9 +736,9 @@ Service* ServiceManager::FindServiceByKeychord(int keychord_id) const {
return nullptr;
}
void ServiceManager::ForEachService(void (*func)(Service* svc)) const {
void ServiceManager::ForEachService(std::function<void(Service*)> callback) const {
for (const auto& s : services_) {
func(s.get());
callback(s.get());
}
}
@ -767,6 +779,53 @@ void ServiceManager::DumpState() const {
INFO("\n");
}
bool ServiceManager::ReapOneProcess() {
int status;
pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
if (pid == 0) {
return false;
} else if (pid == -1) {
ERROR("waitpid failed: %s\n", strerror(errno));
return false;
}
Service* svc = FindServiceByPid(pid);
std::string name;
if (svc) {
name = android::base::StringPrintf("Service '%s' (pid %d)",
svc->name().c_str(), pid);
} else {
name = android::base::StringPrintf("Untracked pid %d", pid);
}
if (WIFEXITED(status)) {
NOTICE("%s exited with status %d\n", name.c_str(), WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
NOTICE("%s killed by signal %d\n", name.c_str(), WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
NOTICE("%s stopped by signal %d\n", name.c_str(), WSTOPSIG(status));
} else {
NOTICE("%s state changed", name.c_str());
}
if (!svc) {
return true;
}
if (svc->Reap()) {
waiting_for_exec = false;
RemoveService(*svc);
}
return true;
}
void ServiceManager::ReapAnyOutstandingChildren() {
while (ReapOneProcess()) {
}
}
bool ServiceParser::ParseSection(const std::vector<std::string>& args,
std::string* err) {
if (args.size() < 3) {

View file

@ -82,6 +82,7 @@ public:
bool Enable();
void Reset();
void Stop();
void Terminate();
void Restart();
void RestartIfNeeded(time_t& process_needs_restart);
bool Reap();
@ -167,17 +168,22 @@ public:
Service* FindServiceByName(const std::string& name) const;
Service* FindServiceByPid(pid_t pid) const;
Service* FindServiceByKeychord(int keychord_id) const;
void ForEachService(void (*func)(Service* svc)) const;
void ForEachService(std::function<void(Service*)> callback) const;
void ForEachServiceInClass(const std::string& classname,
void (*func)(Service* svc)) const;
void ForEachServiceWithFlags(unsigned matchflags,
void (*func)(Service* svc)) const;
void ReapAnyOutstandingChildren();
void RemoveService(const Service& svc);
void DumpState() const;
private:
ServiceManager();
// Cleans up a child process that exited.
// Returns true iff a children was cleaned up.
bool ReapOneProcess();
static int exec_count_; // Every service needs a unique name.
std::vector<std::unique_ptr<Service>> services_;
};

View file

@ -37,62 +37,12 @@
static int signal_write_fd = -1;
static int signal_read_fd = -1;
static std::string DescribeStatus(int status) {
if (WIFEXITED(status)) {
return android::base::StringPrintf("exited with status %d", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
return android::base::StringPrintf("killed by signal %d", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
return android::base::StringPrintf("stopped by signal %d", WSTOPSIG(status));
} else {
return "state changed";
}
}
static bool wait_for_one_process() {
int status;
pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
if (pid == 0) {
return false;
} else if (pid == -1) {
ERROR("waitpid failed: %s\n", strerror(errno));
return false;
}
Service* svc = ServiceManager::GetInstance().FindServiceByPid(pid);
std::string name;
if (svc) {
name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
} else {
name = android::base::StringPrintf("Untracked pid %d", pid);
}
NOTICE("%s %s\n", name.c_str(), DescribeStatus(status).c_str());
if (!svc) {
return true;
}
if (svc->Reap()) {
waiting_for_exec = false;
ServiceManager::GetInstance().RemoveService(*svc);
}
return true;
}
static void reap_any_outstanding_children() {
while (wait_for_one_process()) {
}
}
static void handle_signal() {
// Clear outstanding requests.
char buf[32];
read(signal_read_fd, buf, sizeof(buf));
reap_any_outstanding_children();
ServiceManager::GetInstance().ReapAnyOutstandingChildren();
}
static void SIGCHLD_handler(int) {
@ -119,7 +69,7 @@ void signal_handler_init() {
act.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, 0);
reap_any_outstanding_children();
ServiceManager::GetInstance().ReapAnyOutstandingChildren();
register_epoll_handler(signal_read_fd, handle_signal);
}