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
diff --git a/init/service.cpp b/init/service.cpp
index 40a4bc7..0ddc484 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -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 @@
     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 @@
     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 @@
     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) {