Reland^2: "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. Reland: during reboot, init stops processing property_changed messages from property service, since it will not act on these anyway. This had an unexpected effect of causing future property_set calls to block indefinitely, since the buffer between init and property_service was filling up and the send() call from property_service would then block. This change has init tell property_service to stop sending it property_changed messages once reboot begins. Test: CF boots, walleye boots, properties are set appropriately Change-Id: I26902708e8be788caa6dbcf4b6d2968d90962785
This commit is contained in:
parent
50e408db2d
commit
1ab3dfcab4
11 changed files with 371 additions and 147 deletions
|
@ -130,6 +130,7 @@ cc_library_static {
|
|||
"persistent_properties.cpp",
|
||||
"persistent_properties.proto",
|
||||
"property_service.cpp",
|
||||
"property_service.proto",
|
||||
"property_type.cpp",
|
||||
"reboot.cpp",
|
||||
"reboot_utils.cpp",
|
||||
|
|
|
@ -80,6 +80,7 @@
|
|||
using namespace std::literals::string_literals;
|
||||
|
||||
using android::base::Basename;
|
||||
using android::base::StartsWith;
|
||||
using android::base::StringPrintf;
|
||||
using android::base::unique_fd;
|
||||
using android::fs_mgr::Fstab;
|
||||
|
@ -701,6 +702,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()
|
||||
<< "Cannot set ctl. properties from init; call the Service functions directly";
|
||||
}
|
||||
if (args[1] == kRestoreconProperty) {
|
||||
return Error() << "Cannot set '" << kRestoreconProperty
|
||||
<< "' from init; use the restorecon builtin directly";
|
||||
}
|
||||
|
||||
property_set(args[1], args[2]);
|
||||
return {};
|
||||
}
|
||||
|
@ -1016,7 +1026,20 @@ static Result<void> do_loglevel(const BuiltinArguments& args) {
|
|||
}
|
||||
|
||||
static Result<void> do_load_persist_props(const BuiltinArguments& args) {
|
||||
load_persist_props();
|
||||
// Devices with FDE have load_persist_props called twice; the first time when the temporary
|
||||
// /data partition is mounted and then again once /data is truly mounted. We do not want to
|
||||
// read persistent properties from the temporary /data partition or mark persistent properties
|
||||
// as having been loaded during the first call, so we return in that case.
|
||||
std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
|
||||
std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
|
||||
if (crypto_state == "encrypted" && crypto_type == "block") {
|
||||
static size_t num_calls = 0;
|
||||
if (++num_calls == 1) return {};
|
||||
}
|
||||
|
||||
SendLoadPersistentPropertiesMessage();
|
||||
|
||||
start_waiting_for_property("ro.persistent_properties.ready", "true");
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -615,6 +621,60 @@ static void RecordStageBoottimes(const boot_clock::time_point& second_stage_star
|
|||
selinux_start_time_ns));
|
||||
}
|
||||
|
||||
void SendLoadPersistentPropertiesMessage() {
|
||||
auto init_message = InitMessage{};
|
||||
init_message.set_load_persistent_properties(true);
|
||||
if (auto result = SendMessage(property_fd, init_message); !result) {
|
||||
LOG(ERROR) << "Failed to send load persistent properties message: " << result.error();
|
||||
}
|
||||
}
|
||||
|
||||
void SendStopSendingMessagesMessage() {
|
||||
auto init_message = InitMessage{};
|
||||
init_message.set_stop_sending_messages(true);
|
||||
if (auto result = SendMessage(property_fd, init_message); !result) {
|
||||
LOG(ERROR) << "Failed to send load persistent properties message: " << result.error();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -686,7 +746,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();
|
||||
|
||||
|
|
|
@ -31,16 +31,15 @@ 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();
|
||||
|
||||
void ResetWaitForProp();
|
||||
|
||||
void SendLoadPersistentPropertiesMessage();
|
||||
void SendStopSendingMessagesMessage();
|
||||
|
||||
int SecondStageMain(int argc, char** argv);
|
||||
|
||||
} // namespace init
|
||||
|
|
|
@ -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,11 @@ 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 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 +253,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 +293,9 @@ class SocketConnection {
|
|||
}
|
||||
|
||||
bool SendUint32(uint32_t value) {
|
||||
if (!socket_.ok()) {
|
||||
return true;
|
||||
}
|
||||
int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
|
||||
return result == sizeof(value);
|
||||
}
|
||||
|
@ -318,7 +310,7 @@ class SocketConnection {
|
|||
return true;
|
||||
}
|
||||
|
||||
int socket() { return socket_; }
|
||||
[[nodiscard]] int Release() { return socket_.release(); }
|
||||
|
||||
const ucred& cred() { return cred_; }
|
||||
|
||||
|
@ -389,12 +381,46 @@ 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) {
|
||||
if (init_socket == -1) {
|
||||
*error = "Received control message after shutdown, ignoring";
|
||||
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// We must release the fd before sending it to init, otherwise there will be a race with init.
|
||||
// If init calls close() before Release(), then fdsan will see the wrong tag and abort().
|
||||
int fd = -1;
|
||||
if (socket != nullptr) {
|
||||
fd = socket->Release();
|
||||
control_message->set_fd(fd);
|
||||
}
|
||||
|
||||
if (auto result = SendMessage(init_socket, property_msg); !result) {
|
||||
// We've already released the fd above, so if we fail to send the message to init, we need
|
||||
// to manually free it here.
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
*error = "Failed to send control message: " + result.error().message();
|
||||
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
|
||||
}
|
||||
|
||||
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 +488,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 +526,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 +588,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,7 +617,7 @@ 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;
|
||||
|
@ -741,33 +781,6 @@ static void load_override_properties() {
|
|||
}
|
||||
}
|
||||
|
||||
/* When booting an encrypted system, /data is not mounted when the
|
||||
* property service is started, so any properties stored there are
|
||||
* not loaded. Vold triggers init to load these properties once it
|
||||
* has mounted /data.
|
||||
*/
|
||||
void load_persist_props(void) {
|
||||
// Devices with FDE have load_persist_props called twice; the first time when the temporary
|
||||
// /data partition is mounted and then again once /data is truly mounted. We do not want to
|
||||
// read persistent properties from the temporary /data partition or mark persistent properties
|
||||
// as having been loaded during the first call, so we return in that case.
|
||||
std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
|
||||
std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
|
||||
if (crypto_state == "encrypted" && crypto_type == "block") {
|
||||
static size_t num_calls = 0;
|
||||
if (++num_calls == 1) return;
|
||||
}
|
||||
|
||||
load_override_properties();
|
||||
/* Read persistent properties after all default values have been loaded. */
|
||||
auto persistent_properties = LoadPersistentProperties();
|
||||
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");
|
||||
}
|
||||
|
||||
// If the ro.product.[brand|device|manufacturer|model|name] properties have not been explicitly
|
||||
// set, derive them from ro.product.${partition}.* properties
|
||||
static void property_initialize_ro_product_props() {
|
||||
|
@ -985,21 +998,92 @@ void CreateSerializedPropertyInfo() {
|
|||
selinux_android_restorecon(kPropertyInfosPath, 0);
|
||||
}
|
||||
|
||||
void StartPropertyService(Epoll* epoll) {
|
||||
static void HandleInitSocket() {
|
||||
auto message = ReadMessage(init_socket);
|
||||
if (!message) {
|
||||
LOG(ERROR) << "Could not read message from init_dedicated_recv_socket: " << message.error();
|
||||
return;
|
||||
}
|
||||
|
||||
auto init_message = InitMessage{};
|
||||
if (!init_message.ParseFromString(*message)) {
|
||||
LOG(ERROR) << "Could not parse message from init";
|
||||
return;
|
||||
}
|
||||
|
||||
switch (init_message.msg_case()) {
|
||||
case InitMessage::kLoadPersistentProperties: {
|
||||
load_override_properties();
|
||||
// Read persistent properties after all default values have been loaded.
|
||||
auto persistent_properties = LoadPersistentProperties();
|
||||
for (const auto& persistent_property_record : persistent_properties.properties()) {
|
||||
InitPropertySet(persistent_property_record.name(),
|
||||
persistent_property_record.value());
|
||||
}
|
||||
InitPropertySet("ro.persistent_properties.ready", "true");
|
||||
persistent_properties_loaded = true;
|
||||
break;
|
||||
}
|
||||
case InitMessage::kStopSendingMessages: {
|
||||
init_socket = -1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG(ERROR) << "Unknown message type from init: " << init_message.msg_case();
|
||||
}
|
||||
}
|
||||
|
||||
static void PropertyServiceThread() {
|
||||
Epoll epoll;
|
||||
if (auto result = epoll.Open(); !result) {
|
||||
LOG(FATAL) << result.error();
|
||||
}
|
||||
|
||||
if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
|
||||
LOG(FATAL) << result.error();
|
||||
}
|
||||
|
||||
if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result) {
|
||||
LOG(FATAL) << result.error();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
auto pending_functions = epoll.Wait(std::nullopt);
|
||||
if (!pending_functions) {
|
||||
LOG(ERROR) << pending_functions.error();
|
||||
} else {
|
||||
for (const auto& function : *pending_functions) {
|
||||
(*function)();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StartPropertyService(int* epoll_socket) {
|
||||
property_set("ro.property_service.version", "2");
|
||||
|
||||
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 | SOCK_NONBLOCK,
|
||||
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
|
||||
|
|
|
@ -25,17 +25,15 @@
|
|||
namespace android {
|
||||
namespace init {
|
||||
|
||||
static constexpr const char kRestoreconProperty[] = "selinux.restorecon_recursive";
|
||||
|
||||
bool CanReadProperty(const std::string& source_context, const std::string& name);
|
||||
|
||||
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
|
||||
|
||||
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
|
||||
const std::string& source_context, const ucred& cr, std::string* error);
|
||||
|
||||
void property_init();
|
||||
void property_load_boot_defaults(bool load_debug_prop);
|
||||
void load_persist_props();
|
||||
void StartPropertyService(Epoll* epoll);
|
||||
void StartPropertyService(int* epoll_socket);
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
|
44
init/property_service.proto
Normal file
44
init/property_service.proto
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
syntax = "proto2";
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
message PropertyMessage {
|
||||
message ControlMessage {
|
||||
optional string msg = 1;
|
||||
optional string name = 2;
|
||||
optional int32 pid = 3;
|
||||
optional int32 fd = 4;
|
||||
}
|
||||
|
||||
message ChangedMessage {
|
||||
optional string name = 1;
|
||||
optional string value = 2;
|
||||
}
|
||||
|
||||
oneof msg {
|
||||
ControlMessage control_message = 1;
|
||||
ChangedMessage changed_message = 2;
|
||||
};
|
||||
}
|
||||
|
||||
message InitMessage {
|
||||
oneof msg {
|
||||
bool load_persistent_properties = 1;
|
||||
bool stop_sending_messages = 2;
|
||||
};
|
||||
}
|
62
init/proto_utils.h
Normal file
62
init/proto_utils.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "result.h"
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
constexpr size_t kBufferSize = 4096;
|
||||
|
||||
inline Result<std::string> ReadMessage(int socket) {
|
||||
char buffer[kBufferSize] = {};
|
||||
auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
|
||||
if (result == 0) {
|
||||
return Error();
|
||||
} else if (result < 0) {
|
||||
return ErrnoError();
|
||||
}
|
||||
return std::string(buffer, result);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Result<void> SendMessage(int socket, const T& message) {
|
||||
std::string message_string;
|
||||
if (!message.SerializeToString(&message_string)) {
|
||||
return Error() << "Unable to serialize message";
|
||||
}
|
||||
|
||||
if (message_string.size() > kBufferSize) {
|
||||
return Error() << "Serialized message too long to send";
|
||||
}
|
||||
|
||||
if (auto result =
|
||||
TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));
|
||||
result != static_cast<long>(message_string.size())) {
|
||||
return ErrnoError() << "send() failed to send message contents";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
|
@ -730,6 +730,12 @@ bool HandlePowerctlMessage(const std::string& command) {
|
|||
s->UnSetExec();
|
||||
}
|
||||
|
||||
// We no longer process messages about properties changing coming from property service, so we
|
||||
// need to tell property service to stop sending us these messages, otherwise it'll fill the
|
||||
// buffers and block indefinitely, causing future property sets, including those that init makes
|
||||
// during shutdown in Service::NotifyStateChange() to also block indefinitely.
|
||||
SendStopSendingMessagesMessage();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,16 +18,17 @@
|
|||
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <selinux/android.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "builtins.h"
|
||||
#include "proto_utils.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
@ -59,45 +60,6 @@ const char* const paths_and_secontexts[2][2] = {
|
|||
|
||||
namespace {
|
||||
|
||||
constexpr size_t kBufferSize = 4096;
|
||||
|
||||
Result<std::string> ReadMessage(int socket) {
|
||||
char buffer[kBufferSize] = {};
|
||||
auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
|
||||
if (result == 0) {
|
||||
return Error();
|
||||
} else if (result < 0) {
|
||||
return ErrnoError();
|
||||
}
|
||||
return std::string(buffer, result);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Result<void> SendMessage(int socket, const T& message) {
|
||||
std::string message_string;
|
||||
if (!message.SerializeToString(&message_string)) {
|
||||
return Error() << "Unable to serialize message";
|
||||
}
|
||||
|
||||
if (message_string.size() > kBufferSize) {
|
||||
return Error() << "Serialized message too long to send";
|
||||
}
|
||||
|
||||
if (auto result =
|
||||
TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));
|
||||
result != static_cast<long>(message_string.size())) {
|
||||
return ErrnoError() << "send() failed to send message contents";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> properties_to_set;
|
||||
|
||||
uint32_t SubcontextPropertySet(const std::string& name, const std::string& value) {
|
||||
properties_to_set.emplace_back(name, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
class SubcontextProcess {
|
||||
public:
|
||||
SubcontextProcess(const BuiltinFunctionMap* function_map, std::string context, int init_fd)
|
||||
|
@ -131,14 +93,6 @@ void SubcontextProcess::RunCommand(const SubcontextCommand::ExecuteCommand& exec
|
|||
result = RunBuiltinFunction(map_result->function, args, context_);
|
||||
}
|
||||
|
||||
for (const auto& [name, value] : properties_to_set) {
|
||||
auto property = reply->add_properties_to_set();
|
||||
property->set_name(name);
|
||||
property->set_value(value);
|
||||
}
|
||||
|
||||
properties_to_set.clear();
|
||||
|
||||
if (result) {
|
||||
reply->set_success(true);
|
||||
} else {
|
||||
|
@ -224,7 +178,10 @@ int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map
|
|||
|
||||
SelabelInitialize();
|
||||
|
||||
property_set = SubcontextPropertySet;
|
||||
property_set = [](const std::string& key, const std::string& value) -> uint32_t {
|
||||
android::base::SetProperty(key, value);
|
||||
return 0;
|
||||
};
|
||||
|
||||
auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
|
||||
subcontext_process.MainLoop();
|
||||
|
@ -311,15 +268,6 @@ Result<void> Subcontext::Execute(const std::vector<std::string>& args) {
|
|||
return subcontext_reply.error();
|
||||
}
|
||||
|
||||
for (const auto& property : subcontext_reply->properties_to_set()) {
|
||||
ucred cr = {.pid = pid_, .uid = 0, .gid = 0};
|
||||
std::string error;
|
||||
if (HandlePropertySet(property.name(), property.value(), context_, cr, &error) != 0) {
|
||||
LOG(ERROR) << "Subcontext init could not set '" << property.name() << "' to '"
|
||||
<< property.value() << "': " << error;
|
||||
}
|
||||
}
|
||||
|
||||
if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
|
||||
auto& failure = subcontext_reply->failure();
|
||||
return ResultError(failure.error_string(), failure.error_errno());
|
||||
|
|
|
@ -38,10 +38,4 @@ message SubcontextReply {
|
|||
Failure failure = 2;
|
||||
ExpandArgsReply expand_args_reply = 3;
|
||||
}
|
||||
|
||||
message PropertyToSet {
|
||||
optional string name = 1;
|
||||
optional string value = 2;
|
||||
}
|
||||
repeated PropertyToSet properties_to_set = 4;
|
||||
}
|
Loading…
Reference in a new issue