init: Combine two global sigchld_fd variables into one

Remove the Service::SetSigchldFd() method. Make the Service::GetSigchldFd()
create a signalfd for SIGCHLD. This makes it possible to use a SIGCHLD
signalfd in unit tests.

Change-Id: I0b41caa8f46c79f4d400e49aaba5227fad53c251
Signed-off-by: Bart Van Assche <bvanassche@google.com>
diff --git a/init/init.cpp b/init/init.cpp
index 83cb78b..aeccd66 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -117,7 +117,6 @@
 
 static int property_triggers_enabled = 0;
 
-int sigchld_fd = -1;
 static int sigterm_fd = -1;
 static int property_fd = -1;
 
@@ -717,7 +716,7 @@
 
 static void HandleSignalFd(int signal) {
     signalfd_siginfo siginfo;
-    const int signal_fd = signal == SIGCHLD ? sigchld_fd : sigterm_fd;
+    const int signal_fd = signal == SIGCHLD ? Service::GetSigchldFd() : sigterm_fd;
     ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
     if (bytes_read != sizeof(siginfo)) {
         PLOG(ERROR) << "Failed to read siginfo from signal_fd";
@@ -751,20 +750,24 @@
     }
 }
 
+static Result<void> RegisterSignalFd(Epoll* epoll, int signal, int fd) {
+    return epoll->RegisterHandler(
+            fd, [signal]() { HandleSignalFd(signal); }, EPOLLIN | EPOLLPRI);
+}
+
 static Result<int> CreateAndRegisterSignalFd(Epoll* epoll, int signal) {
     sigset_t mask;
     sigemptyset(&mask);
     sigaddset(&mask, signal);
-    unique_fd signal_fd(signalfd(-1, &mask, SFD_CLOEXEC));
-    if (signal_fd == -1) {
-        return ErrnoError() << "failed to create signalfd for signal " << signal;
+    if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
+        return ErrnoError() << "failed to block signal " << signal;
     }
 
-    auto result = epoll->RegisterHandler(
-            signal_fd.get(), [signal]() { HandleSignalFd(signal); }, EPOLLIN | EPOLLPRI);
-    if (!result.ok()) {
-        return result.error();
+    unique_fd signal_fd(signalfd(-1, &mask, SFD_CLOEXEC));
+    if (signal_fd.get() < 0) {
+        return ErrnoError() << "failed to create signalfd for signal " << signal;
     }
+    OR_RETURN(RegisterSignalFd(epoll, signal, signal_fd.get()));
 
     return signal_fd.release();
 }
@@ -775,34 +778,18 @@
     const struct sigaction act { .sa_flags = SA_NOCLDSTOP, .sa_handler = SIG_DFL };
     sigaction(SIGCHLD, &act, nullptr);
 
-    sigset_t mask;
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGCHLD);
-
-    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.
-        sigaddset(&mask, SIGTERM);
-    }
-
-    if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
-        PLOG(FATAL) << "failed to block signals";
-    }
-
     // Register a handler to unblock signals in the child processes.
     const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
     if (result != 0) {
         LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
     }
 
-    Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGCHLD);
+    Result<void> cs_result = RegisterSignalFd(epoll, SIGCHLD, Service::GetSigchldFd());
     if (!cs_result.ok()) {
         PLOG(FATAL) << cs_result.error();
     }
-    sigchld_fd = cs_result.value();
-    Service::SetSigchldFd(sigchld_fd);
 
-    if (sigismember(&mask, SIGTERM)) {
+    if (!IsRebootCapable()) {
         Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGTERM);
         if (!cs_result.ok()) {
             PLOG(FATAL) << cs_result.error();
diff --git a/init/init.h b/init/init.h
index b781167..9c7e918 100644
--- a/init/init.h
+++ b/init/init.h
@@ -28,8 +28,6 @@
 namespace android {
 namespace init {
 
-extern int sigchld_fd;
-
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
 Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list);
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 5757922..1a26c4d 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -563,7 +563,7 @@
         }
     }
     if (timeout > 0ms) {
-        WaitToBeReaped(sigchld_fd, pids, timeout);
+        WaitToBeReaped(Service::GetSigchldFd(), pids, timeout);
     } else {
         // Even if we don't to wait for services to stop, we still optimistically reap zombies.
         ReapAnyOutstandingChildren();
diff --git a/init/service.cpp b/init/service.cpp
index d351a8f..eb24dd5 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -37,6 +37,7 @@
 #include <cutils/sockets.h>
 #include <processgroup/processgroup.h>
 #include <selinux/selinux.h>
+#include <sys/signalfd.h>
 
 #include <string>
 
@@ -68,6 +69,7 @@
 using android::base::SetProperty;
 using android::base::StartsWith;
 using android::base::StringPrintf;
+using android::base::unique_fd;
 using android::base::WriteStringToFile;
 
 namespace android {
@@ -136,7 +138,6 @@
 
 unsigned long Service::next_start_order_ = 1;
 bool Service::is_exec_service_running_ = false;
-int Service::sigchld_fd_ = -1;
 
 Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
                  const std::string& filename, const std::vector<std::string>& args)
@@ -792,6 +793,35 @@
     mount_namespace_ = IsDefaultMountNamespaceReady() ? NS_DEFAULT : NS_BOOTSTRAP;
 }
 
+static int ThreadCount() {
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/proc/self/task"), closedir);
+    if (!dir) {
+        return -1;
+    }
+
+    int count = 0;
+    dirent* entry;
+    while ((entry = readdir(dir.get())) != nullptr) {
+        if (entry->d_name[0] != '.') {
+            count++;
+        }
+    }
+    return count;
+}
+
+// Must be called BEFORE any threads are created. See also the sigprocmask() man page.
+unique_fd Service::CreateSigchldFd() {
+    CHECK_EQ(ThreadCount(), 1);
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGCHLD);
+    if (sigprocmask(SIG_BLOCK, &mask, nullptr) < 0) {
+        PLOG(FATAL) << "Failed to block SIGCHLD";
+    }
+
+    return unique_fd(signalfd(-1, &mask, SFD_CLOEXEC));
+}
+
 void Service::SetStartedInFirstStage(pid_t pid) {
     LOG(INFO) << "adding first-stage service '" << name_ << "'...";
 
diff --git a/init/service.h b/init/service.h
index 13c8b5f..5e9af25 100644
--- a/init/service.h
+++ b/init/service.h
@@ -156,7 +156,10 @@
     const Subcontext* subcontext() const { return subcontext_; }
     const std::string& filename() const { return filename_; }
     void set_filename(const std::string& name) { filename_ = name; }
-    static void SetSigchldFd(int sigchld_fd) { sigchld_fd_ = sigchld_fd; }
+    static int GetSigchldFd() {
+        static int sigchld_fd = CreateSigchldFd().release();
+        return sigchld_fd;
+    }
 
   private:
     void NotifyStateChange(const std::string& new_state) const;
@@ -169,10 +172,10 @@
     void RunService(const std::vector<Descriptor>& descriptors, InterprocessFifo cgroups_activated,
                     InterprocessFifo setsid_finished);
     void SetMountNamespace();
+    static ::android::base::unique_fd CreateSigchldFd();
 
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
-    static int sigchld_fd_;
 
     const std::string name_;
     std::set<std::string> classnames_;