Merge "init: Allows shutting down cleanly."
This commit is contained in:
commit
f9f4ee8b72
4 changed files with 110 additions and 55 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue