init: Allow clean system shutdown upon SIGTERM

This allows Android to cleanly shutdown when running in a PID namespace
in a way that does not rely on adbd running. This is useful to allow
Android to be running in a container and its lifetime managed by an
OCI-compliant tool.

Bug: 65415372
Test: `kill -TERM 1` as root is correctly dropped.
Test: `kill -TERM 1` from the init PID namespace causes init to cleanly shutdown.
Change-Id: Ia66ebdb436221919081bc4723337c0c7f1e53b09
diff --git a/init/init.cpp b/init/init.cpp
index 678f49f..c3e08fb 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -25,6 +25,7 @@
 #include <string.h>
 #include <sys/epoll.h>
 #include <sys/mount.h>
+#include <sys/signalfd.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -51,7 +52,7 @@
 #include "reboot.h"
 #include "security.h"
 #include "selinux.h"
-#include "signal_handler.h"
+#include "sigchld_handler.h"
 #include "ueventd.h"
 #include "util.h"
 #include "watchdogd.h"
@@ -72,6 +73,7 @@
 std::string default_console = "/dev/console";
 
 static int epoll_fd = -1;
+static int sigterm_signal_fd = -1;
 
 static std::unique_ptr<Timer> waiting_for_prop(nullptr);
 static std::string wait_prop_name;
@@ -392,6 +394,41 @@
     sigaction(SIGTRAP, &action, nullptr);
 }
 
+static void HandleSigtermSignal() {
+    signalfd_siginfo siginfo;
+    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(sigterm_signal_fd, &siginfo, sizeof(siginfo)));
+    if (bytes_read != sizeof(siginfo)) {
+        PLOG(ERROR) << "Failed to read siginfo from sigterm_signal_fd";
+        return;
+    }
+
+    if (siginfo.ssi_pid != 0) {
+        // Drop any userspace SIGTERM requests.
+        LOG(DEBUG) << "Ignoring SIGTERM from pid " << siginfo.ssi_pid;
+        return;
+    }
+
+    LOG(INFO) << "Handling SIGTERM, shutting system down";
+    HandlePowerctlMessage("shutdown");
+}
+
+static void InstallSigtermHandler() {
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGTERM);
+
+    if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
+        PLOG(FATAL) << "failed to block SIGTERM";
+    }
+
+    sigterm_signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+    if (sigterm_signal_fd == -1) {
+        PLOG(FATAL) << "failed to create signalfd for SIGTERM";
+    }
+
+    register_epoll_handler(sigterm_signal_fd, HandleSigtermSignal);
+}
+
 int main(int argc, char** argv) {
     if (!strcmp(basename(argv[0]), "ueventd")) {
         return ueventd_main(argc, argv);
@@ -527,7 +564,13 @@
         exit(1);
     }
 
-    signal_handler_init();
+    sigchld_handler_init();
+
+    if (!IsRebootCapable()) {
+        // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
+        // In that case, receiving SIGTERM will cause the system to shut down.
+        InstallSigtermHandler();
+    }
 
     property_load_boot_defaults();
     export_oem_lock_status();