Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8efca4bb authored by Tom Cherry's avatar Tom Cherry
Browse files

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
parent a033693a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -128,6 +128,7 @@ cc_library_static {
        "persistent_properties.cpp",
        "persistent_properties.proto",
        "property_service.cpp",
        "property_service.proto",
        "property_type.cpp",
        "reboot.cpp",
        "reboot_utils.cpp",
+10 −0
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@
using namespace std::literals::string_literals;

using android::base::Basename;
using android::base::StartsWith;
using android::base::unique_fd;
using android::fs_mgr::Fstab;
using android::fs_mgr::ReadFstabFromFile;
@@ -687,6 +688,15 @@ static Result<void> do_swapon_all(const BuiltinArguments& args) {
}

static Result<void> do_setprop(const BuiltinArguments& args) {
    if (StartsWith(args[1], "ctl.")) {
        return Error() << "InitPropertySet: Do not set ctl. properties from init; call the Service "
                          "functions directly";
    }
    if (args[1] == kRestoreconProperty) {
        return Error() << "InitPropertySet: Do not set '" << kRestoreconProperty
                       << "' from init; use the restorecon builtin directly";
    }

    property_set(args[1], args[2]);
    return {};
}
+50 −1
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@
#include <sys/types.h>
#include <unistd.h>

#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>

#include <functional>
#include <map>
#include <memory>
@@ -61,6 +64,7 @@
#include "mount_handler.h"
#include "mount_namespace.h"
#include "property_service.h"
#include "proto_utils.h"
#include "reboot.h"
#include "reboot_utils.h"
#include "security.h"
@@ -69,6 +73,7 @@
#include "service.h"
#include "service_parser.h"
#include "sigchld_handler.h"
#include "system/core/init/property_service.pb.h"
#include "util.h"

using namespace std::chrono_literals;
@@ -90,6 +95,7 @@ static int property_triggers_enabled = 0;
static char qemu[32];

static int signal_fd = -1;
static int property_fd = -1;

static std::unique_ptr<Timer> waiting_for_prop(nullptr);
static std::string wait_prop_name;
@@ -613,6 +619,44 @@ static void RecordStageBoottimes(const boot_clock::time_point& second_stage_star
                                selinux_start_time_ns));
}

static void HandlePropertyFd() {
    auto message = ReadMessage(property_fd);
    if (!message) {
        LOG(ERROR) << "Could not read message from property service: " << message.error();
        return;
    }

    auto property_message = PropertyMessage{};
    if (!property_message.ParseFromString(*message)) {
        LOG(ERROR) << "Could not parse message from property service";
        return;
    }

    switch (property_message.msg_case()) {
        case PropertyMessage::kControlMessage: {
            auto& control_message = property_message.control_message();
            bool success = HandleControlMessage(control_message.msg(), control_message.name(),
                                                control_message.pid());

            uint32_t response = success ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;
            if (control_message.has_fd()) {
                int fd = control_message.fd();
                TEMP_FAILURE_RETRY(send(fd, &response, sizeof(response), 0));
                close(fd);
            }
            break;
        }
        case PropertyMessage::kChangedMessage: {
            auto& changed_message = property_message.changed_message();
            property_changed(changed_message.name(), changed_message.value());
            break;
        }
        default:
            LOG(ERROR) << "Unknown message type from property service: "
                       << property_message.msg_case();
    }
}

int SecondStageMain(int argc, char** argv) {
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
@@ -684,7 +728,12 @@ int SecondStageMain(int argc, char** argv) {
    UmountDebugRamdisk();
    fs_mgr_vendor_overlay_mount_all();
    export_oem_lock_status();
    StartPropertyService(&epoll);

    StartPropertyService(&property_fd);
    if (auto result = epoll.RegisterHandler(property_fd, HandlePropertyFd); !result) {
        LOG(FATAL) << "Could not register epoll handler for property fd: " << result.error();
    }

    MountHandler mount_handler(&epoll);
    set_usb_controller();

+0 −4
Original line number Diff line number Diff line
@@ -31,10 +31,6 @@ namespace init {
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
Parser CreateServiceOnlyParser(ServiceList& service_list);

bool HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);

void property_changed(const std::string& name, const std::string& value);

bool start_waiting_for_property(const char *name, const char *value);

void DumpState();
+101 −50
Original line number Diff line number Diff line
@@ -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::StartsWith;
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 @@ using android::properties::PropertyInfoEntry;
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 @@ static bool CheckMacPerms(const std::string& name, const char* target_context,
    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 @@ static uint32_t PropertySet(const std::string& name, const std::string& value, s
    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 @@ class AsyncRestorecon {
    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 @@ class SocketConnection {
    }

    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 @@ class SocketConnection {
        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 @@ class SocketConnection {
        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 @@ uint32_t CheckPermissions(const std::string& name, const std::string& value,

// 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 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value,
    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 @@ static void handle_property_set_fd() {

        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 @@ static void handle_property_set_fd() {

        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 @@ void load_persist_props(void) {
    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 @@ void CreateSerializedPropertyInfo() {
    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
Loading