init: Introduce class InterprocessFifo

Prepare for introducing a second interprocess communication channel by
introducing the class InterprocessFifo. Stop using std::unique_ptr<> for
holding the pipe file descriptors. Handle EOF consistently.

Bug: 213617178
Change-Id: Ic0cf18d3d8ea61b8ee17e64de8a9df2736e26728
Signed-off-by: Bart Van Assche <bvanassche@google.com>
diff --git a/init/service.cpp b/init/service.cpp
index 6a9343d..331cd88 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -38,6 +38,7 @@
 
 #include <string>
 
+#include "interprocess_fifo.h"
 #include "lmkd_service.h"
 #include "service_list.h"
 #include "util.h"
@@ -442,14 +443,6 @@
     return {};
 }
 
-static void ClosePipe(const std::array<int, 2>* pipe) {
-    for (const auto fd : *pipe) {
-        if (fd >= 0) {
-            close(fd);
-        }
-    }
-}
-
 Result<void> Service::CheckConsole() {
     if (!(flags_ & SVC_CONSOLE)) {
         return {};
@@ -514,7 +507,7 @@
 
 // Enters namespaces, sets environment variables, writes PID files and runs the service executable.
 void Service::RunService(const std::vector<Descriptor>& descriptors,
-                         std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd) {
+                         InterprocessFifo cgroups_activated) {
     if (auto result = EnterNamespaces(namespaces_, name_, mount_namespace_); !result.ok()) {
         LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
     }
@@ -536,12 +529,12 @@
 
     // Wait until the cgroups have been created and until the cgroup controllers have been
     // activated.
-    char byte = 0;
-    if (read((*pipefd)[0], &byte, 1) < 0) {
-        PLOG(ERROR) << "failed to read from notification channel";
+    Result<uint8_t> byte = cgroups_activated.Read();
+    if (!byte.ok()) {
+        LOG(ERROR) << name_ << ": failed to read from notification channel: " << byte.error();
     }
-    pipefd.reset();
-    if (!byte) {
+    cgroups_activated.Close();
+    if (!*byte) {
         LOG(FATAL) << "Service '" << name_  << "' failed to start due to a fatal error";
         _exit(EXIT_FAILURE);
     }
@@ -605,10 +598,10 @@
         return {};
     }
 
-    std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd(new std::array<int, 2>{-1, -1},
-                                                                     ClosePipe);
-    if (pipe(pipefd->data()) < 0) {
-        return ErrnoError() << "pipe()";
+    InterprocessFifo cgroups_activated;
+
+    if (Result<void> result = cgroups_activated.Initialize(); !result.ok()) {
+        return result;
     }
 
     if (Result<void> result = CheckConsole(); !result.ok()) {
@@ -667,8 +660,11 @@
 
     if (pid == 0) {
         umask(077);
-        RunService(descriptors, std::move(pipefd));
+        cgroups_activated.CloseWriteFd();
+        RunService(descriptors, std::move(cgroups_activated));
         _exit(127);
+    } else {
+        cgroups_activated.CloseReadFd();
     }
 
     if (pid < 0) {
@@ -697,8 +693,9 @@
                          limit_percent_ != -1 || !limit_property_.empty();
         errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
         if (errno != 0) {
-            if (char byte = 0; write((*pipefd)[1], &byte, 1) < 0) {
-                return ErrnoError() << "sending notification failed";
+            Result<void> result = cgroups_activated.Write(0);
+            if (!result.ok()) {
+                return Error() << "Sending notification failed: " << result.error();
             }
             return Error() << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
                            << ") failed for service '" << name_ << "'";
@@ -720,8 +717,8 @@
         LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
     }
 
-    if (char byte = 1; write((*pipefd)[1], &byte, 1) < 0) {
-        return ErrnoError() << "sending notification failed";
+    if (Result<void> result = cgroups_activated.Write(1); !result.ok()) {
+        return Error() << "Sending cgroups activated notification failed: " << result.error();
     }
 
     NotifyStateChange("running");