Loading init/README.md +11 −0 Original line number Original line Diff line number Diff line Loading @@ -174,6 +174,17 @@ If the property `true` becomes `true` *AFTER* `boot` was triggered, nothing will be executed. The condition `boot && property:true=true` will be evaluated to be executed. The condition `boot && property:true=true` will be evaluated to false because the `boot` trigger is a past event. false because the `boot` trigger is a past event. Note that when `ro.property_service.async_persist_writes` is `true`, there is no defined ordering between persistent setprops and non-persistent setprops. For example: on boot setprop a 1 setprop persist.b 2 When `ro.property_service.async_persist_writes` is `true`, triggers for these two properties may execute in any order. Services Services -------- -------- Services are programs which init launches and (optionally) restarts Services are programs which init launches and (optionally) restarts Loading init/property_service.cpp +165 −61 Original line number Original line Diff line number Diff line Loading @@ -98,6 +98,9 @@ using android::sysprop::InitProperties::is_userspace_reboot_supported; namespace android { namespace android { namespace init { namespace init { class PersistWriteThread; constexpr auto FINGERPRINT_PROP = "ro.build.fingerprint"; constexpr auto FINGERPRINT_PROP = "ro.build.fingerprint"; constexpr auto LEGACY_FINGERPRINT_PROP = "ro.build.legacy.fingerprint"; constexpr auto LEGACY_FINGERPRINT_PROP = "ro.build.legacy.fingerprint"; constexpr auto ID_PROP = "ro.build.id"; constexpr auto ID_PROP = "ro.build.id"; Loading @@ -115,6 +118,8 @@ static bool accept_messages = false; static std::mutex accept_messages_lock; static std::mutex accept_messages_lock; static std::thread property_service_thread; static std::thread property_service_thread; static std::unique_ptr<PersistWriteThread> persist_write_thread; static PropertyInfoAreaFile property_info_area; static PropertyInfoAreaFile property_info_area; struct PropertyAuditData { struct PropertyAuditData { Loading Loading @@ -177,48 +182,13 @@ static bool CheckMacPerms(const std::string& name, const char* target_context, return has_access; return has_access; } } static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) { void NotifyPropertyChange(const std::string& name, const std::string& value) { size_t valuelen = value.size(); if (!IsLegalPropertyName(name)) { *error = "Illegal property name"; return PROP_ERROR_INVALID_NAME; } if (auto result = IsLegalPropertyValue(name, value); !result.ok()) { *error = result.error().message(); return PROP_ERROR_INVALID_VALUE; } prop_info* pi = (prop_info*) __system_property_find(name.c_str()); if (pi != nullptr) { // ro.* properties are actually "write-once". if (StartsWith(name, "ro.")) { *error = "Read-only property was already set"; return PROP_ERROR_READ_ONLY_PROPERTY; } __system_property_update(pi, value.c_str(), valuelen); } else { int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen); if (rc < 0) { *error = "__system_property_add failed"; return PROP_ERROR_SET_FAILED; } } // Don't write properties to disk until after we have read all default // properties to prevent them from being overwritten by default values. if (persistent_properties_loaded && StartsWith(name, "persist.")) { WritePersistentProperty(name, value); } // If init hasn't started its main loop, then it won't be handling property changed messages // 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. // anyway, so there's no need to try to send them. auto lock = std::lock_guard{accept_messages_lock}; auto lock = std::lock_guard{accept_messages_lock}; if (accept_messages) { if (accept_messages) { PropertyChanged(name, value); PropertyChanged(name, value); } } return PROP_SUCCESS; } } class AsyncRestorecon { class AsyncRestorecon { Loading Loading @@ -259,7 +229,9 @@ class AsyncRestorecon { class SocketConnection { class SocketConnection { public: public: SocketConnection() = default; SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {} SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {} SocketConnection(SocketConnection&&) = default; bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) { bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) { return RecvFully(value, sizeof(*value), timeout_ms); return RecvFully(value, sizeof(*value), timeout_ms); Loading Loading @@ -318,6 +290,8 @@ class SocketConnection { const ucred& cred() { return cred_; } const ucred& cred() { return cred_; } SocketConnection& operator=(SocketConnection&&) = default; private: private: bool PollIn(uint32_t* timeout_ms) { bool PollIn(uint32_t* timeout_ms) { struct pollfd ufd = { struct pollfd ufd = { Loading Loading @@ -388,9 +362,78 @@ class SocketConnection { unique_fd socket_; unique_fd socket_; ucred cred_; ucred cred_; DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection); DISALLOW_COPY_AND_ASSIGN(SocketConnection); }; }; class PersistWriteThread { public: PersistWriteThread(); void Write(std::string name, std::string value, SocketConnection socket); private: void Work(); private: std::thread thread_; std::mutex mutex_; std::condition_variable cv_; std::deque<std::tuple<std::string, std::string, SocketConnection>> work_; }; static std::optional<uint32_t> PropertySet(const std::string& name, const std::string& value, SocketConnection* socket, std::string* error) { size_t valuelen = value.size(); if (!IsLegalPropertyName(name)) { *error = "Illegal property name"; return {PROP_ERROR_INVALID_NAME}; } if (auto result = IsLegalPropertyValue(name, value); !result.ok()) { *error = result.error().message(); return {PROP_ERROR_INVALID_VALUE}; } prop_info* pi = (prop_info*)__system_property_find(name.c_str()); if (pi != nullptr) { // ro.* properties are actually "write-once". if (StartsWith(name, "ro.")) { *error = "Read-only property was already set"; return {PROP_ERROR_READ_ONLY_PROPERTY}; } __system_property_update(pi, value.c_str(), valuelen); } else { int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen); if (rc < 0) { *error = "__system_property_add failed"; return {PROP_ERROR_SET_FAILED}; } } // Don't write properties to disk until after we have read all default // properties to prevent them from being overwritten by default values. if (socket && persistent_properties_loaded && StartsWith(name, "persist.")) { if (persist_write_thread) { persist_write_thread->Write(name, value, std::move(*socket)); return {}; } WritePersistentProperty(name, value); } NotifyPropertyChange(name, value); return {PROP_SUCCESS}; } // Helper for PropertySet, for the case where no socket is used, and therefore an asynchronous // return is not possible. static uint32_t PropertySetNoSocket(const std::string& name, const std::string& value, std::string* error) { auto ret = PropertySet(name, value, nullptr, error); CHECK(ret.has_value()); return *ret; } static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid, static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid, SocketConnection* socket, std::string* error) { SocketConnection* socket, std::string* error) { auto lock = std::lock_guard{accept_messages_lock}; auto lock = std::lock_guard{accept_messages_lock}; Loading Loading @@ -481,16 +524,17 @@ uint32_t CheckPermissions(const std::string& name, const std::string& value, return PROP_SUCCESS; return PROP_SUCCESS; } } // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*. // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*, or std::nullopt uint32_t HandlePropertySet(const std::string& name, const std::string& value, // if asynchronous. std::optional<uint32_t> HandlePropertySet(const std::string& name, const std::string& value, const std::string& source_context, const ucred& cr, const std::string& source_context, const ucred& cr, SocketConnection* socket, std::string* error) { SocketConnection* socket, std::string* error) { if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) { if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) { return ret; return {ret}; } } if (StartsWith(name, "ctl.")) { if (StartsWith(name, "ctl.")) { return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error); 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 // sys.powerctl is a special property that is used to make the device reboot. We want to log Loading @@ -511,7 +555,7 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value, } } if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) { if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) { *error = "Userspace reboot is not supported by this device"; *error = "Userspace reboot is not supported by this device"; return PROP_ERROR_INVALID_VALUE; return {PROP_ERROR_INVALID_VALUE}; } } } } Loading @@ -522,10 +566,20 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value, if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) { if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) { static AsyncRestorecon async_restorecon; static AsyncRestorecon async_restorecon; async_restorecon.TriggerRestorecon(value); async_restorecon.TriggerRestorecon(value); return PROP_SUCCESS; return {PROP_SUCCESS}; } return PropertySet(name, value, socket, error); } } return PropertySet(name, value, error); // Helper for HandlePropertySet, for the case where no socket is used, and // therefore an asynchronous return is not possible. uint32_t HandlePropertySetNoSocket(const std::string& name, const std::string& value, const std::string& source_context, const ucred& cr, std::string* error) { auto ret = HandlePropertySet(name, value, source_context, cr, nullptr, error); CHECK(ret.has_value()); return *ret; } } static void handle_property_set_fd() { static void handle_property_set_fd() { Loading Loading @@ -576,8 +630,7 @@ static void handle_property_set_fd() { const auto& cr = socket.cred(); const auto& cr = socket.cred(); std::string error; std::string error; uint32_t result = auto result = HandlePropertySetNoSocket(prop_name, prop_value, source_context, cr, &error); HandlePropertySet(prop_name, prop_value, source_context, cr, nullptr, &error); if (result != PROP_SUCCESS) { if (result != PROP_SUCCESS) { LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; Loading @@ -603,14 +656,19 @@ static void handle_property_set_fd() { return; return; } } // HandlePropertySet takes ownership of the socket if the set is handled asynchronously. const auto& cr = socket.cred(); const auto& cr = socket.cred(); std::string error; std::string error; uint32_t result = HandlePropertySet(name, value, source_context, cr, &socket, &error); auto result = HandlePropertySet(name, value, source_context, cr, &socket, &error); if (result != PROP_SUCCESS) { if (!result) { // Result will be sent after completion. return; } if (*result != PROP_SUCCESS) { LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; } } socket.SendUint32(result); socket.SendUint32(*result); break; break; } } Loading @@ -622,10 +680,9 @@ static void handle_property_set_fd() { } } uint32_t InitPropertySet(const std::string& name, const std::string& value) { uint32_t InitPropertySet(const std::string& name, const std::string& value) { uint32_t result = 0; ucred cr = {.pid = 1, .uid = 0, .gid = 0}; ucred cr = {.pid = 1, .uid = 0, .gid = 0}; std::string error; std::string error; result = HandlePropertySet(name, value, kInitContext, cr, nullptr, &error); auto result = HandlePropertySetNoSocket(name, value, kInitContext, cr, &error); if (result != PROP_SUCCESS) { if (result != PROP_SUCCESS) { LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error; LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error; } } Loading Loading @@ -795,7 +852,7 @@ static void load_override_properties() { load_properties_from_file("/data/local.prop", nullptr, &properties); load_properties_from_file("/data/local.prop", nullptr, &properties); for (const auto& [name, value] : properties) { for (const auto& [name, value] : properties) { std::string error; std::string error; if (PropertySet(name, value, &error) != PROP_SUCCESS) { if (PropertySetNoSocket(name, value, &error) != PROP_SUCCESS) { LOG(ERROR) << "Could not set '" << name << "' to '" << value LOG(ERROR) << "Could not set '" << name << "' to '" << value << "' in /data/local.prop: " << error; << "' in /data/local.prop: " << error; } } Loading Loading @@ -861,7 +918,7 @@ static void property_initialize_ro_product_props() { LOG(INFO) << "Setting product property " << base_prop << " to '" << target_prop_val LOG(INFO) << "Setting product property " << base_prop << " to '" << target_prop_val << "' (from " << target_prop << ")"; << "' (from " << target_prop << ")"; std::string error; std::string error; uint32_t res = PropertySet(base_prop, target_prop_val, &error); auto res = PropertySetNoSocket(base_prop, target_prop_val, &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Error setting product property " << base_prop << ": err=" << res LOG(ERROR) << "Error setting product property " << base_prop << ": err=" << res << " (" << error << ")"; << " (" << error << ")"; Loading Loading @@ -890,7 +947,7 @@ static void property_initialize_build_id() { } } std::string error; std::string error; auto res = PropertySet(ID_PROP, build_id, &error); auto res = PropertySetNoSocket(ID_PROP, build_id, &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Failed to set " << ID_PROP << " to " << build_id; LOG(ERROR) << "Failed to set " << ID_PROP << " to " << build_id; } } Loading Loading @@ -938,7 +995,7 @@ static void property_derive_legacy_build_fingerprint() { << legacy_build_fingerprint << "'"; << legacy_build_fingerprint << "'"; std::string error; std::string error; uint32_t res = PropertySet(LEGACY_FINGERPRINT_PROP, legacy_build_fingerprint, &error); auto res = PropertySetNoSocket(LEGACY_FINGERPRINT_PROP, legacy_build_fingerprint, &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Error setting property '" << LEGACY_FINGERPRINT_PROP << "': err=" << res LOG(ERROR) << "Error setting property '" << LEGACY_FINGERPRINT_PROP << "': err=" << res << " (" << error << ")"; << " (" << error << ")"; Loading @@ -956,7 +1013,7 @@ static void property_derive_build_fingerprint() { LOG(INFO) << "Setting property '" << FINGERPRINT_PROP << "' to '" << build_fingerprint << "'"; LOG(INFO) << "Setting property '" << FINGERPRINT_PROP << "' to '" << build_fingerprint << "'"; std::string error; std::string error; uint32_t res = PropertySet(FINGERPRINT_PROP, build_fingerprint, &error); auto res = PropertySetNoSocket(FINGERPRINT_PROP, build_fingerprint, &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Error setting property '" << FINGERPRINT_PROP << "': err=" << res << " (" LOG(ERROR) << "Error setting property '" << FINGERPRINT_PROP << "': err=" << res << " (" << error << ")"; << error << ")"; Loading Loading @@ -1018,7 +1075,7 @@ static void property_initialize_ro_cpu_abilist() { LOG(INFO) << "Setting property '" << prop << "' to '" << prop_val << "'"; LOG(INFO) << "Setting property '" << prop << "' to '" << prop_val << "'"; std::string error; std::string error; uint32_t res = PropertySet(prop, prop_val, &error); auto res = PropertySetNoSocket(prop, prop_val, &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Error setting property '" << prop << "': err=" << res << " (" << error LOG(ERROR) << "Error setting property '" << prop << "': err=" << res << " (" << error << ")"; << ")"; Loading Loading @@ -1052,7 +1109,7 @@ static void property_initialize_ro_vendor_api_level() { int api_level = std::min(read_api_level_props(BOARD_API_LEVEL_PROPS), int api_level = std::min(read_api_level_props(BOARD_API_LEVEL_PROPS), read_api_level_props(DEVICE_API_LEVEL_PROPS)); read_api_level_props(DEVICE_API_LEVEL_PROPS)); std::string error; std::string error; uint32_t res = PropertySet(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error); auto res = PropertySetNoSocket(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << api_level << ": " LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << api_level << ": " << error << "(" << res << ")"; << error << "(" << res << ")"; Loading Loading @@ -1146,7 +1203,7 @@ void PropertyLoadBootDefaults() { for (const auto& [name, value] : properties) { for (const auto& [name, value] : properties) { std::string error; std::string error; if (PropertySet(name, value, &error) != PROP_SUCCESS) { if (PropertySetNoSocket(name, value, &error) != PROP_SUCCESS) { LOG(ERROR) << "Could not set '" << name << "' to '" << value LOG(ERROR) << "Could not set '" << name << "' to '" << value << "' while loading .prop files" << error; << "' while loading .prop files" << error; } } Loading Loading @@ -1388,6 +1445,46 @@ static void PropertyServiceThread() { } } } } PersistWriteThread::PersistWriteThread() { auto new_thread = std::thread([this]() -> void { Work(); }); thread_.swap(new_thread); } void PersistWriteThread::Work() { while (true) { std::tuple<std::string, std::string, SocketConnection> item; // Grab the next item within the lock. { std::unique_lock<std::mutex> lock(mutex_); while (work_.empty()) { cv_.wait(lock); } item = std::move(work_.front()); work_.pop_front(); } std::this_thread::sleep_for(1s); // Perform write/fsync outside the lock. WritePersistentProperty(std::get<0>(item), std::get<1>(item)); NotifyPropertyChange(std::get<0>(item), std::get<1>(item)); SocketConnection& socket = std::get<2>(item); socket.SendUint32(PROP_SUCCESS); } } void PersistWriteThread::Write(std::string name, std::string value, SocketConnection socket) { { std::unique_lock<std::mutex> lock(mutex_); work_.emplace_back(std::move(name), std::move(value), std::move(socket)); } cv_.notify_all(); } void StartPropertyService(int* epoll_socket) { void StartPropertyService(int* epoll_socket) { InitPropertySet("ro.property_service.version", "2"); InitPropertySet("ro.property_service.version", "2"); Loading @@ -1412,6 +1509,13 @@ void StartPropertyService(int* epoll_socket) { auto new_thread = std::thread{PropertyServiceThread}; auto new_thread = std::thread{PropertyServiceThread}; property_service_thread.swap(new_thread); property_service_thread.swap(new_thread); auto async_persist_writes = android::base::GetBoolProperty("ro.property_service.async_persist_writes", false); if (async_persist_writes) { persist_write_thread = std::make_unique<PersistWriteThread>(); } } } } // namespace init } // namespace init Loading init/property_service.h +4 −0 Original line number Original line Diff line number Diff line Loading @@ -18,7 +18,11 @@ #include <sys/socket.h> #include <sys/socket.h> #include <condition_variable> #include <deque> #include <mutex> #include <string> #include <string> #include <thread> #include "epoll.h" #include "epoll.h" Loading Loading
init/README.md +11 −0 Original line number Original line Diff line number Diff line Loading @@ -174,6 +174,17 @@ If the property `true` becomes `true` *AFTER* `boot` was triggered, nothing will be executed. The condition `boot && property:true=true` will be evaluated to be executed. The condition `boot && property:true=true` will be evaluated to false because the `boot` trigger is a past event. false because the `boot` trigger is a past event. Note that when `ro.property_service.async_persist_writes` is `true`, there is no defined ordering between persistent setprops and non-persistent setprops. For example: on boot setprop a 1 setprop persist.b 2 When `ro.property_service.async_persist_writes` is `true`, triggers for these two properties may execute in any order. Services Services -------- -------- Services are programs which init launches and (optionally) restarts Services are programs which init launches and (optionally) restarts Loading
init/property_service.cpp +165 −61 Original line number Original line Diff line number Diff line Loading @@ -98,6 +98,9 @@ using android::sysprop::InitProperties::is_userspace_reboot_supported; namespace android { namespace android { namespace init { namespace init { class PersistWriteThread; constexpr auto FINGERPRINT_PROP = "ro.build.fingerprint"; constexpr auto FINGERPRINT_PROP = "ro.build.fingerprint"; constexpr auto LEGACY_FINGERPRINT_PROP = "ro.build.legacy.fingerprint"; constexpr auto LEGACY_FINGERPRINT_PROP = "ro.build.legacy.fingerprint"; constexpr auto ID_PROP = "ro.build.id"; constexpr auto ID_PROP = "ro.build.id"; Loading @@ -115,6 +118,8 @@ static bool accept_messages = false; static std::mutex accept_messages_lock; static std::mutex accept_messages_lock; static std::thread property_service_thread; static std::thread property_service_thread; static std::unique_ptr<PersistWriteThread> persist_write_thread; static PropertyInfoAreaFile property_info_area; static PropertyInfoAreaFile property_info_area; struct PropertyAuditData { struct PropertyAuditData { Loading Loading @@ -177,48 +182,13 @@ static bool CheckMacPerms(const std::string& name, const char* target_context, return has_access; return has_access; } } static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) { void NotifyPropertyChange(const std::string& name, const std::string& value) { size_t valuelen = value.size(); if (!IsLegalPropertyName(name)) { *error = "Illegal property name"; return PROP_ERROR_INVALID_NAME; } if (auto result = IsLegalPropertyValue(name, value); !result.ok()) { *error = result.error().message(); return PROP_ERROR_INVALID_VALUE; } prop_info* pi = (prop_info*) __system_property_find(name.c_str()); if (pi != nullptr) { // ro.* properties are actually "write-once". if (StartsWith(name, "ro.")) { *error = "Read-only property was already set"; return PROP_ERROR_READ_ONLY_PROPERTY; } __system_property_update(pi, value.c_str(), valuelen); } else { int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen); if (rc < 0) { *error = "__system_property_add failed"; return PROP_ERROR_SET_FAILED; } } // Don't write properties to disk until after we have read all default // properties to prevent them from being overwritten by default values. if (persistent_properties_loaded && StartsWith(name, "persist.")) { WritePersistentProperty(name, value); } // If init hasn't started its main loop, then it won't be handling property changed messages // 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. // anyway, so there's no need to try to send them. auto lock = std::lock_guard{accept_messages_lock}; auto lock = std::lock_guard{accept_messages_lock}; if (accept_messages) { if (accept_messages) { PropertyChanged(name, value); PropertyChanged(name, value); } } return PROP_SUCCESS; } } class AsyncRestorecon { class AsyncRestorecon { Loading Loading @@ -259,7 +229,9 @@ class AsyncRestorecon { class SocketConnection { class SocketConnection { public: public: SocketConnection() = default; SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {} SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {} SocketConnection(SocketConnection&&) = default; bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) { bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) { return RecvFully(value, sizeof(*value), timeout_ms); return RecvFully(value, sizeof(*value), timeout_ms); Loading Loading @@ -318,6 +290,8 @@ class SocketConnection { const ucred& cred() { return cred_; } const ucred& cred() { return cred_; } SocketConnection& operator=(SocketConnection&&) = default; private: private: bool PollIn(uint32_t* timeout_ms) { bool PollIn(uint32_t* timeout_ms) { struct pollfd ufd = { struct pollfd ufd = { Loading Loading @@ -388,9 +362,78 @@ class SocketConnection { unique_fd socket_; unique_fd socket_; ucred cred_; ucred cred_; DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection); DISALLOW_COPY_AND_ASSIGN(SocketConnection); }; }; class PersistWriteThread { public: PersistWriteThread(); void Write(std::string name, std::string value, SocketConnection socket); private: void Work(); private: std::thread thread_; std::mutex mutex_; std::condition_variable cv_; std::deque<std::tuple<std::string, std::string, SocketConnection>> work_; }; static std::optional<uint32_t> PropertySet(const std::string& name, const std::string& value, SocketConnection* socket, std::string* error) { size_t valuelen = value.size(); if (!IsLegalPropertyName(name)) { *error = "Illegal property name"; return {PROP_ERROR_INVALID_NAME}; } if (auto result = IsLegalPropertyValue(name, value); !result.ok()) { *error = result.error().message(); return {PROP_ERROR_INVALID_VALUE}; } prop_info* pi = (prop_info*)__system_property_find(name.c_str()); if (pi != nullptr) { // ro.* properties are actually "write-once". if (StartsWith(name, "ro.")) { *error = "Read-only property was already set"; return {PROP_ERROR_READ_ONLY_PROPERTY}; } __system_property_update(pi, value.c_str(), valuelen); } else { int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen); if (rc < 0) { *error = "__system_property_add failed"; return {PROP_ERROR_SET_FAILED}; } } // Don't write properties to disk until after we have read all default // properties to prevent them from being overwritten by default values. if (socket && persistent_properties_loaded && StartsWith(name, "persist.")) { if (persist_write_thread) { persist_write_thread->Write(name, value, std::move(*socket)); return {}; } WritePersistentProperty(name, value); } NotifyPropertyChange(name, value); return {PROP_SUCCESS}; } // Helper for PropertySet, for the case where no socket is used, and therefore an asynchronous // return is not possible. static uint32_t PropertySetNoSocket(const std::string& name, const std::string& value, std::string* error) { auto ret = PropertySet(name, value, nullptr, error); CHECK(ret.has_value()); return *ret; } static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid, static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid, SocketConnection* socket, std::string* error) { SocketConnection* socket, std::string* error) { auto lock = std::lock_guard{accept_messages_lock}; auto lock = std::lock_guard{accept_messages_lock}; Loading Loading @@ -481,16 +524,17 @@ uint32_t CheckPermissions(const std::string& name, const std::string& value, return PROP_SUCCESS; return PROP_SUCCESS; } } // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*. // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*, or std::nullopt uint32_t HandlePropertySet(const std::string& name, const std::string& value, // if asynchronous. std::optional<uint32_t> HandlePropertySet(const std::string& name, const std::string& value, const std::string& source_context, const ucred& cr, const std::string& source_context, const ucred& cr, SocketConnection* socket, std::string* error) { SocketConnection* socket, std::string* error) { if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) { if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) { return ret; return {ret}; } } if (StartsWith(name, "ctl.")) { if (StartsWith(name, "ctl.")) { return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error); 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 // sys.powerctl is a special property that is used to make the device reboot. We want to log Loading @@ -511,7 +555,7 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value, } } if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) { if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) { *error = "Userspace reboot is not supported by this device"; *error = "Userspace reboot is not supported by this device"; return PROP_ERROR_INVALID_VALUE; return {PROP_ERROR_INVALID_VALUE}; } } } } Loading @@ -522,10 +566,20 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value, if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) { if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) { static AsyncRestorecon async_restorecon; static AsyncRestorecon async_restorecon; async_restorecon.TriggerRestorecon(value); async_restorecon.TriggerRestorecon(value); return PROP_SUCCESS; return {PROP_SUCCESS}; } return PropertySet(name, value, socket, error); } } return PropertySet(name, value, error); // Helper for HandlePropertySet, for the case where no socket is used, and // therefore an asynchronous return is not possible. uint32_t HandlePropertySetNoSocket(const std::string& name, const std::string& value, const std::string& source_context, const ucred& cr, std::string* error) { auto ret = HandlePropertySet(name, value, source_context, cr, nullptr, error); CHECK(ret.has_value()); return *ret; } } static void handle_property_set_fd() { static void handle_property_set_fd() { Loading Loading @@ -576,8 +630,7 @@ static void handle_property_set_fd() { const auto& cr = socket.cred(); const auto& cr = socket.cred(); std::string error; std::string error; uint32_t result = auto result = HandlePropertySetNoSocket(prop_name, prop_value, source_context, cr, &error); HandlePropertySet(prop_name, prop_value, source_context, cr, nullptr, &error); if (result != PROP_SUCCESS) { if (result != PROP_SUCCESS) { LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; Loading @@ -603,14 +656,19 @@ static void handle_property_set_fd() { return; return; } } // HandlePropertySet takes ownership of the socket if the set is handled asynchronously. const auto& cr = socket.cred(); const auto& cr = socket.cred(); std::string error; std::string error; uint32_t result = HandlePropertySet(name, value, source_context, cr, &socket, &error); auto result = HandlePropertySet(name, value, source_context, cr, &socket, &error); if (result != PROP_SUCCESS) { if (!result) { // Result will be sent after completion. return; } if (*result != PROP_SUCCESS) { LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; } } socket.SendUint32(result); socket.SendUint32(*result); break; break; } } Loading @@ -622,10 +680,9 @@ static void handle_property_set_fd() { } } uint32_t InitPropertySet(const std::string& name, const std::string& value) { uint32_t InitPropertySet(const std::string& name, const std::string& value) { uint32_t result = 0; ucred cr = {.pid = 1, .uid = 0, .gid = 0}; ucred cr = {.pid = 1, .uid = 0, .gid = 0}; std::string error; std::string error; result = HandlePropertySet(name, value, kInitContext, cr, nullptr, &error); auto result = HandlePropertySetNoSocket(name, value, kInitContext, cr, &error); if (result != PROP_SUCCESS) { if (result != PROP_SUCCESS) { LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error; LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error; } } Loading Loading @@ -795,7 +852,7 @@ static void load_override_properties() { load_properties_from_file("/data/local.prop", nullptr, &properties); load_properties_from_file("/data/local.prop", nullptr, &properties); for (const auto& [name, value] : properties) { for (const auto& [name, value] : properties) { std::string error; std::string error; if (PropertySet(name, value, &error) != PROP_SUCCESS) { if (PropertySetNoSocket(name, value, &error) != PROP_SUCCESS) { LOG(ERROR) << "Could not set '" << name << "' to '" << value LOG(ERROR) << "Could not set '" << name << "' to '" << value << "' in /data/local.prop: " << error; << "' in /data/local.prop: " << error; } } Loading Loading @@ -861,7 +918,7 @@ static void property_initialize_ro_product_props() { LOG(INFO) << "Setting product property " << base_prop << " to '" << target_prop_val LOG(INFO) << "Setting product property " << base_prop << " to '" << target_prop_val << "' (from " << target_prop << ")"; << "' (from " << target_prop << ")"; std::string error; std::string error; uint32_t res = PropertySet(base_prop, target_prop_val, &error); auto res = PropertySetNoSocket(base_prop, target_prop_val, &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Error setting product property " << base_prop << ": err=" << res LOG(ERROR) << "Error setting product property " << base_prop << ": err=" << res << " (" << error << ")"; << " (" << error << ")"; Loading Loading @@ -890,7 +947,7 @@ static void property_initialize_build_id() { } } std::string error; std::string error; auto res = PropertySet(ID_PROP, build_id, &error); auto res = PropertySetNoSocket(ID_PROP, build_id, &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Failed to set " << ID_PROP << " to " << build_id; LOG(ERROR) << "Failed to set " << ID_PROP << " to " << build_id; } } Loading Loading @@ -938,7 +995,7 @@ static void property_derive_legacy_build_fingerprint() { << legacy_build_fingerprint << "'"; << legacy_build_fingerprint << "'"; std::string error; std::string error; uint32_t res = PropertySet(LEGACY_FINGERPRINT_PROP, legacy_build_fingerprint, &error); auto res = PropertySetNoSocket(LEGACY_FINGERPRINT_PROP, legacy_build_fingerprint, &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Error setting property '" << LEGACY_FINGERPRINT_PROP << "': err=" << res LOG(ERROR) << "Error setting property '" << LEGACY_FINGERPRINT_PROP << "': err=" << res << " (" << error << ")"; << " (" << error << ")"; Loading @@ -956,7 +1013,7 @@ static void property_derive_build_fingerprint() { LOG(INFO) << "Setting property '" << FINGERPRINT_PROP << "' to '" << build_fingerprint << "'"; LOG(INFO) << "Setting property '" << FINGERPRINT_PROP << "' to '" << build_fingerprint << "'"; std::string error; std::string error; uint32_t res = PropertySet(FINGERPRINT_PROP, build_fingerprint, &error); auto res = PropertySetNoSocket(FINGERPRINT_PROP, build_fingerprint, &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Error setting property '" << FINGERPRINT_PROP << "': err=" << res << " (" LOG(ERROR) << "Error setting property '" << FINGERPRINT_PROP << "': err=" << res << " (" << error << ")"; << error << ")"; Loading Loading @@ -1018,7 +1075,7 @@ static void property_initialize_ro_cpu_abilist() { LOG(INFO) << "Setting property '" << prop << "' to '" << prop_val << "'"; LOG(INFO) << "Setting property '" << prop << "' to '" << prop_val << "'"; std::string error; std::string error; uint32_t res = PropertySet(prop, prop_val, &error); auto res = PropertySetNoSocket(prop, prop_val, &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Error setting property '" << prop << "': err=" << res << " (" << error LOG(ERROR) << "Error setting property '" << prop << "': err=" << res << " (" << error << ")"; << ")"; Loading Loading @@ -1052,7 +1109,7 @@ static void property_initialize_ro_vendor_api_level() { int api_level = std::min(read_api_level_props(BOARD_API_LEVEL_PROPS), int api_level = std::min(read_api_level_props(BOARD_API_LEVEL_PROPS), read_api_level_props(DEVICE_API_LEVEL_PROPS)); read_api_level_props(DEVICE_API_LEVEL_PROPS)); std::string error; std::string error; uint32_t res = PropertySet(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error); auto res = PropertySetNoSocket(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error); if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) { LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << api_level << ": " LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << api_level << ": " << error << "(" << res << ")"; << error << "(" << res << ")"; Loading Loading @@ -1146,7 +1203,7 @@ void PropertyLoadBootDefaults() { for (const auto& [name, value] : properties) { for (const auto& [name, value] : properties) { std::string error; std::string error; if (PropertySet(name, value, &error) != PROP_SUCCESS) { if (PropertySetNoSocket(name, value, &error) != PROP_SUCCESS) { LOG(ERROR) << "Could not set '" << name << "' to '" << value LOG(ERROR) << "Could not set '" << name << "' to '" << value << "' while loading .prop files" << error; << "' while loading .prop files" << error; } } Loading Loading @@ -1388,6 +1445,46 @@ static void PropertyServiceThread() { } } } } PersistWriteThread::PersistWriteThread() { auto new_thread = std::thread([this]() -> void { Work(); }); thread_.swap(new_thread); } void PersistWriteThread::Work() { while (true) { std::tuple<std::string, std::string, SocketConnection> item; // Grab the next item within the lock. { std::unique_lock<std::mutex> lock(mutex_); while (work_.empty()) { cv_.wait(lock); } item = std::move(work_.front()); work_.pop_front(); } std::this_thread::sleep_for(1s); // Perform write/fsync outside the lock. WritePersistentProperty(std::get<0>(item), std::get<1>(item)); NotifyPropertyChange(std::get<0>(item), std::get<1>(item)); SocketConnection& socket = std::get<2>(item); socket.SendUint32(PROP_SUCCESS); } } void PersistWriteThread::Write(std::string name, std::string value, SocketConnection socket) { { std::unique_lock<std::mutex> lock(mutex_); work_.emplace_back(std::move(name), std::move(value), std::move(socket)); } cv_.notify_all(); } void StartPropertyService(int* epoll_socket) { void StartPropertyService(int* epoll_socket) { InitPropertySet("ro.property_service.version", "2"); InitPropertySet("ro.property_service.version", "2"); Loading @@ -1412,6 +1509,13 @@ void StartPropertyService(int* epoll_socket) { auto new_thread = std::thread{PropertyServiceThread}; auto new_thread = std::thread{PropertyServiceThread}; property_service_thread.swap(new_thread); property_service_thread.swap(new_thread); auto async_persist_writes = android::base::GetBoolProperty("ro.property_service.async_persist_writes", false); if (async_persist_writes) { persist_write_thread = std::make_unique<PersistWriteThread>(); } } } } // namespace init } // namespace init Loading
init/property_service.h +4 −0 Original line number Original line Diff line number Diff line Loading @@ -18,7 +18,11 @@ #include <sys/socket.h> #include <sys/socket.h> #include <condition_variable> #include <deque> #include <mutex> #include <string> #include <string> #include <thread> #include "epoll.h" #include "epoll.h" Loading