Merge "Include vendor gralloc0 flags in gralloc1 conversion." into oc-dev
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 024bc3d..4783d6e 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -163,6 +163,7 @@
srcs: [
"client/debuggerd_client_test.cpp",
"debuggerd_test.cpp",
+ "tombstoned_client.cpp",
"util.cpp"
],
},
@@ -176,7 +177,8 @@
],
static_libs: [
- "libdebuggerd"
+ "libdebuggerd",
+ "libc_logging",
],
local_include_dirs: [
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index b9fb512..224444f 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -65,24 +65,25 @@
auto time_left = [timeout_ms, &end]() { return end - std::chrono::steady_clock::now(); };
auto set_timeout = [timeout_ms, &time_left](int sockfd) {
if (timeout_ms <= 0) {
- return true;
+ return -1;
}
auto remaining = time_left();
if (remaining < decltype(remaining)::zero()) {
- return false;
+ LOG(ERROR) << "timeout expired";
+ return -1;
}
struct timeval timeout;
populate_timeval(&timeout, remaining);
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
- return false;
+ return -1;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
- return false;
+ return -1;
}
- return true;
+ return sockfd;
};
sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
@@ -91,12 +92,7 @@
return false;
}
- if (!set_timeout(sockfd)) {
- PLOG(ERROR) << "libdebugger_client: failed to set timeout";
- return false;
- }
-
- if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,
+ if (socket_local_client_connect(set_timeout(sockfd.get()), kTombstonedInterceptSocketName,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
return false;
@@ -115,21 +111,35 @@
return false;
}
- if (send_fd(sockfd.get(), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
+ if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
return false;
}
+ // Check to make sure we've successfully registered.
+ InterceptResponse response;
+ ssize_t rc =
+ TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+ if (rc == 0) {
+ LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+ return false;
+ } else if (rc != sizeof(response)) {
+ LOG(ERROR)
+ << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
+ << sizeof(response) << ", received " << rc;
+ return false;
+ }
+
+ if (response.status != InterceptStatus::kRegistered) {
+ LOG(ERROR) << "libdebuggerd_client: unexpected registration response: "
+ << static_cast<int>(response.status);
+ return false;
+ }
+
bool backtrace = dump_type == kDebuggerdBacktrace;
send_signal(pid, backtrace);
- if (!set_timeout(sockfd)) {
- PLOG(ERROR) << "libdebuggerd_client: failed to set timeout";
- return false;
- }
-
- InterceptResponse response;
- ssize_t rc = TEMP_FAILURE_RETRY(recv(sockfd.get(), &response, sizeof(response), MSG_TRUNC));
+ rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
return false;
@@ -140,7 +150,7 @@
return false;
}
- if (response.success != 1) {
+ if (response.status != InterceptStatus::kStarted) {
response.error_message[sizeof(response.error_message) - 1] = '\0';
LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
return false;
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 1a27f3f..1befcb1 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -36,6 +36,7 @@
#include <cutils/sockets.h>
#include <debuggerd/handler.h>
#include <debuggerd/protocol.h>
+#include <debuggerd/tombstoned.h>
#include <debuggerd/util.h>
#include <gtest/gtest.h>
@@ -77,6 +78,54 @@
} \
} while (0)
+static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
+ intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+ if (intercept_fd->get() == -1) {
+ FAIL() << "failed to contact tombstoned: " << strerror(errno);
+ }
+
+ InterceptRequest req = {.pid = target_pid};
+
+ unique_fd output_pipe_write;
+ if (!Pipe(output_fd, &output_pipe_write)) {
+ FAIL() << "failed to create output pipe: " << strerror(errno);
+ }
+
+ std::string pipe_size_str;
+ int pipe_buffer_size;
+ if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
+ FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
+ }
+
+ pipe_size_str = android::base::Trim(pipe_size_str);
+
+ if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
+ FAIL() << "failed to parse pipe max size";
+ }
+
+ if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
+ FAIL() << "failed to set pipe size: " << strerror(errno);
+ }
+
+ if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+ FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
+ }
+
+ InterceptResponse response;
+ ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+ if (rc == -1) {
+ FAIL() << "failed to read response from tombstoned: " << strerror(errno);
+ } else if (rc == 0) {
+ FAIL() << "failed to read response from tombstoned (EOF)";
+ } else if (rc != sizeof(response)) {
+ FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
+ << ", received " << rc;
+ }
+
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+}
+
class CrasherTest : public ::testing::Test {
public:
pid_t crasher_pid = -1;
@@ -118,38 +167,7 @@
FAIL() << "crasher hasn't been started";
}
- intercept_fd.reset(socket_local_client(kTombstonedInterceptSocketName,
- ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
- if (intercept_fd == -1) {
- FAIL() << "failed to contact tombstoned: " << strerror(errno);
- }
-
- InterceptRequest req = {.pid = crasher_pid };
-
- unique_fd output_pipe_write;
- if (!Pipe(output_fd, &output_pipe_write)) {
- FAIL() << "failed to create output pipe: " << strerror(errno);
- }
-
- std::string pipe_size_str;
- int pipe_buffer_size;
- if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
- FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
- }
-
- pipe_size_str = android::base::Trim(pipe_size_str);
-
- if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
- FAIL() << "failed to parse pipe max size";
- }
-
- if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
- FAIL() << "failed to set pipe size: " << strerror(errno);
- }
-
- if (send_fd(intercept_fd.get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
- FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
- }
+ tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd);
}
void CrasherTest::FinishIntercept(int* result) {
@@ -165,7 +183,7 @@
FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
<< ", received " << rc;
} else {
- *result = response.success;
+ *result = response.status == InterceptStatus::kStarted ? 1 : 0;
}
}
@@ -508,3 +526,97 @@
ASSERT_EQ(0, WEXITSTATUS(status));
}
}
+
+TEST(tombstoned, no_notify) {
+ // Do this a few times.
+ for (int i = 0; i < 3; ++i) {
+ pid_t pid = 123'456'789 + i;
+
+ unique_fd intercept_fd, output_fd;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+ {
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
+ }
+
+ pid_t read_pid;
+ ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
+ ASSERT_EQ(read_pid, pid);
+ }
+}
+
+TEST(tombstoned, stress) {
+ // Spawn threads to simultaneously do a bunch of failing dumps and a bunch of successful dumps.
+ static constexpr int kDumpCount = 100;
+
+ std::atomic<bool> start(false);
+ std::vector<std::thread> threads;
+ threads.emplace_back([&start]() {
+ while (!start) {
+ continue;
+ }
+
+ // Use a way out of range pid, to avoid stomping on an actual process.
+ pid_t pid_base = 1'000'000;
+
+ for (int dump = 0; dump < kDumpCount; ++dump) {
+ pid_t pid = pid_base + dump;
+
+ unique_fd intercept_fd, output_fd;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+ // Pretend to crash, and then immediately close the socket.
+ unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+ if (sockfd == -1) {
+ FAIL() << "failed to connect to tombstoned: " << strerror(errno);
+ }
+ TombstonedCrashPacket packet = {};
+ packet.packet_type = CrashPacketType::kDumpRequest;
+ packet.packet.dump_request.pid = pid;
+ if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
+ FAIL() << "failed to write to tombstoned: " << strerror(errno);
+ }
+
+ continue;
+ }
+ });
+
+ threads.emplace_back([&start]() {
+ while (!start) {
+ continue;
+ }
+
+ // Use a way out of range pid, to avoid stomping on an actual process.
+ pid_t pid_base = 2'000'000;
+
+ for (int dump = 0; dump < kDumpCount; ++dump) {
+ pid_t pid = pid_base + dump;
+
+ unique_fd intercept_fd, output_fd;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+ {
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
+ tombstoned_notify_completion(tombstoned_socket.get());
+ }
+
+ // TODO: Fix the race that requires this sleep.
+ std::this_thread::sleep_for(50ms);
+
+ pid_t read_pid;
+ ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
+ ASSERT_EQ(read_pid, pid);
+ }
+ });
+
+ start = true;
+
+ for (std::thread& thread : threads) {
+ thread.join();
+ }
+}
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/include/debuggerd/protocol.h
index bb2ab0d..0756876 100644
--- a/debuggerd/include/debuggerd/protocol.h
+++ b/debuggerd/include/debuggerd/protocol.h
@@ -56,8 +56,14 @@
int32_t pid;
};
+enum class InterceptStatus : uint8_t {
+ kFailed,
+ kStarted,
+ kRegistered,
+};
+
// Sent either immediately upon failure, or when the intercept has been used.
struct InterceptResponse {
- uint8_t success; // 0 or 1
+ InterceptStatus status;
char error_message[127]; // always null-terminated
};
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 789260d..dff942c 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -105,6 +105,7 @@
// We trust the other side, so only do minimal validity checking.
if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits<pid_t>::max()) {
InterceptResponse response = {};
+ response.status = InterceptStatus::kFailed;
snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
intercept_request.pid);
TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
@@ -113,9 +114,10 @@
intercept->intercept_pid = intercept_request.pid;
- // Register the intercept with the InterceptManager.
+ // Check if it's already registered.
if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
InterceptResponse response = {};
+ response.status = InterceptStatus::kFailed;
snprintf(response.error_message, sizeof(response.error_message),
"pid %" PRId32 " already intercepted", intercept_request.pid);
TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
@@ -123,6 +125,15 @@
goto fail;
}
+ // Let the other side know that the intercept has been registered, now that we know we can't
+ // fail. tombstoned is single threaded, so this isn't racy.
+ InterceptResponse response = {};
+ response.status = InterceptStatus::kRegistered;
+ if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
+ PLOG(WARNING) << "failed to notify interceptor of registration";
+ goto fail;
+ }
+
intercept->output_fd = std::move(rcv_fd);
intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
intercept->registered = true;
@@ -174,7 +185,7 @@
LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
InterceptResponse response = {};
- response.success = 1;
+ response.status = InterceptStatus::kStarted;
TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
*out_fd = std::move(intercept->output_fd);
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 6754508..2248a21 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -126,9 +126,7 @@
return result;
}
-static void dequeue_request(Crash* crash) {
- ++num_concurrent_dumps;
-
+static void perform_request(Crash* crash) {
unique_fd output_fd;
if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
output_fd = get_tombstone_fd();
@@ -153,12 +151,22 @@
crash_completed_cb, crash);
event_add(crash->crash_event, &timeout);
}
+
+ ++num_concurrent_dumps;
return;
fail:
delete crash;
}
+static void dequeue_requests() {
+ while (!queued_requests.empty() && num_concurrent_dumps < kMaxConcurrentDumps) {
+ Crash* next_crash = queued_requests.front();
+ queued_requests.pop_front();
+ perform_request(next_crash);
+ }
+}
+
static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
void*) {
event_base* base = evconnlistener_get_base(listener);
@@ -207,7 +215,7 @@
LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
queued_requests.push_back(crash);
} else {
- dequeue_request(crash);
+ perform_request(crash);
}
return;
@@ -247,11 +255,7 @@
delete crash;
// If there's something queued up, let them proceed.
- if (!queued_requests.empty()) {
- Crash* next_crash = queued_requests.front();
- queued_requests.pop_front();
- dequeue_request(next_crash);
- }
+ dequeue_requests();
}
int main(int, char* []) {
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index a762038..7512eb9 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -390,10 +390,12 @@
continue;
}
- // Ensures that hashtree descriptor is either in /vbmeta or in
+ // Ensures that hashtree descriptor is in /vbmeta or /boot or in
// the same partition for verity setup.
std::string vbmeta_partition_name(verify_data.vbmeta_images[i].partition_name);
- if (vbmeta_partition_name != "vbmeta" && vbmeta_partition_name != partition_name) {
+ if (vbmeta_partition_name != "vbmeta" &&
+ vbmeta_partition_name != "boot" && // for legacy device to append top-level vbmeta
+ vbmeta_partition_name != partition_name) {
LWARNING << "Skip vbmeta image at " << verify_data.vbmeta_images[i].partition_name
<< " for partition: " << partition_name.c_str();
continue;
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 2c14c9b..8e49663 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -39,7 +39,28 @@
#include "fs_mgr_avb_ops.h"
#include "fs_mgr_priv.h"
-static struct fstab* fs_mgr_fstab = nullptr;
+static std::string fstab_by_name_prefix;
+
+static std::string extract_by_name_prefix(struct fstab* fstab) {
+ // In AVB, we can assume that there's an entry for the /misc mount
+ // point in the fstab file and use that to get the device file for
+ // the misc partition. The device needs not to have an actual /misc
+ // partition. Then returns the prefix by removing the trailing "misc":
+ //
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/
+
+ struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+ if (fstab_entry == nullptr) {
+ LERROR << "/misc mount point not found in fstab";
+ return "";
+ }
+
+ std::string full_path(fstab_entry->blk_device);
+ size_t end_slash = full_path.find_last_of("/");
+
+ return full_path.substr(0, end_slash + 1);
+}
static AvbIOResult read_from_partition(AvbOps* ops ATTRIBUTE_UNUSED, const char* partition,
int64_t offset, size_t num_bytes, void* buffer,
@@ -49,30 +70,14 @@
// for partitions having 'slotselect' optin in fstab file, but it
// won't be appended to the mount point.
//
- // In AVB, we can assume that there's an entry for the /misc mount
- // point and use that to get the device file for the misc partition.
- // From there we'll assume that a by-name scheme is used
- // so we can just replace the trailing "misc" by the given
- // |partition|, e.g.
+ // Appends |partition| to the fstab_by_name_prefix, which is obtained
+ // by removing the trailing "misc" from the device file of /misc mount
+ // point. e.g.,
//
- // - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/ ->
// - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
- struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
-
- if (fstab_entry == nullptr) {
- LERROR << "/misc mount point not found in fstab";
- return AVB_IO_RESULT_ERROR_IO;
- }
-
- std::string partition_name(partition);
- std::string path(fstab_entry->blk_device);
- // Replaces the last field of device file if it's not misc.
- if (!android::base::StartsWith(partition_name, "misc")) {
- size_t end_slash = path.find_last_of("/");
- std::string by_name_prefix(path.substr(0, end_slash + 1));
- path = by_name_prefix + partition_name;
- }
+ std::string path = fstab_by_name_prefix + partition;
// Ensures the device path (a symlink created by init) is ready to
// access. fs_mgr_test_access() will test a few iterations if the
@@ -168,8 +173,8 @@
AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab) {
AvbOps* ops;
- // Assigns the fstab to the static variable for later use.
- fs_mgr_fstab = fstab;
+ fstab_by_name_prefix = extract_by_name_prefix(fstab);
+ if (fstab_by_name_prefix.empty()) return nullptr;
ops = (AvbOps*)calloc(1, sizeof(AvbOps));
if (ops == nullptr) {
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 5b2f218..cffa6ce 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -56,7 +56,7 @@
return true;
}
- LERROR << "Error finding '" << key << "' in device tree";
+ LINFO << "Error finding '" << key << "' in device tree";
}
return false;
diff --git a/init/Android.mk b/init/Android.mk
index b52c949..1ca88d7 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -16,6 +16,14 @@
-DREBOOT_BOOTLOADER_ON_PANIC=0
endif
+ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
+init_options += \
+ -DSHUTDOWN_ZERO_TIMEOUT=1
+else
+init_options += \
+ -DSHUTDOWN_ZERO_TIMEOUT=0
+endif
+
init_options += -DLOG_UEVENTS=0
init_cflags += \
diff --git a/init/README.md b/init/README.md
index 024d559..e66ade2 100644
--- a/init/README.md
+++ b/init/README.md
@@ -286,11 +286,6 @@
`copy <src> <dst>`
> Copies a file. Similar to write, but useful for binary/large
amounts of data.
- Regarding to the src file, copying from symbol link file and world-writable
- or group-writable files are not allowed.
- Regarding to the dst file, the default mode created is 0600 if it does not
- exist. And it will be truncated if dst file is a normal regular file and
- already exists.
`domainname <name>`
> Set the domain name.
@@ -311,6 +306,12 @@
groups can be provided. No other commands will be run until this one
finishes. _seclabel_ can be a - to denote default. Properties are expanded
within _argument_.
+ Init halts executing commands until the forked process exits.
+
+`exec_start <service>`
+> Start service a given service and halt processing of additional init commands
+ until it returns. It functions similarly to the `exec` command, but uses an
+ existing service definition instead of providing them as arguments.
`export <name> <value>`
> Set the environment variable _name_ equal to _value_ in the
diff --git a/init/action.cpp b/init/action.cpp
index 1bba0f2..2ccf0bc 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,14 +18,14 @@
#include <errno.h>
-#include <android-base/strings.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "builtins.h"
#include "error.h"
#include "init_parser.h"
#include "log.h"
-#include "property_service.h"
#include "util.h"
using android::base::Join;
@@ -219,9 +219,8 @@
found = true;
}
} else {
- std::string prop_val = property_get(trigger_name.c_str());
- if (prop_val.empty() || (trigger_value != "*" &&
- trigger_value != prop_val)) {
+ std::string prop_val = android::base::GetProperty(trigger_name, "");
+ if (prop_val.empty() || (trigger_value != "*" && trigger_value != prop_val)) {
return false;
}
}
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 4a9c32e..beabea1 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -16,8 +16,6 @@
#include "bootchart.h"
-#include "property_service.h"
-
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -39,6 +37,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
using android::base::StringPrintf;
@@ -72,7 +71,7 @@
utsname uts;
if (uname(&uts) == -1) return;
- std::string fingerprint = property_get("ro.build.fingerprint");
+ std::string fingerprint = android::base::GetProperty("ro.build.fingerprint", "");
if (fingerprint.empty()) return;
std::string kernel_cmdline;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 95f1aa0..75b3c61 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -45,16 +45,16 @@
#include <selinux/selinux.h>
#include <selinux/label.h>
-#include <fs_mgr.h>
#include <android-base/file.h>
#include <android-base/parseint.h>
-#include <android-base/strings.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <bootloader_message/bootloader_message.h>
-#include <cutils/partition_utils.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/ext4_crypt.h>
#include <ext4_utils/ext4_crypt_init_extensions.h>
+#include <fs_mgr.h>
#include <logwrap/logwrap.h>
#include "action.h"
@@ -167,19 +167,11 @@
}
static int do_exec(const std::vector<std::string>& args) {
- Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args);
- if (!svc) {
- return -1;
- }
- if (!start_waiting_for_exec()) {
- return -1;
- }
- if (!svc->Start()) {
- stop_waiting_for_exec();
- ServiceManager::GetInstance().RemoveService(*svc);
- return -1;
- }
- return 0;
+ return ServiceManager::GetInstance().Exec(args) ? 0 : -1;
+}
+
+static int do_exec_start(const std::vector<std::string>& args) {
+ return ServiceManager::GetInstance().ExecStart(args[1]) ? 0 : -1;
}
static int do_export(const std::vector<std::string>& args) {
@@ -710,11 +702,51 @@
}
static int do_copy(const std::vector<std::string>& args) {
- std::string data;
- if (read_file(args[1].c_str(), &data)) {
- return write_file(args[2].c_str(), data.data()) ? 0 : 1;
+ char* buffer = NULL;
+ int rc = 0;
+ int fd1 = -1, fd2 = -1;
+ struct stat info;
+ int brtw, brtr;
+ char* p;
+
+ if (stat(args[1].c_str(), &info) < 0) return -1;
+
+ if ((fd1 = open(args[1].c_str(), O_RDONLY | O_CLOEXEC)) < 0) goto out_err;
+
+ if ((fd2 = open(args[2].c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0660)) < 0)
+ goto out_err;
+
+ if (!(buffer = (char*)malloc(info.st_size))) goto out_err;
+
+ p = buffer;
+ brtr = info.st_size;
+ while (brtr) {
+ rc = read(fd1, p, brtr);
+ if (rc < 0) goto out_err;
+ if (rc == 0) break;
+ p += rc;
+ brtr -= rc;
}
- return 1;
+
+ p = buffer;
+ brtw = info.st_size;
+ while (brtw) {
+ rc = write(fd2, p, brtw);
+ if (rc < 0) goto out_err;
+ if (rc == 0) break;
+ p += rc;
+ brtw -= rc;
+ }
+
+ rc = 0;
+ goto out;
+out_err:
+ rc = -1;
+out:
+ if (buffer) free(buffer);
+ if (fd1 >= 0) close(fd1);
+ if (fd2 >= 0) close(fd2);
+ return rc;
}
static int do_chown(const std::vector<std::string>& args) {
@@ -880,16 +912,21 @@
}
static bool is_file_crypto() {
- std::string value = property_get("ro.crypto.type");
- return value == "file";
+ return android::base::GetProperty("ro.crypto.type", "") == "file";
}
static int do_installkey(const std::vector<std::string>& args) {
if (!is_file_crypto()) {
return 0;
}
- return e4crypt_create_device_key(args[1].c_str(),
- do_installkeys_ensure_dir_exists);
+ auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
+ if (do_installkeys_ensure_dir_exists(unencrypted_dir.c_str())) {
+ PLOG(ERROR) << "Failed to create " << unencrypted_dir;
+ return -1;
+ }
+ std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+ "enablefilecrypto"};
+ return do_exec(exec_args);
}
static int do_init_user0(const std::vector<std::string>& args) {
@@ -898,6 +935,7 @@
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+ // clang-format off
static const Map builtin_functions = {
{"bootchart", {1, 1, do_bootchart}},
{"chmod", {2, 2, do_chmod}},
@@ -910,6 +948,7 @@
{"domainname", {1, 1, do_domainname}},
{"enable", {1, 1, do_enable}},
{"exec", {1, kMax, do_exec}},
+ {"exec_start", {1, 1, do_exec_start}},
{"export", {2, 2, do_export}},
{"hostname", {1, 1, do_hostname}},
{"ifup", {1, 1, do_ifup}},
@@ -943,5 +982,6 @@
{"wait_for_prop", {2, 2, do_wait_for_prop}},
{"write", {2, 2, do_write}},
};
+ // clang-format on
return builtin_functions;
}
diff --git a/init/init.cpp b/init/init.cpp
index 23448d6..a1d9f1b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -41,13 +41,10 @@
#include <selinux/android.h>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <cutils/fs.h>
-#include <cutils/iosched_policy.h>
-#include <cutils/list.h>
-#include <cutils/sockets.h>
#include <libavb/libavb.h>
#include <private/android_filesystem_config.h>
@@ -72,6 +69,7 @@
#include "util.h"
#include "watchdogd.h"
+using android::base::GetProperty;
using android::base::StringPrintf;
struct selabel_handle *sehandle;
@@ -86,8 +84,6 @@
const char *ENV[32];
-static std::unique_ptr<Timer> waiting_for_exec(nullptr);
-
static int epoll_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
@@ -135,29 +131,12 @@
return -1;
}
-bool start_waiting_for_exec()
-{
- if (waiting_for_exec) {
- return false;
- }
- waiting_for_exec.reset(new Timer());
- return true;
-}
-
-void stop_waiting_for_exec()
-{
- if (waiting_for_exec) {
- LOG(INFO) << "Wait for exec took " << *waiting_for_exec;
- waiting_for_exec.reset();
- }
-}
-
bool start_waiting_for_property(const char *name, const char *value)
{
if (waiting_for_prop) {
return false;
}
- if (property_get(name) != value) {
+ if (GetProperty(name, "") != value) {
// Current property value is not equal to expected value
wait_prop_name = name;
wait_prop_value = value;
@@ -445,7 +424,7 @@
static int console_init_action(const std::vector<std::string>& args)
{
- std::string console = property_get("ro.boot.console");
+ std::string console = GetProperty("ro.boot.console", "");
if (!console.empty()) {
default_console = "/dev/" + console;
}
@@ -469,11 +448,11 @@
}
static void export_oem_lock_status() {
- if (property_get("ro.oem_unlock_supported") != "1") {
+ if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
return;
}
- std::string value = property_get("ro.boot.verifiedbootstate");
+ std::string value = GetProperty("ro.boot.verifiedbootstate", "");
if (!value.empty()) {
property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
@@ -494,7 +473,7 @@
{ "ro.boot.revision", "ro.revision", "0", },
};
for (size_t i = 0; i < arraysize(prop_map); i++) {
- std::string value = property_get(prop_map[i].src_prop);
+ std::string value = GetProperty(prop_map[i].src_prop, "");
property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
}
}
@@ -896,6 +875,34 @@
}
}
+// The files and directories that were created before initial sepolicy load
+// need to have their security context restored to the proper value.
+// This must happen before /dev is populated by ueventd.
+static void selinux_restore_context() {
+ LOG(INFO) << "Running restorecon...";
+ restorecon("/dev");
+ restorecon("/dev/kmsg");
+ restorecon("/dev/socket");
+ restorecon("/dev/random");
+ restorecon("/dev/urandom");
+ restorecon("/dev/__properties__");
+
+ restorecon("/file_contexts.bin");
+ restorecon("/plat_file_contexts");
+ restorecon("/nonplat_file_contexts");
+ restorecon("/plat_property_contexts");
+ restorecon("/nonplat_property_contexts");
+ restorecon("/plat_seapp_contexts");
+ restorecon("/nonplat_seapp_contexts");
+ restorecon("/plat_service_contexts");
+ restorecon("/nonplat_service_contexts");
+ restorecon("/sepolicy");
+
+ restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+ restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+ restorecon("/dev/device-mapper");
+}
+
// Set the UDC controller for the ConfigFS USB Gadgets.
// Read the UDC controller in use from "/sys/class/udc".
// In case of multiple UDC controllers select the first one.
@@ -1234,22 +1241,7 @@
// Now set up SELinux for second stage.
selinux_initialize(false);
-
- // These directories were necessarily created before initial policy load
- // and therefore need their security context restored to the proper value.
- // This must happen before /dev is populated by ueventd.
- LOG(INFO) << "Running restorecon...";
- restorecon("/dev");
- restorecon("/dev/kmsg");
- restorecon("/dev/socket");
- restorecon("/dev/random");
- restorecon("/dev/urandom");
- restorecon("/dev/__properties__");
- restorecon("/plat_property_contexts");
- restorecon("/nonplat_property_contexts");
- restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
- restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
- restorecon("/dev/device-mapper");
+ selinux_restore_context();
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
@@ -1271,7 +1263,7 @@
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
- std::string bootscript = property_get("ro.boot.init_rc");
+ std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
@@ -1311,7 +1303,7 @@
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
// Don't mount filesystems or start core system services in charger mode.
- std::string bootmode = property_get("ro.bootmode");
+ std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
@@ -1325,10 +1317,10 @@
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
- if (!(waiting_for_exec || waiting_for_prop)) {
+ if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
am.ExecuteOneCommand();
}
- if (!(waiting_for_exec || waiting_for_prop)) {
+ if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
restart_processes();
// If there's a process that needs restarting, wake up in time for that.
diff --git a/init/init.h b/init/init.h
index b4d25fb..fe850ef 100644
--- a/init/init.h
+++ b/init/init.h
@@ -32,10 +32,6 @@
int add_environment(const char* key, const char* val);
-bool start_waiting_for_exec();
-
-void stop_waiting_for_exec();
-
bool start_waiting_for_property(const char *name, const char *value);
#endif /* _INIT_INIT_H */
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 3dbb2f0..5801ea8 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -23,9 +23,10 @@
#include <linux/keychord.h>
#include <unistd.h>
+#include <android-base/properties.h>
+
#include "init.h"
#include "log.h"
-#include "property_service.h"
#include "service.h"
static struct input_keychord *keychords = 0;
@@ -74,7 +75,7 @@
}
// Only handle keychords if adb is enabled.
- std::string adb_enabled = property_get("init.svc.adbd");
+ std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
if (adb_enabled == "running") {
Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
if (svc) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 983e684..a4d8b5f 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -30,10 +30,6 @@
#include <memory>
#include <vector>
-#include <cutils/misc.h>
-#include <cutils/sockets.h>
-#include <cutils/multiuser.h>
-
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
@@ -118,12 +114,6 @@
return check_mac_perms(ctl_name, sctx, cr);
}
-std::string property_get(const char* name) {
- char value[PROP_VALUE_MAX] = {0};
- __system_property_get(name, value);
- return value;
-}
-
static void write_persistent_property(const char *name, const char *value)
{
char tempPath[PATH_MAX];
@@ -592,10 +582,7 @@
static void load_override_properties() {
if (ALLOW_LOCAL_PROP_OVERRIDE) {
- std::string debuggable = property_get("ro.debuggable");
- if (debuggable == "1") {
- load_properties_from_file("/data/local.prop", NULL);
- }
+ load_properties_from_file("/data/local.prop", NULL);
}
}
diff --git a/init/property_service.h b/init/property_service.h
index 5d59473..994da63 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -32,7 +32,6 @@
void load_persist_props(void);
void load_system_props(void);
void start_property_service(void);
-std::string property_get(const char* name);
uint32_t property_set(const std::string& name, const std::string& value);
bool is_legal_property_name(const std::string& name);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 261a437..e34abdb 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -32,17 +32,15 @@
#include <android-base/file.h>
#include <android-base/macros.h>
-#include <android-base/parseint.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
-#include <cutils/partition_utils.h>
#include <fs_mgr.h>
#include <logwrap/logwrap.h>
#include "log.h"
-#include "property_service.h"
#include "reboot.h"
#include "service.h"
#include "util.h"
@@ -342,12 +340,16 @@
abort();
}
- std::string timeout = property_get("ro.build.shutdown_timeout");
/* TODO update default waiting time based on usage data */
- unsigned int shutdownTimeout = 10; // default value
- if (android::base::ParseUint(timeout, &shutdownTimeout)) {
- LOG(INFO) << "ro.build.shutdown_timeout set:" << shutdownTimeout;
+ constexpr unsigned int shutdownTimeoutDefault = 10;
+ unsigned int shutdownTimeout = shutdownTimeoutDefault;
+ if (SHUTDOWN_ZERO_TIMEOUT) { // eng build
+ shutdownTimeout = 0;
+ } else {
+ shutdownTimeout =
+ android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
}
+ LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;
static const constexpr char* shutdown_critical_services[] = {"vold", "watchdogd"};
for (const char* name : shutdown_critical_services) {
diff --git a/init/service.cpp b/init/service.cpp
index c6ef838..3db34db 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -34,6 +34,7 @@
#include <android-base/file.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <system/thread_defs.h>
@@ -191,8 +192,8 @@
}
void Service::NotifyStateChange(const std::string& new_state) const {
- if ((flags_ & SVC_EXEC) != 0) {
- // 'exec' commands don't have properties tracking their state.
+ if ((flags_ & SVC_TEMPORARY) != 0) {
+ // Services created by 'exec' are temporary and don't have properties tracking their state.
return;
}
@@ -210,7 +211,13 @@
LOG(INFO) << "Sending signal " << signal
<< " to service '" << name_
<< "' (pid " << pid_ << ") process group...";
- if (killProcessGroup(uid_, pid_, signal) == -1) {
+ int r;
+ if (signal == SIGTERM) {
+ r = killProcessGroupOnce(uid_, pid_, signal);
+ } else {
+ r = killProcessGroup(uid_, pid_, signal);
+ }
+ if (r == -1) {
PLOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
}
if (kill(-pid_, signal) == -1) {
@@ -259,7 +266,7 @@
}
}
-bool Service::Reap() {
+void Service::Reap() {
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
KillProcessGroup(SIGKILL);
}
@@ -270,7 +277,10 @@
if (flags_ & SVC_EXEC) {
LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished...";
- return true;
+ }
+
+ if (flags_ & SVC_TEMPORARY) {
+ return;
}
pid_ = 0;
@@ -285,7 +295,7 @@
// Disabled and reset processes do not get restarted automatically.
if (flags_ & (SVC_DISABLED | SVC_RESET)) {
NotifyStateChange("stopped");
- return false;
+ return;
}
// If we crash > 4 times in 4 minutes, reboot into recovery.
@@ -309,7 +319,7 @@
onrestart_.ExecuteAllCommands();
NotifyStateChange("restarting");
- return false;
+ return;
}
void Service::DumpState() const {
@@ -577,6 +587,18 @@
return (this->*parser)(args, err);
}
+bool Service::ExecStart(std::unique_ptr<Timer>* exec_waiter) {
+ flags_ |= SVC_EXEC | SVC_ONESHOT;
+
+ exec_waiter->reset(new Timer);
+
+ if (!Start()) {
+ exec_waiter->reset();
+ return false;
+ }
+ return true;
+}
+
bool Service::Start() {
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
@@ -657,7 +679,7 @@
if (iter == writepid_files_.end()) {
// There were no "writepid" instructions for cpusets, check if the system default
// cpuset is specified to be used for the process.
- std::string default_cpuset = property_get("ro.cpuset.default");
+ std::string default_cpuset = android::base::GetProperty("ro.cpuset.default", "");
if (!default_cpuset.empty()) {
// Make sure the cpuset name starts and ends with '/'.
// A single '/' means the 'root' cpuset.
@@ -863,6 +885,35 @@
services_.emplace_back(std::move(service));
}
+bool ServiceManager::Exec(const std::vector<std::string>& args) {
+ Service* svc = MakeExecOneshotService(args);
+ if (!svc) {
+ LOG(ERROR) << "Could not create exec service";
+ return false;
+ }
+ if (!svc->ExecStart(&exec_waiter_)) {
+ LOG(ERROR) << "Could not start exec service";
+ ServiceManager::GetInstance().RemoveService(*svc);
+ return false;
+ }
+ return true;
+}
+
+bool ServiceManager::ExecStart(const std::string& name) {
+ Service* svc = FindServiceByName(name);
+ if (!svc) {
+ LOG(ERROR) << "ExecStart(" << name << "): Service not found";
+ return false;
+ }
+ if (!svc->ExecStart(&exec_waiter_)) {
+ LOG(ERROR) << "ExecStart(" << name << "): Could not start Service";
+ return false;
+ }
+ return true;
+}
+
+bool ServiceManager::IsWaitingForExec() const { return exec_waiter_ != nullptr; }
+
Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
// Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
// SECLABEL can be a - to denote default
@@ -886,7 +937,7 @@
exec_count_++;
std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
- unsigned flags = SVC_EXEC | SVC_ONESHOT;
+ unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
CapSet no_capabilities;
unsigned namespace_flags = 0;
@@ -1026,8 +1077,13 @@
return true;
}
- if (svc->Reap()) {
- stop_waiting_for_exec();
+ svc->Reap();
+
+ if (svc->flags() & SVC_EXEC) {
+ LOG(INFO) << "Wait for exec took " << *exec_waiter_;
+ exec_waiter_.reset();
+ }
+ if (svc->flags() & SVC_TEMPORARY) {
RemoveService(*svc);
}
diff --git a/init/service.h b/init/service.h
index 9a9046b..f08a03f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -44,10 +44,13 @@
#define SVC_RC_DISABLED 0x080 // Remember if the disabled flag was set in the rc script.
#define SVC_RESTART 0x100 // Use to safely restart (stop, wait, start) a service.
#define SVC_DISABLED_START 0x200 // A start was requested but it was disabled at the time.
-#define SVC_EXEC 0x400 // This synthetic service corresponds to an 'exec'.
+#define SVC_EXEC 0x400 // This service was started by either 'exec' or 'exec_start' and stops
+ // init from processing more commands until it completes
#define SVC_SHUTDOWN_CRITICAL 0x800 // This service is critical for shutdown and
// should not be killed during shutdown
+#define SVC_TEMPORARY 0x1000 // This service was started by 'exec' and should be removed from the
+ // service list once it is reaped.
#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
@@ -72,6 +75,7 @@
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
bool ParseLine(const std::vector<std::string>& args, std::string* err);
+ bool ExecStart(std::unique_ptr<Timer>* exec_waiter);
bool Start();
bool StartIfNotDisabled();
bool Enable();
@@ -80,7 +84,7 @@
void Terminate();
void Restart();
void RestartIfNeeded(time_t* process_needs_restart_at);
- bool Reap();
+ void Reap();
void DumpState() const;
void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
@@ -178,6 +182,9 @@
void AddService(std::unique_ptr<Service> service);
Service* MakeExecOneshotService(const std::vector<std::string>& args);
+ bool Exec(const std::vector<std::string>& args);
+ bool ExecStart(const std::string& name);
+ bool IsWaitingForExec() const;
Service* FindServiceByName(const std::string& name) const;
Service* FindServiceByPid(pid_t pid) const;
Service* FindServiceByKeychord(int keychord_id) const;
@@ -198,6 +205,8 @@
bool ReapOneProcess();
static int exec_count_; // Every service needs a unique name.
+ std::unique_ptr<Timer> exec_waiter_;
+
std::vector<std::unique_ptr<Service>> services_;
};
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 1041b82..5e3acac 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -24,8 +24,6 @@
#include <unistd.h>
#include <android-base/stringprintf.h>
-#include <cutils/list.h>
-#include <cutils/sockets.h>
#include "action.h"
#include "init.h"
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 915afbd..f27be64 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -26,6 +26,7 @@
#include <sys/types.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <selinux/selinux.h>
@@ -34,7 +35,6 @@
#include "util.h"
#include "devices.h"
#include "ueventd_parser.h"
-#include "property_service.h"
int ueventd_main(int argc, char **argv)
{
@@ -71,7 +71,7 @@
* TODO: cleanup platform ueventd.rc to remove vendor specific
* device node entries (b/34968103)
*/
- std::string hardware = property_get("ro.hardware");
+ std::string hardware = android::base::GetProperty("ro.hardware", "");
ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
device_init();
diff --git a/init/util.cpp b/init/util.cpp
index 0ba9800..73d97ed 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -38,6 +38,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -48,7 +49,6 @@
#include "init.h"
#include "log.h"
-#include "property_service.h"
#include "reboot.h"
#include "util.h"
@@ -185,8 +185,8 @@
}
bool write_file(const char* path, const char* content) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(
- open(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0600)));
if (fd == -1) {
PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
return false;
@@ -395,7 +395,7 @@
return false;
}
- std::string prop_val = property_get(prop_name.c_str());
+ std::string prop_val = android::base::GetProperty(prop_name, "");
if (prop_val.empty()) {
if (def_val.empty()) {
LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 4e82e76..24c75c4 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -17,15 +17,9 @@
#include "util.h"
#include <errno.h>
-#include <fcntl.h>
-
-#include <sys/stat.h>
#include <gtest/gtest.h>
-#include <android-base/stringprintf.h>
-#include <android-base/test_utils.h>
-
TEST(util, read_file_ENOENT) {
std::string s("hello");
errno = 0;
@@ -34,35 +28,6 @@
EXPECT_EQ("", s); // s was cleared.
}
-TEST(util, read_file_group_writeable) {
- std::string s("hello");
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
- EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
- EXPECT_EQ("", s); // s was cleared.
-}
-
-TEST(util, read_file_world_writeable) {
- std::string s("hello");
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
- EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
- EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
- EXPECT_EQ("", s); // s was cleared.
-}
-
-TEST(util, read_file_symbol_link) {
- std::string s("hello");
- errno = 0;
- // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
- EXPECT_FALSE(read_file("/charger", &s));
- EXPECT_EQ(ELOOP, errno);
- EXPECT_EQ("", s); // s was cleared.
-}
-
TEST(util, read_file_success) {
std::string s("hello");
EXPECT_TRUE(read_file("/proc/version", &s));
@@ -72,42 +37,6 @@
EXPECT_STREQ("Linux", s.c_str());
}
-TEST(util, write_file_not_exist) {
- std::string s("hello");
- std::string s2("hello");
- TemporaryDir test_dir;
- std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
- EXPECT_TRUE(write_file(path.c_str(), s.c_str()));
- EXPECT_TRUE(read_file(path.c_str(), &s2));
- EXPECT_EQ(s, s2);
- struct stat sb;
- int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
- EXPECT_NE(-1, fd);
- EXPECT_EQ(0, fstat(fd, &sb));
- EXPECT_NE(0u, sb.st_mode & S_IRUSR);
- EXPECT_NE(0u, sb.st_mode & S_IWUSR);
- EXPECT_EQ(0u, sb.st_mode & S_IXUSR);
- EXPECT_EQ(0u, sb.st_mode & S_IRGRP);
- EXPECT_EQ(0u, sb.st_mode & S_IWGRP);
- EXPECT_EQ(0u, sb.st_mode & S_IXGRP);
- EXPECT_EQ(0u, sb.st_mode & S_IROTH);
- EXPECT_EQ(0u, sb.st_mode & S_IWOTH);
- EXPECT_EQ(0u, sb.st_mode & S_IXOTH);
- EXPECT_EQ(0, unlink(path.c_str()));
-}
-
-TEST(util, write_file_exist) {
- std::string s2("");
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- EXPECT_TRUE(write_file(tf.path, "1hello1")) << strerror(errno);
- EXPECT_TRUE(read_file(tf.path, &s2));
- EXPECT_STREQ("1hello1", s2.c_str());
- EXPECT_TRUE(write_file(tf.path, "2hello2"));
- EXPECT_TRUE(read_file(tf.path, &s2));
- EXPECT_STREQ("2hello2", s2.c_str());
-}
-
TEST(util, decode_uid) {
EXPECT_EQ(0U, decode_uid("root"));
EXPECT_EQ(UINT_MAX, decode_uid("toot"));
diff --git a/libappfuse/FuseAppLoop.cc b/libappfuse/FuseAppLoop.cc
index a31880e..b6bc191 100644
--- a/libappfuse/FuseAppLoop.cc
+++ b/libappfuse/FuseAppLoop.cc
@@ -16,205 +16,232 @@
#include "libappfuse/FuseAppLoop.h"
+#include <sys/eventfd.h>
#include <sys/stat.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include "libappfuse/EpollController.h"
+
namespace android {
namespace fuse {
namespace {
-void HandleLookUp(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- // AppFuse does not support directory structure now.
- // It can lookup only files under the mount point.
- if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
- LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
- buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
- return;
- }
-
- // Ensure that the filename ends with 0.
- const size_t filename_length =
- buffer->request.header.len - sizeof(fuse_in_header);
- if (buffer->request.lookup_name[filename_length - 1] != 0) {
- LOG(ERROR) << "File name does not end with 0.";
- buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
- return;
- }
-
- const uint64_t inode =
- static_cast<uint64_t>(atol(buffer->request.lookup_name));
- if (inode == 0 || inode == LONG_MAX) {
- LOG(ERROR) << "Invalid filename";
- buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
- return;
- }
-
- const int64_t size = callback->OnGetSize(inode);
- if (size < 0) {
- buffer->response.Reset(0, size, buffer->request.header.unique);
- return;
- }
-
- buffer->response.Reset(sizeof(fuse_entry_out), 0,
- buffer->request.header.unique);
- buffer->response.entry_out.nodeid = inode;
- buffer->response.entry_out.attr_valid = 10;
- buffer->response.entry_out.entry_valid = 10;
- buffer->response.entry_out.attr.ino = inode;
- buffer->response.entry_out.attr.mode = S_IFREG | 0777;
- buffer->response.entry_out.attr.size = size;
-}
-
-void HandleGetAttr(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- const uint64_t nodeid = buffer->request.header.nodeid;
- int64_t size;
- uint32_t mode;
- if (nodeid == FUSE_ROOT_ID) {
- size = 0;
- mode = S_IFDIR | 0777;
- } else {
- size = callback->OnGetSize(buffer->request.header.nodeid);
- if (size < 0) {
- buffer->response.Reset(0, size, buffer->request.header.unique);
- return;
+bool HandleLookUp(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ // AppFuse does not support directory structure now.
+ // It can lookup only files under the mount point.
+ if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
+ LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
+ return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
}
- mode = S_IFREG | 0777;
- }
- buffer->response.Reset(sizeof(fuse_attr_out), 0,
- buffer->request.header.unique);
- buffer->response.attr_out.attr_valid = 10;
- buffer->response.attr_out.attr.ino = nodeid;
- buffer->response.attr_out.attr.mode = mode;
- buffer->response.attr_out.attr.size = size;
+ // Ensure that the filename ends with 0.
+ const size_t filename_length = buffer->request.header.len - sizeof(fuse_in_header);
+ if (buffer->request.lookup_name[filename_length - 1] != 0) {
+ LOG(ERROR) << "File name does not end with 0.";
+ return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
+ }
+
+ const uint64_t inode = static_cast<uint64_t>(atol(buffer->request.lookup_name));
+ if (inode == 0 || inode == LONG_MAX) {
+ LOG(ERROR) << "Invalid filename";
+ return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
+ }
+
+ callback->OnLookup(buffer->request.header.unique, inode);
+ return true;
}
-void HandleOpen(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- const int32_t file_handle = callback->OnOpen(buffer->request.header.nodeid);
- if (file_handle < 0) {
- buffer->response.Reset(0, file_handle, buffer->request.header.unique);
- return;
- }
- buffer->response.Reset(sizeof(fuse_open_out), kFuseSuccess,
- buffer->request.header.unique);
- buffer->response.open_out.fh = file_handle;
+bool HandleGetAttr(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ if (buffer->request.header.nodeid == FUSE_ROOT_ID) {
+ return loop->ReplyGetAttr(buffer->request.header.unique, buffer->request.header.nodeid, 0,
+ S_IFDIR | 0777);
+ } else {
+ callback->OnGetAttr(buffer->request.header.unique, buffer->request.header.nodeid);
+ return true;
+ }
}
-void HandleFsync(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- buffer->response.Reset(0, callback->OnFsync(buffer->request.header.nodeid),
- buffer->request.header.unique);
+bool HandleRead(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ if (buffer->request.read_in.size > kFuseMaxRead) {
+ return loop->ReplySimple(buffer->request.header.unique, -EINVAL);
+ }
+
+ callback->OnRead(buffer->request.header.unique, buffer->request.header.nodeid,
+ buffer->request.read_in.offset, buffer->request.read_in.size);
+ return true;
}
-void HandleRelease(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- buffer->response.Reset(0, callback->OnRelease(buffer->request.header.nodeid),
- buffer->request.header.unique);
+bool HandleWrite(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ if (buffer->request.write_in.size > kFuseMaxWrite) {
+ return loop->ReplySimple(buffer->request.header.unique, -EINVAL);
+ }
+
+ callback->OnWrite(buffer->request.header.unique, buffer->request.header.nodeid,
+ buffer->request.write_in.offset, buffer->request.write_in.size,
+ buffer->request.write_data);
+ return true;
}
-void HandleRead(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- const uint64_t unique = buffer->request.header.unique;
- const uint64_t nodeid = buffer->request.header.nodeid;
- const uint64_t offset = buffer->request.read_in.offset;
- const uint32_t size = buffer->request.read_in.size;
+bool HandleMessage(FuseAppLoop* loop, FuseBuffer* buffer, int fd, FuseAppLoopCallback* callback) {
+ if (!buffer->request.Read(fd)) {
+ return false;
+ }
- if (size > kFuseMaxRead) {
- buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
- return;
- }
+ const uint32_t opcode = buffer->request.header.opcode;
+ LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
+ switch (opcode) {
+ case FUSE_FORGET:
+ // Do not reply to FUSE_FORGET.
+ return true;
- const int32_t read_size = callback->OnRead(nodeid, offset, size,
- buffer->response.read_data);
- if (read_size < 0) {
- buffer->response.Reset(0, read_size, buffer->request.header.unique);
- return;
- }
+ case FUSE_LOOKUP:
+ return HandleLookUp(loop, buffer, callback);
- buffer->response.ResetHeader(read_size, kFuseSuccess, unique);
-}
+ case FUSE_GETATTR:
+ return HandleGetAttr(loop, buffer, callback);
-void HandleWrite(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
- const uint64_t unique = buffer->request.header.unique;
- const uint64_t nodeid = buffer->request.header.nodeid;
- const uint64_t offset = buffer->request.write_in.offset;
- const uint32_t size = buffer->request.write_in.size;
+ case FUSE_OPEN:
+ callback->OnOpen(buffer->request.header.unique, buffer->request.header.nodeid);
+ return true;
- if (size > kFuseMaxWrite) {
- buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
- return;
- }
+ case FUSE_READ:
+ return HandleRead(loop, buffer, callback);
- const int32_t write_size = callback->OnWrite(nodeid, offset, size,
- buffer->request.write_data);
- if (write_size < 0) {
- buffer->response.Reset(0, write_size, buffer->request.header.unique);
- return;
- }
+ case FUSE_WRITE:
+ return HandleWrite(loop, buffer, callback);
- buffer->response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
- buffer->response.write_out.size = write_size;
+ case FUSE_RELEASE:
+ callback->OnRelease(buffer->request.header.unique, buffer->request.header.nodeid);
+ return true;
+
+ case FUSE_FSYNC:
+ callback->OnFsync(buffer->request.header.unique, buffer->request.header.nodeid);
+ return true;
+
+ default:
+ buffer->HandleNotImpl();
+ return buffer->response.Write(fd);
+ }
}
} // namespace
-bool StartFuseAppLoop(int raw_fd, FuseAppLoopCallback* callback) {
- base::unique_fd fd(raw_fd);
- FuseBuffer buffer;
+FuseAppLoopCallback::~FuseAppLoopCallback() = default;
- LOG(DEBUG) << "Start fuse loop.";
- while (callback->IsActive()) {
- if (!buffer.request.Read(fd)) {
- return false;
+FuseAppLoop::FuseAppLoop(base::unique_fd&& fd) : fd_(std::move(fd)) {}
+
+void FuseAppLoop::Break() {
+ const int64_t value = 1;
+ if (write(break_fd_, &value, sizeof(value)) == -1) {
+ PLOG(ERROR) << "Failed to send a break event";
+ }
+}
+
+bool FuseAppLoop::ReplySimple(uint64_t unique, int32_t result) {
+ if (result == -ENOSYS) {
+ // We should not return -ENOSYS because the kernel stops delivering FUSE
+ // command after receiving -ENOSYS as a result for the command.
+ result = -EBADF;
+ }
+ FuseSimpleResponse response;
+ response.Reset(0, result, unique);
+ return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyLookup(uint64_t unique, uint64_t inode, int64_t size) {
+ FuseSimpleResponse response;
+ response.Reset(sizeof(fuse_entry_out), 0, unique);
+ response.entry_out.nodeid = inode;
+ response.entry_out.attr_valid = 10;
+ response.entry_out.entry_valid = 10;
+ response.entry_out.attr.ino = inode;
+ response.entry_out.attr.mode = S_IFREG | 0777;
+ response.entry_out.attr.size = size;
+ return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode) {
+ CHECK(mode == (S_IFREG | 0777) || mode == (S_IFDIR | 0777));
+ FuseSimpleResponse response;
+ response.Reset(sizeof(fuse_attr_out), 0, unique);
+ response.attr_out.attr_valid = 10;
+ response.attr_out.attr.ino = inode;
+ response.attr_out.attr.mode = mode;
+ response.attr_out.attr.size = size;
+ return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyOpen(uint64_t unique, uint64_t fh) {
+ FuseSimpleResponse response;
+ response.Reset(sizeof(fuse_open_out), kFuseSuccess, unique);
+ response.open_out.fh = fh;
+ return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyWrite(uint64_t unique, uint32_t size) {
+ CHECK(size <= kFuseMaxWrite);
+ FuseSimpleResponse response;
+ response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
+ response.write_out.size = size;
+ return response.Write(fd_);
+}
+
+bool FuseAppLoop::ReplyRead(uint64_t unique, uint32_t size, const void* data) {
+ CHECK(size <= kFuseMaxRead);
+ FuseSimpleResponse response;
+ response.ResetHeader(size, kFuseSuccess, unique);
+ return response.WriteWithBody(fd_, sizeof(FuseResponse), data);
+}
+
+void FuseAppLoop::Start(FuseAppLoopCallback* callback) {
+ break_fd_.reset(eventfd(/* initval */ 0, EFD_CLOEXEC));
+ if (break_fd_.get() == -1) {
+ PLOG(ERROR) << "Failed to open FD for break event";
+ return;
}
- const uint32_t opcode = buffer.request.header.opcode;
- LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
- switch (opcode) {
- case FUSE_FORGET:
- // Do not reply to FUSE_FORGET.
- continue;
-
- case FUSE_LOOKUP:
- HandleLookUp(&buffer, callback);
- break;
-
- case FUSE_GETATTR:
- HandleGetAttr(&buffer, callback);
- break;
-
- case FUSE_OPEN:
- HandleOpen(&buffer, callback);
- break;
-
- case FUSE_READ:
- HandleRead(&buffer, callback);
- break;
-
- case FUSE_WRITE:
- HandleWrite(&buffer, callback);
- break;
-
- case FUSE_RELEASE:
- HandleRelease(&buffer, callback);
- break;
-
- case FUSE_FSYNC:
- HandleFsync(&buffer, callback);
- break;
-
- default:
- buffer.HandleNotImpl();
- break;
+ base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
+ if (epoll_fd.get() == -1) {
+ PLOG(ERROR) << "Failed to open FD for epoll";
+ return;
}
- if (!buffer.response.Write(fd)) {
- LOG(ERROR) << "Failed to write a response to the device.";
- return false;
- }
- }
+ int last_event;
+ int break_event;
- return true;
+ std::unique_ptr<EpollController> epoll_controller(new EpollController(std::move(epoll_fd)));
+ if (!epoll_controller->AddFd(fd_, EPOLLIN, &last_event)) {
+ return;
+ }
+ if (!epoll_controller->AddFd(break_fd_, EPOLLIN, &break_event)) {
+ return;
+ }
+
+ last_event = 0;
+ break_event = 0;
+
+ FuseBuffer buffer;
+ while (true) {
+ if (!epoll_controller->Wait(1)) {
+ break;
+ }
+ last_event = 0;
+ *reinterpret_cast<int*>(epoll_controller->events()[0].data.ptr) =
+ epoll_controller->events()[0].events;
+
+ if (break_event != 0 || (last_event & ~EPOLLIN) != 0) {
+ break;
+ }
+
+ if (!HandleMessage(this, &buffer, fd_, callback)) {
+ break;
+ }
+ }
+
+ LOG(VERBOSE) << "FuseAppLoop exit";
}
} // namespace fuse
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 5bc5497..b42a049 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -34,6 +34,8 @@
namespace fuse {
namespace {
+constexpr useconds_t kRetrySleepForWriting = 1000; // 1 ms
+
template <typename T>
bool CheckHeaderLength(const FuseMessage<T>* self, const char* name, size_t max_size) {
const auto& header = static_cast<const T*>(self)->header;
@@ -91,28 +93,35 @@
const char* const buf = reinterpret_cast<const char*>(self);
const auto& header = static_cast<const T*>(self)->header;
- int result;
- if (sockflag) {
- CHECK(data == nullptr);
- result = TEMP_FAILURE_RETRY(send(fd, buf, header.len, sockflag));
- } else if (data) {
- const struct iovec vec[] = {{const_cast<char*>(buf), sizeof(header)},
- {const_cast<void*>(data), header.len - sizeof(header)}};
- result = TEMP_FAILURE_RETRY(writev(fd, vec, arraysize(vec)));
- } else {
- result = TEMP_FAILURE_RETRY(write(fd, buf, header.len));
- }
-
- if (result == -1) {
- if (errno == EAGAIN) {
- return ResultOrAgain::kAgain;
+ while (true) {
+ int result;
+ if (sockflag) {
+ CHECK(data == nullptr);
+ result = TEMP_FAILURE_RETRY(send(fd, buf, header.len, sockflag));
+ } else if (data) {
+ const struct iovec vec[] = {{const_cast<char*>(buf), sizeof(header)},
+ {const_cast<void*>(data), header.len - sizeof(header)}};
+ result = TEMP_FAILURE_RETRY(writev(fd, vec, arraysize(vec)));
+ } else {
+ result = TEMP_FAILURE_RETRY(write(fd, buf, header.len));
}
- PLOG(ERROR) << "Failed to write a FUSE message";
- return ResultOrAgain::kFailure;
+ if (result == -1) {
+ switch (errno) {
+ case ENOBUFS:
+ // When returning ENOBUFS, epoll still reports the FD is writable. Just usleep
+ // and retry again.
+ usleep(kRetrySleepForWriting);
+ continue;
+ case EAGAIN:
+ return ResultOrAgain::kAgain;
+ default:
+ PLOG(ERROR) << "Failed to write a FUSE message";
+ return ResultOrAgain::kFailure;
+ }
+ }
+ CHECK(static_cast<uint32_t>(result) == header.len);
+ return ResultOrAgain::kSuccess;
}
-
- CHECK(static_cast<uint32_t>(result) == header.len);
- return ResultOrAgain::kSuccess;
}
}
@@ -161,7 +170,7 @@
template <typename T>
bool FuseMessage<T>::WriteWithBody(int fd, size_t max_size, const void* data) const {
CHECK(data != nullptr);
- return WriteInternal<T>(this, fd, 0, data, max_size) == ResultOrAgain::kSuccess;
+ return WriteInternal(this, fd, 0, data, max_size) == ResultOrAgain::kSuccess;
}
template <typename T>
diff --git a/libappfuse/include/libappfuse/FuseAppLoop.h b/libappfuse/include/libappfuse/FuseAppLoop.h
index c3edfcc..f2ef2b5 100644
--- a/libappfuse/include/libappfuse/FuseAppLoop.h
+++ b/libappfuse/include/libappfuse/FuseAppLoop.h
@@ -17,23 +17,51 @@
#ifndef ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
#define ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
+#include <memory>
+#include <mutex>
+
+#include <android-base/unique_fd.h>
+
#include "libappfuse/FuseBuffer.h"
namespace android {
namespace fuse {
+class EpollController;
+
class FuseAppLoopCallback {
public:
- virtual bool IsActive() = 0;
- virtual int64_t OnGetSize(uint64_t inode) = 0;
- virtual int32_t OnFsync(uint64_t inode) = 0;
- virtual int32_t OnWrite(
- uint64_t inode, uint64_t offset, uint32_t size, const void* data) = 0;
- virtual int32_t OnRead(
- uint64_t inode, uint64_t offset, uint32_t size, void* data) = 0;
- virtual int32_t OnOpen(uint64_t inode) = 0;
- virtual int32_t OnRelease(uint64_t inode) = 0;
- virtual ~FuseAppLoopCallback() = default;
+ virtual void OnLookup(uint64_t unique, uint64_t inode) = 0;
+ virtual void OnGetAttr(uint64_t unique, uint64_t inode) = 0;
+ virtual void OnFsync(uint64_t unique, uint64_t inode) = 0;
+ virtual void OnWrite(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size,
+ const void* data) = 0;
+ virtual void OnRead(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size) = 0;
+ virtual void OnOpen(uint64_t unique, uint64_t inode) = 0;
+ virtual void OnRelease(uint64_t unique, uint64_t inode) = 0;
+ virtual ~FuseAppLoopCallback();
+};
+
+class FuseAppLoop final {
+ public:
+ FuseAppLoop(base::unique_fd&& fd);
+
+ void Start(FuseAppLoopCallback* callback);
+ void Break();
+
+ bool ReplySimple(uint64_t unique, int32_t result);
+ bool ReplyLookup(uint64_t unique, uint64_t inode, int64_t size);
+ bool ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode);
+ bool ReplyOpen(uint64_t unique, uint64_t fh);
+ bool ReplyWrite(uint64_t unique, uint32_t size);
+ bool ReplyRead(uint64_t unique, uint32_t size, const void* data);
+
+ private:
+ base::unique_fd fd_;
+ base::unique_fd break_fd_;
+
+ // Lock for multi-threading.
+ std::mutex mutex_;
};
bool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback);
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc
index 64dd813..98e3665 100644
--- a/libappfuse/tests/FuseAppLoopTest.cc
+++ b/libappfuse/tests/FuseAppLoopTest.cc
@@ -23,6 +23,9 @@
#include <gtest/gtest.h>
#include <thread>
+#include "libappfuse/EpollController.h"
+#include "libappfuse/FuseBridgeLoop.h"
+
namespace android {
namespace fuse {
namespace {
@@ -37,82 +40,61 @@
class Callback : public FuseAppLoopCallback {
public:
std::vector<CallbackRequest> requests;
+ FuseAppLoop* loop;
- bool IsActive() override {
- return true;
+ void OnGetAttr(uint64_t seq, uint64_t inode) override {
+ EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));
+ EXPECT_TRUE(loop->ReplyGetAttr(seq, inode, kTestFileSize, S_IFREG | 0777));
}
- int64_t OnGetSize(uint64_t inode) override {
- if (inode == FUSE_ROOT_ID) {
- return 0;
- } else {
- return kTestFileSize;
- }
+ void OnLookup(uint64_t unique, uint64_t inode) override {
+ EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));
+ EXPECT_TRUE(loop->ReplyLookup(unique, inode, kTestFileSize));
}
- int32_t OnFsync(uint64_t inode) override {
- requests.push_back({
- .code = FUSE_FSYNC,
- .inode = inode
- });
- return 0;
+ void OnFsync(uint64_t seq, uint64_t inode) override {
+ requests.push_back({.code = FUSE_FSYNC, .inode = inode});
+ loop->ReplySimple(seq, 0);
}
- int32_t OnWrite(uint64_t inode,
- uint64_t offset ATTRIBUTE_UNUSED,
- uint32_t size ATTRIBUTE_UNUSED,
- const void* data ATTRIBUTE_UNUSED) override {
- requests.push_back({
- .code = FUSE_WRITE,
- .inode = inode
- });
- return 0;
+ void OnWrite(uint64_t seq, uint64_t inode, uint64_t offset ATTRIBUTE_UNUSED,
+ uint32_t size ATTRIBUTE_UNUSED, const void* data ATTRIBUTE_UNUSED) override {
+ requests.push_back({.code = FUSE_WRITE, .inode = inode});
+ loop->ReplyWrite(seq, 0);
}
- int32_t OnRead(uint64_t inode,
- uint64_t offset ATTRIBUTE_UNUSED,
- uint32_t size ATTRIBUTE_UNUSED,
- void* data ATTRIBUTE_UNUSED) override {
- requests.push_back({
- .code = FUSE_READ,
- .inode = inode
- });
- return 0;
+ void OnRead(uint64_t seq, uint64_t inode, uint64_t offset ATTRIBUTE_UNUSED,
+ uint32_t size ATTRIBUTE_UNUSED) override {
+ requests.push_back({.code = FUSE_READ, .inode = inode});
+ loop->ReplySimple(seq, 0);
}
- int32_t OnOpen(uint64_t inode) override {
- requests.push_back({
- .code = FUSE_OPEN,
- .inode = inode
- });
- return 0;
+ void OnOpen(uint64_t seq, uint64_t inode) override {
+ requests.push_back({.code = FUSE_OPEN, .inode = inode});
+ loop->ReplyOpen(seq, inode);
}
- int32_t OnRelease(uint64_t inode) override {
- requests.push_back({
- .code = FUSE_RELEASE,
- .inode = inode
- });
- return 0;
+ void OnRelease(uint64_t seq, uint64_t inode) override {
+ requests.push_back({.code = FUSE_RELEASE, .inode = inode});
+ loop->ReplySimple(seq, 0);
}
};
class FuseAppLoopTest : public ::testing::Test {
- private:
- std::thread thread_;
-
protected:
- base::unique_fd sockets_[2];
- Callback callback_;
- FuseRequest request_;
- FuseResponse response_;
+ std::thread thread_;
+ base::unique_fd sockets_[2];
+ Callback callback_;
+ FuseRequest request_;
+ FuseResponse response_;
+ std::unique_ptr<FuseAppLoop> loop_;
- void SetUp() override {
- base::SetMinimumLogSeverity(base::VERBOSE);
- ASSERT_TRUE(SetupMessageSockets(&sockets_));
- thread_ = std::thread([this] {
- StartFuseAppLoop(sockets_[1].release(), &callback_);
- });
+ void SetUp() override {
+ base::SetMinimumLogSeverity(base::VERBOSE);
+ ASSERT_TRUE(SetupMessageSockets(&sockets_));
+ loop_.reset(new FuseAppLoop(std::move(sockets_[1])));
+ callback_.loop = loop_.get();
+ thread_ = std::thread([this] { loop_->Start(&callback_); });
}
void CheckCallback(
@@ -300,5 +282,18 @@
CheckCallback(sizeof(fuse_write_in), FUSE_WRITE, sizeof(fuse_write_out));
}
+TEST_F(FuseAppLoopTest, Break) {
+ // Ensure that the loop started.
+ request_.Reset(sizeof(fuse_open_in), FUSE_OPEN, 1);
+ request_.header.nodeid = 10;
+ ASSERT_TRUE(request_.Write(sockets_[0]));
+ ASSERT_TRUE(response_.Read(sockets_[0]));
+
+ loop_->Break();
+ if (thread_.joinable()) {
+ thread_.join();
+ }
+}
+
} // namespace fuse
} // namespace android
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 11bd8cc..47f6ff3 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -24,6 +24,8 @@
int killProcessGroup(uid_t uid, int initialPid, int signal);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
+
int createProcessGroup(uid_t uid, int initialPid);
void removeAllProcessGroups(void);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index eb66727..1572cb3 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -252,8 +252,7 @@
}
}
-static int killProcessGroupOnce(uid_t uid, int initialPid, int signal)
-{
+static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
int processes = 0;
struct ctx ctx;
pid_t pid;
@@ -282,13 +281,11 @@
return processes;
}
-int killProcessGroup(uid_t uid, int initialPid, int signal)
-{
+static int killProcessGroup(uid_t uid, int initialPid, int signal, int retry) {
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
- int retry = 40;
int processes;
- while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
+ while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
if (retry > 0) {
std::this_thread::sleep_for(5ms);
@@ -313,6 +310,14 @@
}
}
+int killProcessGroup(uid_t uid, int initialPid, int signal) {
+ return killProcessGroup(uid, initialPid, signal, 40 /*maxRetry*/);
+}
+
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
+ return killProcessGroup(uid, initialPid, signal, 0 /*maxRetry*/);
+}
+
static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
{
if (mkdir(path, mode) == -1 && errno != EEXIST) {
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 0d98db9..24737b9 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -760,6 +760,4 @@
ref->mRefs->renameWeakRefId(old_id, new_id);
}
-VirtualLightRefBase::~VirtualLightRefBase() {}
-
}; // namespace android
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/include/utils/LightRefBase.h
new file mode 100644
index 0000000..65257ed
--- /dev/null
+++ b/libutils/include/utils/LightRefBase.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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
+
+/*
+ * See documentation in RefBase.h
+ */
+
+#include <atomic>
+
+#include <sys/types.h>
+
+namespace android {
+
+class ReferenceRenamer;
+
+template <class T>
+class LightRefBase
+{
+public:
+ inline LightRefBase() : mCount(0) { }
+ inline void incStrong(__attribute__((unused)) const void* id) const {
+ mCount.fetch_add(1, std::memory_order_relaxed);
+ }
+ inline void decStrong(__attribute__((unused)) const void* id) const {
+ if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
+ std::atomic_thread_fence(std::memory_order_acquire);
+ delete static_cast<const T*>(this);
+ }
+ }
+ //! DEBUGGING ONLY: Get current strong ref count.
+ inline int32_t getStrongCount() const {
+ return mCount.load(std::memory_order_relaxed);
+ }
+
+ typedef LightRefBase<T> basetype;
+
+protected:
+ inline ~LightRefBase() { }
+
+private:
+ friend class ReferenceMover;
+ inline static void renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { }
+ inline static void renameRefId(T* /*ref*/, const void* /*old_id*/ , const void* /*new_id*/) { }
+
+private:
+ mutable std::atomic<int32_t> mCount;
+};
+
+
+// This is a wrapper around LightRefBase that simply enforces a virtual
+// destructor to eliminate the template requirement of LightRefBase
+class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
+public:
+ virtual ~VirtualLightRefBase() = default;
+};
+
+}; // namespace android
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index a61ea58..223b666 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -177,6 +177,9 @@
#include <stdlib.h>
#include <string.h>
+// LightRefBase used to be declared in this header, so we have to include it
+#include <utils/LightRefBase.h>
+
#include <utils/StrongPointer.h>
#include <utils/TypeHelpers.h>
@@ -216,7 +219,7 @@
class ReferenceRenamer {
protected:
- // destructor is purposedly not virtual so we avoid code overhead from
+ // destructor is purposely not virtual so we avoid code overhead from
// subclasses; we have to make it protected to guarantee that it
// cannot be called from this base class (and to make strict compilers
// happy).
@@ -246,13 +249,13 @@
{
public:
RefBase* refBase() const;
-
+
void incWeak(const void* id);
void decWeak(const void* id);
-
+
// acquires a strong reference if there is already one.
bool attemptIncStrong(const void* id);
-
+
// acquires a weak reference if there is already one.
// This is not always safe. see ProcessState.cpp and BpBinder.cpp
// for proper use.
@@ -268,12 +271,12 @@
// enable -- enable/disable tracking
// retain -- when tracking is enable, if true, then we save a stack trace
// for each reference and dereference; when retain == false, we
- // match up references and dereferences and keep only the
+ // match up references and dereferences and keep only the
// outstanding ones.
-
+
void trackMe(bool enable, bool retain);
};
-
+
weakref_type* createWeak(const void* id) const;
weakref_type* getWeakRefs() const;
@@ -345,54 +348,12 @@
// ---------------------------------------------------------------------------
-template <class T>
-class LightRefBase
-{
-public:
- inline LightRefBase() : mCount(0) { }
- inline void incStrong(__attribute__((unused)) const void* id) const {
- mCount.fetch_add(1, std::memory_order_relaxed);
- }
- inline void decStrong(__attribute__((unused)) const void* id) const {
- if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
- std::atomic_thread_fence(std::memory_order_acquire);
- delete static_cast<const T*>(this);
- }
- }
- //! DEBUGGING ONLY: Get current strong ref count.
- inline int32_t getStrongCount() const {
- return mCount.load(std::memory_order_relaxed);
- }
-
- typedef LightRefBase<T> basetype;
-
-protected:
- inline ~LightRefBase() { }
-
-private:
- friend class ReferenceMover;
- inline static void renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { }
- inline static void renameRefId(T* /*ref*/, const void* /*old_id*/ , const void* /*new_id*/) { }
-
-private:
- mutable std::atomic<int32_t> mCount;
-};
-
-// This is a wrapper around LightRefBase that simply enforces a virtual
-// destructor to eliminate the template requirement of LightRefBase
-class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
-public:
- virtual ~VirtualLightRefBase();
-};
-
-// ---------------------------------------------------------------------------
-
template <typename T>
class wp
{
public:
typedef typename RefBase::weakref_type weakref_type;
-
+
inline wp() : m_ptr(0) { }
wp(T* other); // NOLINT(implicit)
@@ -403,31 +364,31 @@
template<typename U> wp(const wp<U>& other); // NOLINT(implicit)
~wp();
-
+
// Assignment
wp& operator = (T* other);
wp& operator = (const wp<T>& other);
wp& operator = (const sp<T>& other);
-
+
template<typename U> wp& operator = (U* other);
template<typename U> wp& operator = (const wp<U>& other);
template<typename U> wp& operator = (const sp<U>& other);
-
+
void set_object_and_refs(T* other, weakref_type* refs);
// promotion to sp
-
+
sp<T> promote() const;
// Reset
-
+
void clear();
// Accessors
-
+
inline weakref_type* get_refs() const { return m_refs; }
-
+
inline T* unsafe_get() const { return m_ptr; }
// Operators
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1f52d04..bdbe725 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -109,11 +109,11 @@
LogBuffer::LogBuffer(LastLogTimes* times)
: monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
- pthread_mutex_init(&mLogElementsLock, NULL);
+ pthread_mutex_init(&mLogElementsLock, nullptr);
log_id_for_each(i) {
- lastLoggedElements[i] = NULL;
- droppedElements[i] = NULL;
+ lastLoggedElements[i] = nullptr;
+ droppedElements[i] = nullptr;
}
init();
@@ -198,7 +198,7 @@
new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
if (log_id != LOG_ID_SECURITY) {
int prio = ANDROID_LOG_INFO;
- const char* tag = NULL;
+ const char* tag = nullptr;
if (log_id == LOG_ID_EVENTS) {
tag = tagToName(elem->getTag());
} else {
@@ -224,24 +224,24 @@
//
// State Init
// incoming:
- // dropped = NULL
- // currentLast = NULL;
+ // dropped = nullptr
+ // currentLast = nullptr;
// elem = incoming message
// outgoing:
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// currentLast = copy of elem
// log elem
// State 0
// incoming:
// count = 0
- // dropped = NULL
+ // dropped = nullptr
// currentLast = copy of last message
// elem = incoming message
// outgoing: if match != DIFFERENT
// dropped = copy of first identical message -> State 1
// currentLast = reference to elem
// break: if match == DIFFERENT
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// delete copy of last message (incoming currentLast)
// currentLast = copy of elem
// log elem
@@ -268,7 +268,7 @@
// currentLast = reference to elem, sum liblog.
// break: if match == DIFFERENT
// delete dropped
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// log reference to last held-back (currentLast)
// currentLast = copy of elem
// log elem
@@ -287,7 +287,7 @@
// currentLast = reference to elem
// break: if match == DIFFERENT
// log dropped (chatty message)
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// log reference to last held-back (currentLast)
// currentLast = copy of elem
// log elem
@@ -352,7 +352,7 @@
} else { // State 1
delete dropped;
}
- droppedElements[log_id] = NULL;
+ droppedElements[log_id] = nullptr;
log(currentLast); // report last message in the series
} else { // State 0
delete currentLast;
@@ -656,7 +656,7 @@
// mLogElementsLock must be held when this function is called.
//
bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
- LogTimeEntry* oldest = NULL;
+ LogTimeEntry* oldest = nullptr;
bool busy = false;
bool clearAll = pruneRows == ULONG_MAX;
@@ -1078,9 +1078,11 @@
return retval;
}
-log_time LogBuffer::flushTo(
- SocketClient* reader, const log_time& start, bool privileged, bool security,
- int (*filter)(const LogBufferElement* element, void* arg), void* arg) {
+log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
+ pid_t* lastTid, bool privileged, bool security,
+ int (*filter)(const LogBufferElement* element,
+ void* arg),
+ void* arg) {
LogBufferElementCollection::iterator it;
uid_t uid = reader->getUid();
@@ -1109,9 +1111,6 @@
}
log_time max = start;
- // Help detect if the valid message before is from the same source so
- // we can differentiate chatty filter types.
- pid_t lastTid[LOG_ID_MAX] = { 0 };
for (; it != mLogElements.end(); ++it) {
LogBufferElement* element = *it;
@@ -1139,14 +1138,17 @@
}
}
- bool sameTid = lastTid[element->getLogId()] == element->getTid();
- // Dropped (chatty) immediately following a valid log from the
- // same source in the same log buffer indicates we have a
- // multiple identical squash. chatty that differs source
- // is due to spam filter. chatty to chatty of different
- // source is also due to spam filter.
- lastTid[element->getLogId()] =
- (element->getDropped() && !sameTid) ? 0 : element->getTid();
+ bool sameTid = false;
+ if (lastTid) {
+ sameTid = lastTid[element->getLogId()] == element->getTid();
+ // Dropped (chatty) immediately following a valid log from the
+ // same source in the same log buffer indicates we have a
+ // multiple identical squash. chatty that differs source
+ // is due to spam filter. chatty to chatty of different
+ // source is also due to spam filter.
+ lastTid[element->getLogId()] =
+ (element->getDropped() && !sameTid) ? 0 : element->getTid();
+ }
pthread_mutex_unlock(&mLogElementsLock);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index fcf6b9a..19d11cb 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -115,11 +115,15 @@
int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
const char* msg, unsigned short len);
+ // lastTid is an optional context to help detect if the last previous
+ // valid message was from the same source so we can differentiate chatty
+ // filter types (identical or expired)
log_time flushTo(SocketClient* writer, const log_time& start,
+ pid_t* lastTid, // &lastTid[LOG_ID_MAX] or nullptr
bool privileged, bool security,
int (*filter)(const LogBufferElement* element,
- void* arg) = NULL,
- void* arg = NULL);
+ void* arg) = nullptr,
+ void* arg = nullptr);
bool clear(log_id_t id, uid_t uid = AID_ROOT);
unsigned long getSize(log_id_t id);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 81356fe..04a620c 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -235,7 +235,9 @@
}
iovec[1].iov_len = entry.len;
- log_time retval = reader->sendDatav(iovec, 2) ? FLUSH_ERROR : mRealTime;
+ log_time retval = reader->sendDatav(iovec, 1 + (entry.len != 0))
+ ? FLUSH_ERROR
+ : mRealTime;
if (buffer) free(buffer);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 620d4d0..af19279 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -182,7 +182,7 @@
} logFindStart(pid, logMask, sequence,
logbuf().isMonotonic() && android::isMonotonic(start));
- logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli),
+ logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli),
FlushCommand::hasSecurityLogs(cli),
logFindStart.callback, &logFindStart);
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 04e531f..ccc550a 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -15,6 +15,7 @@
*/
#include <errno.h>
+#include <string.h>
#include <sys/prctl.h>
#include <private/android_logger.h>
@@ -47,7 +48,8 @@
mEnd(log_time(android_log_clockid())) {
mTimeout.tv_sec = timeout / NS_PER_SEC;
mTimeout.tv_nsec = timeout % NS_PER_SEC;
- pthread_cond_init(&threadTriggeredCondition, NULL);
+ memset(mLastTid, 0, sizeof(mLastTid));
+ pthread_cond_init(&threadTriggeredCondition, nullptr);
cleanSkip_Locked();
}
@@ -98,7 +100,7 @@
it++;
}
- me->mClient = NULL;
+ me->mClient = nullptr;
reader.release(client);
}
@@ -122,7 +124,7 @@
SocketClient* client = me->mClient;
if (!client) {
me->error();
- return NULL;
+ return nullptr;
}
LogBuffer& logbuf = me->mReader.logbuf();
@@ -151,12 +153,12 @@
unlock();
if (me->mTail) {
- logbuf.flushTo(client, start, privileged, security, FilterFirstPass,
- me);
+ logbuf.flushTo(client, start, nullptr, privileged, security,
+ FilterFirstPass, me);
me->leadingDropped = true;
}
- start = logbuf.flushTo(client, start, privileged, security,
- FilterSecondPass, me);
+ start = logbuf.flushTo(client, start, me->mLastTid, privileged,
+ security, FilterSecondPass, me);
lock();
@@ -182,7 +184,7 @@
pthread_cleanup_pop(true);
- return NULL;
+ return nullptr;
}
// A first pass to count the number of elements
@@ -281,7 +283,5 @@
}
void LogTimeEntry::cleanSkip_Locked(void) {
- for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t)(i + 1)) {
- skipAhead[i] = 0;
- }
+ memset(skipAhead, 0, sizeof(skipAhead));
}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 9a3ddab..ec8252e 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -44,6 +44,7 @@
const unsigned int mLogMask;
const pid_t mPid;
unsigned int skipAhead[LOG_ID_MAX];
+ pid_t mLastTid[LOG_ID_MAX];
unsigned long mCount;
unsigned long mTail;
unsigned long mIndex;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 77b173d..28406c8 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -599,7 +599,7 @@
on nonencrypted
# A/B update verifier that marks a successful boot.
- exec - root cache -- /system/bin/update_verifier nonencrypted
+ exec_start update_verifier_nonencrypted
class_start main
class_start late_start
@@ -622,12 +622,12 @@
on property:vold.decrypt=trigger_restart_min_framework
# A/B update verifier that marks a successful boot.
- exec - root cache -- /system/bin/update_verifier trigger_restart_min_framework
+ exec_start update_verifier
class_start main
on property:vold.decrypt=trigger_restart_framework
# A/B update verifier that marks a successful boot.
- exec - root cache -- /system/bin/update_verifier trigger_restart_framework
+ exec_start update_verifier
class_start main
class_start late_start
diff --git a/storaged/README.properties b/storaged/README.properties
index 70e6026..2d8397f 100644
--- a/storaged/README.properties
+++ b/storaged/README.properties
@@ -1,6 +1,5 @@
ro.storaged.event.interval # interval storaged scans for IO stats, in seconds
ro.storaged.event.perf_check # check for time spent in event loop, in microseconds
ro.storaged.disk_stats_pub # interval storaged publish disk stats, in seconds
-ro.storaged.emmc_info_pub # interval storaged publish emmc info, in seconds
ro.storaged.uid_io.interval # interval storaged checks Per UID IO usage, in seconds
ro.storaged.uid_io.threshold # Per UID IO usage limit, in bytes
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index bd1391c..b6a0850 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -230,7 +230,6 @@
// Periodic chores intervals in seconds
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH ( 86400 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)
@@ -240,7 +239,6 @@
struct storaged_config {
int periodic_chores_interval_unit;
int periodic_chores_interval_disk_stats_publish;
- int periodic_chores_interval_emmc_info_publish;
int periodic_chores_interval_uid_io;
bool proc_uid_io_available; // whether uid_io is accessible
bool diskstats_available; // whether diskstats is accessible
@@ -253,7 +251,6 @@
storaged_config mConfig;
disk_stats_publisher mDiskStats;
disk_stats_monitor mDsm;
- storage_info_t *info = nullptr;
uid_monitor mUidm;
time_t mStarttime;
public:
@@ -264,9 +261,6 @@
void pause(void) {
sleep(mConfig.periodic_chores_interval_unit);
}
- void set_storage_info(storage_info_t *storage_info) {
- info = storage_info;
- }
time_t get_starttime(void) {
return mStarttime;
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index cb5b8a8..913c814 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -24,43 +24,42 @@
using namespace std;
-// two characters in string for each byte
-struct str_hex {
- char str[2];
-};
-
class storage_info_t {
protected:
FRIEND_TEST(storaged_test, storage_info_t);
- uint8_t eol; // pre-eol (end of life) information
- uint8_t lifetime_a; // device life time estimation (type A)
- uint8_t lifetime_b; // device life time estimation (type B)
+ uint16_t eol; // pre-eol (end of life) information
+ uint16_t lifetime_a; // device life time estimation (type A)
+ uint16_t lifetime_b; // device life time estimation (type B)
string version; // version string
-public:
void publish();
+public:
+ storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0) {}
virtual ~storage_info_t() {}
- virtual bool init() = 0;
- virtual bool update() = 0;
+ virtual bool report() = 0;
};
class emmc_info_t : public storage_info_t {
private:
- // minimum size of a ext_csd file
- const int EXT_CSD_FILE_MIN_SIZE = 1024;
- // List of interesting offsets
- const size_t EXT_CSD_REV_IDX = 192 * sizeof(str_hex);
- const size_t EXT_PRE_EOL_INFO_IDX = 267 * sizeof(str_hex);
- const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * sizeof(str_hex);
- const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * sizeof(str_hex);
-
- const char* ext_csd_file = "/d/mmc0/mmc0:0001/ext_csd";
- const char* emmc_ver_str[8] = {
- "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
+ const string emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
+ const string emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
+ const char* emmc_ver_str[9] = {
+ "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
};
public:
virtual ~emmc_info_t() {}
- bool init();
- bool update();
+ bool report();
+ bool report_sysfs();
+ bool report_debugfs();
};
+class ufs_info_t : public storage_info_t {
+private:
+ const string health_file = "/sys/devices/soc/624000.ufshc/health";
+public:
+ virtual ~ufs_info_t() {}
+ bool report();
+};
+
+void report_storage_health();
+
#endif /* _STORAGED_INFO_H_ */
diff --git a/storaged/main.cpp b/storaged/main.cpp
index e25298b..2f2273d 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -43,7 +43,6 @@
#include <storaged_utils.h>
storaged_t storaged;
-emmc_info_t emmc_info;
// Function of storaged's main thread
void* storaged_main(void* s) {
@@ -114,10 +113,7 @@
}
if (flag_main_service) { // start main thread
- if (emmc_info.init()) {
- storaged.set_storage_info(&emmc_info);
- }
-
+ report_storage_health();
// Start the main thread of storaged
pthread_t storaged_main_thread;
errno = pthread_create(&storaged_main_thread, NULL, storaged_main, &storaged);
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 88fbb7a..1770922 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -170,6 +170,9 @@
}
void storaged_t::init_battery_service() {
+ if (!mConfig.proc_uid_io_available)
+ return;
+
sp<IBatteryPropertiesRegistrar> battery_properties = get_battery_properties_service();
if (battery_properties == NULL) {
LOG_TO(SYSTEM, WARNING) << "failed to find batteryproperties service";
@@ -203,9 +206,6 @@
mConfig.periodic_chores_interval_disk_stats_publish =
property_get_int32("ro.storaged.disk_stats_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
- mConfig.periodic_chores_interval_emmc_info_publish =
- property_get_int32("ro.storaged.emmc_info_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH);
-
mConfig.periodic_chores_interval_uid_io =
property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
@@ -221,12 +221,6 @@
}
}
- if (info && mTimer &&
- (mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) {
- info->update();
- info->publish();
- }
-
if (mConfig.proc_uid_io_available && mTimer &&
(mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
mUidm.report();
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 73d611c..434bd74 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -16,83 +16,169 @@
#define LOG_TAG "storaged"
+#include <stdio.h>
#include <string.h>
#include <android-base/file.h>
-#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
#include <log/log_event_list.h>
#include "storaged.h"
using namespace std;
-using namespace android;
using namespace android::base;
+void report_storage_health()
+{
+ emmc_info_t mmc;
+ ufs_info_t ufs;
+
+ mmc.report();
+ ufs.report();
+}
+
void storage_info_t::publish()
{
- if (eol == 0 && lifetime_a == 0 && lifetime_b == 0) {
- return;
- }
-
android_log_event_list(EVENTLOGTAG_EMMCINFO)
<< version << eol << lifetime_a << lifetime_b
<< LOG_ID_EVENTS;
}
-bool emmc_info_t::init()
+bool emmc_info_t::report()
+{
+ if (!report_sysfs() && !report_debugfs())
+ return false;
+
+ publish();
+ return true;
+}
+
+bool emmc_info_t::report_sysfs()
{
string buffer;
- if (!ReadFileToString(ext_csd_file, &buffer) ||
- buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
+ uint16_t rev = 0;
+
+ if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
return false;
}
- string ver_str = buffer.substr(EXT_CSD_REV_IDX, sizeof(str_hex));
- uint8_t ext_csd_rev;
- if (!ParseUint(ver_str, &ext_csd_rev)) {
- LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_CSD_REV.";
+ if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
+ rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
return false;
}
version = "emmc ";
- version += (ext_csd_rev < ARRAY_SIZE(emmc_ver_str)) ?
- emmc_ver_str[ext_csd_rev] : "Unknown";
+ version += emmc_ver_str[rev];
- if (ext_csd_rev < 7) {
+ if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
return false;
}
- return update();
-}
-
-bool emmc_info_t::update()
-{
- string buffer;
- if (!ReadFileToString(ext_csd_file, &buffer) ||
- buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
+ if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
return false;
}
- string str = buffer.substr(EXT_PRE_EOL_INFO_IDX, sizeof(str_hex));
- if (!ParseUint(str, &eol)) {
- LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_PRE_EOL_INFO.";
+ if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
return false;
}
- str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, sizeof(str_hex));
- if (!ParseUint(str, &lifetime_a)) {
- LOG_TO(SYSTEM, ERROR)
- << "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_A.";
- return false;
- }
-
- str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, sizeof(str_hex));
- if (!ParseUint(str, &lifetime_b)) {
- LOG_TO(SYSTEM, ERROR)
- << "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_B.";
+ if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
+ (lifetime_a == 0 && lifetime_b == 0)) {
return false;
}
return true;
}
+
+const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
+/* 2 characters in string for each byte */
+const size_t EXT_CSD_REV_IDX = 192 * 2;
+const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
+const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
+const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
+
+bool emmc_info_t::report_debugfs()
+{
+ string buffer;
+ uint16_t rev = 0;
+
+ if (!ReadFileToString(emmc_debugfs, &buffer) ||
+ buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
+ return false;
+ }
+
+ string str = buffer.substr(EXT_CSD_REV_IDX, 2);
+ if (!ParseUint(str, &rev) ||
+ rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
+ return false;
+ }
+
+ version = "emmc ";
+ version += emmc_ver_str[rev];
+
+ str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
+ if (!ParseUint(str, &eol)) {
+ return false;
+ }
+
+ str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
+ if (!ParseUint(str, &lifetime_a)) {
+ return false;
+ }
+
+ str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
+ if (!ParseUint(str, &lifetime_b)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ufs_info_t::report()
+{
+ string buffer;
+ if (!ReadFileToString(health_file, &buffer)) {
+ return false;
+ }
+
+ vector<string> lines = Split(buffer, "\n");
+ if (lines.empty()) {
+ return false;
+ }
+
+ char rev[8];
+ if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
+ return false;
+ }
+
+ version = "ufs " + string(rev);
+
+ for (size_t i = 1; i < lines.size(); i++) {
+ char token[32];
+ uint16_t val;
+ int ret;
+ if ((ret = sscanf(lines[i].c_str(),
+ "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx",
+ token, &val)) < 2) {
+ continue;
+ }
+
+ if (string(token) == "bPreEOLInfo") {
+ eol = val;
+ } else if (string(token) == "bDeviceLifeTimeEstA") {
+ lifetime_a = val;
+ } else if (string(token) == "bDeviceLifeTimeEstB") {
+ lifetime_b = val;
+ }
+ }
+
+ if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
+ return false;
+ }
+
+ publish();
+ return true;
+}
+
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index e335cad..b103ac1 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -29,7 +29,6 @@
#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
-#define EMMC_EXT_CSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
static void pause(uint32_t sec) {
const char* path = "/cache/test";
@@ -58,13 +57,8 @@
const char* DISK_STATS_PATH;
TEST(storaged_test, retvals) {
struct disk_stats stats;
- emmc_info_t info;
memset(&stats, 0, sizeof(struct disk_stats));
- if (info.init()) {
- EXPECT_TRUE(info.update());
- }
-
if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
DISK_STATS_PATH = MMC_DISK_STATS_PATH;
} else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {
@@ -127,20 +121,6 @@
}
}
-TEST(storaged_test, storage_info_t) {
- emmc_info_t info;
-
- if (access(EMMC_EXT_CSD_PATH, R_OK) >= 0) {
- int ret = info.init();
- if (ret) {
- EXPECT_TRUE(info.version.empty());
- ASSERT_TRUE(info.update());
- // update should put something in info.
- EXPECT_TRUE(info.eol || info.lifetime_a || info.lifetime_b);
- }
- }
-}
-
static double mean(std::deque<uint32_t> nums) {
double sum = 0.0;
for (uint32_t i : nums) {