Merge UP1A.230119.002
Change-Id: I7ed93ddd712083f5a5e93811c576ba600c104794
This commit is contained in:
commit
65f954819e
|
@ -40,15 +40,6 @@ on post-fs-data
|
||||||
chown system log /data/misc/bootstat/time_since_last_boot
|
chown system log /data/misc/bootstat/time_since_last_boot
|
||||||
# end ota transitional support
|
# end ota transitional support
|
||||||
|
|
||||||
# Record the time at which the user has successfully entered the pin to decrypt
|
|
||||||
# the device, /data is decrypted, and the system is entering the main boot phase.
|
|
||||||
#
|
|
||||||
# post-fs-data: /data is writable
|
|
||||||
# property:init.svc.bootanim=running: The boot animation is running
|
|
||||||
# property:ro.crypto.type=block: FDE device
|
|
||||||
on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
|
|
||||||
exec_background - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
|
|
||||||
|
|
||||||
# Initialize bootstat state machine.
|
# Initialize bootstat state machine.
|
||||||
#
|
#
|
||||||
# sys.bootstat.first_boot_completed: responsible for making sure that record_boot_complete happens
|
# sys.bootstat.first_boot_completed: responsible for making sure that record_boot_complete happens
|
||||||
|
|
|
@ -88,7 +88,7 @@ GwpAsanCrashData::GwpAsanCrashData(unwindstack::Memory* process_memory,
|
||||||
thread_id_ = thread_info.tid;
|
thread_id_ = thread_info.tid;
|
||||||
|
|
||||||
// Grab the internal error address, if it exists.
|
// Grab the internal error address, if it exists.
|
||||||
uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_);
|
uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_, crash_address_);
|
||||||
if (internal_crash_address) {
|
if (internal_crash_address) {
|
||||||
crash_address_ = internal_crash_address;
|
crash_address_ = internal_crash_address;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,9 +44,12 @@ ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
|
||||||
__scudo_get_stack_depot_size());
|
__scudo_get_stack_depot_size());
|
||||||
auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
|
auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
|
||||||
__scudo_get_region_info_size());
|
__scudo_get_region_info_size());
|
||||||
auto ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
|
std::unique_ptr<char[]> ring_buffer;
|
||||||
process_info.scudo_ring_buffer_size);
|
if (process_info.scudo_ring_buffer_size != 0) {
|
||||||
if (!stack_depot || !region_info || !ring_buffer) {
|
ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
|
||||||
|
process_info.scudo_ring_buffer_size);
|
||||||
|
}
|
||||||
|
if (!stack_depot || !region_info) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1082,7 +1082,8 @@ static void flash_buf(const std::string& partition, struct fastboot_buffer* buf)
|
||||||
sparse_file** s;
|
sparse_file** s;
|
||||||
|
|
||||||
if (partition == "boot" || partition == "boot_a" || partition == "boot_b" ||
|
if (partition == "boot" || partition == "boot_a" || partition == "boot_b" ||
|
||||||
partition == "init_boot" || partition == "init_boot_a" || partition == "init_boot_b") {
|
partition == "init_boot" || partition == "init_boot_a" || partition == "init_boot_b" ||
|
||||||
|
partition == "recovery" || partition == "recovery_a" || partition == "recovery_b") {
|
||||||
copy_avb_footer(partition, buf);
|
copy_avb_footer(partition, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,5 +27,16 @@
|
||||||
{
|
{
|
||||||
"name": "cow_api_test"
|
"name": "cow_api_test"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"kernel-presubmit": [
|
||||||
|
{
|
||||||
|
"name": "vts_libdm_test"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vts_core_liblp_test"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vts_libsnapshot_test"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -305,19 +305,16 @@ bool ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
|
||||||
if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
|
if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
|
||||||
LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
|
LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
|
||||||
}
|
}
|
||||||
} else if (StartsWith(flag, "zoned_device=")) {
|
} else if (flag == "zoned_device") {
|
||||||
std::string zoned;
|
if (access("/dev/block/by-name/zoned_device", F_OK) == 0) {
|
||||||
if (ReadFileToString("/sys/class/block/" + arg + "/queue/zoned", &zoned) &&
|
entry->zoned_device = "/dev/block/by-name/zoned_device";
|
||||||
android::base::StartsWith(zoned, "host-managed")) {
|
|
||||||
entry->zoned_device = "/dev/block/" + arg;
|
|
||||||
|
|
||||||
// atgc in f2fs does not support a zoned device
|
// atgc in f2fs does not support a zoned device
|
||||||
auto options = Split(entry->fs_options, ",");
|
auto options = Split(entry->fs_options, ",");
|
||||||
options.erase(std::remove(options.begin(), options.end(), "atgc"), options.end());
|
options.erase(std::remove(options.begin(), options.end(), "atgc"), options.end());
|
||||||
entry->fs_options = android::base::Join(options, ",");
|
entry->fs_options = android::base::Join(options, ",");
|
||||||
LINFO << "Removed ATGC in fs_options as " << entry->fs_options;
|
LINFO << "Removed ATGC in fs_options as " << entry->fs_options
|
||||||
} else {
|
<< " for zoned device=" << entry->zoned_device;
|
||||||
LWARNING << "Warning: cannot find the zoned device: " << arg;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LWARNING << "Warning: unknown flag: " << flag;
|
LWARNING << "Warning: unknown flag: " << flag;
|
||||||
|
|
|
@ -35,18 +35,18 @@ images. After `/data` is mounted however, there are two problems:
|
||||||
|
|
||||||
We break the problem down into three scenarios.
|
We break the problem down into three scenarios.
|
||||||
|
|
||||||
### FDE and Metadata Encrypted Devices
|
### Metadata Encrypted Devices
|
||||||
|
|
||||||
When FDE or metadata encryption is used, `/data` is not mounted from
|
When metadata encryption is used, `/data` is not mounted from
|
||||||
`/dev/block/by-name/data`. Instead, it is mounted from an intermediate
|
`/dev/block/by-name/data`. Instead, it is mounted from an intermediate
|
||||||
`dm-crypt` or `dm-default-key` device. This means the underlying device is
|
`dm-default-key` device. This means the underlying device is not marked in use,
|
||||||
not marked in use, and we can create new dm-linear devices on top of it.
|
and we can create new dm-linear devices on top of it.
|
||||||
|
|
||||||
On these devices, a block device for an image will consist of a single
|
On these devices, a block device for an image will consist of a single
|
||||||
device-mapper device with a `dm-linear` table entry for each extent in the
|
device-mapper device with a `dm-linear` table entry for each extent in the
|
||||||
backing file.
|
backing file.
|
||||||
|
|
||||||
### Unencrypted and FBE-encrypted Devices
|
### Unencrypted and FBE-only Devices
|
||||||
|
|
||||||
When a device is unencrypted, or is encrypted with FBE but not metadata
|
When a device is unencrypted, or is encrypted with FBE but not metadata
|
||||||
encryption, we instead use a loop device with `LOOP_SET_DIRECT_IO` enabled.
|
encryption, we instead use a loop device with `LOOP_SET_DIRECT_IO` enabled.
|
||||||
|
|
|
@ -1498,6 +1498,7 @@ void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
|
||||||
if (UpdateUsesUserSnapshots(lock) && !device()->IsTestDevice()) {
|
if (UpdateUsesUserSnapshots(lock) && !device()->IsTestDevice()) {
|
||||||
if (snapuserd_client_) {
|
if (snapuserd_client_) {
|
||||||
snapuserd_client_->DetachSnapuserd();
|
snapuserd_client_->DetachSnapuserd();
|
||||||
|
snapuserd_client_->RemoveTransitionedDaemonIndicator();
|
||||||
snapuserd_client_ = nullptr;
|
snapuserd_client_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,11 +37,13 @@ cc_defaults {
|
||||||
cc_library_static {
|
cc_library_static {
|
||||||
name: "libsnapshot_snapuserd",
|
name: "libsnapshot_snapuserd",
|
||||||
defaults: [
|
defaults: [
|
||||||
|
"fs_mgr_defaults",
|
||||||
"libsnapshot_snapuserd_defaults",
|
"libsnapshot_snapuserd_defaults",
|
||||||
],
|
],
|
||||||
recovery_available: true,
|
recovery_available: true,
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"libcutils_sockets",
|
"libcutils_sockets",
|
||||||
|
"libfs_mgr",
|
||||||
],
|
],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"libbase",
|
"libbase",
|
||||||
|
@ -49,6 +51,7 @@ cc_library_static {
|
||||||
],
|
],
|
||||||
export_include_dirs: ["include"],
|
export_include_dirs: ["include"],
|
||||||
ramdisk_available: true,
|
ramdisk_available: true,
|
||||||
|
vendor_ramdisk_available: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
cc_defaults {
|
cc_defaults {
|
||||||
|
@ -86,6 +89,7 @@ cc_defaults {
|
||||||
"libgflags",
|
"libgflags",
|
||||||
"liblog",
|
"liblog",
|
||||||
"libsnapshot_cow",
|
"libsnapshot_cow",
|
||||||
|
"libsnapshot_snapuserd",
|
||||||
"libz",
|
"libz",
|
||||||
"liblz4",
|
"liblz4",
|
||||||
"libext4_utils",
|
"libext4_utils",
|
||||||
|
|
|
@ -32,6 +32,7 @@ static constexpr uint32_t PACKET_SIZE = 512;
|
||||||
|
|
||||||
static constexpr char kSnapuserdSocket[] = "snapuserd";
|
static constexpr char kSnapuserdSocket[] = "snapuserd";
|
||||||
static constexpr char kSnapuserdSocketProxy[] = "snapuserd_proxy";
|
static constexpr char kSnapuserdSocketProxy[] = "snapuserd_proxy";
|
||||||
|
static constexpr char kDaemonAliveIndicator[] = "daemon-alive-indicator";
|
||||||
|
|
||||||
// Ensure that the second-stage daemon for snapuserd is running.
|
// Ensure that the second-stage daemon for snapuserd is running.
|
||||||
bool EnsureSnapuserdStarted();
|
bool EnsureSnapuserdStarted();
|
||||||
|
@ -44,9 +45,11 @@ class SnapuserdClient {
|
||||||
std::string Receivemsg();
|
std::string Receivemsg();
|
||||||
|
|
||||||
bool ValidateConnection();
|
bool ValidateConnection();
|
||||||
|
std::string GetDaemonAliveIndicatorPath();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SnapuserdClient(android::base::unique_fd&& sockfd);
|
explicit SnapuserdClient(android::base::unique_fd&& sockfd);
|
||||||
|
SnapuserdClient(){};
|
||||||
|
|
||||||
static std::unique_ptr<SnapuserdClient> Connect(const std::string& socket_name,
|
static std::unique_ptr<SnapuserdClient> Connect(const std::string& socket_name,
|
||||||
std::chrono::milliseconds timeout_ms);
|
std::chrono::milliseconds timeout_ms);
|
||||||
|
@ -91,6 +94,17 @@ class SnapuserdClient {
|
||||||
// Check the update verification status - invoked by update_verifier during
|
// Check the update verification status - invoked by update_verifier during
|
||||||
// boot
|
// boot
|
||||||
bool QueryUpdateVerification();
|
bool QueryUpdateVerification();
|
||||||
|
|
||||||
|
// Check if Snapuser daemon is ready post selinux transition after OTA boot
|
||||||
|
// This is invoked only by init as there is no sockets setup yet during
|
||||||
|
// selinux transition
|
||||||
|
bool IsTransitionedDaemonReady();
|
||||||
|
|
||||||
|
// Remove the daemon-alive-indicator path post snapshot merge
|
||||||
|
bool RemoveTransitionedDaemonIndicator();
|
||||||
|
|
||||||
|
// Notify init that snapuserd daemon is ready post selinux transition
|
||||||
|
void NotifyTransitionDaemonIsReady();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace snapshot
|
} // namespace snapshot
|
||||||
|
|
|
@ -29,10 +29,12 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <android-base/file.h>
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
#include <android-base/parseint.h>
|
#include <android-base/parseint.h>
|
||||||
#include <android-base/properties.h>
|
#include <android-base/properties.h>
|
||||||
#include <android-base/strings.h>
|
#include <android-base/strings.h>
|
||||||
|
#include <fs_mgr/file_wait.h>
|
||||||
#include <snapuserd/snapuserd_client.h>
|
#include <snapuserd/snapuserd_client.h>
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
@ -279,5 +281,42 @@ bool SnapuserdClient::QueryUpdateVerification() {
|
||||||
return response == "success";
|
return response == "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string SnapuserdClient::GetDaemonAliveIndicatorPath() {
|
||||||
|
return "/metadata/ota/" + std::string(kDaemonAliveIndicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SnapuserdClient::IsTransitionedDaemonReady() {
|
||||||
|
if (!android::fs_mgr::WaitForFile(GetDaemonAliveIndicatorPath(), 10s)) {
|
||||||
|
LOG(ERROR) << "Timed out waiting for daemon indicator path: "
|
||||||
|
<< GetDaemonAliveIndicatorPath();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SnapuserdClient::RemoveTransitionedDaemonIndicator() {
|
||||||
|
std::string error;
|
||||||
|
std::string filePath = GetDaemonAliveIndicatorPath();
|
||||||
|
if (!android::base::RemoveFileIfExists(filePath, &error)) {
|
||||||
|
LOG(ERROR) << "Failed to remove DaemonAliveIndicatorPath - error: " << error;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!android::fs_mgr::WaitForFileDeleted(filePath, 5s)) {
|
||||||
|
LOG(ERROR) << "Timed out waiting for " << filePath << " to unlink";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SnapuserdClient::NotifyTransitionDaemonIsReady() {
|
||||||
|
if (!android::base::WriteStringToFile("1", GetDaemonAliveIndicatorPath())) {
|
||||||
|
PLOG(ERROR) << "Unable to write daemon alive indicator path: "
|
||||||
|
<< GetDaemonAliveIndicatorPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace snapshot
|
} // namespace snapshot
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
|
@ -119,6 +119,12 @@ bool Daemon::StartServerForUserspaceSnapshots(int arg_start, int argc, char** ar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We reach this point only during selinux transition during device boot.
|
||||||
|
// At this point, all threads are spin up and are ready to serve the I/O
|
||||||
|
// requests for dm-user. Lets inform init.
|
||||||
|
auto client = std::make_unique<SnapuserdClient>();
|
||||||
|
client->NotifyTransitionDaemonIsReady();
|
||||||
|
|
||||||
// Skip the accept() call to avoid spurious log spam. The server will still
|
// Skip the accept() call to avoid spurious log spam. The server will still
|
||||||
// run until all handlers have completed.
|
// run until all handlers have completed.
|
||||||
return user_server_.WaitForSocket();
|
return user_server_.WaitForSocket();
|
||||||
|
|
|
@ -454,15 +454,7 @@ cc_test {
|
||||||
defaults: ["init_defaults"],
|
defaults: ["init_defaults"],
|
||||||
require_root: true,
|
require_root: true,
|
||||||
|
|
||||||
compile_multilib: "both",
|
compile_multilib: "first",
|
||||||
multilib: {
|
|
||||||
lib32: {
|
|
||||||
suffix: "32",
|
|
||||||
},
|
|
||||||
lib64: {
|
|
||||||
suffix: "64",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
srcs: [
|
srcs: [
|
||||||
"devices_test.cpp",
|
"devices_test.cpp",
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
|
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
|
||||||
<option name="cleanup" value="true" />
|
<option name="cleanup" value="true" />
|
||||||
<option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
|
<option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
|
||||||
<option name="append-bitness" value="true" />
|
|
||||||
</target_preparer>
|
</target_preparer>
|
||||||
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
|
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
|
||||||
<option name="throw-on-error" value="false" />
|
<option name="throw-on-error" value="false" />
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -431,6 +431,12 @@ std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uev
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string model;
|
||||||
|
if (ReadFileToString("/sys/class/block/" + uevent.device_name + "/queue/zoned", &model) &&
|
||||||
|
!StartsWith(model, "none")) {
|
||||||
|
links.emplace_back("/dev/block/by-name/zoned_device");
|
||||||
|
}
|
||||||
|
|
||||||
auto last_slash = uevent.path.rfind('/');
|
auto last_slash = uevent.path.rfind('/');
|
||||||
links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
|
links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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 = {
|
||||||
|
@ -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};
|
||||||
|
@ -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.
|
||||||
const std::string& source_context, const ucred& cr,
|
std::optional<uint32_t> HandlePropertySet(const std::string& name, const std::string& value,
|
||||||
SocketConnection* socket, 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) {
|
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
|
||||||
|
@ -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};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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, error);
|
return PropertySet(name, value, socket, 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() {
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 << ")";
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 << ")";
|
||||||
|
@ -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 << ")";
|
||||||
|
@ -976,7 +1033,7 @@ static void property_derive_build_product() {
|
||||||
LOG(INFO) << "Setting property 'ro.build.product' to '" << build_product << "'";
|
LOG(INFO) << "Setting property 'ro.build.product' to '" << build_product << "'";
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
uint32_t res = PropertySet("ro.build.product", build_product, &error);
|
auto res = PropertySetNoSocket("ro.build.product", build_product, &error);
|
||||||
if (res != PROP_SUCCESS) {
|
if (res != PROP_SUCCESS) {
|
||||||
LOG(ERROR) << "Error setting property 'ro.build.product': err=" << res << " (" << error
|
LOG(ERROR) << "Error setting property 'ro.build.product': err=" << res << " (" << error
|
||||||
<< ")";
|
<< ")";
|
||||||
|
@ -1006,7 +1063,7 @@ static void property_derive_build_description() {
|
||||||
LOG(INFO) << "Setting property 'ro.build.description' to '" << build_description << "'";
|
LOG(INFO) << "Setting property 'ro.build.description' to '" << build_description << "'";
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
uint32_t res = PropertySet("ro.build.description", build_description, &error);
|
auto res = PropertySetNoSocket("ro.build.description", build_description, &error);
|
||||||
if (res != PROP_SUCCESS) {
|
if (res != PROP_SUCCESS) {
|
||||||
LOG(ERROR) << "Error setting property 'ro.build.description': err=" << res << " (" << error
|
LOG(ERROR) << "Error setting property 'ro.build.description': err=" << res << " (" << error
|
||||||
<< ")";
|
<< ")";
|
||||||
|
@ -1052,7 +1109,7 @@ static void property_derive_build_display_id() {
|
||||||
LOG(INFO) << "Setting property 'ro.build.display.id' to '" << build_display_id << "'";
|
LOG(INFO) << "Setting property 'ro.build.display.id' to '" << build_display_id << "'";
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
uint32_t res = PropertySet("ro.build.display.id", build_display_id, &error);
|
auto res = PropertySetNoSocket("ro.build.display.id", build_display_id, &error);
|
||||||
if (res != PROP_SUCCESS) {
|
if (res != PROP_SUCCESS) {
|
||||||
LOG(ERROR) << "Error setting property 'ro.build.display.id': err=" << res << " (" << error
|
LOG(ERROR) << "Error setting property 'ro.build.display.id': err=" << res << " (" << error
|
||||||
<< ")";
|
<< ")";
|
||||||
|
@ -1121,7 +1178,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
|
||||||
<< ")";
|
<< ")";
|
||||||
|
@ -1155,7 +1212,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 << ")";
|
||||||
|
@ -1249,7 +1306,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;
|
||||||
}
|
}
|
||||||
|
@ -1491,6 +1548,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");
|
||||||
|
|
||||||
|
@ -1515,6 +1612,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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,10 @@ void LaunchFirstStageSnapuserd(SnapshotDriver driver) {
|
||||||
|
|
||||||
setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
|
setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
|
||||||
|
|
||||||
|
if (!client->RemoveTransitionedDaemonIndicator()) {
|
||||||
|
LOG(ERROR) << "RemoveTransitionedDaemonIndicator failed";
|
||||||
|
}
|
||||||
|
|
||||||
LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
|
LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,6 +267,19 @@ void SnapuserdSelinuxHelper::FinishTransition() {
|
||||||
* we may see audit logs.
|
* we may see audit logs.
|
||||||
*/
|
*/
|
||||||
bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() {
|
bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() {
|
||||||
|
// Wait for the daemon to be fully up. Daemon will write to path
|
||||||
|
// /metadata/ota/daemon-alive-indicator only when all the threads
|
||||||
|
// are ready and attached to dm-user.
|
||||||
|
//
|
||||||
|
// This check will fail for GRF devices with vendor on Android S.
|
||||||
|
// snapuserd binary from Android S won't be able to communicate
|
||||||
|
// and hence, we will fallback and issue I/O to verify
|
||||||
|
// the presence of daemon.
|
||||||
|
auto client = std::make_unique<SnapuserdClient>();
|
||||||
|
if (!client->IsTransitionedDaemonReady()) {
|
||||||
|
LOG(ERROR) << "IsTransitionedDaemonReady failed";
|
||||||
|
}
|
||||||
|
|
||||||
std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix();
|
std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix();
|
||||||
android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT));
|
android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT));
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
|
|
|
@ -8,5 +8,10 @@
|
||||||
{
|
{
|
||||||
"name": "libcutils_test"
|
"name": "libcutils_test"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"kernel-presubmit": [
|
||||||
|
{
|
||||||
|
"name": "libcutils_test"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
BasedOnStyle: Google
|
||||||
|
AllowShortIfStatementsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
ColumnLimit: 100
|
||||||
|
CommentPragmas: NOLINT:.*
|
||||||
|
ContinuationIndentWidth: 8
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
IndentWidth: 4
|
||||||
|
PointerAlignment: Left
|
||||||
|
TabWidth: 4
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^"Log\.h"'
|
||||||
|
Priority: -1
|
|
@ -0,0 +1,71 @@
|
||||||
|
|
||||||
|
//
|
||||||
|
// Copyright (C) 2023 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.
|
||||||
|
//
|
||||||
|
package {
|
||||||
|
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_library {
|
||||||
|
name: "libexpresslog",
|
||||||
|
srcs: [
|
||||||
|
"Counter.cpp",
|
||||||
|
],
|
||||||
|
cflags: [
|
||||||
|
"-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash",
|
||||||
|
"-Wall",
|
||||||
|
"-Werror",
|
||||||
|
],
|
||||||
|
header_libs: [
|
||||||
|
"libtextclassifier_hash_headers",
|
||||||
|
],
|
||||||
|
static_libs: [
|
||||||
|
"libstatslog_express",
|
||||||
|
"libtextclassifier_hash_static",
|
||||||
|
],
|
||||||
|
shared_libs: [
|
||||||
|
"libbase",
|
||||||
|
"libstatssocket",
|
||||||
|
],
|
||||||
|
export_include_dirs: ["include"],
|
||||||
|
}
|
||||||
|
|
||||||
|
genrule {
|
||||||
|
name: "statslog_express.h",
|
||||||
|
tools: ["stats-log-api-gen"],
|
||||||
|
cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_express.h --module expresslog --namespace android,expresslog",
|
||||||
|
out: [
|
||||||
|
"statslog_express.h",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
genrule {
|
||||||
|
name: "statslog_express.cpp",
|
||||||
|
tools: ["stats-log-api-gen"],
|
||||||
|
cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_express.cpp --module expresslog --namespace android,expresslog --importHeader statslog_express.h",
|
||||||
|
out: [
|
||||||
|
"statslog_express.cpp",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_library_static {
|
||||||
|
name: "libstatslog_express",
|
||||||
|
generated_sources: ["statslog_express.cpp"],
|
||||||
|
generated_headers: ["statslog_express.h"],
|
||||||
|
export_generated_headers: ["statslog_express.h"],
|
||||||
|
shared_libs: [
|
||||||
|
"libstatssocket",
|
||||||
|
],
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2023 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "include/Counter.h"
|
||||||
|
|
||||||
|
#include <statslog_express.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <utils/hash/farmhash.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace expresslog {
|
||||||
|
|
||||||
|
void Counter::logIncrement(const char* metricName, int64_t amount) {
|
||||||
|
const int64_t metricIdHash = farmhash::Fingerprint64(metricName, strlen(metricName));
|
||||||
|
stats_write(EXPRESS_EVENT_REPORTED, metricIdHash, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace expresslog
|
||||||
|
} // namespace android
|
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2023 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 <stdint.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace expresslog {
|
||||||
|
|
||||||
|
/** Counter encapsulates StatsD write API calls */
|
||||||
|
class Counter final {
|
||||||
|
public:
|
||||||
|
static void logIncrement(const char* metricId, int64_t amount = 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace expresslog
|
||||||
|
} // namespace android
|
|
@ -1,20 +1,18 @@
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/sysmacros.h>
|
#include <sys/sysmacros.h>
|
||||||
#include <dirent.h>
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <stdarg.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
#include <linux/kdev_t.h>
|
#include <linux/kdev_t.h>
|
||||||
|
|
||||||
|
@ -23,23 +21,12 @@
|
||||||
|
|
||||||
/* NOTES
|
/* NOTES
|
||||||
**
|
**
|
||||||
** - see buffer-format.txt from the linux kernel docs for
|
** - see https://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt
|
||||||
** an explanation of this file format
|
** for an explanation of this file format
|
||||||
** - dotfiles are ignored
|
** - dotfiles are ignored
|
||||||
** - directories named 'root' are ignored
|
** - directories named 'root' are ignored
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void die(const char* why, ...) {
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, why);
|
|
||||||
fprintf(stderr,"error: ");
|
|
||||||
vfprintf(stderr, why, ap);
|
|
||||||
fprintf(stderr,"\n");
|
|
||||||
va_end(ap);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct fs_config_entry {
|
struct fs_config_entry {
|
||||||
char* name;
|
char* name;
|
||||||
int uid, gid, mode;
|
int uid, gid, mode;
|
||||||
|
@ -48,17 +35,8 @@ struct fs_config_entry {
|
||||||
static struct fs_config_entry* canned_config = NULL;
|
static struct fs_config_entry* canned_config = NULL;
|
||||||
static const char* target_out_path = NULL;
|
static const char* target_out_path = NULL;
|
||||||
|
|
||||||
/* Each line in the canned file should be a path plus three ints (uid,
|
|
||||||
* gid, mode). */
|
|
||||||
#ifdef PATH_MAX
|
|
||||||
#define CANNED_LINE_LENGTH (PATH_MAX+100)
|
|
||||||
#else
|
|
||||||
#define CANNED_LINE_LENGTH (1024)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define TRAILER "TRAILER!!!"
|
#define TRAILER "TRAILER!!!"
|
||||||
|
|
||||||
static int verbose = 0;
|
|
||||||
static int total_size = 0;
|
static int total_size = 0;
|
||||||
|
|
||||||
static void fix_stat(const char *path, struct stat *s)
|
static void fix_stat(const char *path, struct stat *s)
|
||||||
|
@ -134,7 +112,7 @@ static void _eject(struct stat *s, char *out, int olen, char *data, unsigned dat
|
||||||
|
|
||||||
total_size += 6 + 8*13 + olen + 1;
|
total_size += 6 + 8*13 + olen + 1;
|
||||||
|
|
||||||
if(strlen(out) != (unsigned int)olen) die("ACK!");
|
if(strlen(out) != (unsigned int)olen) errx(1, "ACK!");
|
||||||
|
|
||||||
while(total_size & 3) {
|
while(total_size & 3) {
|
||||||
total_size++;
|
total_size++;
|
||||||
|
@ -168,23 +146,16 @@ static int compare(const void* a, const void* b) {
|
||||||
static void _archive_dir(char *in, char *out, int ilen, int olen)
|
static void _archive_dir(char *in, char *out, int ilen, int olen)
|
||||||
{
|
{
|
||||||
int i, t;
|
int i, t;
|
||||||
DIR *d;
|
|
||||||
struct dirent *de;
|
struct dirent *de;
|
||||||
|
|
||||||
if(verbose) {
|
DIR* d = opendir(in);
|
||||||
fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n",
|
if (d == NULL) err(1, "cannot open directory '%s'", in);
|
||||||
in, out, ilen, olen);
|
|
||||||
}
|
|
||||||
|
|
||||||
d = opendir(in);
|
|
||||||
if(d == 0) die("cannot open directory '%s'", in);
|
|
||||||
|
|
||||||
int size = 32;
|
int size = 32;
|
||||||
int entries = 0;
|
int entries = 0;
|
||||||
char** names = malloc(size * sizeof(char*));
|
char** names = malloc(size * sizeof(char*));
|
||||||
if (names == NULL) {
|
if (names == NULL) {
|
||||||
fprintf(stderr, "failed to allocate dir names array (size %d)\n", size);
|
errx(1, "failed to allocate dir names array (size %d)", size);
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while((de = readdir(d)) != 0){
|
while((de = readdir(d)) != 0){
|
||||||
|
@ -198,16 +169,12 @@ static void _archive_dir(char *in, char *out, int ilen, int olen)
|
||||||
size *= 2;
|
size *= 2;
|
||||||
names = realloc(names, size * sizeof(char*));
|
names = realloc(names, size * sizeof(char*));
|
||||||
if (names == NULL) {
|
if (names == NULL) {
|
||||||
fprintf(stderr, "failed to reallocate dir names array (size %d)\n",
|
errx(1, "failed to reallocate dir names array (size %d)", size);
|
||||||
size);
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
names[entries] = strdup(de->d_name);
|
names[entries] = strdup(de->d_name);
|
||||||
if (names[entries] == NULL) {
|
if (names[entries] == NULL) {
|
||||||
fprintf(stderr, "failed to strdup name \"%s\"\n",
|
errx(1, "failed to strdup name \"%s\"", de->d_name);
|
||||||
de->d_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
++entries;
|
++entries;
|
||||||
}
|
}
|
||||||
|
@ -241,26 +208,17 @@ static void _archive_dir(char *in, char *out, int ilen, int olen)
|
||||||
static void _archive(char *in, char *out, int ilen, int olen)
|
static void _archive(char *in, char *out, int ilen, int olen)
|
||||||
{
|
{
|
||||||
struct stat s;
|
struct stat s;
|
||||||
|
if(lstat(in, &s)) err(1, "could not stat '%s'", in);
|
||||||
if(verbose) {
|
|
||||||
fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
|
|
||||||
in, out, ilen, olen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(lstat(in, &s)) die("could not stat '%s'\n", in);
|
|
||||||
|
|
||||||
if(S_ISREG(s.st_mode)){
|
if(S_ISREG(s.st_mode)){
|
||||||
char *tmp;
|
int fd = open(in, O_RDONLY);
|
||||||
int fd;
|
if(fd < 0) err(1, "cannot open '%s' for read", in);
|
||||||
|
|
||||||
fd = open(in, O_RDONLY);
|
char* tmp = (char*) malloc(s.st_size);
|
||||||
if(fd < 0) die("cannot open '%s' for read", in);
|
if(tmp == 0) errx(1, "cannot allocate %zd bytes", s.st_size);
|
||||||
|
|
||||||
tmp = (char*) malloc(s.st_size);
|
|
||||||
if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
|
|
||||||
|
|
||||||
if(read(fd, tmp, s.st_size) != s.st_size) {
|
if(read(fd, tmp, s.st_size) != s.st_size) {
|
||||||
die("cannot read %d bytes", s.st_size);
|
err(1, "cannot read %zd bytes", s.st_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
_eject(&s, out, olen, tmp, s.st_size);
|
_eject(&s, out, olen, tmp, s.st_size);
|
||||||
|
@ -274,13 +232,13 @@ static void _archive(char *in, char *out, int ilen, int olen)
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
int size;
|
int size;
|
||||||
size = readlink(in, buf, 1024);
|
size = readlink(in, buf, 1024);
|
||||||
if(size < 0) die("cannot read symlink '%s'", in);
|
if(size < 0) err(1, "cannot read symlink '%s'", in);
|
||||||
_eject(&s, out, olen, buf, size);
|
_eject(&s, out, olen, buf, size);
|
||||||
} else if(S_ISBLK(s.st_mode) || S_ISCHR(s.st_mode) ||
|
} else if(S_ISBLK(s.st_mode) || S_ISCHR(s.st_mode) ||
|
||||||
S_ISFIFO(s.st_mode) || S_ISSOCK(s.st_mode)) {
|
S_ISFIFO(s.st_mode) || S_ISSOCK(s.st_mode)) {
|
||||||
_eject(&s, out, olen, NULL, 0);
|
_eject(&s, out, olen, NULL, 0);
|
||||||
} else {
|
} else {
|
||||||
die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
|
errx(1, "Unknown '%s' (mode %d)?", in, s.st_mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,17 +260,18 @@ static void read_canned_config(char* filename)
|
||||||
canned_config =
|
canned_config =
|
||||||
(struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry));
|
(struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry));
|
||||||
|
|
||||||
char line[CANNED_LINE_LENGTH];
|
FILE* fp = fopen(filename, "r");
|
||||||
FILE* f = fopen(filename, "r");
|
if (fp == NULL) err(1, "failed to open canned file '%s'", filename);
|
||||||
if (f == NULL) die("failed to open canned file '%s'", filename);
|
|
||||||
|
|
||||||
while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) {
|
char* line = NULL;
|
||||||
|
size_t allocated_len;
|
||||||
|
while (getline(&line, &allocated_len, fp) != -1) {
|
||||||
if (!line[0]) break;
|
if (!line[0]) break;
|
||||||
if (used >= allocated) {
|
if (used >= allocated) {
|
||||||
allocated *= 2;
|
allocated *= 2;
|
||||||
canned_config = (struct fs_config_entry*)realloc(
|
canned_config = (struct fs_config_entry*)realloc(
|
||||||
canned_config, allocated * sizeof(struct fs_config_entry));
|
canned_config, allocated * sizeof(struct fs_config_entry));
|
||||||
if (canned_config == NULL) die("failed to reallocate memory");
|
if (canned_config == NULL) errx(1, "failed to reallocate memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct fs_config_entry* cc = canned_config + used;
|
struct fs_config_entry* cc = canned_config + used;
|
||||||
|
@ -332,17 +291,18 @@ static void read_canned_config(char* filename)
|
||||||
++allocated;
|
++allocated;
|
||||||
canned_config = (struct fs_config_entry*)realloc(
|
canned_config = (struct fs_config_entry*)realloc(
|
||||||
canned_config, allocated * sizeof(struct fs_config_entry));
|
canned_config, allocated * sizeof(struct fs_config_entry));
|
||||||
if (canned_config == NULL) die("failed to reallocate memory");
|
if (canned_config == NULL) errx(1, "failed to reallocate memory");
|
||||||
}
|
}
|
||||||
canned_config[used].name = NULL;
|
canned_config[used].name = NULL;
|
||||||
|
|
||||||
fclose(f);
|
free(line);
|
||||||
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void devnodes_desc_error(const char* filename, unsigned long line_num,
|
static void devnodes_desc_error(const char* filename, unsigned long line_num,
|
||||||
const char* msg)
|
const char* msg)
|
||||||
{
|
{
|
||||||
errx(EXIT_FAILURE, "failed to read nodes desc file '%s' line %lu: %s", filename, line_num, msg);
|
errx(1, "failed to read nodes desc file '%s' line %lu: %s", filename, line_num, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int append_devnodes_desc_dir(char* path, char* args)
|
static int append_devnodes_desc_dir(char* path, char* args)
|
||||||
|
@ -386,15 +346,15 @@ static int append_devnodes_desc_nod(char* path, char* args)
|
||||||
|
|
||||||
static void append_devnodes_desc(const char* filename)
|
static void append_devnodes_desc(const char* filename)
|
||||||
{
|
{
|
||||||
FILE* f = fopen(filename, "re");
|
FILE* fp = fopen(filename, "re");
|
||||||
if (!f) err(EXIT_FAILURE, "failed to open nodes description file '%s'", filename);
|
if (!fp) err(1, "failed to open nodes description file '%s'", filename);
|
||||||
|
|
||||||
char *line, *args, *type, *path;
|
|
||||||
unsigned long line_num = 0;
|
unsigned long line_num = 0;
|
||||||
size_t allocated_len;
|
|
||||||
|
|
||||||
while (getline(&line, &allocated_len, f) != -1) {
|
char* line = NULL;
|
||||||
char* type;
|
size_t allocated_len;
|
||||||
|
while (getline(&line, &allocated_len, fp) != -1) {
|
||||||
|
char *type, *path, *args;
|
||||||
|
|
||||||
line_num++;
|
line_num++;
|
||||||
|
|
||||||
|
@ -428,7 +388,7 @@ static void append_devnodes_desc(const char* filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
free(line);
|
free(line);
|
||||||
fclose(f);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct option long_options[] = {
|
static const struct option long_options[] = {
|
||||||
|
@ -448,7 +408,8 @@ static void usage(void)
|
||||||
"\t-f, --file=FILE: Canned configuration file\n"
|
"\t-f, --file=FILE: Canned configuration file\n"
|
||||||
"\t-h, --help: Print this help\n"
|
"\t-h, --help: Print this help\n"
|
||||||
"\t-n, --nodes=FILE: Dev nodes description file\n"
|
"\t-n, --nodes=FILE: Dev nodes description file\n"
|
||||||
"\nDev nodes description:\n"
|
"\n"
|
||||||
|
"Dev nodes description:\n"
|
||||||
"\t[dir|nod] [perms] [uid] [gid] [c|b] [minor] [major]\n"
|
"\t[dir|nod] [perms] [uid] [gid] [c|b] [minor] [major]\n"
|
||||||
"\tExample:\n"
|
"\tExample:\n"
|
||||||
"\t\t# My device nodes\n"
|
"\t\t# My device nodes\n"
|
||||||
|
@ -477,7 +438,7 @@ int main(int argc, char *argv[])
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
die("Unknown option %s", argv[optind - 1]);
|
errx(1, "Unknown option %s", argv[optind - 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,7 +447,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
if (num_dirs <= 0) {
|
if (num_dirs <= 0) {
|
||||||
usage();
|
usage();
|
||||||
die("no directories to process?!");
|
errx(1, "no directories to process?!");
|
||||||
}
|
}
|
||||||
|
|
||||||
while(num_dirs-- > 0){
|
while(num_dirs-- > 0){
|
||||||
|
|
|
@ -660,7 +660,7 @@ on late-fs
|
||||||
chmod 0755 /sys/kernel/tracing
|
chmod 0755 /sys/kernel/tracing
|
||||||
chmod 0755 /sys/kernel/debug/tracing
|
chmod 0755 /sys/kernel/debug/tracing
|
||||||
|
|
||||||
# HALs required before storage encryption can get unlocked (FBE/FDE)
|
# HALs required before storage encryption can get unlocked (FBE)
|
||||||
class_start early_hal
|
class_start early_hal
|
||||||
|
|
||||||
# Load trusted keys from dm-verity protected partitions
|
# Load trusted keys from dm-verity protected partitions
|
||||||
|
@ -763,9 +763,8 @@ on post-fs-data
|
||||||
|
|
||||||
# /data/apex is now available. Start apexd to scan and activate APEXes.
|
# /data/apex is now available. Start apexd to scan and activate APEXes.
|
||||||
#
|
#
|
||||||
# To handle userspace reboots as well as devices that use FDE, make sure
|
# To handle userspace reboots, make sure that apexd is started cleanly here
|
||||||
# that apexd is started cleanly here (set apexd.status="") and that it is
|
# (set apexd.status="") and that it is restarted if it's already running.
|
||||||
# restarted if it's already running.
|
|
||||||
#
|
#
|
||||||
# /data/apex uses encryption=None because direct I/O support is needed on
|
# /data/apex uses encryption=None because direct I/O support is needed on
|
||||||
# APEX files, but some devices don't support direct I/O on encrypted files.
|
# APEX files, but some devices don't support direct I/O on encrypted files.
|
||||||
|
@ -1303,6 +1302,7 @@ service console /system/bin/sh
|
||||||
group shell log readproc
|
group shell log readproc
|
||||||
seclabel u:r:shell:s0
|
seclabel u:r:shell:s0
|
||||||
setenv HOSTNAME console
|
setenv HOSTNAME console
|
||||||
|
shutdown critical
|
||||||
|
|
||||||
on property:ro.debuggable=1
|
on property:ro.debuggable=1
|
||||||
# Give writes to anyone for the trace folder on debug builds.
|
# Give writes to anyone for the trace folder on debug builds.
|
||||||
|
|
Loading…
Reference in New Issue