Reland: "init: run property service in a thread"

It's been a long standing issue that init cannot respond to property
set messages when it is running a builtin command.  This is
particularly problematic when the commands involve IPC to vold or
other daemons, as it prevents them from being able to set properties.

This change has init run property service in a thread, which
eliminates the above issue.

This change may also serve as a starting block to running property
service in an entirely different process to better isolate init from
handling property requests.

Test: CF boots, walleye boots, properties are set appropriately
Change-Id: I13b8bf240c9fcb1d2d5890a8be2f0ef74efd4adf
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 3408ff3..c78b81b 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -42,6 +42,7 @@
 #include <map>
 #include <memory>
 #include <mutex>
+#include <optional>
 #include <queue>
 #include <thread>
 #include <vector>
@@ -63,8 +64,10 @@
 #include "init.h"
 #include "persistent_properties.h"
 #include "property_type.h"
+#include "proto_utils.h"
 #include "selinux.h"
 #include "subcontext.h"
+#include "system/core/init/property_service.pb.h"
 #include "util.h"
 
 using namespace std::literals;
@@ -76,6 +79,7 @@
 using android::base::StringPrintf;
 using android::base::Timer;
 using android::base::Trim;
+using android::base::unique_fd;
 using android::base::WriteStringToFile;
 using android::properties::BuildTrie;
 using android::properties::ParsePropertyInfoFile;
@@ -85,18 +89,13 @@
 namespace android {
 namespace init {
 
-static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
-
 static bool persistent_properties_loaded = false;
 
 static int property_set_fd = -1;
+static int init_socket = -1;
 
 static PropertyInfoAreaFile property_info_area;
 
-uint32_t InitPropertySet(const std::string& name, const std::string& value);
-
-uint32_t (*property_set)(const std::string& name, const std::string& value) = InitPropertySet;
-
 void CreateSerializedPropertyInfo();
 
 struct PropertyAuditData {
@@ -164,6 +163,17 @@
     return has_access;
 }
 
+static void SendPropertyChanged(const std::string& name, const std::string& value) {
+    auto property_msg = PropertyMessage{};
+    auto* changed_message = property_msg.mutable_changed_message();
+    changed_message->set_name(name);
+    changed_message->set_value(value);
+
+    if (auto result = SendMessage(init_socket, property_msg); !result) {
+        LOG(ERROR) << "Failed to send property changed message: " << result.error();
+    }
+}
+
 static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
     size_t valuelen = value.size();
 
@@ -199,7 +209,16 @@
     if (persistent_properties_loaded && StartsWith(name, "persist.")) {
         WritePersistentProperty(name, value);
     }
-    property_changed(name, value);
+    // If init sets ro.persistent_properties.ready to true, then it has finished writing persistent
+    // properties, and we should write future persistent properties to disk.
+    if (name == "ro.persistent_properties.ready" && value == "true") {
+        persistent_properties_loaded = true;
+    }
+    // If init hasn't started its main loop, then it won't be handling property changed messages
+    // anyway, so there's no need to try to send them.
+    if (init_socket != -1) {
+        SendPropertyChanged(name, value);
+    }
     return PROP_SUCCESS;
 }
 
@@ -239,35 +258,10 @@
     bool thread_started_ = false;
 };
 
-uint32_t InitPropertySet(const std::string& name, const std::string& value) {
-    if (StartsWith(name, "ctl.")) {
-        LOG(ERROR) << "InitPropertySet: Do not set ctl. properties from init; call the Service "
-                      "functions directly";
-        return PROP_ERROR_INVALID_NAME;
-    }
-    if (name == kRestoreconProperty) {
-        LOG(ERROR) << "InitPropertySet: Do not set '" << kRestoreconProperty
-                   << "' from init; use the restorecon builtin directly";
-        return PROP_ERROR_INVALID_NAME;
-    }
-
-    uint32_t result = 0;
-    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
-    std::string error;
-    result = HandlePropertySet(name, value, kInitContext.c_str(), cr, &error);
-    if (result != PROP_SUCCESS) {
-        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
-    }
-
-    return result;
-}
-
 class SocketConnection {
   public:
     SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {}
 
-    ~SocketConnection() { close(socket_); }
-
     bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
         return RecvFully(value, sizeof(*value), timeout_ms);
     }
@@ -304,6 +298,9 @@
     }
 
     bool SendUint32(uint32_t value) {
+        if (!socket_.ok()) {
+            return false;
+        }
         int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
         return result == sizeof(value);
     }
@@ -318,7 +315,9 @@
         return true;
     }
 
-    int socket() { return socket_; }
+    int Release() { return socket_.release(); }
+
+    int socket() { return socket_.get(); }
 
     const ucred& cred() { return cred_; }
 
@@ -389,12 +388,34 @@
         return bytes_left == 0;
     }
 
-    int socket_;
+    unique_fd socket_;
     ucred cred_;
-
-    DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
 };
 
+static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
+                                   SocketConnection* socket, std::string* error) {
+    auto property_msg = PropertyMessage{};
+    auto* control_message = property_msg.mutable_control_message();
+    control_message->set_msg(msg);
+    control_message->set_name(name);
+    control_message->set_pid(pid);
+    if (socket != nullptr) {
+        control_message->set_fd(socket->socket());
+    }
+
+    if (auto result = SendMessage(init_socket, property_msg); !result) {
+        *error = "Failed to send control message: " + result.error().message();
+        return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+    }
+
+    if (socket != nullptr) {
+        // We've successfully sent the fd to init, so release it here.
+        socket->Release();
+    }
+
+    return PROP_SUCCESS;
+}
+
 bool CheckControlPropertyPerms(const std::string& name, const std::string& value,
                                const std::string& source_context, const ucred& cr) {
     // We check the legacy method first but these properties are dontaudit, so we only log an audit
@@ -462,15 +483,14 @@
 
 // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
 uint32_t HandlePropertySet(const std::string& name, const std::string& value,
-                           const std::string& source_context, const ucred& cr, std::string* error) {
+                           const std::string& source_context, const ucred& cr,
+                           SocketConnection* socket, std::string* error) {
     if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
         return ret;
     }
 
     if (StartsWith(name, "ctl.")) {
-        return HandleControlMessage(name.c_str() + 4, value, cr.pid)
-                       ? PROP_SUCCESS
-                       : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
+        return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error);
     }
 
     // sys.powerctl is a special property that is used to make the device reboot.  We want to log
@@ -501,6 +521,20 @@
     return PropertySet(name, value, error);
 }
 
+uint32_t InitPropertySet(const std::string& name, const std::string& value) {
+    uint32_t result = 0;
+    ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+    std::string error;
+    result = HandlePropertySet(name, value, kInitContext.c_str(), cr, nullptr, &error);
+    if (result != PROP_SUCCESS) {
+        LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
+    }
+
+    return result;
+}
+
+uint32_t (*property_set)(const std::string& name, const std::string& value) = InitPropertySet;
+
 static void handle_property_set_fd() {
     static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
 
@@ -549,7 +583,8 @@
 
         const auto& cr = socket.cred();
         std::string error;
-        uint32_t result = HandlePropertySet(prop_name, prop_value, source_context, cr, &error);
+        uint32_t result =
+                HandlePropertySet(prop_name, prop_value, source_context, cr, nullptr, &error);
         if (result != PROP_SUCCESS) {
             LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                        << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
@@ -577,11 +612,12 @@
 
         const auto& cr = socket.cred();
         std::string error;
-        uint32_t result = HandlePropertySet(name, value, source_context, cr, &error);
+        uint32_t result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
         if (result != PROP_SUCCESS) {
             LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
                        << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
         }
+
         socket.SendUint32(result);
         break;
       }
@@ -764,7 +800,6 @@
     for (const auto& persistent_property_record : persistent_properties.properties()) {
         property_set(persistent_property_record.name(), persistent_property_record.value());
     }
-    persistent_properties_loaded = true;
     property_set("ro.persistent_properties.ready", "true");
 }
 
@@ -985,21 +1020,37 @@
     selinux_android_restorecon(kPropertyInfosPath, 0);
 }
 
-void StartPropertyService(Epoll* epoll) {
+static void PropertyServiceThread() {
+    while (true) {
+        handle_property_set_fd();
+    }
+}
+
+void StartPropertyService(int* epoll_socket) {
     property_set("ro.property_service.version", "2");
 
-    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                   false, 0666, 0, 0, {})) {
+    int sockets[2];
+    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
+        PLOG(FATAL) << "Failed to socketpair() between property_service and init";
+    }
+    *epoll_socket = sockets[0];
+    init_socket = sockets[1];
+
+    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC, false, 0666, 0, 0,
+                                   {})) {
         property_set_fd = *result;
     } else {
-        PLOG(FATAL) << "start_property_service socket creation failed: " << result.error();
+        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
     }
 
     listen(property_set_fd, 8);
 
-    if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
-        PLOG(FATAL) << result.error();
-    }
+    std::thread{PropertyServiceThread}.detach();
+
+    property_set = [](const std::string& key, const std::string& value) -> uint32_t {
+        android::base::SetProperty(key, value);
+        return 0;
+    };
 }
 
 }  // namespace init