Merge "Add DwarfMemory class."
diff --git a/.clang-format-2 b/.clang-format-2
index aab4665..41591ce 100644
--- a/.clang-format-2
+++ b/.clang-format-2
@@ -1,4 +1,5 @@
BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
diff --git a/.clang-format-4 b/.clang-format-4
index 1497447..ae4a451 100644
--- a/.clang-format-4
+++ b/.clang-format-4
@@ -1,5 +1,6 @@
BasedOnStyle: Google
AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 7058acb..31d3dc6 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -75,7 +75,7 @@
bool directory_exists(const std::string& path) {
struct stat sb;
- return lstat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode);
+ return stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode);
}
std::string escape_arg(const std::string& s) {
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index a3bc445..e1b6287 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -55,7 +55,6 @@
ASSERT_FALSE(directory_exists(subdir(profiles_dir, "does-not-exist")));
#else
ASSERT_TRUE(directory_exists("/proc"));
- ASSERT_FALSE(directory_exists("/proc/self")); // Symbolic link.
ASSERT_FALSE(directory_exists("/proc/does-not-exist"));
#endif
}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 3de7be6..4979eef 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -782,9 +782,16 @@
return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
}
-static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz,
- bool show_progress)
-{
+static int adb_download_buffer(const char* service, const char* filename) {
+ std::string content;
+ if (!android::base::ReadFileToString(filename, &content)) {
+ fprintf(stderr, "error: couldn't read %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
+ unsigned sz = content.size();
+
std::string error;
int fd = adb_connect(android::base::StringPrintf("%s:%d", service, sz), &error);
if (fd < 0) {
@@ -798,10 +805,8 @@
unsigned total = sz;
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
- if (show_progress) {
- const char* x = strrchr(service, ':');
- if (x) service = x + 1;
- }
+ const char* x = strrchr(service, ':');
+ if (x) service = x + 1;
while (sz > 0) {
unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz;
@@ -814,14 +819,10 @@
}
sz -= xfer;
ptr += xfer;
- if (show_progress) {
- printf("sending: '%s' %4d%% \r", fn, (int)(100LL - ((100LL * sz) / (total))));
- fflush(stdout);
- }
+ printf("sending: '%s' %4d%% \r", filename, (int)(100LL - ((100LL * sz) / (total))));
+ fflush(stdout);
}
- if (show_progress) {
- printf("\n");
- }
+ printf("\n");
if (!adb_status(fd, &error)) {
fprintf(stderr,"* error response '%s' *\n", error.c_str());
@@ -854,38 +855,40 @@
* - When the other side sends "DONEDONE" instead of a block number,
* we hang up.
*/
-static int adb_sideload_host(const char* fn) {
- fprintf(stderr, "loading: '%s'...\n", fn);
-
- std::string content;
- if (!android::base::ReadFileToString(fn, &content)) {
- fprintf(stderr, "failed: %s\n", strerror(errno));
+static int adb_sideload_host(const char* filename) {
+ fprintf(stderr, "opening '%s'...\n", filename);
+ struct stat sb;
+ if (stat(filename, &sb) == -1) {
+ fprintf(stderr, "failed to stat file %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+ unique_fd package_fd(adb_open(filename, O_RDONLY));
+ if (package_fd == -1) {
+ fprintf(stderr, "failed to open file %s: %s\n", filename, strerror(errno));
return -1;
}
- const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
- unsigned sz = content.size();
-
fprintf(stderr, "connecting...\n");
- std::string service =
- android::base::StringPrintf("sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE);
+ std::string service = android::base::StringPrintf(
+ "sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
std::string error;
- unique_fd fd(adb_connect(service, &error));
- if (fd < 0) {
- // Try falling back to the older sideload method. Maybe this
+ unique_fd device_fd(adb_connect(service, &error));
+ if (device_fd < 0) {
+ // Try falling back to the older (<= K) sideload method. Maybe this
// is an older device that doesn't support sideload-host.
fprintf(stderr, "falling back to older sideload method...\n");
- return adb_download_buffer("sideload", fn, data, sz, true);
+ return adb_download_buffer("sideload", filename);
}
int opt = SIDELOAD_HOST_BLOCK_SIZE;
- adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
+ adb_setsockopt(device_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
+
+ char buf[SIDELOAD_HOST_BLOCK_SIZE];
size_t xfer = 0;
int last_percent = -1;
while (true) {
- char buf[9];
- if (!ReadFdExactly(fd, buf, 8)) {
+ if (!ReadFdExactly(device_fd, buf, 8)) {
fprintf(stderr, "* failed to read command: %s\n", strerror(errno));
return -1;
}
@@ -893,26 +896,35 @@
if (strcmp("DONEDONE", buf) == 0) {
printf("\rTotal xfer: %.2fx%*s\n",
- (double)xfer / (sz ? sz : 1), (int)strlen(fn)+10, "");
+ static_cast<double>(xfer) / (sb.st_size ? sb.st_size : 1),
+ static_cast<int>(strlen(filename) + 10), "");
return 0;
}
int block = strtol(buf, NULL, 10);
size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
- if (offset >= sz) {
+ if (offset >= static_cast<size_t>(sb.st_size)) {
fprintf(stderr, "* attempt to read block %d past end\n", block);
return -1;
}
- const uint8_t* start = data + offset;
- size_t offset_end = offset + SIDELOAD_HOST_BLOCK_SIZE;
+
size_t to_write = SIDELOAD_HOST_BLOCK_SIZE;
- if (offset_end > sz) {
- to_write = sz - offset;
+ if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<size_t>(sb.st_size)) {
+ to_write = sb.st_size - offset;
}
- if (!WriteFdExactly(fd, start, to_write)) {
- adb_status(fd, &error);
+ if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
+ fprintf(stderr, "* failed to seek to package block: %s\n", strerror(errno));
+ return -1;
+ }
+ if (!ReadFdExactly(package_fd, buf, to_write)) {
+ fprintf(stderr, "* failed to read package block: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (!WriteFdExactly(device_fd, buf, to_write)) {
+ adb_status(device_fd, &error);
fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
return -1;
}
@@ -924,9 +936,9 @@
// extra access to things like the zip central directory).
// This estimate of the completion becomes 100% when we've
// transferred ~2.13 (=100/47) times the package size.
- int percent = (int)(xfer * 47LL / (sz ? sz : 1));
+ int percent = static_cast<int>(xfer * 47LL / (sb.st_size ? sb.st_size : 1));
if (percent != last_percent) {
- printf("\rserving: '%s' (~%d%%) ", fn, percent);
+ printf("\rserving: '%s' (~%d%%) ", filename, percent);
fflush(stdout);
last_percent = percent;
}
diff --git a/base/file.cpp b/base/file.cpp
index 378a405..d4e5894 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -49,6 +49,14 @@
bool ReadFdToString(int fd, std::string* content) {
content->clear();
+ // Although original we had small files in mind, this code gets used for
+ // very large files too, where the std::string growth heuristics might not
+ // be suitable. https://code.google.com/p/android/issues/detail?id=258500.
+ struct stat sb;
+ if (fstat(fd, &sb) != -1 && sb.st_size > 0) {
+ content->reserve(sb.st_size);
+ }
+
char buf[BUFSIZ];
ssize_t n;
while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 266131e..02b431d 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -214,3 +214,46 @@
EXPECT_EQ(".", android::base::Dirname("sh"));
EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh/"));
}
+
+TEST(file, ReadFileToString_capacity) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ // For a huge file, the overhead should still be small.
+ std::string s;
+ size_t size = 16 * 1024 * 1024;
+ ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
+ EXPECT_EQ(size, s.size());
+ EXPECT_LT(s.capacity(), size + 16);
+
+ // Even for weird badly-aligned sizes.
+ size += 12345;
+ ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
+ EXPECT_EQ(size, s.size());
+ EXPECT_LT(s.capacity(), size + 16);
+
+ // We'll shrink an enormous string if you read a small file into it.
+ size = 64;
+ ASSERT_TRUE(android::base::WriteStringToFile(std::string(size, 'x'), tf.path));
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
+ EXPECT_EQ(size, s.size());
+ EXPECT_LT(s.capacity(), size + 16);
+}
+
+TEST(file, ReadFileToString_capacity_0) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ // Because /proc reports its files as zero-length, we don't actually trust
+ // any file that claims to be zero-length. Rather than add increasingly
+ // complex heuristics for shrinking the passed-in string in that case, we
+ // currently leave it alone.
+ std::string s;
+ size_t initial_capacity = s.capacity();
+ ASSERT_TRUE(android::base::WriteStringToFile("", tf.path));
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s));
+ EXPECT_EQ(0U, s.size());
+ EXPECT_EQ(initial_capacity, s.capacity());
+}
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 4de5e57..041586c 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -62,15 +62,14 @@
// Waits for the system property `key` to have the value `expected_value`.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
-bool WaitForProperty(const std::string& key,
- const std::string& expected_value,
- std::chrono::milliseconds relative_timeout);
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
+ std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
// Waits for the system property `key` to be created.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
-bool WaitForPropertyCreation(const std::string& key,
- std::chrono::milliseconds relative_timeout);
+bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
+ std::chrono::milliseconds::max());
} // namespace base
} // namespace android
diff --git a/base/properties.cpp b/base/properties.cpp
index 32c0128..816bca0 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -101,22 +101,24 @@
}
// TODO: chrono_utils?
-static void DurationToTimeSpec(timespec& ts, std::chrono::nanoseconds d) {
+static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
ts.tv_sec = s.count();
ts.tv_nsec = ns.count();
}
+// TODO: boot_clock?
using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
-static void UpdateTimeSpec(timespec& ts,
- const AbsTime& timeout) {
+static void UpdateTimeSpec(timespec& ts, std::chrono::milliseconds relative_timeout,
+ const AbsTime& start_time) {
auto now = std::chrono::steady_clock::now();
- auto remaining_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - now);
- if (remaining_timeout < 0ns) {
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed >= relative_timeout) {
ts = { 0, 0 };
} else {
+ auto remaining_timeout = relative_timeout - time_elapsed;
DurationToTimeSpec(ts, remaining_timeout);
}
}
@@ -127,11 +129,7 @@
// Returns nullptr on timeout.
static const prop_info* WaitForPropertyCreation(const std::string& key,
const std::chrono::milliseconds& relative_timeout,
- AbsTime& absolute_timeout) {
- // TODO: boot_clock?
- auto now = std::chrono::steady_clock::now();
- absolute_timeout = now + relative_timeout;
-
+ const AbsTime& start_time) {
// Find the property's prop_info*.
const prop_info* pi;
unsigned global_serial = 0;
@@ -139,17 +137,16 @@
// The property doesn't even exist yet.
// Wait for a global change and then look again.
timespec ts;
- UpdateTimeSpec(ts, absolute_timeout);
+ UpdateTimeSpec(ts, relative_timeout, start_time);
if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
}
return pi;
}
-bool WaitForProperty(const std::string& key,
- const std::string& expected_value,
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
std::chrono::milliseconds relative_timeout) {
- AbsTime absolute_timeout;
- const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, absolute_timeout);
+ auto start_time = std::chrono::steady_clock::now();
+ const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, start_time);
if (pi == nullptr) return false;
WaitForPropertyData data;
@@ -162,7 +159,7 @@
if (data.done) return true;
// It didn't, so wait for the property to change before checking again.
- UpdateTimeSpec(ts, absolute_timeout);
+ UpdateTimeSpec(ts, relative_timeout, start_time);
uint32_t unused;
if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
}
@@ -170,8 +167,8 @@
bool WaitForPropertyCreation(const std::string& key,
std::chrono::milliseconds relative_timeout) {
- AbsTime absolute_timeout;
- return (WaitForPropertyCreation(key, relative_timeout, absolute_timeout) != nullptr);
+ auto start_time = std::chrono::steady_clock::now();
+ return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
}
} // namespace base
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index 1bbe482..de5f3dc 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -151,6 +151,38 @@
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
}
+TEST(properties, WaitForProperty_MaxTimeout) {
+ std::atomic<bool> flag{false};
+ std::thread thread([&]() {
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+ while (!flag) std::this_thread::yield();
+ std::this_thread::sleep_for(500ms);
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+ });
+
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+ flag = true;
+ // Test that this does not immediately return false due to overflow issues with the timeout.
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
+ thread.join();
+}
+
+TEST(properties, WaitForProperty_NegativeTimeout) {
+ std::atomic<bool> flag{false};
+ std::thread thread([&]() {
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+ while (!flag) std::this_thread::yield();
+ std::this_thread::sleep_for(500ms);
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+ });
+
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+ flag = true;
+ // Assert that this immediately returns with a negative timeout
+ ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
+ thread.join();
+}
+
TEST(properties, WaitForPropertyCreation) {
std::thread thread([&]() {
std::this_thread::sleep_for(100ms);
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c
index b89c395..e52762e 100644
--- a/cpio/mkbootfs.c
+++ b/cpio/mkbootfs.c
@@ -301,6 +301,7 @@
allocated *= 2;
canned_config = (struct fs_config_entry*)realloc(
canned_config, allocated * sizeof(struct fs_config_entry));
+ if (canned_config == NULL) die("failed to reallocate memory");
}
struct fs_config_entry* cc = canned_config + used;
@@ -320,6 +321,7 @@
++allocated;
canned_config = (struct fs_config_entry*)realloc(
canned_config, allocated * sizeof(struct fs_config_entry));
+ if (canned_config == NULL) die("failed to reallocate memory");
}
canned_config[used].name = NULL;
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 2d6c7f5..024bc3d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -161,6 +161,7 @@
target: {
android: {
srcs: [
+ "client/debuggerd_client_test.cpp",
"debuggerd_test.cpp",
"util.cpp"
],
@@ -171,6 +172,7 @@
"libbacktrace",
"libbase",
"libcutils",
+ "libdebuggerd_client",
],
static_libs: [
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index f2975d1..b9fb512 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -19,12 +19,14 @@
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
+#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <chrono>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
@@ -33,6 +35,8 @@
#include <debuggerd/protocol.h>
#include <debuggerd/util.h>
+using namespace std::chrono_literals;
+
using android::base::unique_fd;
static bool send_signal(pid_t pid, bool backtrace) {
@@ -45,28 +49,31 @@
return true;
}
+template <typename Duration>
+static void populate_timeval(struct timeval* tv, const Duration& duration) {
+ auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
+ auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(duration - seconds);
+ tv->tv_sec = static_cast<long>(seconds.count());
+ tv->tv_usec = static_cast<long>(microseconds.count());
+}
+
bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
int timeout_ms) {
LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
unique_fd sockfd;
const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
- auto set_timeout = [timeout_ms, &sockfd, &end]() {
+ 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;
}
- auto now = std::chrono::steady_clock::now();
- if (now > end) {
+ auto remaining = time_left();
+ if (remaining < decltype(remaining)::zero()) {
return false;
}
-
- auto time_left = std::chrono::duration_cast<std::chrono::microseconds>(end - now);
- auto seconds = std::chrono::duration_cast<std::chrono::seconds>(time_left);
- auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(time_left - seconds);
- struct timeval timeout = {
- .tv_sec = static_cast<long>(seconds.count()),
- .tv_usec = static_cast<long>(microseconds.count()),
- };
+ struct timeval timeout;
+ populate_timeval(&timeout, remaining);
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
return false;
@@ -84,7 +91,7 @@
return false;
}
- if (!set_timeout()) {
+ if (!set_timeout(sockfd)) {
PLOG(ERROR) << "libdebugger_client: failed to set timeout";
return false;
}
@@ -96,11 +103,19 @@
}
InterceptRequest req = {.pid = pid };
- if (!set_timeout()) {
+ if (!set_timeout(sockfd)) {
PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+ return false;
}
- if (send_fd(sockfd.get(), &req, sizeof(req), std::move(output_fd)) != sizeof(req)) {
+ // Create an intermediate pipe to pass to the other end.
+ unique_fd pipe_read, pipe_write;
+ if (!Pipe(&pipe_read, &pipe_write)) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to create pipe";
+ return false;
+ }
+
+ if (send_fd(sockfd.get(), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
return false;
}
@@ -108,8 +123,9 @@
bool backtrace = dump_type == kDebuggerdBacktrace;
send_signal(pid, backtrace);
- if (!set_timeout()) {
- PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+ if (!set_timeout(sockfd)) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to set timeout";
+ return false;
}
InterceptResponse response;
@@ -127,6 +143,48 @@
if (response.success != 1) {
response.error_message[sizeof(response.error_message) - 1] = '\0';
LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
+ return false;
+ }
+
+ // Forward output from the pipe to the output fd.
+ while (true) {
+ auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_left());
+ if (remaining_ms <= 1ms) {
+ LOG(ERROR) << "libdebuggerd_client: timeout expired";
+ return false;
+ }
+
+ struct pollfd pfd = {
+ .fd = pipe_read.get(), .events = POLLIN, .revents = 0,
+ };
+
+ rc = poll(&pfd, 1, remaining_ms.count());
+ if (rc == -1) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ PLOG(ERROR) << "libdebuggerd_client: error while polling";
+ return false;
+ }
+ } else if (rc == 0) {
+ LOG(ERROR) << "libdebuggerd_client: timeout expired";
+ return false;
+ }
+
+ char buf[1024];
+ rc = TEMP_FAILURE_RETRY(read(pipe_read.get(), buf, sizeof(buf)));
+ if (rc == 0) {
+ // Done.
+ break;
+ } else if (rc == -1) {
+ PLOG(ERROR) << "libdebuggerd_client: error while reading";
+ return false;
+ }
+
+ if (!android::base::WriteFully(output_fd.get(), buf, rc)) {
+ PLOG(ERROR) << "libdebuggerd_client: error while writing";
+ return false;
+ }
}
LOG(INFO) << "libdebuggerd_client: done dumping process " << pid;
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
new file mode 100644
index 0000000..86d0314
--- /dev/null
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 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.
+ */
+
+#include <debuggerd/client.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include <debuggerd/util.h>
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+
+TEST(debuggerd_client, race) {
+ static constexpr int THREAD_COUNT = 1024;
+ pid_t forkpid = fork();
+
+ ASSERT_NE(-1, forkpid);
+
+ if (forkpid == 0) {
+ // Spawn a bunch of threads, to make crash_dump take longer.
+ std::vector<std::thread> threads;
+ for (int i = 0; i < THREAD_COUNT; ++i) {
+ threads.emplace_back([]() {
+ while (true) {
+ std::this_thread::sleep_for(60s);
+ }
+ });
+ }
+
+ std::this_thread::sleep_for(60s);
+ exit(0);
+ }
+
+ unique_fd pipe_read, pipe_write;
+ ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));
+
+ // 64 kB should be enough for everyone.
+ constexpr int PIPE_SIZE = 64 * 1024 * 1024;
+ ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE));
+
+ // Wait for a bit to let the child spawn all of its threads.
+ std::this_thread::sleep_for(250ms);
+
+ ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(pipe_write), kDebuggerdBacktrace, 10000));
+ // Immediately kill the forked child, to make sure that the dump didn't return early.
+ ASSERT_EQ(0, kill(forkpid, SIGKILL)) << strerror(errno);
+
+ // Check the output.
+ std::string result;
+ ASSERT_TRUE(android::base::ReadFdToString(pipe_read.get(), &result));
+
+ // Look for "----- end <PID> -----"
+ int found_end = 0;
+
+ std::string expected_end = android::base::StringPrintf("----- end %d -----", forkpid);
+
+ std::vector<std::string> lines = android::base::Split(result, "\n");
+ for (const std::string& line : lines) {
+ if (line == expected_end) {
+ ++found_end;
+ }
+ }
+
+ EXPECT_EQ(1, found_end) << "\nOutput: \n" << result;
+}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index cf24d57..cd00dc5 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -120,7 +120,7 @@
}
if (signum == DEBUGGER_SIGNAL) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "Requested dump for tid %d (%s)", gettid(),
+ __libc_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", gettid(),
thread_name);
return;
}
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 5ee07cd..325210d 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -77,7 +77,9 @@
resetLogs();
elf_set_fake_build_id("");
siginfo_t si;
+ memset(&si, 0, sizeof(si));
si.si_signo = SIGABRT;
+ si.si_code = SI_KERNEL;
ptrace_set_fake_getsiginfo(si);
}
@@ -292,7 +294,9 @@
map_mock_->AddMap(map);
siginfo_t si;
+ memset(&si, 0, sizeof(si));
si.si_signo = SIGBUS;
+ si.si_code = SI_KERNEL;
si.si_addr = reinterpret_cast<void*>(0x1000);
ptrace_set_fake_getsiginfo(si);
dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
@@ -348,7 +352,9 @@
map_mock_->AddMap(map);
siginfo_t si;
+ memset(&si, 0, sizeof(si));
si.si_signo = SIGBUS;
+ si.si_code = SI_KERNEL;
si.si_addr = reinterpret_cast<void*>(0xa533000);
ptrace_set_fake_getsiginfo(si);
dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
@@ -404,7 +410,9 @@
map_mock_->AddMap(map);
siginfo_t si;
+ memset(&si, 0, sizeof(si));
si.si_signo = SIGBUS;
+ si.si_code = SI_KERNEL;
si.si_addr = reinterpret_cast<void*>(0xa534040);
ptrace_set_fake_getsiginfo(si);
dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
@@ -458,7 +466,9 @@
map_mock_->AddMap(map);
siginfo_t si;
+ memset(&si, 0, sizeof(si));
si.si_signo = SIGBUS;
+ si.si_code = SI_KERNEL;
#if defined(__LP64__)
si.si_addr = reinterpret_cast<void*>(0x12345a534040UL);
#else
@@ -503,7 +513,7 @@
map_mock_->AddMap(map);
siginfo_t si;
- si.si_signo = 0;
+ memset(&si, 0, sizeof(si));
ptrace_set_fake_getsiginfo(si);
dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
@@ -539,7 +549,9 @@
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
siginfo_t si;
+ memset(&si, 0, sizeof(si));
si.si_signo = i;
+ si.si_code = SI_KERNEL;
si.si_addr = reinterpret_cast<void*>(0x1000);
ptrace_set_fake_getsiginfo(si);
dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100);
@@ -592,7 +604,7 @@
TEST_F(TombstoneTest, dump_signal_info_error) {
siginfo_t si;
- si.si_signo = 0;
+ memset(&si, 0, sizeof(si));
ptrace_set_fake_getsiginfo(si);
dump_signal_info(&log_, 123);
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index c23da44..0c38449 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -283,7 +283,7 @@
if (BacktraceMap::IsValid(map) && !map.name.empty()) {
line += " " + map.name;
uintptr_t offset = 0;
- std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset));
+ std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset, &map));
if (!func_name.empty()) {
line += " (" + func_name;
if (offset) {
diff --git a/demangle/demangle.cpp b/demangle/demangle.cpp
index be4d2dd..66e5e58 100644
--- a/demangle/demangle.cpp
+++ b/demangle/demangle.cpp
@@ -20,22 +20,25 @@
#include <string.h>
#include <unistd.h>
+#include <cctype>
#include <string>
#include <demangle.h>
extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
-void usage(const char* prog_name) {
- printf("Usage: %s [-c] <NAME_TO_DEMANGLE>\n", prog_name);
- printf(" -c\n");
- printf(" Compare the results of __cxa_demangle against the current\n");
- printf(" demangler.\n");
+static void Usage(const char* prog_name) {
+ printf("usage: %s [-c] [NAME_TO_DEMANGLE...]\n", prog_name);
+ printf("\n");
+ printf("Demangles C++ mangled names if supplied on the command-line, or found\n");
+ printf("reading from stdin otherwise.\n");
+ printf("\n");
+ printf("-c\tCompare against __cxa_demangle\n");
+ printf("\n");
}
-std::string DemangleWithCxa(const char* name) {
+static std::string DemangleWithCxa(const char* name) {
const char* cxa_demangle = __cxa_demangle(name, nullptr, nullptr, nullptr);
-
if (cxa_demangle == nullptr) {
return name;
}
@@ -54,6 +57,49 @@
return demangled_str;
}
+static void Compare(const char* name, const std::string& demangled_name) {
+ std::string cxa_demangled_name(DemangleWithCxa(name));
+ if (cxa_demangled_name != demangled_name) {
+ printf("\nMismatch!\n");
+ printf("\tmangled name: %s\n", name);
+ printf("\tour demangle: %s\n", demangled_name.c_str());
+ printf("\tcxa demangle: %s\n", cxa_demangled_name.c_str());
+ exit(1);
+ }
+}
+
+static int Filter(bool compare) {
+ char* line = nullptr;
+ size_t line_length = 0;
+
+ while ((getline(&line, &line_length, stdin)) != -1) {
+ char* p = line;
+ char* name;
+ while ((name = strstr(p, "_Z")) != nullptr) {
+ // Output anything before the identifier.
+ *name = 0;
+ printf("%s", p);
+ *name = '_';
+
+ // Extract the identifier.
+ p = name;
+ while (*p && (std::isalnum(*p) || *p == '_' || *p == '.' || *p == '$')) ++p;
+
+ // Demangle and output.
+ std::string identifier(name, p);
+ std::string demangled_name = demangle(identifier.c_str());
+ printf("%s", demangled_name.c_str());
+
+ if (compare) Compare(identifier.c_str(), demangled_name);
+ }
+ // Output anything after the last identifier.
+ printf("%s", p);
+ }
+
+ free(line);
+ return 0;
+}
+
int main(int argc, char** argv) {
#ifdef __BIONIC__
const char* prog_name = getprogname();
@@ -67,31 +113,21 @@
if (opt_char == 'c') {
compare = true;
} else {
- usage(prog_name);
+ Usage(prog_name);
return 1;
}
}
- if (optind >= argc || argc - optind != 1) {
- printf("Must supply a single argument.\n\n");
- usage(prog_name);
- return 1;
- }
- const char* name = argv[optind];
- std::string demangled_name = demangle(name);
+ // With no arguments, act as a filter.
+ if (optind == argc) return Filter(compare);
- printf("%s\n", demangled_name.c_str());
+ // Otherwise demangle each argument.
+ while (optind < argc) {
+ const char* name = argv[optind++];
+ std::string demangled_name = demangle(name);
+ printf("%s\n", demangled_name.c_str());
- if (compare) {
- std::string cxa_demangle_str(DemangleWithCxa(name));
-
- if (cxa_demangle_str != demangled_name) {
- printf("Mismatch\n");
- printf("cxa demangle: %s\n", cxa_demangle_str.c_str());
- return 1;
- } else {
- printf("Match\n");
- }
+ if (compare) Compare(name, demangled_name);
}
return 0;
}
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 2cb7e34..a762038 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -454,12 +454,12 @@
// be returned when there is an error.
std::string hash_alg;
- if (fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg) == 0) {
- if (hash_alg == "sha256" || hash_alg == "sha512") {
- return true;
- }
+ if (!fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg)) {
+ return false;
}
-
+ if (hash_alg == "sha256" || hash_alg == "sha512") {
+ return true;
+ }
return false;
}
@@ -489,7 +489,13 @@
// fs_mgr only deals with HASHTREE partitions.
const char *requested_partitions[] = {nullptr};
std::string ab_suffix;
- fs_mgr_get_boot_config("slot_suffix", &ab_suffix);
+ std::string slot;
+ if (fs_mgr_get_boot_config("slot", &slot)) {
+ ab_suffix = "_" + slot;
+ } else {
+ // remove slot_suffix once bootloaders update to new androidboot.slot param
+ fs_mgr_get_boot_config("slot_suffix", &ab_suffix);
+ }
AvbSlotVerifyResult verify_result =
avb_slot_verify(fs_mgr_avb_ops, requested_partitions, ab_suffix.c_str(),
fs_mgr_vbmeta_prop.allow_verification_error, &fs_mgr_avb_verify_data);
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index dd8c7e7..2c14c9b 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -78,7 +78,7 @@
// access. fs_mgr_test_access() will test a few iterations if the
// path doesn't exist yet.
if (fs_mgr_test_access(path.c_str()) < 0) {
- return AVB_IO_RESULT_ERROR_IO;
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
}
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
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/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index a4f88ba..720ec03 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -646,7 +646,10 @@
std::string hw;
std::string default_fstab;
- if (fs_mgr_get_boot_config("hardware", &hw)) {
+ // Use different fstab paths for normal boot and recovery boot, respectively
+ if (access("/sbin/recovery", F_OK) == 0) {
+ default_fstab = "/etc/recovery.fstab";
+ } else if (fs_mgr_get_boot_config("hardware", &hw)) { // normal boot
for (const char *prefix : {"/odm/etc/fstab.","/vendor/etc/fstab.", "/fstab."}) {
default_fstab = prefix + hw;
if (access(default_fstab.c_str(), F_OK) == 0) break;
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index f3bba7b..7a45473 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -31,14 +31,16 @@
char *tmp;
if (!got_suffix) {
- if (!fs_mgr_get_boot_config("slot_suffix", &suffix)) {
- return -1;
+ std::string slot;
+ if (fs_mgr_get_boot_config("slot", &slot)) {
+ suffix = "_" + slot;
+ } else if (!fs_mgr_get_boot_config("slot_suffix", &suffix)) {
+ // remove slot_suffix once bootloaders update to new androidboot.slot param
+ return -1;
}
- got_suffix = 1;
}
- if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device,
- suffix.c_str()) > 0) {
+ if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device, suffix.c_str()) > 0) {
free(fstab->recs[n].blk_device);
fstab->recs[n].blk_device = tmp;
} else {
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
index d28ba41..523e1f1 100644
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -77,6 +77,10 @@
return healthd_get_property(id, val);
}
+void BatteryPropertiesRegistrar::scheduleUpdate() {
+ healthd_battery_update();
+}
+
status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& /*args*/) {
IPCThreadState* self = IPCThreadState::self();
const int pid = self->getCallingPid();
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
index 095f3d3..14e9145 100644
--- a/healthd/BatteryPropertiesRegistrar.h
+++ b/healthd/BatteryPropertiesRegistrar.h
@@ -32,6 +32,7 @@
public:
void publish(const sp<BatteryPropertiesRegistrar>& service);
void notifyListeners(const struct BatteryProperties& props);
+ void scheduleUpdate();
private:
Mutex mRegistrationLock;
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
index c896ab8..4f73a65 100644
--- a/include/backtrace/Backtrace.h
+++ b/include/backtrace/Backtrace.h
@@ -104,8 +104,10 @@
virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL) = 0;
// Get the function name and offset into the function given the pc.
- // If the string is empty, then no valid function name was found.
- virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset);
+ // If the string is empty, then no valid function name was found,
+ // or the pc is not in any valid map.
+ virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset,
+ const backtrace_map_t* map = NULL);
// Fill in the map data associated with the given pc.
virtual void FillInMap(uintptr_t pc, backtrace_map_t* map);
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h
index df48dfe..8ab0dfa 100644
--- a/include/backtrace/BacktraceMap.h
+++ b/include/backtrace/BacktraceMap.h
@@ -33,6 +33,10 @@
#include <string>
#include <vector>
+// Special flag to indicate a map is in /dev/. However, a map in
+// /dev/ashmem/... does not set this flag.
+static constexpr int PROT_DEVICE_MAP = 0x8000;
+
struct backtrace_map_t {
uintptr_t start = 0;
uintptr_t end = 0;
diff --git a/include/private/android_filesystem_capability.h b/include/private/android_filesystem_capability.h
new file mode 120000
index 0000000..f310b35
--- /dev/null
+++ b/include/private/android_filesystem_capability.h
@@ -0,0 +1 @@
+../../libcutils/include/private/android_filesystem_capability.h
\ No newline at end of file
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
deleted file mode 100644
index d200070..0000000
--- a/include/private/android_filesystem_config.h
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-/* This file is used to define the properties of the filesystem
-** images generated by build tools (mkbootfs and mkyaffs2image) and
-** by the device side of adb.
-*/
-
-/*
- * This file is consumed by build/tools/fs_config and is used
- * for generating various files. Anything #define AID_<name>
- * becomes the mapping for getpwnam/getpwuid, etc. The <name>
- * field is lowercased.
- * For example:
- * #define AID_FOO_BAR 6666 becomes a friendly name of "foo_bar"
- *
- * The above holds true with the exception of:
- * mediacodec
- * mediaex
- * mediadrm
- * Whose friendly names do not match the #define statements.
- *
- * Additionally, AID_OEM_RESERVED_START and AID_OEM_RESERVED_END
- * can be used to define reserved OEM ranges used for sanity checks
- * during the build process. The rules are, they must end with START/END
- * The proper convention is incrementing a number like so:
- * AID_OEM_RESERVED_START
- * AID_OEM_RESERVED_1_START
- * AID_OEM_RESERVED_2_START
- * ...
- * The same applies to the END.
- * They are not required to be in order, but must not overlap each other and
- * must define a START and END'ing range. START must be smaller than END.
- */
-
-#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
-#define _ANDROID_FILESYSTEM_CONFIG_H_
-
-#include <sys/cdefs.h>
-#include <sys/types.h>
-#include <stdint.h>
-
-#if defined(__ANDROID__)
-#include <linux/capability.h>
-#else
-#include "android_filesystem_capability.h"
-#endif
-
-#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
-
-/* This is the master Users and Groups config for the platform.
- * DO NOT EVER RENUMBER
- */
-
-#define AID_ROOT 0 /* traditional unix root user */
-
-#define AID_SYSTEM 1000 /* system server */
-
-#define AID_RADIO 1001 /* telephony subsystem, RIL */
-#define AID_BLUETOOTH 1002 /* bluetooth subsystem */
-#define AID_GRAPHICS 1003 /* graphics devices */
-#define AID_INPUT 1004 /* input devices */
-#define AID_AUDIO 1005 /* audio devices */
-#define AID_CAMERA 1006 /* camera devices */
-#define AID_LOG 1007 /* log devices */
-#define AID_COMPASS 1008 /* compass device */
-#define AID_MOUNT 1009 /* mountd socket */
-#define AID_WIFI 1010 /* wifi subsystem */
-#define AID_ADB 1011 /* android debug bridge (adbd) */
-#define AID_INSTALL 1012 /* group for installing packages */
-#define AID_MEDIA 1013 /* mediaserver process */
-#define AID_DHCP 1014 /* dhcp client */
-#define AID_SDCARD_RW 1015 /* external storage write access */
-#define AID_VPN 1016 /* vpn system */
-#define AID_KEYSTORE 1017 /* keystore subsystem */
-#define AID_USB 1018 /* USB devices */
-#define AID_DRM 1019 /* DRM server */
-#define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */
-#define AID_GPS 1021 /* GPS daemon */
-#define AID_UNUSED1 1022 /* deprecated, DO NOT USE */
-#define AID_MEDIA_RW 1023 /* internal media storage write access */
-#define AID_MTP 1024 /* MTP USB driver access */
-#define AID_UNUSED2 1025 /* deprecated, DO NOT USE */
-#define AID_DRMRPC 1026 /* group for drm rpc */
-#define AID_NFC 1027 /* nfc subsystem */
-#define AID_SDCARD_R 1028 /* external storage read access */
-#define AID_CLAT 1029 /* clat part of nat464 */
-#define AID_LOOP_RADIO 1030 /* loop radio devices */
-#define AID_MEDIA_DRM 1031 /* MediaDrm plugins */
-#define AID_PACKAGE_INFO 1032 /* access to installed package details */
-#define AID_SDCARD_PICS 1033 /* external storage photos access */
-#define AID_SDCARD_AV 1034 /* external storage audio/video access */
-#define AID_SDCARD_ALL 1035 /* access all users external storage */
-#define AID_LOGD 1036 /* log daemon */
-#define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */
-#define AID_DBUS 1038 /* dbus-daemon IPC broker process */
-#define AID_TLSDATE 1039 /* tlsdate unprivileged user */
-#define AID_MEDIA_EX 1040 /* mediaextractor process */
-#define AID_AUDIOSERVER 1041 /* audioserver process */
-#define AID_METRICS_COLL 1042 /* metrics_collector process */
-#define AID_METRICSD 1043 /* metricsd process */
-#define AID_WEBSERV 1044 /* webservd process */
-#define AID_DEBUGGERD 1045 /* debuggerd unprivileged user */
-#define AID_MEDIA_CODEC 1046 /* mediacodec process */
-#define AID_CAMERASERVER 1047 /* cameraserver process */
-#define AID_FIREWALL 1048 /* firewalld process */
-#define AID_TRUNKS 1049 /* trunksd process (TPM daemon) */
-#define AID_NVRAM 1050 /* Access-controlled NVRAM */
-#define AID_DNS 1051 /* DNS resolution daemon (system: netd) */
-#define AID_DNS_TETHER 1052 /* DNS resolution daemon (tether: dnsmasq) */
-#define AID_WEBVIEW_ZYGOTE 1053 /* WebView zygote process */
-#define AID_VEHICLE_NETWORK 1054 /* Vehicle network service */
-#define AID_MEDIA_AUDIO 1055 /* GID for audio files on internal media storage */
-#define AID_MEDIA_VIDEO 1056 /* GID for video files on internal media storage */
-#define AID_MEDIA_IMAGE 1057 /* GID for image files on internal media storage */
-#define AID_TOMBSTONED 1058 /* tombstoned user */
-#define AID_MEDIA_OBB 1059 /* GID for OBB files on internal media storage */
-#define AID_ESE 1060 /* embedded secure element (eSE) subsystem */
-#define AID_OTA_UPDATE 1061 /* resource tracking UID for OTA updates */
-/* Changes to this file must be made in AOSP, *not* in internal branches. */
-
-#define AID_SHELL 2000 /* adb and debug shell user */
-#define AID_CACHE 2001 /* cache access */
-#define AID_DIAG 2002 /* access to diagnostic resources */
-
-/* The range 2900-2999 is reserved for OEM, and must never be
- * used here */
-#define AID_OEM_RESERVED_START 2900
-#define AID_OEM_RESERVED_END 2999
-
-/* The 3000 series are intended for use as supplemental group id's only.
- * They indicate special Android capabilities that the kernel is aware of. */
-#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */
-#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */
-#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
-#define AID_NET_RAW 3004 /* can create raw INET sockets */
-#define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */
-#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
-#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
-#define AID_READPROC 3009 /* Allow /proc read access */
-#define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */
-
-/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
-#define AID_OEM_RESERVED_2_START 5000
-#define AID_OEM_RESERVED_2_END 5999
-
-#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
-#define AID_MISC 9998 /* access to misc storage */
-#define AID_NOBODY 9999
-
-#define AID_APP 10000 /* TODO: switch users over to AID_APP_START */
-#define AID_APP_START 10000 /* first app user */
-#define AID_APP_END 19999 /* last app user */
-
-#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
-#define AID_CACHE_GID_END 29999 /* end of gids for apps to mark cached data */
-
-#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
-#define AID_EXT_GID_END 39999 /* end of gids for apps to mark external data */
-
-#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
-#define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */
-
-#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
-#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */
-
-#define AID_USER 100000 /* TODO: switch users over to AID_USER_OFFSET */
-#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
-
-/*
- * android_ids has moved to pwd/grp functionality.
- * If you need to add one, the structure is now
- * auto-generated based on the AID_ constraints
- * documented at the top of this header file.
- * Also see build/tools/fs_config for more details.
- */
-
-#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
-
-struct fs_path_config {
- unsigned mode;
- unsigned uid;
- unsigned gid;
- uint64_t capabilities;
- const char *prefix;
-};
-
-/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
-
-__BEGIN_DECLS
-
-/*
- * Used in:
- * build/tools/fs_config/fs_config.c
- * build/tools/fs_get_stats/fs_get_stats.c
- * system/extras/ext4_utils/make_ext4fs_main.c
- * external/squashfs-tools/squashfs-tools/android.c
- * system/core/cpio/mkbootfs.c
- * system/core/adb/file_sync_service.cpp
- * system/extras/ext4_utils/canned_fs_config.c
- */
-void fs_config(const char *path, int dir, const char *target_out_path,
- unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities);
-
-ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc);
-
-__END_DECLS
-
-#endif
-#endif
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
new file mode 120000
index 0000000..f28a564
--- /dev/null
+++ b/include/private/android_filesystem_config.h
@@ -0,0 +1 @@
+../../libcutils/include/private/android_filesystem_config.h
\ No newline at end of file
diff --git a/include/private/canned_fs_config.h b/include/private/canned_fs_config.h
new file mode 120000
index 0000000..8f92b2d
--- /dev/null
+++ b/include/private/canned_fs_config.h
@@ -0,0 +1 @@
+../../libcutils/include/private/canned_fs_config.h
\ No newline at end of file
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 0b6ede4..41ca2e1 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -17,15 +17,16 @@
#ifndef LIBZIPARCHIVE_ZIPWRITER_H_
#define LIBZIPARCHIVE_ZIPWRITER_H_
-#include "android-base/macros.h"
-#include <utils/Compat.h>
-
#include <cstdio>
#include <ctime>
+#include <zlib.h>
+
#include <memory>
#include <string>
#include <vector>
-#include <zlib.h>
+
+#include "android-base/macros.h"
+#include "utils/Compat.h"
/**
* Writes a Zip file via a stateful interface.
@@ -63,6 +64,20 @@
kAlign32 = 0x02,
};
+ /**
+ * A struct representing a zip file entry.
+ */
+ struct FileEntry {
+ std::string path;
+ uint16_t compression_method;
+ uint32_t crc32;
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ uint32_t local_file_header_offset;
+ };
+
static const char* ErrorCodeString(int32_t error_code);
/**
@@ -122,6 +137,19 @@
int32_t FinishEntry();
/**
+ * Discards the last-written entry. Can only be called after an entry has been written using
+ * FinishEntry().
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t DiscardLastEntry();
+
+ /**
+ * Sets `out_entry` to the last entry written after a call to FinishEntry().
+ * Returns 0 on success, and an error value < 0 if no entries have been written.
+ */
+ int32_t GetLastEntry(FileEntry* out_entry);
+
+ /**
* Writes the Central Directory Headers and flushes the zip file stream.
* Returns 0 on success, and an error value < 0 on failure.
*/
@@ -130,22 +158,11 @@
private:
DISALLOW_COPY_AND_ASSIGN(ZipWriter);
- struct FileInfo {
- std::string path;
- uint16_t compression_method;
- uint32_t crc32;
- uint32_t compressed_size;
- uint32_t uncompressed_size;
- uint16_t last_mod_time;
- uint16_t last_mod_date;
- uint32_t local_file_header_offset;
- };
-
int32_t HandleError(int32_t error_code);
int32_t PrepareDeflate();
- int32_t StoreBytes(FileInfo* file, const void* data, size_t len);
- int32_t CompressBytes(FileInfo* file, const void* data, size_t len);
- int32_t FlushCompressedBytes(FileInfo* file);
+ int32_t StoreBytes(FileEntry* file, const void* data, size_t len);
+ int32_t CompressBytes(FileEntry* file, const void* data, size_t len);
+ int32_t FlushCompressedBytes(FileEntry* file);
enum class State {
kWritingZip,
@@ -157,7 +174,8 @@
FILE* file_;
off64_t current_offset_;
State state_;
- std::vector<FileInfo> files_;
+ std::vector<FileEntry> files_;
+ FileEntry current_file_entry_;
std::unique_ptr<z_stream, void(*)(z_stream*)> z_stream_;
std::vector<uint8_t> buffer_;
diff --git a/init/Android.mk b/init/Android.mk
index 2a1ad9c..1ca88d7 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -5,9 +5,23 @@
# --
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-init_options += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_PERMISSIVE_SELINUX=1
+init_options += \
+ -DALLOW_LOCAL_PROP_OVERRIDE=1 \
+ -DALLOW_PERMISSIVE_SELINUX=1 \
+ -DREBOOT_BOOTLOADER_ON_PANIC=1
else
-init_options += -DALLOW_LOCAL_PROP_OVERRIDE=0 -DALLOW_PERMISSIVE_SELINUX=0
+init_options += \
+ -DALLOW_LOCAL_PROP_OVERRIDE=0 \
+ -DALLOW_PERMISSIVE_SELINUX=0 \
+ -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
@@ -71,6 +85,7 @@
init.cpp \
keychords.cpp \
property_service.cpp \
+ reboot.cpp \
signal_handler.cpp \
ueventd.cpp \
ueventd_parser.cpp \
@@ -108,34 +123,6 @@
libnl \
libavb
-# Include SELinux policy. We do this here because different modules
-# need to be included based on the value of PRODUCT_FULL_TREBLE. This
-# type of conditional inclusion cannot be done in top-level files such
-# as build/target/product/embedded.mk.
-# This conditional inclusion closely mimics the conditional logic
-# inside init/init.cpp for loading SELinux policy from files.
-ifeq ($(PRODUCT_FULL_TREBLE),true)
-# Use split SELinux policy
-LOCAL_REQUIRED_MODULES += \
- mapping_sepolicy.cil \
- nonplat_sepolicy.cil \
- plat_sepolicy.cil \
- plat_sepolicy.cil.sha256 \
- secilc \
- nonplat_file_contexts \
- plat_file_contexts
-
-# Include precompiled policy, unless told otherwise
-ifneq ($(PRODUCT_PRECOMPILED_SEPOLICY),false)
-LOCAL_REQUIRED_MODULES += precompiled_sepolicy precompiled_sepolicy.plat.sha256
-endif
-
-else
-# Use monolithic SELinux policy
-LOCAL_REQUIRED_MODULES += sepolicy \
- file_contexts.bin
-endif
-
# Create symlinks.
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
diff --git a/init/README.md b/init/README.md
index d3dd73a..0d8f495 100644
--- a/init/README.md
+++ b/init/README.md
@@ -192,18 +192,21 @@
`oneshot`
> Do not restart the service when it exits.
-`class <name>`
-> Specify a class name for the service. All services in a
+`class <name> [ <name>\* ]`
+> Specify class names for the service. All services in a
named class may be started or stopped together. A service
is in the class "default" if one is not specified via the
- class option.
+ class option. Additional classnames beyond the (required) first
+ one are used to group services.
`onrestart`
> Execute a Command (see below) when service restarts.
-`writepid <file...>`
+`writepid <file> [ <file>\* ]`
> Write the child's pid to the given files when it forks. Meant for
- cgroup/cpuset usage.
+ cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the
+ system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.
+ '/foreground'), then the pid is written to file /dev/cpuset/_cpuset\_name_/tasks.
`priority <priority>`
> Scheduling priority of the service process. This value has to be in range
@@ -277,9 +280,17 @@
currently running, without disabling them. They can be restarted
later using `class_start`.
+`class_restart <serviceclass>`
+> Restarts all services of the specified class.
+
`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.
@@ -300,6 +311,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
@@ -351,7 +368,8 @@
"sys.powerctl" system property, used to implement rebooting.
`restart <service>`
-> Like stop, but doesn't disable the service.
+> Stops and restarts a running service, does nothing if the service is currently
+ restarting, otherwise, it just starts the service.
`restorecon <path> [ <path>\* ]`
> Restore the file named by _path_ to the security context specified
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 43eb378..02e314f 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"
@@ -64,6 +64,7 @@
#include "init_parser.h"
#include "log.h"
#include "property_service.h"
+#include "reboot.h"
#include "service.h"
#include "signal_handler.h"
#include "util.h"
@@ -71,7 +72,6 @@
using namespace std::literals::string_literals;
#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
-#define UNMOUNT_CHECK_TIMES 10
static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
@@ -116,114 +116,14 @@
return ret;
}
-// Turn off backlight while we are performing power down cleanup activities.
-static void turnOffBacklight() {
- static const char off[] = "0";
-
- android::base::WriteStringToFile(off, "/sys/class/leds/lcd-backlight/brightness");
-
- static const char backlightDir[] = "/sys/class/backlight";
- std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(backlightDir), closedir);
- if (!dir) {
- return;
- }
-
- struct dirent *dp;
- while ((dp = readdir(dir.get())) != NULL) {
- if (((dp->d_type != DT_DIR) && (dp->d_type != DT_LNK)) ||
- (dp->d_name[0] == '.')) {
- continue;
- }
-
- std::string fileName = android::base::StringPrintf("%s/%s/brightness",
- backlightDir,
- dp->d_name);
- android::base::WriteStringToFile(off, fileName);
- }
-}
-
static int reboot_into_recovery(const std::vector<std::string>& options) {
std::string err;
if (!write_bootloader_message(options, &err)) {
LOG(ERROR) << "failed to set bootloader message: " << err;
return -1;
}
- reboot("recovery");
-}
-
-static void unmount_and_fsck(const struct mntent *entry) {
- if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
- return;
-
- /* First, lazily unmount the directory. This unmount request finishes when
- * all processes that open a file or directory in |entry->mnt_dir| exit.
- */
- TEMP_FAILURE_RETRY(umount2(entry->mnt_dir, MNT_DETACH));
-
- /* Next, kill all processes except init, kthreadd, and kthreadd's
- * children to finish the lazy unmount. Killing all processes here is okay
- * because this callback function is only called right before reboot().
- * It might be cleaner to selectively kill processes that actually use
- * |entry->mnt_dir| rather than killing all, probably by reusing a function
- * like killProcessesWithOpenFiles() in vold/, but the selinux policy does
- * not allow init to scan /proc/<pid> files which the utility function
- * heavily relies on. The policy does not allow the process to execute
- * killall/pkill binaries either. Note that some processes might
- * automatically restart after kill(), but that is not really a problem
- * because |entry->mnt_dir| is no longer visible to such new processes.
- */
- ServiceManager::GetInstance().ForEachService([] (Service* s) { s->Stop(); });
- TEMP_FAILURE_RETRY(kill(-1, SIGKILL));
-
- // Restart Watchdogd to allow us to complete umounting and fsck
- Service *svc = ServiceManager::GetInstance().FindServiceByName("watchdogd");
- if (svc) {
- do {
- sched_yield(); // do not be so eager, let cleanup have priority
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
- } while (svc->flags() & SVC_RUNNING); // Paranoid Cargo
- svc->Start();
- }
-
- turnOffBacklight();
-
- int count = 0;
- while (count++ < UNMOUNT_CHECK_TIMES) {
- int fd = TEMP_FAILURE_RETRY(open(entry->mnt_fsname, O_RDONLY | O_EXCL));
- if (fd >= 0) {
- /* |entry->mnt_dir| has sucessfully been unmounted. */
- close(fd);
- break;
- } else if (errno == EBUSY) {
- // Some processes using |entry->mnt_dir| are still alive. Wait for a
- // while then retry.
- std::this_thread::sleep_for(5000ms / UNMOUNT_CHECK_TIMES);
- continue;
- } else {
- /* Cannot open the device. Give up. */
- return;
- }
- }
-
- // NB: With watchdog still running, there is no cap on the time it takes
- // to complete the fsck, from the users perspective the device graphics
- // and responses are locked-up and they may choose to hold the power
- // button in frustration if it drags out.
-
- int st;
- if (!strcmp(entry->mnt_type, "f2fs")) {
- const char *f2fs_argv[] = {
- "/system/bin/fsck.f2fs", "-f", entry->mnt_fsname,
- };
- android_fork_execvp_ext(arraysize(f2fs_argv), (char **)f2fs_argv,
- &st, true, LOG_KLOG, true, NULL, NULL, 0);
- } else if (!strcmp(entry->mnt_type, "ext4")) {
- const char *ext4_argv[] = {
- "/system/bin/e2fsck", "-f", "-y", entry->mnt_fsname,
- };
- android_fork_execvp_ext(arraysize(ext4_argv), (char **)ext4_argv,
- &st, true, LOG_KLOG, true, NULL, NULL, 0);
- }
+ DoReboot(ANDROID_RB_RESTART2, "reboot", "recovery", false);
+ return 0;
}
static int do_class_start(const std::vector<std::string>& args) {
@@ -248,6 +148,12 @@
return 0;
}
+static int do_class_restart(const std::vector<std::string>& args) {
+ ServiceManager::GetInstance().
+ ForEachServiceInClass(args[1], [] (Service* s) { s->Restart(); });
+ return 0;
+}
+
static int do_domainname(const std::vector<std::string>& args) {
return write_file("/proc/sys/kernel/domainname", args[1].c_str()) ? 0 : 1;
}
@@ -261,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) {
@@ -706,86 +604,51 @@
}
static int do_powerctl(const std::vector<std::string>& args) {
- const char* command = args[1].c_str();
- int len = 0;
+ const std::string& command = args[1];
unsigned int cmd = 0;
- const char *reboot_target = "";
- void (*callback_on_ro_remount)(const struct mntent*) = NULL;
+ std::vector<std::string> cmd_params = android::base::Split(command, ",");
+ std::string reason_string = cmd_params[0];
+ std::string reboot_target = "";
+ bool runFsck = false;
+ bool commandInvalid = false;
- if (strncmp(command, "shutdown", 8) == 0) {
+ if (cmd_params.size() > 2) {
+ commandInvalid = true;
+ } else if (cmd_params[0] == "shutdown") {
cmd = ANDROID_RB_POWEROFF;
- len = 8;
- } else if (strncmp(command, "reboot", 6) == 0) {
+ if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
+ // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
+ // Run fsck once the file system is remounted in read-only mode.
+ runFsck = true;
+ reason_string = cmd_params[1];
+ }
+ } else if (cmd_params[0] == "reboot") {
cmd = ANDROID_RB_RESTART2;
- len = 6;
- } else if (strncmp(command, "thermal-shutdown", 16) == 0) {
+ if (cmd_params.size() == 2) {
+ reboot_target = cmd_params[1];
+ // When rebooting to the bootloader notify the bootloader writing
+ // also the BCB.
+ if (reboot_target == "bootloader") {
+ std::string err;
+ if (!write_reboot_bootloader(&err)) {
+ LOG(ERROR) << "reboot-bootloader: Error writing "
+ "bootloader_message: "
+ << err;
+ }
+ }
+ }
+ } else if (command == "thermal-shutdown") { // no additional parameter allowed
cmd = ANDROID_RB_THERMOFF;
- len = 16;
} else {
+ commandInvalid = true;
+ }
+ if (commandInvalid) {
LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
return -EINVAL;
}
- if (command[len] == ',') {
- if (cmd == ANDROID_RB_POWEROFF &&
- !strcmp(&command[len + 1], "userrequested")) {
- // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
- // Run fsck once the file system is remounted in read-only mode.
- callback_on_ro_remount = unmount_and_fsck;
- } else if (cmd == ANDROID_RB_RESTART2) {
- reboot_target = &command[len + 1];
- // When rebooting to the bootloader notify the bootloader writing
- // also the BCB.
- if (strcmp(reboot_target, "bootloader") == 0) {
- std::string err;
- if (!write_reboot_bootloader(&err)) {
- LOG(ERROR) << "reboot-bootloader: Error writing "
- "bootloader_message: " << err;
- }
- }
- }
- } else if (command[len] != '\0') {
- LOG(ERROR) << "powerctl: unrecognized reboot target '" << &command[len] << "'";
- return -EINVAL;
- }
-
- std::string timeout = property_get("ro.build.shutdown_timeout");
- unsigned int delay = 0;
-
- if (android::base::ParseUint(timeout, &delay) && delay > 0) {
- Timer t;
- // Ask all services to terminate.
- ServiceManager::GetInstance().ForEachService(
- [] (Service* s) { s->Terminate(); });
-
- while (t.duration_s() < delay) {
- ServiceManager::GetInstance().ReapAnyOutstandingChildren();
-
- int service_count = 0;
- ServiceManager::GetInstance().ForEachService(
- [&service_count] (Service* s) {
- // Count the number of services running.
- // Exclude the console as it will ignore the SIGTERM signal
- // and not exit.
- // Note: SVC_CONSOLE actually means "requires console" but
- // it is only used by the shell.
- if (s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
- service_count++;
- }
- });
-
- if (service_count == 0) {
- // All terminable services terminated. We can exit early.
- break;
- }
-
- // Wait a bit before recounting the number or running services.
- std::this_thread::sleep_for(50ms);
- }
- LOG(VERBOSE) << "Terminating running services took " << t;
- }
-
- return android_reboot_with_callback(cmd, 0, reboot_target, callback_on_ro_remount);
+ DoReboot(cmd, reason_string, reboot_target, runFsck);
+ return 0;
}
static int do_trigger(const std::vector<std::string>& args) {
@@ -839,61 +702,11 @@
}
static int do_copy(const std::vector<std::string>& args) {
- 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;
+ std::string data;
+ if (read_file(args[1].c_str(), &data)) {
+ return write_file(args[2].c_str(), data.data()) ? 0 : 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;
+ return 1;
}
static int do_chown(const std::vector<std::string>& args) {
@@ -1059,8 +872,7 @@
}
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) {
@@ -1077,17 +889,20 @@
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}},
{"chown", {2, 3, do_chown}},
{"class_reset", {1, 1, do_class_reset}},
+ {"class_restart", {1, 1, do_class_restart}},
{"class_start", {1, 1, do_class_start}},
{"class_stop", {1, 1, do_class_stop}},
{"copy", {2, 2, do_copy}},
{"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}},
@@ -1121,5 +936,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 d095685..6f3b3a6 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,24 @@
}
}
+// 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("/plat_property_contexts");
+ restorecon("/nonplat_property_contexts");
+ 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.
@@ -1099,6 +1096,31 @@
return success;
}
+static void install_reboot_signal_handlers() {
+ // Instead of panic'ing the kernel as is the default behavior when init crashes,
+ // we prefer to reboot to bootloader on development builds, as this will prevent
+ // boot looping bad configurations and allow both developers and test farms to easily
+ // recover.
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ sigfillset(&action.sa_mask);
+ action.sa_handler = [](int) {
+ // panic() reboots to bootloader
+ panic();
+ };
+ action.sa_flags = SA_RESTART;
+ sigaction(SIGABRT, &action, nullptr);
+ sigaction(SIGBUS, &action, nullptr);
+ sigaction(SIGFPE, &action, nullptr);
+ sigaction(SIGILL, &action, nullptr);
+ sigaction(SIGSEGV, &action, nullptr);
+#if defined(SIGSTKFLT)
+ sigaction(SIGSTKFLT, &action, nullptr);
+#endif
+ sigaction(SIGSYS, &action, nullptr);
+ sigaction(SIGTRAP, &action, nullptr);
+}
+
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
@@ -1108,6 +1130,10 @@
return watchdogd_main(argc, argv);
}
+ if (REBOOT_BOOTLOADER_ON_PANIC) {
+ install_reboot_signal_handlers();
+ }
+
add_environment("PATH", _PATH_DEFPATH);
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
@@ -1205,22 +1231,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) {
@@ -1242,7 +1253,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(
@@ -1282,7 +1293,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 {
@@ -1293,22 +1304,24 @@
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
while (true) {
- if (!(waiting_for_exec || waiting_for_prop)) {
- am.ExecuteOneCommand();
- restart_processes();
- }
-
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
- // If there's a process that needs restarting, wake up in time for that.
- if (process_needs_restart_at != 0) {
- epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
- if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+ if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ am.ExecuteOneCommand();
}
+ if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ restart_processes();
- // If there's more work to do, wake up again immediately.
- if (am.HasMoreCommands()) epoll_timeout_ms = 0;
+ // If there's a process that needs restarting, wake up in time for that.
+ if (process_needs_restart_at != 0) {
+ epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
+ if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+ }
+
+ // If there's more work to do, wake up again immediately.
+ if (am.HasMoreCommands()) epoll_timeout_ms = 0;
+ }
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
diff --git a/init/init.h b/init/init.h
index 3768c02..fe850ef 100644
--- a/init/init.h
+++ b/init/init.h
@@ -19,9 +19,6 @@
#include <string>
-class Action;
-class Service;
-
extern const char *ENV[32];
extern std::string default_console;
extern struct selabel_handle *sehandle;
@@ -35,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
new file mode 100644
index 0000000..e34abdb
--- /dev/null
+++ b/init/reboot.cpp
@@ -0,0 +1,431 @@
+/*
+ * 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.
+ */
+#include <dirent.h>
+#include <fcntl.h>
+#include <mntent.h>
+#include <sys/cdefs.h>
+#include <sys/mount.h>
+#include <sys/quota.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.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 <fs_mgr.h>
+#include <logwrap/logwrap.h>
+
+#include "log.h"
+#include "reboot.h"
+#include "service.h"
+#include "util.h"
+
+using android::base::StringPrintf;
+
+// represents umount status during reboot / shutdown.
+enum UmountStat {
+ /* umount succeeded. */
+ UMOUNT_STAT_SUCCESS = 0,
+ /* umount was not run. */
+ UMOUNT_STAT_SKIPPED = 1,
+ /* umount failed with timeout. */
+ UMOUNT_STAT_TIMEOUT = 2,
+ /* could not run due to error */
+ UMOUNT_STAT_ERROR = 3,
+ /* not used by init but reserved for other part to use this to represent the
+ the state where umount status before reboot is not found / available. */
+ UMOUNT_STAT_NOT_AVAILABLE = 4,
+};
+
+// Utility for struct mntent
+class MountEntry {
+ public:
+ explicit MountEntry(const mntent& entry, bool isMounted = true)
+ : mnt_fsname_(entry.mnt_fsname),
+ mnt_dir_(entry.mnt_dir),
+ mnt_type_(entry.mnt_type),
+ is_mounted_(isMounted) {}
+
+ bool IsF2Fs() const { return mnt_type_ == "f2fs"; }
+
+ bool IsExt4() const { return mnt_type_ == "ext4"; }
+
+ bool is_mounted() const { return is_mounted_; }
+
+ void set_is_mounted() { is_mounted_ = false; }
+
+ const std::string& mnt_fsname() const { return mnt_fsname_; }
+
+ const std::string& mnt_dir() const { return mnt_dir_; }
+
+ static bool IsBlockDevice(const struct mntent& mntent) {
+ return android::base::StartsWith(mntent.mnt_fsname, "/dev/block");
+ }
+
+ static bool IsEmulatedDevice(const struct mntent& mntent) {
+ static const std::string SDCARDFS_NAME = "sdcardfs";
+ return android::base::StartsWith(mntent.mnt_fsname, "/data/") &&
+ SDCARDFS_NAME == mntent.mnt_type;
+ }
+
+ private:
+ std::string mnt_fsname_;
+ std::string mnt_dir_;
+ std::string mnt_type_;
+ bool is_mounted_;
+};
+
+// Turn off backlight while we are performing power down cleanup activities.
+static void TurnOffBacklight() {
+ static constexpr char OFF[] = "0";
+
+ android::base::WriteStringToFile(OFF, "/sys/class/leds/lcd-backlight/brightness");
+
+ static const char backlightDir[] = "/sys/class/backlight";
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(backlightDir), closedir);
+ if (!dir) {
+ return;
+ }
+
+ struct dirent* dp;
+ while ((dp = readdir(dir.get())) != nullptr) {
+ if (((dp->d_type != DT_DIR) && (dp->d_type != DT_LNK)) || (dp->d_name[0] == '.')) {
+ continue;
+ }
+
+ std::string fileName = StringPrintf("%s/%s/brightness", backlightDir, dp->d_name);
+ android::base::WriteStringToFile(OFF, fileName);
+ }
+}
+
+static void DoFsck(const MountEntry& entry) {
+ static constexpr int UNMOUNT_CHECK_TIMES = 10;
+
+ if (!entry.IsF2Fs() && !entry.IsExt4()) return;
+
+ int count = 0;
+ while (count++ < UNMOUNT_CHECK_TIMES) {
+ int fd = TEMP_FAILURE_RETRY(open(entry.mnt_fsname().c_str(), O_RDONLY | O_EXCL));
+ if (fd >= 0) {
+ /* |entry->mnt_dir| has sucessfully been unmounted. */
+ close(fd);
+ break;
+ } else if (errno == EBUSY) {
+ // Some processes using |entry->mnt_dir| are still alive. Wait for a
+ // while then retry.
+ std::this_thread::sleep_for(5000ms / UNMOUNT_CHECK_TIMES);
+ continue;
+ } else {
+ /* Cannot open the device. Give up. */
+ return;
+ }
+ }
+
+ // NB: With watchdog still running, there is no cap on the time it takes
+ // to complete the fsck, from the users perspective the device graphics
+ // and responses are locked-up and they may choose to hold the power
+ // button in frustration if it drags out.
+
+ int st;
+ if (entry.IsF2Fs()) {
+ const char* f2fs_argv[] = {
+ "/system/bin/fsck.f2fs", "-f", entry.mnt_fsname().c_str(),
+ };
+ android_fork_execvp_ext(arraysize(f2fs_argv), (char**)f2fs_argv, &st, true, LOG_KLOG, true,
+ nullptr, nullptr, 0);
+ } else if (entry.IsExt4()) {
+ const char* ext4_argv[] = {
+ "/system/bin/e2fsck", "-f", "-y", entry.mnt_fsname().c_str(),
+ };
+ android_fork_execvp_ext(arraysize(ext4_argv), (char**)ext4_argv, &st, true, LOG_KLOG, true,
+ nullptr, nullptr, 0);
+ }
+}
+
+static void ShutdownVold() {
+ const char* vdc_argv[] = {"/system/bin/vdc", "volume", "shutdown"};
+ int status;
+ android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true, LOG_KLOG, true,
+ nullptr, nullptr, 0);
+}
+
+static void LogShutdownTime(UmountStat stat, Timer* t) {
+ LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration_ms()) << ":" << stat;
+}
+
+static void __attribute__((noreturn))
+RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+ LOG(INFO) << "Reboot ending, jumping to kernel";
+ switch (cmd) {
+ case ANDROID_RB_POWEROFF:
+ reboot(RB_POWER_OFF);
+ break;
+
+ case ANDROID_RB_RESTART2:
+ syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+ LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
+ break;
+
+ case ANDROID_RB_THERMOFF:
+ reboot(RB_POWER_OFF);
+ break;
+ }
+ // In normal case, reboot should not return.
+ PLOG(FATAL) << "reboot call returned";
+ abort();
+}
+
+static void DoSync() {
+ // quota sync is not done by sync call, so should be done separately.
+ // quota sync is in VFS level, so do it before sync, which goes down to fs level.
+ int r = quotactl(QCMD(Q_SYNC, 0), nullptr, 0 /* do not care */, 0 /* do not care */);
+ if (r < 0) {
+ PLOG(ERROR) << "quotactl failed";
+ }
+ sync();
+}
+
+/* Find all read+write block devices and emulated devices in /proc/mounts
+ * and add them to correpsponding list.
+ */
+static bool FindPartitionsToUmount(std::vector<MountEntry>* blockDevPartitions,
+ std::vector<MountEntry>* emulatedPartitions) {
+ std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
+ if (fp == nullptr) {
+ PLOG(ERROR) << "Failed to open /proc/mounts";
+ return false;
+ }
+ mntent* mentry;
+ while ((mentry = getmntent(fp.get())) != nullptr) {
+ if (MountEntry::IsBlockDevice(*mentry) && hasmntopt(mentry, "rw")) {
+ blockDevPartitions->emplace_back(*mentry);
+ } else if (MountEntry::IsEmulatedDevice(*mentry)) {
+ emulatedPartitions->emplace_back(*mentry);
+ }
+ }
+ return true;
+}
+
+static bool UmountPartitions(std::vector<MountEntry>* partitions, int maxRetry, int flags) {
+ static constexpr int SLEEP_AFTER_RETRY_US = 100000;
+
+ bool umountDone;
+ int retryCounter = 0;
+
+ while (true) {
+ umountDone = true;
+ for (auto& entry : *partitions) {
+ if (entry.is_mounted()) {
+ int r = umount2(entry.mnt_dir().c_str(), flags);
+ if (r == 0) {
+ entry.set_is_mounted();
+ LOG(INFO) << StringPrintf("umounted %s, flags:0x%x", entry.mnt_fsname().c_str(),
+ flags);
+ } else {
+ umountDone = false;
+ PLOG(WARNING) << StringPrintf("cannot umount %s, flags:0x%x",
+ entry.mnt_fsname().c_str(), flags);
+ }
+ }
+ }
+ if (umountDone) break;
+ retryCounter++;
+ if (retryCounter >= maxRetry) break;
+ usleep(SLEEP_AFTER_RETRY_US);
+ }
+ return umountDone;
+}
+
+static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
+
+/* Try umounting all emulated file systems R/W block device cfile systems.
+ * This will just try umount and give it up if it fails.
+ * For fs like ext4, this is ok as file system will be marked as unclean shutdown
+ * and necessary check can be done at the next reboot.
+ * For safer shutdown, caller needs to make sure that
+ * all processes / emulated partition for the target fs are all cleaned-up.
+ *
+ * return true when umount was successful. false when timed out.
+ */
+static UmountStat TryUmountAndFsck(bool runFsck, int timeoutMs) {
+ Timer t;
+ std::vector<MountEntry> emulatedPartitions;
+ std::vector<MountEntry> blockDevRwPartitions;
+
+ TurnOffBacklight(); // this part can take time. save power.
+
+ if (!FindPartitionsToUmount(&blockDevRwPartitions, &emulatedPartitions)) {
+ return UMOUNT_STAT_ERROR;
+ }
+ if (emulatedPartitions.size() > 0) {
+ LOG(WARNING) << "emulated partitions still exist, will umount";
+ /* Pending writes in emulated partitions can fail umount. After a few trials, detach
+ * it so that it can be umounted when all writes are done.
+ */
+ if (!UmountPartitions(&emulatedPartitions, 1, 0)) {
+ UmountPartitions(&emulatedPartitions, 1, MNT_DETACH);
+ }
+ }
+ DoSync(); // emulated partition change can lead to update
+ UmountStat stat = UMOUNT_STAT_SUCCESS;
+ /* data partition needs all pending writes to be completed and all emulated partitions
+ * umounted. If umount failed in the above step, it DETACH is requested, so umount can
+ * still happen while waiting for /data. If the current waiting is not good enough, give
+ * up and leave it to e2fsck after reboot to fix it.
+ */
+ int remainingTimeMs = timeoutMs - t.duration_ms();
+ // each retry takes 100ms, and run at least once.
+ int retry = std::max(remainingTimeMs / 100, 1);
+ if (!UmountPartitions(&blockDevRwPartitions, retry, 0)) {
+ /* Last resort, kill all and try again */
+ LOG(WARNING) << "umount still failing, trying kill all";
+ KillAllProcesses();
+ DoSync();
+ if (!UmountPartitions(&blockDevRwPartitions, 1, 0)) {
+ stat = UMOUNT_STAT_TIMEOUT;
+ }
+ }
+ // fsck part is excluded from timeout check. It only runs for user initiated shutdown
+ // and should not affect reboot time.
+ if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
+ for (auto& entry : blockDevRwPartitions) {
+ DoFsck(entry);
+ }
+ }
+
+ return stat;
+}
+
+static void __attribute__((noreturn)) DoThermalOff() {
+ LOG(WARNING) << "Thermal system shutdown";
+ DoSync();
+ RebootSystem(ANDROID_RB_THERMOFF, "");
+ abort();
+}
+
+void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
+ bool runFsck) {
+ Timer t;
+ LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
+
+ android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE);
+
+ if (cmd == ANDROID_RB_THERMOFF) { // do not wait if it is thermal
+ DoThermalOff();
+ abort();
+ }
+
+ /* TODO update default waiting time based on usage data */
+ 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) {
+ Service* s = ServiceManager::GetInstance().FindServiceByName(name);
+ if (s == nullptr) {
+ LOG(WARNING) << "Shutdown critical service not found:" << name;
+ continue;
+ }
+ s->Start(); // make sure that it is running.
+ s->SetShutdownCritical();
+ }
+ // optional shutdown step
+ // 1. terminate all services except shutdown critical ones. wait for delay to finish
+ if (shutdownTimeout > 0) {
+ LOG(INFO) << "terminating init services";
+ // tombstoned can write to data when other services are killed. so finish it first.
+ static const constexpr char* first_to_kill[] = {"tombstoned"};
+ for (const char* name : first_to_kill) {
+ Service* s = ServiceManager::GetInstance().FindServiceByName(name);
+ if (s != nullptr) s->Stop();
+ }
+
+ // Ask all services to terminate except shutdown critical ones.
+ ServiceManager::GetInstance().ForEachService([](Service* s) {
+ if (!s->IsShutdownCritical()) s->Terminate();
+ });
+
+ int service_count = 0;
+ // Up to half as long as shutdownTimeout or 3 seconds, whichever is lower.
+ unsigned int terminationWaitTimeout = std::min<unsigned int>((shutdownTimeout + 1) / 2, 3);
+ while (t.duration_s() < terminationWaitTimeout) {
+ ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+
+ service_count = 0;
+ ServiceManager::GetInstance().ForEachService([&service_count](Service* s) {
+ // Count the number of services running except shutdown critical.
+ // Exclude the console as it will ignore the SIGTERM signal
+ // and not exit.
+ // Note: SVC_CONSOLE actually means "requires console" but
+ // it is only used by the shell.
+ if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
+ service_count++;
+ }
+ });
+
+ if (service_count == 0) {
+ // All terminable services terminated. We can exit early.
+ break;
+ }
+
+ // Wait a bit before recounting the number or running services.
+ std::this_thread::sleep_for(50ms);
+ }
+ LOG(INFO) << "Terminating running services took " << t
+ << " with remaining services:" << service_count;
+ }
+
+ // minimum safety steps before restarting
+ // 2. kill all services except ones that are necessary for the shutdown sequence.
+ ServiceManager::GetInstance().ForEachService([](Service* s) {
+ if (!s->IsShutdownCritical()) s->Stop();
+ });
+ ServiceManager::GetInstance().ReapAnyOutstandingChildren();
+
+ // 3. send volume shutdown to vold
+ Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
+ if (voldService != nullptr && voldService->IsRunning()) {
+ ShutdownVold();
+ } else {
+ LOG(INFO) << "vold not running, skipping vold shutdown";
+ }
+ // 4. sync, try umount, and optionally run fsck for user shutdown
+ DoSync();
+ UmountStat stat = TryUmountAndFsck(runFsck, shutdownTimeout * 1000 - t.duration_ms());
+ LogShutdownTime(stat, &t);
+ // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
+ RebootSystem(cmd, rebootTarget);
+ abort();
+}
diff --git a/init/reboot.h b/init/reboot.h
new file mode 100644
index 0000000..3956249
--- /dev/null
+++ b/init/reboot.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef _INIT_REBOOT_H
+#define _INIT_REBOOT_H
+
+/* Reboot / shutdown the system.
+ * cmd ANDROID_RB_* as defined in android_reboot.h
+ * reason Reason string like "reboot", "userrequested"
+ * rebootTarget Reboot target string like "bootloader". Otherwise, it should be an
+ * empty string.
+ * runFsck Whether to run fsck after umount is done.
+ */
+void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
+ bool runFsck) __attribute__((__noreturn__));
+
+#endif
diff --git a/init/service.cpp b/init/service.cpp
index ba901fd..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>
@@ -149,33 +150,50 @@
: name(name), value(value) {
}
-Service::Service(const std::string& name, const std::string& classname,
- const std::vector<std::string>& args)
- : name_(name), classname_(classname), flags_(0), pid_(0),
- crash_count_(0), uid_(0), gid_(0), namespace_flags_(0),
- seclabel_(""), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0),
- priority_(0), oom_score_adjust_(-1000), args_(args) {
+Service::Service(const std::string& name, const std::vector<std::string>& args)
+ : name_(name),
+ classnames_({"default"}),
+ flags_(0),
+ pid_(0),
+ crash_count_(0),
+ uid_(0),
+ gid_(0),
+ namespace_flags_(0),
+ seclabel_(""),
+ ioprio_class_(IoSchedClass_NONE),
+ ioprio_pri_(0),
+ priority_(0),
+ oom_score_adjust_(-1000),
+ args_(args) {
onrestart_.InitSingleTrigger("onrestart");
}
-Service::Service(const std::string& name, const std::string& classname,
- unsigned flags, uid_t uid, gid_t gid,
- const std::vector<gid_t>& supp_gids,
- const CapSet& capabilities, unsigned namespace_flags,
- const std::string& seclabel,
+Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
+ const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
+ unsigned namespace_flags, const std::string& seclabel,
const std::vector<std::string>& args)
- : name_(name), classname_(classname), flags_(flags), pid_(0),
- crash_count_(0), uid_(uid), gid_(gid),
- supp_gids_(supp_gids), capabilities_(capabilities),
- namespace_flags_(namespace_flags), seclabel_(seclabel),
- ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), priority_(0),
- oom_score_adjust_(-1000), args_(args) {
+ : name_(name),
+ classnames_({"default"}),
+ flags_(flags),
+ pid_(0),
+ crash_count_(0),
+ uid_(uid),
+ gid_(gid),
+ supp_gids_(supp_gids),
+ capabilities_(capabilities),
+ namespace_flags_(namespace_flags),
+ seclabel_(seclabel),
+ ioprio_class_(IoSchedClass_NONE),
+ ioprio_pri_(0),
+ priority_(0),
+ oom_score_adjust_(-1000),
+ args_(args) {
onrestart_.InitSingleTrigger("onrestart");
}
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;
}
@@ -193,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) {
@@ -242,7 +266,7 @@
}
}
-bool Service::Reap() {
+void Service::Reap() {
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
KillProcessGroup(SIGKILL);
}
@@ -253,7 +277,10 @@
if (flags_ & SVC_EXEC) {
LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished...";
- return true;
+ }
+
+ if (flags_ & SVC_TEMPORARY) {
+ return;
}
pid_ = 0;
@@ -268,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.
@@ -292,12 +319,12 @@
onrestart_.ExecuteAllCommands();
NotifyStateChange("restarting");
- return false;
+ return;
}
void Service::DumpState() const {
LOG(INFO) << "service " << name_;
- LOG(INFO) << " class '" << classname_ << "'";
+ LOG(INFO) << " class '" << android::base::Join(classnames_, " ") << "'";
LOG(INFO) << " exec "<< android::base::Join(args_, " ");
std::for_each(descriptors_.begin(), descriptors_.end(),
[] (const auto& info) { LOG(INFO) << *info; });
@@ -334,7 +361,7 @@
}
bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
- classname_ = args[1];
+ classnames_ = std::set<std::string>(args.begin() + 1, args.end());
return true;
}
@@ -516,10 +543,11 @@
Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+ // clang-format off
static const Map option_parsers = {
{"capabilities",
{1, kMax, &Service::ParseCapabilities}},
- {"class", {1, 1, &Service::ParseClass}},
+ {"class", {1, kMax, &Service::ParseClass}},
{"console", {0, 1, &Service::ParseConsole}},
{"critical", {0, 0, &Service::ParseCritical}},
{"disabled", {0, 0, &Service::ParseDisabled}},
@@ -539,6 +567,7 @@
{"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}},
};
+ // clang-format on
return option_parsers;
}
@@ -558,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.
@@ -630,6 +671,28 @@
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
+ // See if there were "writepid" instructions to write to files under /dev/cpuset/.
+ auto cpuset_predicate = [](const std::string& path) {
+ return android::base::StartsWith(path, "/dev/cpuset/");
+ };
+ auto iter = std::find_if(writepid_files_.begin(), writepid_files_.end(), cpuset_predicate);
+ 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 = 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.
+ if (default_cpuset.front() != '/') {
+ default_cpuset.insert(0, 1, '/');
+ }
+ if (default_cpuset.back() != '/') {
+ default_cpuset.push_back('/');
+ }
+ writepid_files_.push_back(
+ StringPrintf("/dev/cpuset%stasks", default_cpuset.c_str()));
+ }
+ }
std::string pid_str = StringPrintf("%d", getpid());
for (const auto& file : writepid_files_) {
if (!WriteStringToFile(pid_str, file)) {
@@ -822,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
@@ -845,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;
@@ -867,15 +959,10 @@
}
}
- std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid, supp_gids,
- no_capabilities, namespace_flags, seclabel,
- str_args));
- if (!svc_p) {
- LOG(ERROR) << "Couldn't allocate service for exec of '" << str_args[0] << "'";
- return nullptr;
- }
+ auto svc_p = std::make_unique<Service>(name, flags, uid, gid, supp_gids, no_capabilities,
+ namespace_flags, seclabel, str_args);
Service* svc = svc_p.get();
- services_.push_back(std::move(svc_p));
+ services_.emplace_back(std::move(svc_p));
return svc;
}
@@ -923,7 +1010,7 @@
void ServiceManager::ForEachServiceInClass(const std::string& classname,
void (*func)(Service* svc)) const {
for (const auto& s : services_) {
- if (classname == s->classname()) {
+ if (s->classnames().find(classname) != s->classnames().end()) {
func(s.get());
}
}
@@ -990,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);
}
@@ -1017,7 +1109,7 @@
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
- service_ = std::make_unique<Service>(name, "default", str_args);
+ service_ = std::make_unique<Service>(name, str_args);
return true;
}
diff --git a/init/service.h b/init/service.h
index 013e65f..f08a03f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -22,6 +22,7 @@
#include <cutils/iosched_policy.h>
#include <memory>
+#include <set>
#include <string>
#include <vector>
@@ -32,18 +33,24 @@
#include "keyword_map.h"
#include "util.h"
-#define SVC_DISABLED 0x001 // do not autostart with class
-#define SVC_ONESHOT 0x002 // do not restart on exit
-#define SVC_RUNNING 0x004 // currently active
-#define SVC_RESTARTING 0x008 // waiting to restart
-#define SVC_CONSOLE 0x010 // requires console
-#define SVC_CRITICAL 0x020 // will reboot into recovery if keeps crashing
-#define SVC_RESET 0x040 // Use when stopping a process,
+#define SVC_DISABLED 0x001 // do not autostart with class
+#define SVC_ONESHOT 0x002 // do not restart on exit
+#define SVC_RUNNING 0x004 // currently active
+#define SVC_RESTARTING 0x008 // waiting to restart
+#define SVC_CONSOLE 0x010 // requires console
+#define SVC_CRITICAL 0x020 // will reboot into recovery if keeps crashing
+#define SVC_RESET 0x040 // Use when stopping a process,
// but not disabling so it can be restarted with its class.
-#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_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
@@ -58,17 +65,17 @@
};
class Service {
-public:
- Service(const std::string& name, const std::string& classname,
- const std::vector<std::string>& args);
+ public:
+ Service(const std::string& name, const std::vector<std::string>& args);
- Service(const std::string& name, const std::string& classname,
- unsigned flags, uid_t uid, gid_t gid,
+ Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
unsigned namespace_flags, const std::string& seclabel,
const std::vector<std::string>& args);
+ 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();
@@ -77,11 +84,13 @@
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; }
const std::string& name() const { return name_; }
- const std::string& classname() const { return classname_; }
+ const std::set<std::string>& classnames() const { return classnames_; }
unsigned flags() const { return flags_; }
pid_t pid() const { return pid_; }
uid_t uid() const { return uid_; }
@@ -94,7 +103,7 @@
void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
const std::vector<std::string>& args() const { return args_; }
-private:
+ private:
using OptionParser = bool (Service::*) (const std::vector<std::string>& args,
std::string* err);
class OptionParserMap;
@@ -130,7 +139,7 @@
bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
std::string name_;
- std::string classname_;
+ std::set<std::string> classnames_;
std::string console_;
unsigned flags_;
@@ -173,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;
@@ -193,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 888a366..8a19939 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,7 @@
#include "init.h"
#include "log.h"
-#include "property_service.h"
+#include "reboot.h"
#include "util.h"
static unsigned int do_decode_uid(const char *s)
@@ -163,7 +164,7 @@
bool read_file(const char* path, std::string* content) {
content->clear();
- int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (fd == -1) {
return false;
}
@@ -176,17 +177,16 @@
return false;
}
if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
- PLOG(ERROR) << "skipping insecure file '" << path << "'";
+ LOG(ERROR) << "skipping insecure file '" << path << "'";
return false;
}
- bool okay = android::base::ReadFdToString(fd, content);
- close(fd);
- return okay;
+ return android::base::ReadFdToString(fd, content);
}
bool write_file(const char* path, const char* content) {
- int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+ open(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
if (fd == -1) {
PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
return false;
@@ -195,7 +195,6 @@
if (!success) {
PLOG(ERROR) << "write_file: Unable to write to '" << path << "'";
}
- close(fd);
return success;
}
@@ -396,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 << "'";
@@ -412,18 +411,9 @@
return true;
}
-void reboot(const char* destination) {
- android_reboot(ANDROID_RB_RESTART2, 0, destination);
- // We're init, so android_reboot will actually have been a syscall so there's nothing
- // to wait for. If android_reboot returns, just abort so that the kernel will reboot
- // itself when init dies.
- PLOG(FATAL) << "reboot failed";
- abort();
-}
-
void panic() {
LOG(ERROR) << "panic: rebooting to bootloader";
- reboot("bootloader");
+ DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
}
std::ostream& operator<<(std::ostream& os, const Timer& t) {
diff --git a/init/util.h b/init/util.h
index 5c38dc3..81c64d7 100644
--- a/init/util.h
+++ b/init/util.h
@@ -78,7 +78,6 @@
bool is_dir(const char* pathname);
bool expand_props(const std::string& src, std::string* dst);
-void reboot(const char* destination) __attribute__((__noreturn__));
void panic() __attribute__((__noreturn__));
#endif
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 24c75c4..4e82e76 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -17,9 +17,15 @@
#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;
@@ -28,6 +34,35 @@
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));
@@ -37,6 +72,42 @@
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/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 0e7c6f3..8f74a1a 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -114,7 +114,7 @@
none: true,
},
cflags: ["-O0"],
- srcs: ["backtrace_testlib.c"],
+ srcs: ["backtrace_testlib.cpp"],
target: {
linux: {
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 0d2e11b..3545661 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -52,7 +52,16 @@
}
}
-std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
+std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset, const backtrace_map_t* map) {
+ backtrace_map_t map_value;
+ if (map == nullptr) {
+ FillInMap(pc, &map_value);
+ map = &map_value;
+ }
+ // If no map is found, or this map is backed by a device, then return nothing.
+ if (map->start == 0 || (map->flags & PROT_DEVICE_MAP)) {
+ return "";
+ }
std::string func_name = GetFunctionNameRaw(pc, offset);
return func_name;
}
@@ -146,7 +155,7 @@
case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
return "Thread doesn't exist";
case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
- return "Thread has not repsonded to signal in time";
+ return "Thread has not responded to signal in time";
case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
return "Attempt to use an unsupported feature";
case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
diff --git a/libbacktrace/BacktraceCurrent.h b/libbacktrace/BacktraceCurrent.h
index 8aad36d..072ffd2 100644
--- a/libbacktrace/BacktraceCurrent.h
+++ b/libbacktrace/BacktraceCurrent.h
@@ -36,7 +36,7 @@
class BacktraceMap;
class BacktraceCurrent : public Backtrace {
-public:
+ public:
BacktraceCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
virtual ~BacktraceCurrent() {}
@@ -46,10 +46,10 @@
bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
-protected:
+ protected:
bool DiscardFrame(const backtrace_frame_data_t& frame);
-private:
+ private:
bool UnwindThread(size_t num_ignore_frames);
virtual bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) = 0;
diff --git a/libbacktrace/BacktracePtrace.h b/libbacktrace/BacktracePtrace.h
index 1d49811..760817b 100644
--- a/libbacktrace/BacktracePtrace.h
+++ b/libbacktrace/BacktracePtrace.h
@@ -25,7 +25,7 @@
class BacktraceMap;
class BacktracePtrace : public Backtrace {
-public:
+ public:
BacktracePtrace(pid_t pid, pid_t tid, BacktraceMap* map) : Backtrace(pid, tid, map) {}
virtual ~BacktracePtrace() {}
diff --git a/libbacktrace/ThreadEntry.h b/libbacktrace/ThreadEntry.h
index 11924a3..caa5497 100644
--- a/libbacktrace/ThreadEntry.h
+++ b/libbacktrace/ThreadEntry.h
@@ -22,7 +22,7 @@
#include <ucontext.h>
class ThreadEntry {
-public:
+ public:
static ThreadEntry* Get(pid_t pid, pid_t tid, bool create = true);
static void Remove(ThreadEntry* entry);
@@ -47,7 +47,7 @@
inline ucontext_t* GetUcontext() { return &ucontext_; }
-private:
+ private:
ThreadEntry(pid_t pid, pid_t tid);
~ThreadEntry();
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 666c481..3c509e6 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -23,12 +23,23 @@
#define UNW_LOCAL_ONLY
#include <libunwind.h>
+#include <android-base/logging.h>
#include <backtrace/Backtrace.h>
#include "BacktraceLog.h"
#include "UnwindCurrent.h"
std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ if (!initialized_) {
+ // If init local is not called, then trying to get a function name will
+ // fail, so try to initialize first.
+ std::unique_ptr<unw_cursor_t> cursor(new unw_cursor_t);
+ if (unw_init_local(cursor.get(), &context_) < 0) {
+ return "";
+ }
+ initialized_ = true;
+ }
+
*offset = 0;
char buf[512];
unw_word_t value;
@@ -85,6 +96,7 @@
error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
+ initialized_ = true;
size_t num_frames = 0;
do {
@@ -115,7 +127,7 @@
if (num_ignore_frames == 0) {
// GetFunctionName is an expensive call, only do it if we are
// keeping the frame.
- frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
+ frame->func_name = GetFunctionName(frame->pc, &frame->func_offset, &frame->map);
if (num_frames > 0) {
// Set the stack size for the previous frame.
backtrace_frame_data_t* prev = &frames_.at(num_frames-1);
@@ -124,8 +136,23 @@
num_frames++;
} else {
num_ignore_frames--;
+ // Set the number of frames to zero to remove the frame added
+ // above. By definition, if we still have frames to ignore
+ // there should only be one frame in the vector.
+ CHECK(num_frames == 0);
+ frames_.resize(0);
}
}
+ // If the pc is in a device map, then don't try to step.
+ if (frame->map.flags & PROT_DEVICE_MAP) {
+ break;
+ }
+ // Verify the sp is not in a device map too.
+ backtrace_map_t map;
+ FillInMap(frame->sp, &map);
+ if (map.flags & PROT_DEVICE_MAP) {
+ break;
+ }
ret = unw_step (cursor.get());
} while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
diff --git a/libbacktrace/UnwindCurrent.h b/libbacktrace/UnwindCurrent.h
index 3023996..3656104 100644
--- a/libbacktrace/UnwindCurrent.h
+++ b/libbacktrace/UnwindCurrent.h
@@ -32,18 +32,20 @@
#include <libunwind.h>
class UnwindCurrent : public BacktraceCurrent {
-public:
+ public:
UnwindCurrent(pid_t pid, pid_t tid, BacktraceMap* map) : BacktraceCurrent(pid, tid, map) {}
virtual ~UnwindCurrent() {}
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
-private:
+ private:
void GetUnwContextFromUcontext(const ucontext_t* ucontext);
bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
unw_context_t context_;
+
+ bool initialized_ = false;
};
#endif // _LIBBACKTRACE_UNWIND_CURRENT_H
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
index d5bec06..6ffdafd 100644
--- a/libbacktrace/UnwindMap.h
+++ b/libbacktrace/UnwindMap.h
@@ -28,28 +28,28 @@
// libunwind.h first then this header.
class UnwindMap : public BacktraceMap {
-public:
+ public:
explicit UnwindMap(pid_t pid);
unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
-protected:
+ protected:
unw_map_cursor_t map_cursor_;
};
class UnwindMapRemote : public UnwindMap {
-public:
+ public:
explicit UnwindMapRemote(pid_t pid);
virtual ~UnwindMapRemote();
bool Build() override;
-private:
+ private:
bool GenerateMap();
};
class UnwindMapLocal : public UnwindMap {
-public:
+ public:
UnwindMapLocal();
virtual ~UnwindMapLocal();
@@ -60,7 +60,7 @@
void LockIterator() override { pthread_rwlock_rdlock(&map_lock_); }
void UnlockIterator() override { pthread_rwlock_unlock(&map_lock_); }
-private:
+ private:
bool GenerateMap();
bool map_created_;
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 306d2ac..42ac1bc 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -37,6 +37,7 @@
_UPT_destroy(upt_info_);
upt_info_ = nullptr;
}
+
if (addr_space_) {
// Remove the map from the address space before destroying it.
// It will be freed in the UnwindMap destructor.
@@ -47,18 +48,14 @@
}
}
-bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
- if (GetMap() == nullptr) {
- // Without a map object, we can't do anything.
- error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
- return false;
+bool UnwindPtrace::Init() {
+ if (upt_info_) {
+ return true;
}
- error_ = BACKTRACE_UNWIND_NO_ERROR;
-
- if (ucontext) {
- BACK_LOGW("Unwinding from a specified context not supported yet.");
- error_ = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
+ if (addr_space_) {
+ // If somehow the addr_space_ gets initialized but upt_info_ doesn't,
+ // then that indicates there is some kind of failure.
return false;
}
@@ -79,6 +76,28 @@
return false;
}
+ return true;
+}
+
+bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
+ if (GetMap() == nullptr) {
+ // Without a map object, we can't do anything.
+ error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
+ return false;
+ }
+
+ error_ = BACKTRACE_UNWIND_NO_ERROR;
+
+ if (ucontext) {
+ BACK_LOGW("Unwinding from a specified context not supported yet.");
+ error_ = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
+ return false;
+ }
+
+ if (!Init()) {
+ return false;
+ }
+
unw_cursor_t cursor;
int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
if (ret < 0) {
@@ -115,14 +134,30 @@
prev->stack_size = frame->sp - prev->sp;
}
- frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
-
FillInMap(frame->pc, &frame->map);
+ frame->func_name = GetFunctionName(frame->pc, &frame->func_offset, &frame->map);
+
num_frames++;
+ // If the pc is in a device map, then don't try to step.
+ if (frame->map.flags & PROT_DEVICE_MAP) {
+ break;
+ }
} else {
+ // If the pc is in a device map, then don't try to step.
+ backtrace_map_t map;
+ FillInMap(pc, &map);
+ if (map.flags & PROT_DEVICE_MAP) {
+ break;
+ }
num_ignore_frames--;
}
+ // Verify the sp is not in a device map.
+ backtrace_map_t map;
+ FillInMap(sp, &map);
+ if (map.flags & PROT_DEVICE_MAP) {
+ break;
+ }
ret = unw_step (&cursor);
} while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
@@ -130,6 +165,10 @@
}
std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
+ if (!Init()) {
+ return "";
+ }
+
*offset = 0;
char buf[512];
unw_word_t value;
diff --git a/libbacktrace/UnwindPtrace.h b/libbacktrace/UnwindPtrace.h
index ab04abf..4688110 100644
--- a/libbacktrace/UnwindPtrace.h
+++ b/libbacktrace/UnwindPtrace.h
@@ -30,7 +30,7 @@
#include "BacktracePtrace.h"
class UnwindPtrace : public BacktracePtrace {
-public:
+ public:
UnwindPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
virtual ~UnwindPtrace();
@@ -38,7 +38,9 @@
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
-private:
+ private:
+ bool Init();
+
unw_addr_space_t addr_space_;
struct UPT_info* upt_info_;
};
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index e25c8e9..fb463b0 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -36,13 +36,16 @@
#include <algorithm>
#include <list>
#include <memory>
+#include <ostream>
#include <string>
#include <vector>
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
+#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <cutils/atomic.h>
#include <cutils/threads.h>
@@ -50,6 +53,7 @@
// For the THREAD_SIGNAL definition.
#include "BacktraceCurrent.h"
+#include "backtrace_testlib.h"
#include "thread_utils.h"
// Number of microseconds per milliseconds.
@@ -78,20 +82,13 @@
int32_t done;
};
-extern "C" {
-// Prototypes for functions in the test library.
-int test_level_one(int, int, int, int, void (*)(void*), void*);
-
-int test_recursive_call(int, void (*)(void*), void*);
-}
-
-uint64_t NanoTime() {
+static uint64_t NanoTime() {
struct timespec t = { 0, 0 };
clock_gettime(CLOCK_MONOTONIC, &t);
return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
}
-std::string DumpFrames(Backtrace* backtrace) {
+static std::string DumpFrames(Backtrace* backtrace) {
if (backtrace->NumFrames() == 0) {
return " No frames to dump.\n";
}
@@ -103,7 +100,7 @@
return frame;
}
-void WaitForStop(pid_t pid) {
+static void WaitForStop(pid_t pid) {
uint64_t start = NanoTime();
siginfo_t si;
@@ -116,7 +113,28 @@
}
}
-bool ReadyLevelBacktrace(Backtrace* backtrace) {
+static void CreateRemoteProcess(pid_t* pid) {
+ if ((*pid = fork()) == 0) {
+ while (true)
+ ;
+ _exit(0);
+ }
+ ASSERT_NE(-1, *pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, *pid, 0, 0) == 0);
+
+ // Wait for the process to get to a stopping point.
+ WaitForStop(*pid);
+}
+
+static void FinishRemoteProcess(pid_t pid) {
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+}
+
+static bool ReadyLevelBacktrace(Backtrace* backtrace) {
// See if test_level_four is in the backtrace.
bool found = false;
for (Backtrace::const_iterator it = backtrace->begin(); it != backtrace->end(); ++it) {
@@ -129,7 +147,7 @@
return found;
}
-void VerifyLevelDump(Backtrace* backtrace) {
+static void VerifyLevelDump(Backtrace* backtrace) {
ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0))
<< DumpFrames(backtrace);
ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
@@ -157,7 +175,7 @@
<< DumpFrames(backtrace);
}
-void VerifyLevelBacktrace(void*) {
+static void VerifyLevelBacktrace(void*) {
std::unique_ptr<Backtrace> backtrace(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
@@ -167,11 +185,11 @@
VerifyLevelDump(backtrace.get());
}
-bool ReadyMaxBacktrace(Backtrace* backtrace) {
+static bool ReadyMaxBacktrace(Backtrace* backtrace) {
return (backtrace->NumFrames() == MAX_BACKTRACE_FRAMES);
}
-void VerifyMaxDump(Backtrace* backtrace) {
+static void VerifyMaxDump(Backtrace* backtrace) {
ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
<< DumpFrames(backtrace);
// Verify that the last frame is our recursive call.
@@ -179,7 +197,7 @@
<< DumpFrames(backtrace);
}
-void VerifyMaxBacktrace(void*) {
+static void VerifyMaxBacktrace(void*) {
std::unique_ptr<Backtrace> backtrace(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
@@ -189,7 +207,7 @@
VerifyMaxDump(backtrace.get());
}
-void ThreadSetState(void* data) {
+static void ThreadSetState(void* data) {
thread_t* thread = reinterpret_cast<thread_t*>(data);
android_atomic_acquire_store(1, &thread->state);
volatile int i = 0;
@@ -198,16 +216,7 @@
}
}
-void VerifyThreadTest(pid_t tid, void (*VerifyFunc)(Backtrace*)) {
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), tid));
- ASSERT_TRUE(backtrace.get() != nullptr);
- ASSERT_TRUE(backtrace->Unwind(0));
- ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
-
- VerifyFunc(backtrace.get());
-}
-
-bool WaitForNonZero(int32_t* value, uint64_t seconds) {
+static bool WaitForNonZero(int32_t* value, uint64_t seconds) {
uint64_t start = NanoTime();
do {
if (android_atomic_acquire_load(value)) {
@@ -240,9 +249,8 @@
ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
}
-void VerifyIgnoreFrames(
- Backtrace* bt_all, Backtrace* bt_ign1,
- Backtrace* bt_ign2, const char* cur_proc) {
+static void VerifyIgnoreFrames(Backtrace* bt_all, Backtrace* bt_ign1, Backtrace* bt_ign2,
+ const char* cur_proc) {
EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1)
<< "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 1 backtrace:\n" << DumpFrames(bt_ign1);
EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2)
@@ -266,7 +274,7 @@
}
}
-void VerifyLevelIgnoreFrames(void*) {
+static void VerifyLevelIgnoreFrames(void*) {
std::unique_ptr<Backtrace> all(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(all.get() != nullptr);
@@ -296,9 +304,8 @@
ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, nullptr), 0);
}
-void VerifyProcTest(pid_t pid, pid_t tid, bool share_map,
- bool (*ReadyFunc)(Backtrace*),
- void (*VerifyFunc)(Backtrace*)) {
+static void VerifyProcTest(pid_t pid, pid_t tid, bool share_map, bool (*ReadyFunc)(Backtrace*),
+ void (*VerifyFunc)(Backtrace*)) {
pid_t ptrace_tid;
if (tid < 0) {
ptrace_tid = pid;
@@ -376,7 +383,7 @@
ASSERT_EQ(waitpid(pid, &status, 0), pid);
}
-void VerifyProcessIgnoreFrames(Backtrace* bt_all) {
+static void VerifyProcessIgnoreFrames(Backtrace* bt_all) {
std::unique_ptr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
@@ -404,12 +411,12 @@
}
// Create a process with multiple threads and dump all of the threads.
-void* PtraceThreadLevelRun(void*) {
+static void* PtraceThreadLevelRun(void*) {
EXPECT_NE(test_level_one(1, 2, 3, 4, nullptr, nullptr), 0);
return nullptr;
}
-void GetThreads(pid_t pid, std::vector<pid_t>* threads) {
+static void GetThreads(pid_t pid, std::vector<pid_t>* threads) {
// Get the list of tasks.
char task_path[128];
snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
@@ -461,11 +468,8 @@
}
VerifyProcTest(pid, *it, false, ReadyLevelBacktrace, VerifyLevelDump);
}
- ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
- kill(pid, SIGKILL);
- int status;
- ASSERT_EQ(waitpid(pid, &status, 0), pid);
+ FinishRemoteProcess(pid);
}
void VerifyLevelThread(void*) {
@@ -481,7 +485,7 @@
ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, nullptr), 0);
}
-void VerifyMaxThread(void*) {
+static void VerifyMaxThread(void*) {
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
@@ -494,7 +498,7 @@
ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, nullptr), 0);
}
-void* ThreadLevelRun(void* data) {
+static void* ThreadLevelRun(void* data) {
thread_t* thread = reinterpret_cast<thread_t*>(data);
thread->tid = gettid();
@@ -585,7 +589,7 @@
android_atomic_acquire_store(0, &thread_data.state);
}
-void* ThreadMaxRun(void* data) {
+static void* ThreadMaxRun(void* data) {
thread_t* thread = reinterpret_cast<thread_t*>(data);
thread->tid = gettid();
@@ -616,7 +620,7 @@
android_atomic_acquire_store(0, &thread_data.state);
}
-void* ThreadDump(void* data) {
+static void* ThreadDump(void* data) {
dump_thread_t* dump = reinterpret_cast<dump_thread_t*>(data);
while (true) {
if (android_atomic_acquire_load(dump->now)) {
@@ -873,11 +877,9 @@
uintptr_t end;
};
-bool map_sort(map_test_t i, map_test_t j) {
- return i.start < j.start;
-}
+static bool map_sort(map_test_t i, map_test_t j) { return i.start < j.start; }
-void VerifyMap(pid_t pid) {
+static void VerifyMap(pid_t pid) {
char buffer[4096];
snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
@@ -908,29 +910,15 @@
TEST(libbacktrace, verify_map_remote) {
pid_t pid;
-
- if ((pid = fork()) == 0) {
- while (true) {
- }
- _exit(0);
- }
- ASSERT_LT(0, pid);
-
- ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
-
- // Wait for the process to get to a stopping point.
- WaitForStop(pid);
+ CreateRemoteProcess(&pid);
// The maps should match exactly since the forked process has been paused.
VerifyMap(pid);
- ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
-
- kill(pid, SIGKILL);
- ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+ FinishRemoteProcess(pid);
}
-void InitMemory(uint8_t* memory, size_t bytes) {
+static void InitMemory(uint8_t* memory, size_t bytes) {
for (size_t i = 0; i < bytes; i++) {
memory[i] = i;
if (memory[i] == '\0') {
@@ -941,7 +929,7 @@
}
}
-void* ThreadReadTest(void* data) {
+static void* ThreadReadTest(void* data) {
thread_t* thread_data = reinterpret_cast<thread_t*>(data);
thread_data->tid = gettid();
@@ -982,7 +970,7 @@
return nullptr;
}
-void RunReadTest(Backtrace* backtrace, uintptr_t read_addr) {
+static void RunReadTest(Backtrace* backtrace, uintptr_t read_addr) {
size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
// Create a page of data to use to do quick compares.
@@ -1043,7 +1031,7 @@
volatile uintptr_t g_ready = 0;
volatile uintptr_t g_addr = 0;
-void ForkedReadTest() {
+static void ForkedReadTest() {
// Create two map pages.
size_t pagesize = static_cast<size_t>(sysconf(_SC_PAGE_SIZE));
uint8_t* memory;
@@ -1117,7 +1105,7 @@
ASSERT_TRUE(test_executed);
}
-void VerifyFunctionsFound(const std::vector<std::string>& found_functions) {
+static void VerifyFunctionsFound(const std::vector<std::string>& found_functions) {
// We expect to find these functions in libbacktrace_test. If we don't
// find them, that's a bug in the memory read handling code in libunwind.
std::list<std::string> expected_functions;
@@ -1137,7 +1125,7 @@
ASSERT_TRUE(expected_functions.empty()) << "Not all functions found in shared library.";
}
-const char* CopySharedLibrary() {
+static const char* CopySharedLibrary() {
#if defined(__LP64__)
const char* lib_name = "lib64";
#else
@@ -1293,7 +1281,7 @@
VerifyFunctionsFound(found_functions);
}
-bool FindFuncFrameInBacktrace(Backtrace* backtrace, uintptr_t test_func, size_t* frame_num) {
+static bool FindFuncFrameInBacktrace(Backtrace* backtrace, uintptr_t test_func, size_t* frame_num) {
backtrace_map_t map;
backtrace->FillInMap(test_func, &map);
if (!BacktraceMap::IsValid(map)) {
@@ -1312,7 +1300,7 @@
return false;
}
-void VerifyUnreadableElfFrame(Backtrace* backtrace, uintptr_t test_func, size_t frame_num) {
+static void VerifyUnreadableElfFrame(Backtrace* backtrace, uintptr_t test_func, size_t frame_num) {
ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
<< DumpFrames(backtrace);
@@ -1324,7 +1312,7 @@
ASSERT_LT(diff, 200U) << DumpFrames(backtrace);
}
-void VerifyUnreadableElfBacktrace(uintptr_t test_func) {
+static void VerifyUnreadableElfBacktrace(uintptr_t test_func) {
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
@@ -1418,12 +1406,347 @@
ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError());
}
+TEST(libbacktrace, local_get_function_name_before_unwind) {
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+
+ // Verify that trying to get a function name before doing an unwind works.
+ uintptr_t cur_func_offset = reinterpret_cast<uintptr_t>(&test_level_one) + 1;
+ size_t offset;
+ ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
+}
+
+TEST(libbacktrace, remote_get_function_name_before_unwind) {
+ pid_t pid;
+ CreateRemoteProcess(&pid);
+
+ // Now create an unwind object.
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+
+ // Verify that trying to get a function name before doing an unwind works.
+ uintptr_t cur_func_offset = reinterpret_cast<uintptr_t>(&test_level_one) + 1;
+ size_t offset;
+ ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset));
+
+ FinishRemoteProcess(pid);
+}
+
+static void SetUcontextSp(uintptr_t sp, ucontext_t* ucontext) {
+#if defined(__arm__)
+ ucontext->uc_mcontext.arm_sp = sp;
+#elif defined(__aarch64__)
+ ucontext->uc_mcontext.sp = sp;
+#elif defined(__i386__)
+ ucontext->uc_mcontext.gregs[REG_ESP] = sp;
+#elif defined(__x86_64__)
+ ucontext->uc_mcontext.gregs[REG_RSP] = sp;
+#else
+ UNUSED(sp);
+ UNUSED(ucontext);
+ ASSERT_TRUE(false) << "Unsupported architecture";
+#endif
+}
+
+static void SetUcontextPc(uintptr_t pc, ucontext_t* ucontext) {
+#if defined(__arm__)
+ ucontext->uc_mcontext.arm_pc = pc;
+#elif defined(__aarch64__)
+ ucontext->uc_mcontext.pc = pc;
+#elif defined(__i386__)
+ ucontext->uc_mcontext.gregs[REG_EIP] = pc;
+#elif defined(__x86_64__)
+ ucontext->uc_mcontext.gregs[REG_RIP] = pc;
+#else
+ UNUSED(pc);
+ UNUSED(ucontext);
+ ASSERT_TRUE(false) << "Unsupported architecture";
+#endif
+}
+
+static void SetUcontextLr(uintptr_t lr, ucontext_t* ucontext) {
+#if defined(__arm__)
+ ucontext->uc_mcontext.arm_lr = lr;
+#elif defined(__aarch64__)
+ ucontext->uc_mcontext.regs[30] = lr;
+#elif defined(__i386__)
+ // The lr is on the stack.
+ ASSERT_TRUE(lr != 0);
+ ASSERT_TRUE(ucontext != nullptr);
+#elif defined(__x86_64__)
+ // The lr is on the stack.
+ ASSERT_TRUE(lr != 0);
+ ASSERT_TRUE(ucontext != nullptr);
+#else
+ UNUSED(lr);
+ UNUSED(ucontext);
+ ASSERT_TRUE(false) << "Unsupported architecture";
+#endif
+}
+
+static constexpr size_t DEVICE_MAP_SIZE = 1024;
+
+static void SetupDeviceMap(void** device_map) {
+ // Make sure that anything in a device map will result in fails
+ // to read.
+ android::base::unique_fd device_fd(open("/dev/zero", O_RDONLY | O_CLOEXEC));
+
+ *device_map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE, device_fd, 0);
+ ASSERT_TRUE(*device_map != MAP_FAILED);
+
+ // Make sure the map is readable.
+ ASSERT_EQ(0, reinterpret_cast<int*>(*device_map)[0]);
+}
+
+static void UnwindFromDevice(Backtrace* backtrace, void* device_map) {
+ uintptr_t device_map_uint = reinterpret_cast<uintptr_t>(device_map);
+
+ backtrace_map_t map;
+ backtrace->FillInMap(device_map_uint, &map);
+ // Verify the flag is set.
+ ASSERT_EQ(PROT_DEVICE_MAP, map.flags & PROT_DEVICE_MAP);
+
+ // Quick sanity checks.
+ size_t offset;
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset));
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(0, &offset));
+
+ uintptr_t cur_func_offset = reinterpret_cast<uintptr_t>(&test_level_one) + 1;
+ // Now verify the device map flag actually causes the function name to be empty.
+ backtrace->FillInMap(cur_func_offset, &map);
+ ASSERT_TRUE((map.flags & PROT_DEVICE_MAP) == 0);
+ ASSERT_NE(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset, &map));
+ map.flags |= PROT_DEVICE_MAP;
+ ASSERT_EQ(std::string(""), backtrace->GetFunctionName(cur_func_offset, &offset, &map));
+
+ ucontext_t ucontext;
+
+ // Create a context that has the pc in the device map, but the sp
+ // in a non-device map.
+ memset(&ucontext, 0, sizeof(ucontext));
+ SetUcontextSp(reinterpret_cast<uintptr_t>(&ucontext), &ucontext);
+ SetUcontextPc(device_map_uint, &ucontext);
+ SetUcontextLr(cur_func_offset, &ucontext);
+
+ ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+
+ // The buffer should only be a single element.
+ ASSERT_EQ(1U, backtrace->NumFrames());
+ const backtrace_frame_data_t* frame = backtrace->GetFrame(0);
+ ASSERT_EQ(device_map_uint, frame->pc);
+ ASSERT_EQ(reinterpret_cast<uintptr_t>(&ucontext), frame->sp);
+
+ // Check what happens when skipping the first frame.
+ ASSERT_TRUE(backtrace->Unwind(1, &ucontext));
+ ASSERT_EQ(0U, backtrace->NumFrames());
+
+ // Create a context that has the sp in the device map, but the pc
+ // in a non-device map.
+ memset(&ucontext, 0, sizeof(ucontext));
+ SetUcontextSp(device_map_uint, &ucontext);
+ SetUcontextPc(cur_func_offset, &ucontext);
+ SetUcontextLr(cur_func_offset, &ucontext);
+
+ ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+
+ // The buffer should only be a single element.
+ ASSERT_EQ(1U, backtrace->NumFrames());
+ frame = backtrace->GetFrame(0);
+ ASSERT_EQ(cur_func_offset, frame->pc);
+ ASSERT_EQ(device_map_uint, frame->sp);
+
+ // Check what happens when skipping the first frame.
+ ASSERT_TRUE(backtrace->Unwind(1, &ucontext));
+ ASSERT_EQ(0U, backtrace->NumFrames());
+}
+
+TEST(libbacktrace, unwind_disallow_device_map_local) {
+ void* device_map;
+ SetupDeviceMap(&device_map);
+
+ // Now create an unwind object.
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
+ ASSERT_TRUE(backtrace);
+
+ UnwindFromDevice(backtrace.get(), device_map);
+
+ munmap(device_map, DEVICE_MAP_SIZE);
+}
+
+TEST(libbacktrace, unwind_disallow_device_map_remote) {
+ void* device_map;
+ SetupDeviceMap(&device_map);
+
+ // Fork a process to do a remote backtrace.
+ pid_t pid;
+ CreateRemoteProcess(&pid);
+
+ // Now create an unwind object.
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+
+ // TODO: Currently unwind from context doesn't work on remote
+ // unwind. Keep this test because the new unwinder should support
+ // this eventually, or we can delete this test.
+ // properly with unwind from context.
+ // UnwindFromDevice(backtrace.get(), device_map);
+
+ FinishRemoteProcess(pid);
+
+ munmap(device_map, DEVICE_MAP_SIZE);
+}
+
+class ScopedSignalHandler {
+ public:
+ ScopedSignalHandler(int signal_number, void (*handler)(int)) : signal_number_(signal_number) {
+ memset(&action_, 0, sizeof(action_));
+ action_.sa_handler = handler;
+ sigaction(signal_number_, &action_, &old_action_);
+ }
+
+ ScopedSignalHandler(int signal_number, void (*action)(int, siginfo_t*, void*))
+ : signal_number_(signal_number) {
+ memset(&action_, 0, sizeof(action_));
+ action_.sa_flags = SA_SIGINFO;
+ action_.sa_sigaction = action;
+ sigaction(signal_number_, &action_, &old_action_);
+ }
+
+ ~ScopedSignalHandler() { sigaction(signal_number_, &old_action_, nullptr); }
+
+ private:
+ struct sigaction action_;
+ struct sigaction old_action_;
+ const int signal_number_;
+};
+
+static void SetValueAndLoop(void* data) {
+ volatile int* value = reinterpret_cast<volatile int*>(data);
+
+ *value = 1;
+ for (volatile int i = 0;; i++)
+ ;
+}
+
+static void UnwindThroughSignal(bool use_action) {
+ volatile int value = 0;
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ if (use_action) {
+ ScopedSignalHandler ssh(SIGUSR1, test_signal_action);
+
+ test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+ } else {
+ ScopedSignalHandler ssh(SIGUSR1, test_signal_handler);
+
+ test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+ }
+ }
+ ASSERT_NE(-1, pid);
+
+ int read_value = 0;
+ uint64_t start = NanoTime();
+ while (read_value == 0) {
+ usleep(1000);
+
+ // Loop until the remote function gets into the final function.
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ WaitForStop(pid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+
+ size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(const_cast<int*>(&value)),
+ reinterpret_cast<uint8_t*>(&read_value), sizeof(read_value));
+ ASSERT_EQ(sizeof(read_value), bytes_read);
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ ASSERT_TRUE(NanoTime() - start < 5 * NS_PER_SEC)
+ << "Remote process did not execute far enough in 5 seconds.";
+ }
+
+ // Now need to send a signal to the remote process.
+ kill(pid, SIGUSR1);
+
+ // Wait for the process to get to the signal handler loop.
+ Backtrace::const_iterator frame_iter;
+ start = NanoTime();
+ std::unique_ptr<Backtrace> backtrace;
+ while (true) {
+ usleep(1000);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ WaitForStop(pid);
+
+ backtrace.reset(Backtrace::Create(pid, pid));
+ ASSERT_TRUE(backtrace->Unwind(0));
+ bool found = false;
+ for (frame_iter = backtrace->begin(); frame_iter != backtrace->end(); ++frame_iter) {
+ if (frame_iter->func_name == "test_loop_forever") {
+ ++frame_iter;
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ break;
+ }
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ ASSERT_TRUE(NanoTime() - start < 5 * NS_PER_SEC)
+ << "Remote process did not get in signal handler in 5 seconds." << std::endl
+ << DumpFrames(backtrace.get());
+ }
+
+ std::vector<std::string> names;
+ // Loop through the frames, and save the function names.
+ size_t frame = 0;
+ for (; frame_iter != backtrace->end(); ++frame_iter) {
+ if (frame_iter->func_name == "test_level_four") {
+ frame = names.size() + 1;
+ }
+ names.push_back(frame_iter->func_name);
+ }
+ ASSERT_NE(0U, frame) << "Unable to find test_level_four in backtrace" << std::endl
+ << DumpFrames(backtrace.get());
+
+ // The expected order of the frames:
+ // test_loop_forever
+ // test_signal_handler|test_signal_action
+ // <OPTIONAL_FRAME> May or may not exist.
+ // SetValueAndLoop (but the function name might be empty)
+ // test_level_four
+ // test_level_three
+ // test_level_two
+ // test_level_one
+ ASSERT_LE(frame + 2, names.size()) << DumpFrames(backtrace.get());
+ ASSERT_LE(2U, frame) << DumpFrames(backtrace.get());
+ if (use_action) {
+ ASSERT_EQ("test_signal_action", names[0]) << DumpFrames(backtrace.get());
+ } else {
+ ASSERT_EQ("test_signal_handler", names[0]) << DumpFrames(backtrace.get());
+ }
+ ASSERT_EQ("test_level_three", names[frame]) << DumpFrames(backtrace.get());
+ ASSERT_EQ("test_level_two", names[frame + 1]) << DumpFrames(backtrace.get());
+ ASSERT_EQ("test_level_one", names[frame + 2]) << DumpFrames(backtrace.get());
+
+ FinishRemoteProcess(pid);
+}
+
+TEST(libbacktrace, unwind_remote_through_signal_using_handler) { UnwindThroughSignal(false); }
+
+TEST(libbacktrace, unwind_remote_through_signal_using_action) { UnwindThroughSignal(true); }
+
#if defined(ENABLE_PSS_TESTS)
#include "GetPss.h"
#define MAX_LEAK_BYTES (32*1024UL)
-void CheckForLeak(pid_t pid, pid_t tid) {
+static void CheckForLeak(pid_t pid, pid_t tid) {
// Do a few runs to get the PSS stable.
for (size_t i = 0; i < 100; i++) {
Backtrace* backtrace = Backtrace::Create(pid, tid);
@@ -1472,24 +1795,10 @@
TEST(libbacktrace, check_for_leak_remote) {
pid_t pid;
-
- if ((pid = fork()) == 0) {
- while (true) {
- }
- _exit(0);
- }
- ASSERT_LT(0, pid);
-
- ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
-
- // Wait for the process to get to a stopping point.
- WaitForStop(pid);
+ CreateRemoteProcess(&pid);
CheckForLeak(pid, BACKTRACE_CURRENT_THREAD);
- ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
-
- kill(pid, SIGKILL);
- ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
+ FinishRemoteProcess(pid);
}
#endif
diff --git a/libbacktrace/backtrace_testlib.c b/libbacktrace/backtrace_testlib.cpp
similarity index 61%
rename from libbacktrace/backtrace_testlib.c
rename to libbacktrace/backtrace_testlib.cpp
index 6f6b535..3081d64 100644
--- a/libbacktrace/backtrace_testlib.c
+++ b/libbacktrace/backtrace_testlib.cpp
@@ -15,32 +15,42 @@
*/
#include <libunwind.h>
+#include <signal.h>
#include <stdio.h>
-int test_level_four(int one, int two, int three, int four,
- void (*callback_func)(void*), void* data) {
+#include "backtrace_testlib.h"
+
+void test_loop_forever() {
+ while (1)
+ ;
+}
+
+void test_signal_handler(int) { test_loop_forever(); }
+
+void test_signal_action(int, siginfo_t*, void*) { test_loop_forever(); }
+
+int test_level_four(int one, int two, int three, int four, void (*callback_func)(void*),
+ void* data) {
if (callback_func != NULL) {
callback_func(data);
} else {
- while (1) {
- }
+ while (1)
+ ;
}
return one + two + three + four;
}
-int test_level_three(int one, int two, int three, int four,
- void (*callback_func)(void*), void* data) {
- return test_level_four(one+3, two+6, three+9, four+12, callback_func, data) + 3;
+int test_level_three(int one, int two, int three, int four, void (*callback_func)(void*),
+ void* data) {
+ return test_level_four(one + 3, two + 6, three + 9, four + 12, callback_func, data) + 3;
}
-int test_level_two(int one, int two, int three, int four,
- void (*callback_func)(void*), void* data) {
- return test_level_three(one+2, two+4, three+6, four+8, callback_func, data) + 2;
+int test_level_two(int one, int two, int three, int four, void (*callback_func)(void*), void* data) {
+ return test_level_three(one + 2, two + 4, three + 6, four + 8, callback_func, data) + 2;
}
-int test_level_one(int one, int two, int three, int four,
- void (*callback_func)(void*), void* data) {
- return test_level_two(one+1, two+2, three+3, four+4, callback_func, data) + 1;
+int test_level_one(int one, int two, int three, int four, void (*callback_func)(void*), void* data) {
+ return test_level_two(one + 1, two + 2, three + 3, four + 4, callback_func, data) + 1;
}
int test_recursive_call(int level, void (*callback_func)(void*), void* data) {
diff --git a/libbacktrace/backtrace_testlib.h b/libbacktrace/backtrace_testlib.h
new file mode 100644
index 0000000..16fedc4
--- /dev/null
+++ b/libbacktrace/backtrace_testlib.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBBACKTRACE_BACKTRACE_TESTLIB_H
+#define _LIBBACKTRACE_BACKTRACE_TESTLIB_H
+
+#include <sys/cdefs.h>
+
+#include <libunwind.h>
+
+__BEGIN_DECLS
+
+void test_loop_forever();
+void test_signal_handler(int);
+void test_signal_action(int, siginfo_t*, void*);
+int test_level_four(int, int, int, int, void (*)(void*), void*);
+int test_level_three(int, int, int, int, void (*)(void*), void*);
+int test_level_two(int, int, int, int, void (*)(void*), void*);
+int test_level_one(int, int, int, int, void (*)(void*), void*);
+int test_recursive_call(int, void (*)(void*), void*);
+void test_get_context_and_wait(unw_context_t*, volatile int*);
+
+__END_DECLS
+
+#endif // _LIBBACKTRACE_BACKTRACE_TESTLIB_H
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
index 06026d1..a33e45f 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.c
@@ -13,259 +13,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-#include <errno.h>
-#include <fcntl.h>
-#include <mntent.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <sys/cdefs.h>
-#include <sys/mount.h>
-#include <sys/reboot.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <unistd.h>
#include <cutils/android_reboot.h>
-#include <cutils/klog.h>
-#include <cutils/list.h>
+#include <cutils/properties.h>
#define TAG "android_reboot"
-#define READONLY_CHECK_MS 5000
-#define READONLY_CHECK_TIMES 50
-typedef struct {
- struct listnode list;
- struct mntent entry;
-} mntent_list;
-
-static bool is_block_device(const char* fsname)
-{
- return !strncmp(fsname, "/dev/block", 10);
-}
-
-/* Find all read+write block devices in /proc/mounts and add them to
- * |rw_entries|.
- */
-static void find_rw(struct listnode* rw_entries)
-{
- FILE* fp;
- struct mntent* mentry;
-
- if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
- KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
- return;
- }
- while ((mentry = getmntent(fp)) != NULL) {
- if (is_block_device(mentry->mnt_fsname) && hasmntopt(mentry, "rw")) {
- mntent_list* item = (mntent_list*)calloc(1, sizeof(mntent_list));
- item->entry = *mentry;
- item->entry.mnt_fsname = strdup(mentry->mnt_fsname);
- item->entry.mnt_dir = strdup(mentry->mnt_dir);
- item->entry.mnt_type = strdup(mentry->mnt_type);
- item->entry.mnt_opts = strdup(mentry->mnt_opts);
- list_add_tail(rw_entries, &item->list);
- }
- }
- endmntent(fp);
-}
-
-static void free_entries(struct listnode* entries)
-{
- struct listnode* node;
- struct listnode* n;
- list_for_each_safe(node, n, entries) {
- mntent_list* item = node_to_item(node, mntent_list, list);
- free(item->entry.mnt_fsname);
- free(item->entry.mnt_dir);
- free(item->entry.mnt_type);
- free(item->entry.mnt_opts);
- free(item);
- }
-}
-
-static mntent_list* find_item(struct listnode* rw_entries, const char* fsname_to_find)
-{
- struct listnode* node;
- list_for_each(node, rw_entries) {
- mntent_list* item = node_to_item(node, mntent_list, list);
- if (!strcmp(item->entry.mnt_fsname, fsname_to_find)) {
- return item;
- }
- }
- return NULL;
-}
-
-/* Remounting filesystems read-only is difficult when there are files
- * opened for writing or pending deletes on the filesystem. There is
- * no way to force the remount with the mount(2) syscall. The magic sysrq
- * 'u' command does an emergency remount read-only on all writable filesystems
- * that have a block device (i.e. not tmpfs filesystems) by calling
- * emergency_remount(), which knows how to force the remount to read-only.
- * Unfortunately, that is asynchronous, and just schedules the work and
- * returns. The best way to determine if it is done is to read /proc/mounts
- * repeatedly until there are no more writable filesystems mounted on
- * block devices.
- */
-static void remount_ro(void (*cb_on_remount)(const struct mntent*))
-{
- int fd, cnt;
- FILE* fp;
- struct mntent* mentry;
- struct listnode* node;
-
- list_declare(rw_entries);
- list_declare(ro_entries);
-
- sync();
- find_rw(&rw_entries);
-
- /* Trigger the remount of the filesystems as read-only,
- * which also marks them clean.
- */
- fd = TEMP_FAILURE_RETRY(open("/proc/sysrq-trigger", O_WRONLY));
- if (fd < 0) {
- KLOG_WARNING(TAG, "Failed to open sysrq-trigger.\n");
- /* TODO: Try to remount each rw parition manually in readonly mode.
- * This may succeed if no process is using the partition.
- */
- goto out;
- }
- if (TEMP_FAILURE_RETRY(write(fd, "u", 1)) != 1) {
- close(fd);
- KLOG_WARNING(TAG, "Failed to write to sysrq-trigger.\n");
- /* TODO: The same. Manually remount the paritions. */
- goto out;
- }
- close(fd);
-
- /* Now poll /proc/mounts till it's done */
- cnt = 0;
- while (cnt < READONLY_CHECK_TIMES) {
- if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
- /* If we can't read /proc/mounts, just give up. */
- KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
- goto out;
- }
- while ((mentry = getmntent(fp)) != NULL) {
- if (!is_block_device(mentry->mnt_fsname) || !hasmntopt(mentry, "ro")) {
- continue;
- }
- mntent_list* item = find_item(&rw_entries, mentry->mnt_fsname);
- if (item) {
- /* |item| has now been ro remounted. */
- list_remove(&item->list);
- list_add_tail(&ro_entries, &item->list);
- }
- }
- endmntent(fp);
- if (list_empty(&rw_entries)) {
- /* All rw block devices are now readonly. */
- break;
- }
- TEMP_FAILURE_RETRY(
- usleep(READONLY_CHECK_MS * 1000 / READONLY_CHECK_TIMES));
- cnt++;
- }
-
- list_for_each(node, &rw_entries) {
- mntent_list* item = node_to_item(node, mntent_list, list);
- KLOG_WARNING(TAG, "Failed to remount %s in readonly mode.\n",
- item->entry.mnt_fsname);
- }
-
- if (cb_on_remount) {
- list_for_each(node, &ro_entries) {
- mntent_list* item = node_to_item(node, mntent_list, list);
- cb_on_remount(&item->entry);
- }
- }
-
-out:
- free_entries(&rw_entries);
- free_entries(&ro_entries);
-}
-
-static void save_reboot_reason(int cmd, const char *arg)
-{
- FILE *fp;
- const char *reason = NULL;
-
- fp = fopen(LAST_REBOOT_REASON_FILE, "w");
- if (fp == NULL) {
- KLOG_WARNING(TAG, "Error creating " LAST_REBOOT_REASON_FILE
- ": %s\n", strerror(errno));
- return;
- }
- switch (cmd) {
- case ANDROID_RB_RESTART:
- reason = "restart";
- break;
-
- case ANDROID_RB_POWEROFF:
- reason = "power-off";
- break;
-
- case ANDROID_RB_RESTART2:
- reason = arg && strlen(arg) ? arg : "restart";
- break;
-
- case ANDROID_RB_THERMOFF:
- reason = "thermal-shutdown";
- break;
-
- default:
- fprintf(fp,"0x%08X\n", cmd);
- break;
- }
-
- if (reason) {
- if (fprintf(fp, "%s\n", reason) < 0) {
- KLOG_WARNING(TAG, "Error writing " LAST_REBOOT_REASON_FILE
- ": %s\n", strerror(errno));
- }
- }
-
- fclose(fp);
-}
-
-int android_reboot_with_callback(
- int cmd, int flags __unused, const char *arg,
- void (*cb_on_remount)(const struct mntent*))
-{
+int android_reboot(int cmd, int flags __unused, const char* arg) {
int ret;
+ const char* restart_cmd = NULL;
+ char* prop_value;
- save_reboot_reason(cmd, arg);
- remount_ro(cb_on_remount);
switch (cmd) {
- case ANDROID_RB_RESTART:
- ret = reboot(RB_AUTOBOOT);
- break;
-
- case ANDROID_RB_POWEROFF:
- ret = reboot(RB_POWER_OFF);
- break;
-
+ case ANDROID_RB_RESTART: // deprecated
case ANDROID_RB_RESTART2:
- ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
- LINUX_REBOOT_CMD_RESTART2, arg);
+ restart_cmd = "reboot";
break;
-
+ case ANDROID_RB_POWEROFF:
+ restart_cmd = "shutdown";
+ break;
case ANDROID_RB_THERMOFF:
- ret = reboot(RB_POWER_OFF);
+ restart_cmd = "thermal-shutdown";
break;
-
- default:
- ret = -1;
}
-
+ if (!restart_cmd) return -1;
+ if (arg) {
+ ret = asprintf(&prop_value, "%s,%s", restart_cmd, arg);
+ } else {
+ ret = asprintf(&prop_value, "%s", restart_cmd);
+ }
+ if (ret < 0) return -1;
+ ret = property_set(ANDROID_RB_PROPERTY, prop_value);
+ free(prop_value);
return ret;
}
-
-int android_reboot(int cmd, int flags, const char *arg)
-{
- return android_reboot_with_callback(cmd, flags, arg, NULL);
-}
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
index e0e6a34..96ca566 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.c
@@ -17,6 +17,7 @@
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -41,7 +42,7 @@
}
int load_canned_fs_config(const char* fn) {
- char line[PATH_MAX + 200];
+ char buf[PATH_MAX + 200];
FILE* f;
f = fopen(fn, "r");
@@ -50,17 +51,21 @@
return -1;
}
- while (fgets(line, sizeof(line), f)) {
+ while (fgets(buf, sizeof(buf), f)) {
Path* p;
char* token;
+ char* line = buf;
+ bool rootdir;
while (canned_used >= canned_alloc) {
canned_alloc = (canned_alloc+1) * 2;
canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
}
p = canned_data + canned_used;
- p->path = strdup(strtok(line, " "));
- p->uid = atoi(strtok(NULL, " "));
+ if (line[0] == '/') line++;
+ rootdir = line[0] == ' ';
+ p->path = strdup(rootdir ? "" : strtok(line, " "));
+ p->uid = atoi(strtok(rootdir ? line : NULL, " "));
p->gid = atoi(strtok(NULL, " "));
p->mode = strtol(strtok(NULL, " "), NULL, 8); // mode is in octal
p->capabilities = 0;
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index f99519a..6a57a41 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -116,16 +116,21 @@
* although the developer is advised to restrict the scope to the /vendor or
* oem/ file-system since the intent is to provide support for customized
* portions of a separate vendor.img or oem.img. Has to remain open so that
- * customization can also land on /system/vendor or /system/orm. We expect
- * build-time checking or filtering when constructing the associated
- * fs_config_* files.
+ * customization can also land on /system/vendor, /system/oem or /system/odm.
+ * We expect build-time checking or filtering when constructing the associated
+ * fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
*/
static const char ven_conf_dir[] = "/vendor/etc/fs_config_dirs";
static const char ven_conf_file[] = "/vendor/etc/fs_config_files";
static const char oem_conf_dir[] = "/oem/etc/fs_config_dirs";
static const char oem_conf_file[] = "/oem/etc/fs_config_files";
+static const char odm_conf_dir[] = "/odm/etc/fs_config_dirs";
+static const char odm_conf_file[] = "/odm/etc/fs_config_files";
static const char* conf[][2] = {
- {sys_conf_file, sys_conf_dir}, {ven_conf_file, ven_conf_dir}, {oem_conf_file, oem_conf_dir},
+ {sys_conf_file, sys_conf_dir},
+ {ven_conf_file, ven_conf_dir},
+ {oem_conf_file, oem_conf_dir},
+ {odm_conf_file, odm_conf_dir},
};
static const struct fs_path_config android_files[] = {
@@ -142,6 +147,8 @@
{ 00600, AID_ROOT, AID_ROOT, 0, "default.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "odm/build.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "odm/default.prop" },
+ { 00444, AID_ROOT, AID_ROOT, 0, odm_conf_dir + 1 },
+ { 00444, AID_ROOT, AID_ROOT, 0, odm_conf_file + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_file + 1 },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
@@ -160,6 +167,7 @@
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
{ 00440, AID_ROOT, AID_ROOT, 0, "system/etc/recovery.img" },
+ { 00440, AID_RADIO, AID_ROOT, 0, "system/etc/xtables.lock" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/build.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/default.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, ven_conf_dir + 1 },
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index 2e3b429..716567a 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -17,12 +17,11 @@
#ifndef __CUTILS_ANDROID_REBOOT_H__
#define __CUTILS_ANDROID_REBOOT_H__
-#include <mntent.h>
__BEGIN_DECLS
/* Commands */
-#define ANDROID_RB_RESTART 0xDEAD0001
+#define ANDROID_RB_RESTART 0xDEAD0001 /* deprecated. Use RESTART2. */
#define ANDROID_RB_POWEROFF 0xDEAD0002
#define ANDROID_RB_RESTART2 0xDEAD0003
#define ANDROID_RB_THERMOFF 0xDEAD0004
@@ -33,10 +32,12 @@
/* Android reboot reason stored in this file */
#define LAST_REBOOT_REASON_FILE "/data/misc/reboot/last_reboot_reason"
+/* Reboot or shutdown the system.
+ * This call uses ANDROID_RB_PROPERTY to request reboot to init process.
+ * Due to that, process calling this should have proper selinux permission
+ * to write to the property. Otherwise, the call will fail.
+ */
int android_reboot(int cmd, int flags, const char *arg);
-int android_reboot_with_callback(
- int cmd, int flags, const char *arg,
- void (*cb_on_remount)(const struct mntent*));
__END_DECLS
diff --git a/include/private/android_filesystem_capability.h b/libcutils/include/private/android_filesystem_capability.h
similarity index 86%
rename from include/private/android_filesystem_capability.h
rename to libcutils/include/private/android_filesystem_capability.h
index b92d3db..0227b1d 100644
--- a/include/private/android_filesystem_capability.h
+++ b/libcutils/include/private/android_filesystem_capability.h
@@ -35,15 +35,15 @@
#define _LINUX_CAPABILITY_U32S_3 2
typedef struct __user_cap_header_struct {
- __u32 version;
- int pid;
-} __user *cap_user_header_t;
+ __u32 version;
+ int pid;
+} __user* cap_user_header_t;
typedef struct __user_cap_data_struct {
- __u32 effective;
- __u32 permitted;
- __u32 inheritable;
-} __user *cap_user_data_t;
+ __u32 effective;
+ __u32 permitted;
+ __u32 inheritable;
+} __user* cap_user_data_t;
#define VFS_CAP_REVISION_MASK 0xFF000000
#define VFS_CAP_REVISION_SHIFT 24
@@ -51,20 +51,20 @@
#define VFS_CAP_FLAGS_EFFECTIVE 0x000001
#define VFS_CAP_REVISION_1 0x01000000
#define VFS_CAP_U32_1 1
-#define XATTR_CAPS_SZ_1 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_1))
+#define XATTR_CAPS_SZ_1 (sizeof(__le32) * (1 + 2 * VFS_CAP_U32_1))
#define VFS_CAP_REVISION_2 0x02000000
#define VFS_CAP_U32_2 2
-#define XATTR_CAPS_SZ_2 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_2))
+#define XATTR_CAPS_SZ_2 (sizeof(__le32) * (1 + 2 * VFS_CAP_U32_2))
#define XATTR_CAPS_SZ XATTR_CAPS_SZ_2
#define VFS_CAP_U32 VFS_CAP_U32_2
#define VFS_CAP_REVISION VFS_CAP_REVISION_2
struct vfs_cap_data {
- __le32 magic_etc;
- struct {
- __le32 permitted;
- __le32 inheritable;
- } data[VFS_CAP_U32];
+ __le32 magic_etc;
+ struct {
+ __le32 permitted;
+ __le32 inheritable;
+ } data[VFS_CAP_U32];
};
#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1
@@ -110,7 +110,7 @@
#define CAP_LAST_CAP CAP_AUDIT_READ
#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
#define CAP_TO_INDEX(x) ((x) >> 5)
-#define CAP_TO_MASK(x) (1 << ((x) & 31))
+#define CAP_TO_MASK(x) (1 << ((x)&31))
#undef __user
#undef __u32
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
new file mode 100644
index 0000000..0037f15
--- /dev/null
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+/* This file is used to define the properties of the filesystem
+** images generated by build tools (mkbootfs and mkyaffs2image) and
+** by the device side of adb.
+*/
+
+/*
+ * This file is consumed by build/tools/fs_config and is used
+ * for generating various files. Anything #define AID_<name>
+ * becomes the mapping for getpwnam/getpwuid, etc. The <name>
+ * field is lowercased.
+ * For example:
+ * #define AID_FOO_BAR 6666 becomes a friendly name of "foo_bar"
+ *
+ * The above holds true with the exception of:
+ * mediacodec
+ * mediaex
+ * mediadrm
+ * Whose friendly names do not match the #define statements.
+ *
+ * Additionally, AID_OEM_RESERVED_START and AID_OEM_RESERVED_END
+ * can be used to define reserved OEM ranges used for sanity checks
+ * during the build process. The rules are, they must end with START/END
+ * The proper convention is incrementing a number like so:
+ * AID_OEM_RESERVED_START
+ * AID_OEM_RESERVED_1_START
+ * AID_OEM_RESERVED_2_START
+ * ...
+ * The same applies to the END.
+ * They are not required to be in order, but must not overlap each other and
+ * must define a START and END'ing range. START must be smaller than END.
+ */
+
+#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
+#define _ANDROID_FILESYSTEM_CONFIG_H_
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#if defined(__ANDROID__)
+#include <linux/capability.h>
+#else
+#include "android_filesystem_capability.h"
+#endif
+
+#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))
+
+/* This is the master Users and Groups config for the platform.
+ * DO NOT EVER RENUMBER
+ */
+
+#define AID_ROOT 0 /* traditional unix root user */
+
+#define AID_SYSTEM 1000 /* system server */
+
+#define AID_RADIO 1001 /* telephony subsystem, RIL */
+#define AID_BLUETOOTH 1002 /* bluetooth subsystem */
+#define AID_GRAPHICS 1003 /* graphics devices */
+#define AID_INPUT 1004 /* input devices */
+#define AID_AUDIO 1005 /* audio devices */
+#define AID_CAMERA 1006 /* camera devices */
+#define AID_LOG 1007 /* log devices */
+#define AID_COMPASS 1008 /* compass device */
+#define AID_MOUNT 1009 /* mountd socket */
+#define AID_WIFI 1010 /* wifi subsystem */
+#define AID_ADB 1011 /* android debug bridge (adbd) */
+#define AID_INSTALL 1012 /* group for installing packages */
+#define AID_MEDIA 1013 /* mediaserver process */
+#define AID_DHCP 1014 /* dhcp client */
+#define AID_SDCARD_RW 1015 /* external storage write access */
+#define AID_VPN 1016 /* vpn system */
+#define AID_KEYSTORE 1017 /* keystore subsystem */
+#define AID_USB 1018 /* USB devices */
+#define AID_DRM 1019 /* DRM server */
+#define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */
+#define AID_GPS 1021 /* GPS daemon */
+#define AID_UNUSED1 1022 /* deprecated, DO NOT USE */
+#define AID_MEDIA_RW 1023 /* internal media storage write access */
+#define AID_MTP 1024 /* MTP USB driver access */
+#define AID_UNUSED2 1025 /* deprecated, DO NOT USE */
+#define AID_DRMRPC 1026 /* group for drm rpc */
+#define AID_NFC 1027 /* nfc subsystem */
+#define AID_SDCARD_R 1028 /* external storage read access */
+#define AID_CLAT 1029 /* clat part of nat464 */
+#define AID_LOOP_RADIO 1030 /* loop radio devices */
+#define AID_MEDIA_DRM 1031 /* MediaDrm plugins */
+#define AID_PACKAGE_INFO 1032 /* access to installed package details */
+#define AID_SDCARD_PICS 1033 /* external storage photos access */
+#define AID_SDCARD_AV 1034 /* external storage audio/video access */
+#define AID_SDCARD_ALL 1035 /* access all users external storage */
+#define AID_LOGD 1036 /* log daemon */
+#define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */
+#define AID_DBUS 1038 /* dbus-daemon IPC broker process */
+#define AID_TLSDATE 1039 /* tlsdate unprivileged user */
+#define AID_MEDIA_EX 1040 /* mediaextractor process */
+#define AID_AUDIOSERVER 1041 /* audioserver process */
+#define AID_METRICS_COLL 1042 /* metrics_collector process */
+#define AID_METRICSD 1043 /* metricsd process */
+#define AID_WEBSERV 1044 /* webservd process */
+#define AID_DEBUGGERD 1045 /* debuggerd unprivileged user */
+#define AID_MEDIA_CODEC 1046 /* mediacodec process */
+#define AID_CAMERASERVER 1047 /* cameraserver process */
+#define AID_FIREWALL 1048 /* firewalld process */
+#define AID_TRUNKS 1049 /* trunksd process (TPM daemon) */
+#define AID_NVRAM 1050 /* Access-controlled NVRAM */
+#define AID_DNS 1051 /* DNS resolution daemon (system: netd) */
+#define AID_DNS_TETHER 1052 /* DNS resolution daemon (tether: dnsmasq) */
+#define AID_WEBVIEW_ZYGOTE 1053 /* WebView zygote process */
+#define AID_VEHICLE_NETWORK 1054 /* Vehicle network service */
+#define AID_MEDIA_AUDIO 1055 /* GID for audio files on internal media storage */
+#define AID_MEDIA_VIDEO 1056 /* GID for video files on internal media storage */
+#define AID_MEDIA_IMAGE 1057 /* GID for image files on internal media storage */
+#define AID_TOMBSTONED 1058 /* tombstoned user */
+#define AID_MEDIA_OBB 1059 /* GID for OBB files on internal media storage */
+#define AID_ESE 1060 /* embedded secure element (eSE) subsystem */
+#define AID_OTA_UPDATE 1061 /* resource tracking UID for OTA updates */
+/* Changes to this file must be made in AOSP, *not* in internal branches. */
+
+#define AID_SHELL 2000 /* adb and debug shell user */
+#define AID_CACHE 2001 /* cache access */
+#define AID_DIAG 2002 /* access to diagnostic resources */
+
+/* The range 2900-2999 is reserved for OEM, and must never be
+ * used here */
+#define AID_OEM_RESERVED_START 2900
+#define AID_OEM_RESERVED_END 2999
+
+/* The 3000 series are intended for use as supplemental group id's only.
+ * They indicate special Android capabilities that the kernel is aware of. */
+#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */
+#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */
+#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
+#define AID_NET_RAW 3004 /* can create raw INET sockets */
+#define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */
+#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
+#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
+#define AID_READPROC 3009 /* Allow /proc read access */
+#define AID_WAKELOCK 3010 /* Allow system wakelock read/write access */
+
+/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
+#define AID_OEM_RESERVED_2_START 5000
+#define AID_OEM_RESERVED_2_END 5999
+
+#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
+#define AID_MISC 9998 /* access to misc storage */
+#define AID_NOBODY 9999
+
+#define AID_APP 10000 /* TODO: switch users over to AID_APP_START */
+#define AID_APP_START 10000 /* first app user */
+#define AID_APP_END 19999 /* last app user */
+
+#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
+#define AID_CACHE_GID_END 29999 /* end of gids for apps to mark cached data */
+
+#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
+#define AID_EXT_GID_END 39999 /* end of gids for apps to mark external data */
+
+#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
+#define AID_SHARED_GID_END 59999 /* end of gids for apps in each user to share */
+
+#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
+#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */
+
+#define AID_USER 100000 /* TODO: switch users over to AID_USER_OFFSET */
+#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
+
+/*
+ * android_ids has moved to pwd/grp functionality.
+ * If you need to add one, the structure is now
+ * auto-generated based on the AID_ constraints
+ * documented at the top of this header file.
+ * Also see build/tools/fs_config for more details.
+ */
+
+#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
+
+struct fs_path_config {
+ unsigned mode;
+ unsigned uid;
+ unsigned gid;
+ uint64_t capabilities;
+ const char* prefix;
+};
+
+/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
+
+__BEGIN_DECLS
+
+/*
+ * Used in:
+ * build/tools/fs_config/fs_config.c
+ * build/tools/fs_get_stats/fs_get_stats.c
+ * system/extras/ext4_utils/make_ext4fs_main.c
+ * external/squashfs-tools/squashfs-tools/android.c
+ * system/core/cpio/mkbootfs.c
+ * system/core/adb/file_sync_service.cpp
+ * system/extras/ext4_utils/canned_fs_config.c
+ */
+void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
+ unsigned* mode, uint64_t* capabilities);
+
+ssize_t fs_config_generate(char* buffer, size_t length, const struct fs_path_config* pc);
+
+__END_DECLS
+
+#endif
+#endif
diff --git a/include/private/canned_fs_config.h b/libcutils/include/private/canned_fs_config.h
similarity index 88%
rename from include/private/canned_fs_config.h
rename to libcutils/include/private/canned_fs_config.h
index d9f51ca..71e1537 100644
--- a/include/private/canned_fs_config.h
+++ b/libcutils/include/private/canned_fs_config.h
@@ -20,7 +20,7 @@
#include <inttypes.h>
int load_canned_fs_config(const char* fn);
-void canned_fs_config(const char* path, int dir, const char* target_out_path,
- unsigned* uid, unsigned* gid, unsigned* mode, uint64_t* capabilities);
+void canned_fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid,
+ unsigned* gid, unsigned* mode, uint64_t* capabilities);
#endif
diff --git a/libcutils/include_vndk/private b/libcutils/include_vndk/private
new file mode 120000
index 0000000..2245a85
--- /dev/null
+++ b/libcutils/include_vndk/private
@@ -0,0 +1 @@
+../include/private
\ No newline at end of file
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index 7e3ad59..73ca518 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -51,13 +51,8 @@
static pthread_once_t the_once = PTHREAD_ONCE_INIT;
-static int __sys_supports_schedgroups = -1;
static int __sys_supports_timerslack = -1;
-// File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error.
-static int bg_cgroup_fd = -1;
-static int fg_cgroup_fd = -1;
-
// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
static int system_bg_cpuset_fd = -1;
static int bg_cpuset_fd = -1;
@@ -151,23 +146,6 @@
static void __initialize() {
const char* filename;
- if (!access("/dev/cpuctl/tasks", W_OK)) {
- __sys_supports_schedgroups = 1;
-
- filename = "/dev/cpuctl/tasks";
- fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
- if (fg_cgroup_fd < 0) {
- SLOGE("open of %s failed: %s\n", filename, strerror(errno));
- }
-
- filename = "/dev/cpuctl/bg_non_interactive/tasks";
- bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
- if (bg_cgroup_fd < 0) {
- SLOGE("open of %s failed: %s\n", filename, strerror(errno));
- }
- } else {
- __sys_supports_schedgroups = 0;
- }
if (cpusets_enabled()) {
if (!access("/dev/cpuset/tasks", W_OK)) {
@@ -276,48 +254,26 @@
}
pthread_once(&the_once, __initialize);
- if (__sys_supports_schedgroups) {
- char grpBuf[32];
+ char grpBuf[32];
- if (cpusets_enabled()) {
- if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0)
- return -1;
- if (grpBuf[0] == '\0') {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "foreground")) {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "background")) {
- *policy = SP_BACKGROUND;
- } else if (!strcmp(grpBuf, "top-app")) {
- *policy = SP_TOP_APP;
- } else {
- errno = ERANGE;
- return -1;
- }
- } else {
- if (getCGroupSubsys(tid, "cpu", grpBuf, sizeof(grpBuf)) < 0)
- return -1;
- if (grpBuf[0] == '\0') {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "bg_non_interactive")) {
- *policy = SP_BACKGROUND;
- } else {
- errno = ERANGE;
- return -1;
- }
- }
- } else {
- int rc = sched_getscheduler(tid);
- if (rc < 0)
- return -1;
- else if (rc == SCHED_NORMAL)
+ if (cpusets_enabled()) {
+ if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0) return -1;
+ if (grpBuf[0] == '\0') {
*policy = SP_FOREGROUND;
- else if (rc == SCHED_BATCH)
+ } else if (!strcmp(grpBuf, "foreground")) {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "background")) {
*policy = SP_BACKGROUND;
- else {
+ } else if (!strcmp(grpBuf, "top-app")) {
+ *policy = SP_TOP_APP;
+ } else {
errno = ERANGE;
return -1;
}
+ } else {
+ // In b/34193533, we removed bg_non_interactive cgroup, so now
+ // all threads are in FOREGROUND cgroup
+ *policy = SP_FOREGROUND;
}
return 0;
}
@@ -440,49 +396,30 @@
}
#endif
- if (__sys_supports_schedgroups) {
- int fd = -1;
+ if (schedboost_enabled()) {
int boost_fd = -1;
switch (policy) {
case SP_BACKGROUND:
- fd = bg_cgroup_fd;
boost_fd = bg_schedboost_fd;
break;
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
- fd = fg_cgroup_fd;
boost_fd = fg_schedboost_fd;
break;
case SP_TOP_APP:
- fd = fg_cgroup_fd;
boost_fd = ta_schedboost_fd;
break;
default:
- fd = -1;
boost_fd = -1;
break;
}
- if (add_tid_to_cgroup(tid, fd) != 0) {
+ if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
if (errno != ESRCH && errno != ENOENT)
return -errno;
}
- if (schedboost_enabled()) {
- if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
- if (errno != ESRCH && errno != ENOENT)
- return -errno;
- }
- }
- } else {
- struct sched_param param;
-
- param.sched_priority = 0;
- sched_setscheduler(tid,
- (policy == SP_BACKGROUND) ?
- SCHED_BATCH : SCHED_NORMAL,
- ¶m);
}
if (__sys_supports_timerslack) {
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index cc95425..0538c4c 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1854,8 +1854,22 @@
fprintf(stderr, "WARNING: setting ro.device_owner to a domain\n");
static const char domain[] = "com.google.android.SecOps.DeviceOwner";
property_set(readonly_key, domain);
- usleep(20000); // property system does not guarantee performance, rest ...
- property_get(readonly_key, readonly, nothing_val);
+ useconds_t total_time = 0;
+ static const useconds_t seconds = 1000000;
+ static const useconds_t max_time = 5 * seconds; // not going to happen
+ static const useconds_t rest = 20 * 1000;
+ for (; total_time < max_time; total_time += rest) {
+ usleep(rest); // property system does not guarantee performance.
+ property_get(readonly_key, readonly, nothing_val);
+ if (!strcmp(readonly, domain)) {
+ if (total_time > rest) {
+ fprintf(stderr, "INFO: took %u.%06u seconds to set property\n",
+ (unsigned)(total_time / seconds),
+ (unsigned)(total_time % seconds));
+ }
+ break;
+ }
+ }
EXPECT_STREQ(readonly, domain);
} else if (!strcasecmp(readonly, "false") || !readonly[0]) {
// not enough permissions to run
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/include/utils/Compat.h b/libutils/include/utils/Compat.h
index 2709e3b..dee577e 100644
--- a/libutils/include/utils/Compat.h
+++ b/libutils/include/utils/Compat.h
@@ -37,6 +37,10 @@
return pwrite(fd, buf, nbytes, offset);
}
+static inline int ftruncate64(int fd, off64_t length) {
+ return ftruncate(fd, length);
+}
+
#endif /* __APPLE__ */
#if defined(_WIN32)
diff --git a/libutils/include/utils/FastStrcmp.h b/libutils/include/utils/FastStrcmp.h
index 3844e7d..5cadc94 100644
--- a/libutils/include/utils/FastStrcmp.h
+++ b/libutils/include/utils/FastStrcmp.h
@@ -17,6 +17,13 @@
#ifndef _ANDROID_UTILS_FASTSTRCMP_H__
#define _ANDROID_UTILS_FASTSTRCMP_H__
+#include <ctype.h>
+#include <string.h>
+
+#ifndef __predict_true
+#define __predict_true(exp) __builtin_expect((exp) != 0, 1)
+#endif
+
#ifdef __cplusplus
// Optimized for instruction cache locality
@@ -28,25 +35,41 @@
//
// fastcmp<strncmp>(str1, str2, len)
//
-// NB: Does not work for the case insensitive str*cmp functions.
+// NB: use fasticmp for the case insensitive str*cmp functions.
// NB: Returns boolean, do not use if expecting to check negative value.
// Thus not semantically identical to the expected function behavior.
-template <int (*cmp)(const char *l, const char *r, const size_t s)>
-static inline int fastcmp(const char *l, const char *r, const size_t s) {
- return (*l != *r) || cmp(l + 1, r + 1, s - 1);
+template <int (*cmp)(const char* l, const char* r, const size_t s)>
+static inline int fastcmp(const char* l, const char* r, const size_t s) {
+ const ssize_t n = s; // To help reject negative sizes, treat like zero
+ return __predict_true(n > 0) &&
+ ((*l != *r) || (__predict_true(n > 1) && cmp(l + 1, r + 1, n - 1)));
}
-template <int (*cmp)(const void *l, const void *r, const size_t s)>
-static inline int fastcmp(const void *lv, const void *rv, const size_t s) {
- const char *l = static_cast<const char *>(lv);
- const char *r = static_cast<const char *>(rv);
- return (*l != *r) || cmp(l + 1, r + 1, s - 1);
+template <int (*cmp)(const char* l, const char* r, const size_t s)>
+static inline int fasticmp(const char* l, const char* r, const size_t s) {
+ const ssize_t n = s; // To help reject negative sizes, treat like zero
+ return __predict_true(n > 0) &&
+ ((tolower(*l) != tolower(*r)) || (__predict_true(n > 1) && cmp(l + 1, r + 1, n - 1)));
}
-template <int (*cmp)(const char *l, const char *r)>
-static inline int fastcmp(const char *l, const char *r) {
- return (*l != *r) || cmp(l + 1, r + 1);
+template <int (*cmp)(const void* l, const void* r, const size_t s)>
+static inline int fastcmp(const void* lv, const void* rv, const size_t s) {
+ const char* l = static_cast<const char*>(lv);
+ const char* r = static_cast<const char*>(rv);
+ const ssize_t n = s; // To help reject negative sizes, treat like zero
+ return __predict_true(n > 0) &&
+ ((*l != *r) || (__predict_true(n > 1) && cmp(l + 1, r + 1, n - 1)));
+}
+
+template <int (*cmp)(const char* l, const char* r)>
+static inline int fastcmp(const char* l, const char* r) {
+ return (*l != *r) || (__predict_true(*l) && cmp(l + 1, r + 1));
+}
+
+template <int (*cmp)(const char* l, const char* r)>
+static inline int fasticmp(const char* l, const char* r) {
+ return (tolower(*l) != tolower(*r)) || (__predict_true(*l) && cmp(l + 1, r + 1));
}
#endif
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index b72ed7f..7600528 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -14,21 +14,23 @@
* limitations under the License.
*/
-#include "entry_name_utils-inl.h"
-#include "zip_archive_common.h"
#include "ziparchive/zip_writer.h"
-#include <utils/Log.h>
-
-#include <sys/param.h>
-
-#include <cassert>
#include <cstdio>
-#include <memory>
-#include <vector>
+#include <sys/param.h>
#include <zlib.h>
#define DEF_MEM_LEVEL 8 // normally in zutil.h?
+#include <memory>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "utils/Compat.h"
+#include "utils/Log.h"
+
+#include "entry_name_utils-inl.h"
+#include "zip_archive_common.h"
+
#if !defined(powerof2)
#define powerof2(x) ((((x)-1)&(x))==0)
#endif
@@ -171,12 +173,12 @@
return kInvalidAlignment;
}
- FileInfo fileInfo = {};
- fileInfo.path = std::string(path);
- fileInfo.local_file_header_offset = current_offset_;
+ current_file_entry_ = {};
+ current_file_entry_.path = path;
+ current_file_entry_.local_file_header_offset = current_offset_;
- if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(fileInfo.path.data()),
- fileInfo.path.size())) {
+ if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(current_file_entry_.path.data()),
+ current_file_entry_.path.size())) {
return kInvalidEntryName;
}
@@ -188,24 +190,24 @@
header.gpb_flags |= kGPBDDFlagMask;
if (flags & ZipWriter::kCompress) {
- fileInfo.compression_method = kCompressDeflated;
+ current_file_entry_.compression_method = kCompressDeflated;
int32_t result = PrepareDeflate();
if (result != kNoError) {
return result;
}
} else {
- fileInfo.compression_method = kCompressStored;
+ current_file_entry_.compression_method = kCompressStored;
}
- header.compression_method = fileInfo.compression_method;
+ header.compression_method = current_file_entry_.compression_method;
- ExtractTimeAndDate(time, &fileInfo.last_mod_time, &fileInfo.last_mod_date);
- header.last_mod_time = fileInfo.last_mod_time;
- header.last_mod_date = fileInfo.last_mod_date;
+ ExtractTimeAndDate(time, ¤t_file_entry_.last_mod_time, ¤t_file_entry_.last_mod_date);
+ header.last_mod_time = current_file_entry_.last_mod_time;
+ header.last_mod_date = current_file_entry_.last_mod_date;
- header.file_name_length = fileInfo.path.size();
+ header.file_name_length = current_file_entry_.path.size();
- off64_t offset = current_offset_ + sizeof(header) + fileInfo.path.size();
+ off64_t offset = current_offset_ + sizeof(header) + current_file_entry_.path.size();
std::vector<char> zero_padding;
if (alignment != 0 && (offset & (alignment - 1))) {
// Pad the extra field so the data will be aligned.
@@ -220,7 +222,8 @@
return HandleError(kIoError);
}
- if (fwrite(path, sizeof(*path), fileInfo.path.size(), file_) != fileInfo.path.size()) {
+ if (fwrite(path, sizeof(*path), current_file_entry_.path.size(), file_)
+ != current_file_entry_.path.size()) {
return HandleError(kIoError);
}
@@ -230,15 +233,37 @@
return HandleError(kIoError);
}
- files_.emplace_back(std::move(fileInfo));
-
current_offset_ = offset;
state_ = State::kWritingEntry;
return kNoError;
}
+int32_t ZipWriter::DiscardLastEntry() {
+ if (state_ != State::kWritingZip || files_.empty()) {
+ return kInvalidState;
+ }
+
+ FileEntry& last_entry = files_.back();
+ current_offset_ = last_entry.local_file_header_offset;
+ if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
+ return HandleError(kIoError);
+ }
+ files_.pop_back();
+ return kNoError;
+}
+
+int32_t ZipWriter::GetLastEntry(FileEntry* out_entry) {
+ CHECK(out_entry != nullptr);
+
+ if (files_.empty()) {
+ return kInvalidState;
+ }
+ *out_entry = files_.back();
+ return kNoError;
+}
+
int32_t ZipWriter::PrepareDeflate() {
- assert(state_ == State::kWritingZip);
+ CHECK(state_ == State::kWritingZip);
// Initialize the z_stream for compression.
z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
@@ -269,25 +294,25 @@
return HandleError(kInvalidState);
}
- FileInfo& currentFile = files_.back();
int32_t result = kNoError;
- if (currentFile.compression_method & kCompressDeflated) {
- result = CompressBytes(¤tFile, data, len);
+ if (current_file_entry_.compression_method & kCompressDeflated) {
+ result = CompressBytes(¤t_file_entry_, data, len);
} else {
- result = StoreBytes(¤tFile, data, len);
+ result = StoreBytes(¤t_file_entry_, data, len);
}
if (result != kNoError) {
return result;
}
- currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
- currentFile.uncompressed_size += len;
+ current_file_entry_.crc32 = crc32(current_file_entry_.crc32,
+ reinterpret_cast<const Bytef*>(data), len);
+ current_file_entry_.uncompressed_size += len;
return kNoError;
}
-int32_t ZipWriter::StoreBytes(FileInfo* file, const void* data, size_t len) {
- assert(state_ == State::kWritingEntry);
+int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, size_t len) {
+ CHECK(state_ == State::kWritingEntry);
if (fwrite(data, 1, len, file_) != len) {
return HandleError(kIoError);
@@ -297,11 +322,11 @@
return kNoError;
}
-int32_t ZipWriter::CompressBytes(FileInfo* file, const void* data, size_t len) {
- assert(state_ == State::kWritingEntry);
- assert(z_stream_);
- assert(z_stream_->next_out != nullptr);
- assert(z_stream_->avail_out != 0);
+int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, size_t len) {
+ CHECK(state_ == State::kWritingEntry);
+ CHECK(z_stream_);
+ CHECK(z_stream_->next_out != nullptr);
+ CHECK(z_stream_->avail_out != 0);
// Prepare the input.
z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
@@ -331,17 +356,17 @@
return kNoError;
}
-int32_t ZipWriter::FlushCompressedBytes(FileInfo* file) {
- assert(state_ == State::kWritingEntry);
- assert(z_stream_);
- assert(z_stream_->next_out != nullptr);
- assert(z_stream_->avail_out != 0);
+int32_t ZipWriter::FlushCompressedBytes(FileEntry* file) {
+ CHECK(state_ == State::kWritingEntry);
+ CHECK(z_stream_);
+ CHECK(z_stream_->next_out != nullptr);
+ CHECK(z_stream_->avail_out != 0);
// Keep deflating while there isn't enough space in the buffer to
// to complete the compress.
int zerr;
while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) {
- assert(z_stream_->avail_out == 0);
+ CHECK(z_stream_->avail_out == 0);
size_t write_bytes = z_stream_->next_out - buffer_.data();
if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
return HandleError(kIoError);
@@ -373,9 +398,8 @@
return kInvalidState;
}
- FileInfo& currentFile = files_.back();
- if (currentFile.compression_method & kCompressDeflated) {
- int32_t result = FlushCompressedBytes(¤tFile);
+ if (current_file_entry_.compression_method & kCompressDeflated) {
+ int32_t result = FlushCompressedBytes(¤t_file_entry_);
if (result != kNoError) {
return result;
}
@@ -388,13 +412,15 @@
}
DataDescriptor dd = {};
- dd.crc32 = currentFile.crc32;
- dd.compressed_size = currentFile.compressed_size;
- dd.uncompressed_size = currentFile.uncompressed_size;
+ dd.crc32 = current_file_entry_.crc32;
+ dd.compressed_size = current_file_entry_.compressed_size;
+ dd.uncompressed_size = current_file_entry_.uncompressed_size;
if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
return HandleError(kIoError);
}
+ files_.emplace_back(std::move(current_file_entry_));
+
current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
state_ = State::kWritingZip;
return kNoError;
@@ -406,7 +432,7 @@
}
off64_t startOfCdr = current_offset_;
- for (FileInfo& file : files_) {
+ for (FileEntry& file : files_) {
CentralDirectoryRecord cdr = {};
cdr.record_signature = CentralDirectoryRecord::kSignature;
cdr.gpb_flags |= kGPBDDFlagMask;
@@ -442,11 +468,19 @@
return HandleError(kIoError);
}
+ current_offset_ += sizeof(er);
+
+ // Since we can BackUp() and potentially finish writing at an offset less than one we had
+ // already written at, we must truncate the file.
+
+ if (ftruncate64(fileno(file_), current_offset_) != 0) {
+ return HandleError(kIoError);
+ }
+
if (fflush(file_) != 0) {
return HandleError(kIoError);
}
- current_offset_ += sizeof(er);
state_ = State::kDone;
return kNoError;
}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 16a574d..30f4950 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -23,6 +23,10 @@
#include <memory>
#include <vector>
+static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
+ ZipArchiveHandle handle,
+ ZipEntry* zip_entry);
+
struct zipwriter : public ::testing::Test {
TemporaryFile* temp_file_;
int fd_;
@@ -59,16 +63,10 @@
ZipEntry data;
ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
- EXPECT_EQ(strlen(expected), data.compressed_length);
- EXPECT_EQ(strlen(expected), data.uncompressed_length);
EXPECT_EQ(kCompressStored, data.method);
-
- char buffer[6];
- EXPECT_EQ(0,
- ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)));
- buffer[5] = 0;
-
- EXPECT_STREQ(expected, buffer);
+ EXPECT_EQ(strlen(expected), data.compressed_length);
+ ASSERT_EQ(strlen(expected), data.uncompressed_length);
+ ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
CloseArchive(handle);
}
@@ -94,26 +92,19 @@
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
- char buffer[4];
ZipEntry data;
ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(2u, data.compressed_length);
- EXPECT_EQ(2u, data.uncompressed_length);
- ASSERT_EQ(0,
- ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
- buffer[2] = 0;
- EXPECT_STREQ("he", buffer);
+ ASSERT_EQ(2u, data.uncompressed_length);
+ ASSERT_TRUE(AssertFileEntryContentsEq("he", handle, &data));
ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
EXPECT_EQ(kCompressStored, data.method);
EXPECT_EQ(3u, data.compressed_length);
- EXPECT_EQ(3u, data.uncompressed_length);
- ASSERT_EQ(0,
- ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
- buffer[3] = 0;
- EXPECT_STREQ("llo", buffer);
+ ASSERT_EQ(3u, data.uncompressed_length);
+ ASSERT_TRUE(AssertFileEntryContentsEq("llo", handle, &data));
ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
EXPECT_EQ(kCompressStored, data.method);
@@ -143,7 +134,7 @@
CloseArchive(handle);
}
-void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
+static void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
memset(tm, 0, sizeof(struct tm));
tm->tm_hour = (zip_time >> 11) & 0x1f;
tm->tm_min = (zip_time >> 5) & 0x3f;
@@ -264,14 +255,8 @@
ZipEntry data;
ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
EXPECT_EQ(kCompressDeflated, data.method);
- EXPECT_EQ(4u, data.uncompressed_length);
-
- char buffer[5];
- ASSERT_EQ(0,
- ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
- buffer[4] = 0;
-
- EXPECT_STREQ("helo", buffer);
+ ASSERT_EQ(4u, data.uncompressed_length);
+ ASSERT_TRUE(AssertFileEntryContentsEq("helo", handle, &data));
CloseArchive(handle);
}
@@ -319,3 +304,111 @@
ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
}
+
+TEST_F(zipwriter, BackupRemovesTheLastFile) {
+ ZipWriter writer(file_);
+
+ const char* kKeepThis = "keep this";
+ const char* kDropThis = "drop this";
+ const char* kReplaceWithThis = "replace with this";
+
+ ZipWriter::FileEntry entry;
+ EXPECT_LT(writer.GetLastEntry(&entry), 0);
+
+ ASSERT_EQ(0, writer.StartEntry("keep.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes(kKeepThis, strlen(kKeepThis)));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.GetLastEntry(&entry));
+ EXPECT_EQ("keep.txt", entry.path);
+
+ ASSERT_EQ(0, writer.StartEntry("drop.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes(kDropThis, strlen(kDropThis)));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.GetLastEntry(&entry));
+ EXPECT_EQ("drop.txt", entry.path);
+
+ ASSERT_EQ(0, writer.DiscardLastEntry());
+
+ ASSERT_EQ(0, writer.GetLastEntry(&entry));
+ EXPECT_EQ("keep.txt", entry.path);
+
+ ASSERT_EQ(0, writer.StartEntry("replace.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes(kReplaceWithThis, strlen(kReplaceWithThis)));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.GetLastEntry(&entry));
+ EXPECT_EQ("replace.txt", entry.path);
+
+ ASSERT_EQ(0, writer.Finish());
+
+ // Verify that "drop.txt" does not exist.
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("keep.txt"), &data));
+ ASSERT_TRUE(AssertFileEntryContentsEq(kKeepThis, handle, &data));
+
+ ASSERT_NE(0, FindEntry(handle, ZipString("drop.txt"), &data));
+
+ ASSERT_EQ(0, FindEntry(handle, ZipString("replace.txt"), &data));
+ ASSERT_TRUE(AssertFileEntryContentsEq(kReplaceWithThis, handle, &data));
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, TruncateFileAfterBackup) {
+ ZipWriter writer(file_);
+
+ const char* kSmall = "small";
+
+ ASSERT_EQ(0, writer.StartEntry("small.txt", 0));
+ ASSERT_EQ(0, writer.WriteBytes(kSmall, strlen(kSmall)));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ ASSERT_EQ(0, writer.StartEntry("large.txt", 0));
+ std::vector<uint8_t> data;
+ data.resize(1024*1024, 0xef);
+ ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+
+ off_t before_len = ftello(file_);
+
+ ZipWriter::FileEntry entry;
+ ASSERT_EQ(0, writer.GetLastEntry(&entry));
+ ASSERT_EQ(0, writer.DiscardLastEntry());
+
+ ASSERT_EQ(0, writer.Finish());
+
+ off_t after_len = ftello(file_);
+
+ ASSERT_GT(before_len, after_len);
+}
+
+static ::testing::AssertionResult AssertFileEntryContentsEq(const std::string& expected,
+ ZipArchiveHandle handle,
+ ZipEntry* zip_entry) {
+ if (expected.size() != zip_entry->uncompressed_length) {
+ return ::testing::AssertionFailure() << "uncompressed entry size "
+ << zip_entry->uncompressed_length << " does not match expected size " << expected.size();
+ }
+
+ std::string actual;
+ actual.resize(expected.size());
+
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(&*actual.begin());
+ if (ExtractToMemory(handle, zip_entry, buffer, actual.size()) != 0) {
+ return ::testing::AssertionFailure() << "failed to extract entry";
+ }
+
+ if (expected != actual) {
+ return ::testing::AssertionFailure() << "actual zip_entry data '" << actual
+ << "' does not match expected '" << expected << "'";
+ }
+ return ::testing::AssertionSuccess();
+}
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 0895834..a3a0176 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -479,6 +479,56 @@
ASSERT_EQ(1, count);
}
+TEST(logcat, End_to_End_multitude) {
+ pid_t pid = getpid();
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+
+ FILE* fp[256]; // does this count as a multitude!
+ memset(fp, 0, sizeof(fp));
+ logcat_define(ctx[sizeof(fp) / sizeof(fp[0])]);
+ size_t num = 0;
+ do {
+ EXPECT_TRUE(NULL !=
+ (fp[num] = logcat_popen(
+ ctx[num], "logcat -v brief -b events -t 100")));
+ if (!fp[num]) {
+ fprintf(stderr,
+ "WARNING: limiting to %zu simultaneous logcat operations\n",
+ num);
+ break;
+ }
+ } while (++num < sizeof(fp) / sizeof(fp[0]));
+
+ char buffer[BIG_BUFFER];
+
+ size_t count = 0;
+
+ for (size_t idx = 0; idx < sizeof(fp) / sizeof(fp[0]); ++idx) {
+ if (!fp[idx]) break;
+ while (fgets(buffer, sizeof(buffer), fp[idx])) {
+ int p;
+ unsigned long long t;
+
+ if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t)) ||
+ (p != pid)) {
+ continue;
+ }
+
+ log_time tx((const char*)&t);
+ if (ts == tx) {
+ ++count;
+ }
+ }
+
+ logcat_pclose(ctx[idx], fp[idx]);
+ }
+
+ ASSERT_EQ(num, count);
+}
+
static int get_groups(const char* cmd) {
FILE* fp;
logcat_define(ctx);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 352fc18..86ea6b4 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -132,11 +132,11 @@
LogBufferElement* last) {
// is it mostly identical?
// if (!elem) return DIFFERENT;
- unsigned short lenl = elem->getMsgLen();
- if (!lenl) return DIFFERENT;
+ ssize_t lenl = elem->getMsgLen();
+ if (lenl <= 0) return DIFFERENT; // value if this represents a chatty elem
// if (!last) return DIFFERENT;
- unsigned short lenr = last->getMsgLen();
- if (!lenr) return DIFFERENT;
+ ssize_t lenr = last->getMsgLen();
+ if (lenr <= 0) return DIFFERENT; // value if this represents a chatty elem
// if (elem->getLogId() != last->getLogId()) return DIFFERENT;
if (elem->getUid() != last->getUid()) return DIFFERENT;
if (elem->getPid() != last->getPid()) return DIFFERENT;
@@ -162,8 +162,6 @@
}
// audit message (except sequence number) identical?
- static const char avc[] = "): avc: ";
-
if (last->isBinary()) {
if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
sizeof(int32_t)))
@@ -173,6 +171,7 @@
msgr += sizeof(android_log_event_string_t);
lenr -= sizeof(android_log_event_string_t);
}
+ static const char avc[] = "): avc: ";
const char* avcl = android::strnstr(msgl, lenl, avc);
if (!avcl) return DIFFERENT;
lenl -= avcl - msgl;
@@ -180,10 +179,7 @@
if (!avcr) return DIFFERENT;
lenr -= avcr - msgr;
if (lenl != lenr) return DIFFERENT;
- // TODO: After b/35468874 is addressed, revisit "lenl > strlen(avc)"
- // condition, it might become superfluous.
- if (lenl > strlen(avc) &&
- fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc),
+ if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc),
lenl - strlen(avc))) {
return DIFFERENT;
}
@@ -1092,12 +1088,12 @@
// client wants to start from the beginning
it = mLogElements.begin();
} else {
- LogBufferElementCollection::iterator last = mLogElements.begin();
+ LogBufferElementCollection::iterator last;
// 30 second limit to continue search for out-of-order entries.
log_time min = start - log_time(30, 0);
// Client wants to start from some specified time. Chances are
// we are better off starting from the end of the time sorted list.
- for (it = mLogElements.end(); it != mLogElements.begin();
+ for (last = it = mLogElements.end(); it != mLogElements.begin();
/* do nothing */) {
--it;
LogBufferElement* element = *it;
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/LogKlog.cpp b/logd/LogKlog.cpp
index 9dfa14e..d23254d 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -37,46 +37,49 @@
static const char priority_message[] = { KMSG_PRIORITY(LOG_INFO), '\0' };
+// List of the _only_ needles we supply here to android::strnstr
+static const char suspendStr[] = "PM: suspend entry ";
+static const char resumeStr[] = "PM: suspend exit ";
+static const char suspendedStr[] = "Suspended for ";
+static const char healthdStr[] = "healthd";
+static const char batteryStr[] = ": battery ";
+static const char auditStr[] = " audit(";
+static const char klogdStr[] = "logd.klogd: ";
+
// Parsing is hard
// called if we see a '<', s is the next character, returns pointer after '>'
-static char* is_prio(char* s, size_t len) {
- if (!len || !isdigit(*s++)) {
- return NULL;
- }
+static char* is_prio(char* s, ssize_t len) {
+ if ((len <= 0) || !isdigit(*s++)) return nullptr;
--len;
static const size_t max_prio_len = (len < 4) ? len : 4;
size_t priolen = 0;
char c;
while (((c = *s++)) && (++priolen <= max_prio_len)) {
- if (!isdigit(c)) {
- return ((c == '>') && (*s == '[')) ? s : NULL;
- }
+ if (!isdigit(c)) return ((c == '>') && (*s == '[')) ? s : nullptr;
}
- return NULL;
+ return nullptr;
}
// called if we see a '[', s is the next character, returns pointer after ']'
-static char* is_timestamp(char* s, size_t len) {
- while (len && (*s == ' ')) {
+static char* is_timestamp(char* s, ssize_t len) {
+ while ((len > 0) && (*s == ' ')) {
++s;
--len;
}
- if (!len || !isdigit(*s++)) {
- return NULL;
- }
+ if ((len <= 0) || !isdigit(*s++)) return nullptr;
--len;
bool first_period = true;
char c;
- while (len && ((c = *s++))) {
+ while ((len > 0) && ((c = *s++))) {
--len;
if ((c == '.') && first_period) {
first_period = false;
} else if (!isdigit(c)) {
- return ((c == ']') && !first_period && (*s == ' ')) ? s : NULL;
+ return ((c == ']') && !first_period && (*s == ' ')) ? s : nullptr;
}
}
- return NULL;
+ return nullptr;
}
// Like strtok_r with "\r\n" except that we look for log signatures (regex)
@@ -93,96 +96,82 @@
// space is one more than <digit> of 9
#define OPEN_BRACKET_SPACE ((char)(OPEN_BRACKET_SIG | 10))
-char* log_strntok_r(char* s, size_t* len, char** last, size_t* sublen) {
- *sublen = 0;
- if (!*len) {
- return NULL;
- }
+char* android::log_strntok_r(char* s, ssize_t& len, char*& last,
+ ssize_t& sublen) {
+ sublen = 0;
+ if (len <= 0) return nullptr;
if (!s) {
- if (!(s = *last)) {
- return NULL;
- }
+ if (!(s = last)) return nullptr;
// fixup for log signature split <,
// LESS_THAN_SIG + <digit>
if ((*s & SIGNATURE_MASK) == LESS_THAN_SIG) {
*s = (*s & ~SIGNATURE_MASK) + '0';
*--s = '<';
- ++*len;
+ ++len;
}
// fixup for log signature split [,
// OPEN_BRACKET_SPACE is space, OPEN_BRACKET_SIG + <digit>
if ((*s & SIGNATURE_MASK) == OPEN_BRACKET_SIG) {
- if (*s == OPEN_BRACKET_SPACE) {
- *s = ' ';
- } else {
- *s = (*s & ~SIGNATURE_MASK) + '0';
- }
+ *s = (*s == OPEN_BRACKET_SPACE) ? ' ' : (*s & ~SIGNATURE_MASK) + '0';
*--s = '[';
- ++*len;
+ ++len;
}
}
- while (*len && ((*s == '\r') || (*s == '\n'))) {
+ while ((len > 0) && ((*s == '\r') || (*s == '\n'))) {
++s;
- --*len;
+ --len;
}
- if (!*len) {
- *last = NULL;
- return NULL;
- }
+ if (len <= 0) return last = nullptr;
char *peek, *tok = s;
for (;;) {
- if (*len == 0) {
- *last = NULL;
+ if (len <= 0) {
+ last = nullptr;
return tok;
}
char c = *s++;
- --*len;
- size_t adjust;
+ --len;
+ ssize_t adjust;
switch (c) {
case '\r':
case '\n':
s[-1] = '\0';
- *last = s;
+ last = s;
return tok;
case '<':
- peek = is_prio(s, *len);
- if (!peek) {
- break;
- }
+ peek = is_prio(s, len);
+ if (!peek) break;
if (s != (tok + 1)) { // not first?
s[-1] = '\0';
*s &= ~SIGNATURE_MASK;
*s |= LESS_THAN_SIG; // signature for '<'
- *last = s;
+ last = s;
return tok;
}
adjust = peek - s;
- if (adjust > *len) {
- adjust = *len;
+ if (adjust > len) {
+ adjust = len;
}
- *sublen += adjust;
- *len -= adjust;
+ sublen += adjust;
+ len -= adjust;
s = peek;
- if ((*s == '[') && ((peek = is_timestamp(s + 1, *len - 1)))) {
+ if ((*s == '[') && ((peek = is_timestamp(s + 1, len - 1)))) {
adjust = peek - s;
- if (adjust > *len) {
- adjust = *len;
+ if (adjust > len) {
+ adjust = len;
}
- *sublen += adjust;
- *len -= adjust;
+ sublen += adjust;
+ len -= adjust;
s = peek;
}
break;
case '[':
- peek = is_timestamp(s, *len);
- if (!peek) {
- break;
- }
+ peek = is_timestamp(s, len);
+ if (!peek) break;
if (s != (tok + 1)) { // not first?
s[-1] = '\0';
if (*s == ' ') {
@@ -191,19 +180,19 @@
*s &= ~SIGNATURE_MASK;
*s |= OPEN_BRACKET_SIG; // signature for '['
}
- *last = s;
+ last = s;
return tok;
}
adjust = peek - s;
- if (adjust > *len) {
- adjust = *len;
+ if (adjust > len) {
+ adjust = len;
}
- *sublen += adjust;
- *len -= adjust;
+ sublen += adjust;
+ len -= adjust;
s = peek;
break;
}
- ++*sublen;
+ ++sublen;
}
// NOTREACHED
}
@@ -222,9 +211,10 @@
initialized(false),
enableLogging(true),
auditd(auditd) {
- static const char klogd_message[] = "%slogd.klogd: %" PRIu64 "\n";
- char buffer[sizeof(priority_message) + sizeof(klogd_message) + 20 - 4];
- snprintf(buffer, sizeof(buffer), klogd_message, priority_message,
+ static const char klogd_message[] = "%s%s%" PRIu64 "\n";
+ char buffer[strlen(priority_message) + strlen(klogdStr) +
+ strlen(klogd_message) + 20];
+ snprintf(buffer, sizeof(buffer), klogd_message, priority_message, klogdStr,
signature.nsec());
write(fdWrite, buffer, strlen(buffer));
}
@@ -237,15 +227,15 @@
}
char buffer[LOGGER_ENTRY_MAX_PAYLOAD];
- size_t len = 0;
+ ssize_t len = 0;
for (;;) {
ssize_t retval = 0;
- if ((sizeof(buffer) - 1 - len) > 0) {
+ if (len < (ssize_t)(sizeof(buffer) - 1)) {
retval =
read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len);
}
- if ((retval == 0) && (len == 0)) {
+ if ((retval == 0) && (len <= 0)) {
break;
}
if (retval < 0) {
@@ -255,15 +245,16 @@
bool full = len == (sizeof(buffer) - 1);
char* ep = buffer + len;
*ep = '\0';
- size_t sublen;
- for (char *ptr = NULL, *tok = buffer;
- ((tok = log_strntok_r(tok, &len, &ptr, &sublen))); tok = NULL) {
+ ssize_t sublen;
+ for (char *ptr = nullptr, *tok = buffer;
+ !!(tok = android::log_strntok_r(tok, len, ptr, sublen));
+ tok = nullptr) {
if (((tok + sublen) >= ep) && (retval != 0) && full) {
- memmove(buffer, tok, sublen);
+ if (sublen > 0) memmove(buffer, tok, sublen);
len = sublen;
break;
}
- if (*tok) {
+ if ((sublen > 0) && *tok) {
log(tok, sublen);
}
}
@@ -273,9 +264,12 @@
}
void LogKlog::calculateCorrection(const log_time& monotonic,
- const char* real_string, size_t len) {
+ const char* real_string, ssize_t len) {
+ static const char real_format[] = "%Y-%m-%d %H:%M:%S.%09q UTC";
+ if (len < (ssize_t)(strlen(real_format) + 5)) return;
+
log_time real;
- const char* ep = real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC");
+ const char* ep = real.strptime(real_string, real_format);
if (!ep || (ep > &real_string[len]) || (real > log_time(CLOCK_REALTIME))) {
return;
}
@@ -299,73 +293,50 @@
}
}
-static const char suspendStr[] = "PM: suspend entry ";
-static const char resumeStr[] = "PM: suspend exit ";
-static const char suspendedStr[] = "Suspended for ";
-
-const char* android::strnstr(const char* s, size_t len, const char* needle) {
- char c;
-
- if (!len) return NULL;
- if ((c = *needle++) != 0) {
- size_t needleLen = strlen(needle);
- do {
- do {
- if (len <= needleLen) return NULL;
- --len;
- } while (*s++ != c);
- } while (fastcmp<memcmp>(s, needle, needleLen));
- s--;
- }
- return s;
-}
-
-void LogKlog::sniffTime(log_time& now, const char** buf, size_t len,
+void LogKlog::sniffTime(log_time& now, const char*& buf, ssize_t len,
bool reverse) {
- const char* cp = now.strptime(*buf, "[ %s.%q]");
- if (cp && (cp >= &(*buf)[len])) {
- cp = NULL;
+ if (len <= 0) return;
+
+ const char* cp = nullptr;
+ if ((len > 10) && (*buf == '[')) {
+ cp = now.strptime(buf, "[ %s.%q]"); // can index beyond buffer bounds
+ if (cp && (cp > &buf[len - 1])) cp = nullptr;
}
if (cp) {
- static const char healthd[] = "healthd";
- static const char battery[] = ": battery ";
-
- len -= cp - *buf;
- if (len && isspace(*cp)) {
+ len -= cp - buf;
+ if ((len > 0) && isspace(*cp)) {
++cp;
--len;
}
- *buf = cp;
+ buf = cp;
- if (isMonotonic()) {
- return;
- }
+ if (isMonotonic()) return;
const char* b;
if (((b = android::strnstr(cp, len, suspendStr))) &&
- ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
+ (((b += strlen(suspendStr)) - cp) < len)) {
len -= b - cp;
calculateCorrection(now, b, len);
} else if (((b = android::strnstr(cp, len, resumeStr))) &&
- ((size_t)((b += sizeof(resumeStr) - 1) - cp) < len)) {
+ (((b += strlen(resumeStr)) - cp) < len)) {
len -= b - cp;
calculateCorrection(now, b, len);
- } else if (((b = android::strnstr(cp, len, healthd))) &&
- ((size_t)((b += sizeof(healthd) - 1) - cp) < len) &&
- ((b = android::strnstr(b, len -= b - cp, battery))) &&
- ((size_t)((b += sizeof(battery) - 1) - cp) < len)) {
+ } else if (((b = android::strnstr(cp, len, healthdStr))) &&
+ (((b += strlen(healthdStr)) - cp) < len) &&
+ ((b = android::strnstr(b, len -= b - cp, batteryStr))) &&
+ (((b += strlen(batteryStr)) - cp) < len)) {
// NB: healthd is roughly 150us late, so we use it instead to
// trigger a check for ntp-induced or hardware clock drift.
log_time real(CLOCK_REALTIME);
log_time mono(CLOCK_MONOTONIC);
correction = (real < mono) ? log_time::EPOCH : (real - mono);
} else if (((b = android::strnstr(cp, len, suspendedStr))) &&
- ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
+ (((b += strlen(suspendStr)) - cp) < len)) {
len -= b - cp;
log_time real;
char* endp;
real.tv_sec = strtol(b, &endp, 10);
- if ((*endp == '.') && ((size_t)(endp - b) < len)) {
+ if ((*endp == '.') && ((endp - b) < len)) {
unsigned long multiplier = NS_PER_SEC;
real.tv_nsec = 0;
len -= endp - b;
@@ -394,8 +365,15 @@
}
}
-pid_t LogKlog::sniffPid(const char** buf, size_t len) {
- const char* cp = *buf;
+pid_t LogKlog::sniffPid(const char*& buf, ssize_t len) {
+ if (len <= 0) return 0;
+
+ const char* cp = buf;
+ // sscanf does a strlen, let's check if the string is not nul terminated.
+ // pseudo out-of-bounds access since we always have an extra char on buffer.
+ if (((ssize_t)strnlen(cp, len) == len) && cp[len]) {
+ return 0;
+ }
// HTC kernels with modified printk "c0 1648 "
if ((len > 9) && (cp[0] == 'c') && isdigit(cp[1]) &&
(isdigit(cp[2]) || (cp[2] == ' ')) && (cp[3] == ' ')) {
@@ -412,7 +390,7 @@
int pid = 0;
char dummy;
if (sscanf(cp + 4, "%d%c", &pid, &dummy) == 2) {
- *buf = cp + 10; // skip-it-all
+ buf = cp + 10; // skip-it-all
return pid;
}
}
@@ -434,28 +412,28 @@
}
// kernel log prefix, convert to a kernel log priority number
-static int parseKernelPrio(const char** buf, size_t len) {
+static int parseKernelPrio(const char*& buf, ssize_t len) {
int pri = LOG_USER | LOG_INFO;
- const char* cp = *buf;
- if (len && (*cp == '<')) {
+ const char* cp = buf;
+ if ((len > 0) && (*cp == '<')) {
pri = 0;
while (--len && isdigit(*++cp)) {
pri = (pri * 10) + *cp - '0';
}
- if (len && (*cp == '>')) {
+ if ((len > 0) && (*cp == '>')) {
++cp;
} else {
- cp = *buf;
+ cp = buf;
pri = LOG_USER | LOG_INFO;
}
- *buf = cp;
+ buf = cp;
}
return pri;
}
// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
// compensated start time.
-void LogKlog::synchronize(const char* buf, size_t len) {
+void LogKlog::synchronize(const char* buf, ssize_t len) {
const char* cp = android::strnstr(buf, len, suspendStr);
if (!cp) {
cp = android::strnstr(buf, len, resumeStr);
@@ -471,10 +449,10 @@
if (*cp == '\n') {
++cp;
}
- parseKernelPrio(&cp, len - (cp - buf));
+ parseKernelPrio(cp, len - (cp - buf));
log_time now;
- sniffTime(now, &cp, len - (cp - buf), true);
+ sniffTime(now, cp, len - (cp - buf), true);
const char* suspended = android::strnstr(buf, len, suspendedStr);
if (!suspended || (suspended > cp)) {
@@ -488,9 +466,9 @@
if (*cp == '\n') {
++cp;
}
- parseKernelPrio(&cp, len - (cp - buf));
+ parseKernelPrio(cp, len - (cp - buf));
- sniffTime(now, &cp, len - (cp - buf), true);
+ sniffTime(now, cp, len - (cp - buf), true);
}
// Convert kernel log priority number into an Android Logger priority number
@@ -523,9 +501,9 @@
return ANDROID_LOG_INFO;
}
-static const char* strnrchr(const char* s, size_t len, char c) {
- const char* save = NULL;
- for (; len; ++s, len--) {
+static const char* strnrchr(const char* s, ssize_t len, char c) {
+ const char* save = nullptr;
+ for (; len > 0; ++s, len--) {
if (*s == c) {
save = s;
}
@@ -566,22 +544,21 @@
// logd.klogd:
// return -1 if message logd.klogd: <signature>
//
-int LogKlog::log(const char* buf, size_t len) {
- if (auditd && android::strnstr(buf, len, " audit(")) {
+int LogKlog::log(const char* buf, ssize_t len) {
+ if (auditd && android::strnstr(buf, len, auditStr)) {
return 0;
}
const char* p = buf;
- int pri = parseKernelPrio(&p, len);
+ int pri = parseKernelPrio(p, len);
log_time now;
- sniffTime(now, &p, len - (p - buf), false);
+ sniffTime(now, p, len - (p - buf), false);
// sniff for start marker
- const char klogd_message[] = "logd.klogd: ";
- const char* start = android::strnstr(p, len - (p - buf), klogd_message);
+ const char* start = android::strnstr(p, len - (p - buf), klogdStr);
if (start) {
- uint64_t sig = strtoll(start + sizeof(klogd_message) - 1, NULL, 10);
+ uint64_t sig = strtoll(start + strlen(klogdStr), nullptr, 10);
if (sig == signature.nsec()) {
if (initialized) {
enableLogging = true;
@@ -598,7 +575,7 @@
}
// Parse pid, tid and uid
- const pid_t pid = sniffPid(&p, len - (p - buf));
+ const pid_t pid = sniffPid(p, len - (p - buf));
const pid_t tid = pid;
uid_t uid = AID_ROOT;
if (pid) {
@@ -620,11 +597,11 @@
start = p;
const char* tag = "";
const char* etag = tag;
- size_t taglen = len - (p - buf);
+ ssize_t taglen = len - (p - buf);
const char* bt = p;
static const char infoBrace[] = "[INFO]";
- static const size_t infoBraceLen = strlen(infoBrace);
+ static const ssize_t infoBraceLen = strlen(infoBrace);
if ((taglen >= infoBraceLen) &&
!fastcmp<strncmp>(p, infoBrace, infoBraceLen)) {
// <PRI>[<TIME>] "[INFO]"<tag> ":" message
@@ -633,26 +610,26 @@
}
const char* et;
- for (et = bt; taglen && *et && (*et != ':') && !isspace(*et);
+ for (et = bt; (taglen > 0) && *et && (*et != ':') && !isspace(*et);
++et, --taglen) {
// skip ':' within [ ... ]
if (*et == '[') {
- while (taglen && *et && *et != ']') {
+ while ((taglen > 0) && *et && *et != ']') {
++et;
--taglen;
}
- if (!taglen) {
+ if (taglen <= 0) {
break;
}
}
}
const char* cp;
- for (cp = et; taglen && isspace(*cp); ++cp, --taglen) {
+ for (cp = et; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
}
// Validate tag
- size_t size = et - bt;
- if (taglen && size) {
+ ssize_t size = et - bt;
+ if ((taglen > 0) && (size > 0)) {
if (*cp == ':') {
// ToDo: handle case insensitive colon separated logging stutter:
// <tag> : <tag>: ...
@@ -672,12 +649,12 @@
const char* b = cp;
cp += size;
taglen -= size;
- while (--taglen && !isspace(*++cp) && (*cp != ':')) {
+ while ((--taglen > 0) && !isspace(*++cp) && (*cp != ':')) {
}
const char* e;
- for (e = cp; taglen && isspace(*cp); ++cp, --taglen) {
+ for (e = cp; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
}
- if (taglen && (*cp == ':')) {
+ if ((taglen > 0) && (*cp == ':')) {
tag = b;
etag = e;
p = cp + 1;
@@ -685,7 +662,7 @@
} else {
// what about <PRI>[<TIME>] <tag>_host '<tag><stuff>' : message
static const char host[] = "_host";
- static const size_t hostlen = strlen(host);
+ static const ssize_t hostlen = strlen(host);
if ((size > hostlen) &&
!fastcmp<strncmp>(bt + size - hostlen, host, hostlen) &&
!fastcmp<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
@@ -693,12 +670,14 @@
cp += size - hostlen;
taglen -= size - hostlen;
if (*cp == '.') {
- while (--taglen && !isspace(*++cp) && (*cp != ':')) {
+ while ((--taglen > 0) && !isspace(*++cp) &&
+ (*cp != ':')) {
}
const char* e;
- for (e = cp; taglen && isspace(*cp); ++cp, --taglen) {
+ for (e = cp; (taglen > 0) && isspace(*cp);
+ ++cp, --taglen) {
}
- if (taglen && (*cp == ':')) {
+ if ((taglen > 0) && (*cp == ':')) {
tag = b;
etag = e;
p = cp + 1;
@@ -711,13 +690,13 @@
} else {
// <PRI>[<TIME>] <tag> <stuff>' : message
twoWord:
- while (--taglen && !isspace(*++cp) && (*cp != ':')) {
+ while ((--taglen > 0) && !isspace(*++cp) && (*cp != ':')) {
}
const char* e;
- for (e = cp; taglen && isspace(*cp); ++cp, --taglen) {
+ for (e = cp; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
}
// Two words
- if (taglen && (*cp == ':')) {
+ if ((taglen > 0) && (*cp == ':')) {
tag = bt;
etag = e;
p = cp + 1;
@@ -726,13 +705,13 @@
} // else no tag
static const char cpu[] = "CPU";
- static const size_t cpuLen = strlen(cpu);
+ static const ssize_t cpuLen = strlen(cpu);
static const char warning[] = "WARNING";
- static const size_t warningLen = strlen(warning);
+ static const ssize_t warningLen = strlen(warning);
static const char error[] = "ERROR";
- static const size_t errorLen = strlen(error);
+ static const ssize_t errorLen = strlen(error);
static const char info[] = "INFO";
- static const size_t infoLen = strlen(info);
+ static const ssize_t infoLen = strlen(info);
size = etag - tag;
if ((size <= 1) ||
@@ -756,13 +735,13 @@
// Mediatek-special printk induced stutter
const char* mp = strnrchr(tag, taglen, ']');
if (mp && (++mp < etag)) {
- size_t s = etag - mp;
+ ssize_t s = etag - mp;
if (((s + s) < taglen) && !fastcmp<memcmp>(mp, mp - 1 - s, s)) {
taglen = mp - tag;
}
}
// Deal with sloppy and simplistic harmless p = cp + 1 etc above.
- if (len < (size_t)(p - buf)) {
+ if (len < (p - buf)) {
p = &buf[len];
}
// skip leading space
@@ -770,12 +749,12 @@
++p;
}
// truncate trailing space or nuls
- size_t b = len - (p - buf);
- while (b && (isspace(p[b - 1]) || !p[b - 1])) {
+ ssize_t b = len - (p - buf);
+ while ((b > 0) && (isspace(p[b - 1]) || !p[b - 1])) {
--b;
}
// trick ... allow tag with empty content to be logged. log() drops empty
- if (!b && taglen) {
+ if ((b <= 0) && (taglen > 0)) {
p = " ";
b = 1;
}
@@ -787,9 +766,9 @@
taglen = LOGGER_ENTRY_MAX_PAYLOAD;
}
// calculate buffer copy requirements
- size_t n = 1 + taglen + 1 + b + 1;
+ ssize_t n = 1 + taglen + 1 + b + 1;
// paranoid sanity check, first two just can not happen ...
- if ((taglen > n) || (b > n) || (n > USHRT_MAX)) {
+ if ((taglen > n) || (b > n) || (n > (ssize_t)USHRT_MAX) || (n <= 0)) {
return -EINVAL;
}
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 976afc8..bb92dd2 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -20,8 +20,6 @@
#include <private/android_logger.h>
#include <sysutils/SocketListener.h>
-char* log_strntok_r(char* s, size_t* len, char** saveptr, size_t* sublen);
-
class LogBuffer;
class LogReader;
@@ -43,8 +41,8 @@
public:
LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
bool auditd);
- int log(const char* buf, size_t len);
- void synchronize(const char* buf, size_t len);
+ int log(const char* buf, ssize_t len);
+ void synchronize(const char* buf, ssize_t len);
bool isMonotonic() {
return logbuf->isMonotonic();
@@ -57,10 +55,10 @@
}
protected:
- void sniffTime(log_time& now, const char** buf, size_t len, bool reverse);
- pid_t sniffPid(const char** buf, size_t len);
+ void sniffTime(log_time& now, const char*& buf, ssize_t len, bool reverse);
+ pid_t sniffPid(const char*& buf, ssize_t len);
void calculateCorrection(const log_time& monotonic, const char* real_string,
- size_t len);
+ ssize_t len);
virtual bool onDataAvailable(SocketClient* cli);
};
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 93d2a79..fa9f398 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -43,8 +43,25 @@
const char* tagToName(uint32_t tag);
void ReReadEventLogTags();
-// Furnished by LogKlog.cpp.
-const char* strnstr(const char* s, size_t len, const char* needle);
+// Furnished by LogKlog.cpp
+char* log_strntok_r(char* s, ssize_t& len, char*& saveptr, ssize_t& sublen);
+
+// needle should reference a string longer than 1 character
+static inline const char* strnstr(const char* s, ssize_t len,
+ const char* needle) {
+ if (len <= 0) return nullptr;
+
+ const char c = *needle++;
+ const size_t needleLen = strlen(needle);
+ do {
+ do {
+ if (len <= (ssize_t)needleLen) return nullptr;
+ --len;
+ } while (*s++ != c);
+ } while (fastcmp<memcmp>(s, needle, needleLen));
+ s--;
+ return s;
+}
}
// Furnished in LogCommand.cpp
diff --git a/logd/main.cpp b/logd/main.cpp
index 3334506..946485b 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -221,7 +221,7 @@
static sem_t reinit;
static bool reinit_running = false;
-static LogBuffer* logBuf = NULL;
+static LogBuffer* logBuf = nullptr;
static bool package_list_parser_cb(pkg_info* info, void* /* userdata */) {
bool rc = true;
@@ -254,9 +254,9 @@
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
// uidToName Privileged Worker
if (uid) {
- name = NULL;
+ name = nullptr;
- packagelist_parse(package_list_parser_cb, NULL);
+ packagelist_parse(package_list_parser_cb, nullptr);
uid = 0;
sem_post(&uidName);
@@ -291,19 +291,19 @@
// Anything that reads persist.<property>
if (logBuf) {
logBuf->init();
- logBuf->initPrune(NULL);
+ logBuf->initPrune(nullptr);
}
android::ReReadEventLogTags();
}
- return NULL;
+ return nullptr;
}
static sem_t sem_name;
char* android::uidToName(uid_t u) {
if (!u || !reinit_running) {
- return NULL;
+ return nullptr;
}
sem_wait(&sem_name);
@@ -311,7 +311,7 @@
// Not multi-thread safe, we use sem_name to protect
uid = u;
- name = NULL;
+ name = nullptr;
sem_post(&reinit);
sem_wait(&uidName);
char* ret = name;
@@ -332,12 +332,13 @@
return;
}
- int rc = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
+ int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
if (rc <= 0) {
return;
}
- size_t len = rc + 1024; // Margin for additional input race or trailing nul
+ // Margin for additional input race or trailing nul
+ ssize_t len = rc + 1024;
std::unique_ptr<char[]> buf(new char[len]);
rc = klogctl(KLOG_READ_ALL, buf.get(), len);
@@ -345,7 +346,7 @@
return;
}
- if ((size_t)rc < len) {
+ if (rc < len) {
len = rc + 1;
}
buf[--len] = '\0';
@@ -354,10 +355,11 @@
kl->synchronize(buf.get(), len);
}
- size_t sublen;
- for (char *ptr = NULL, *tok = buf.get();
- (rc >= 0) && ((tok = log_strntok_r(tok, &len, &ptr, &sublen)));
- tok = NULL) {
+ ssize_t sublen;
+ for (char *ptr = nullptr, *tok = buf.get();
+ (rc >= 0) && !!(tok = android::log_strntok_r(tok, len, ptr, sublen));
+ tok = nullptr) {
+ if ((sublen <= 0) || !*tok) continue;
if (al) {
rc = al->log(tok, sublen);
}
@@ -444,7 +446,7 @@
if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
pthread_t thread;
reinit_running = true;
- if (pthread_create(&thread, &attr, reinit_thread_start, NULL)) {
+ if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
reinit_running = false;
}
}
@@ -507,7 +509,7 @@
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
- LogAudit* al = NULL;
+ LogAudit* al = nullptr;
if (auditd) {
al = new LogAudit(logBuf, reader,
__android_logger_property_get_bool(
@@ -516,9 +518,9 @@
: -1);
}
- LogKlog* kl = NULL;
+ LogKlog* kl = nullptr;
if (klogd) {
- kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != NULL);
+ kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);
}
readDmesg(al, kl);
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index 7ee0464..ccb1aa6 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -32,3 +32,22 @@
"-Werror",
],
}
+
+// ========================================================
+// Benchmark
+// ========================================================
+cc_benchmark {
+ name: "android_fork_execvp_ext_benchmark",
+ srcs: [
+ "android_fork_execvp_ext_benchmark.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "liblogwrap",
+ ],
+ cflags: [
+ "-Werror",
+ ]
+}
diff --git a/logwrapper/android_fork_execvp_ext_benchmark.cpp b/logwrapper/android_fork_execvp_ext_benchmark.cpp
new file mode 100644
index 0000000..1abd932
--- /dev/null
+++ b/logwrapper/android_fork_execvp_ext_benchmark.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#include "logwrap/logwrap.h"
+
+#include <android-base/logging.h>
+#include <benchmark/benchmark.h>
+
+static void BM_android_fork_execvp_ext(benchmark::State& state) {
+ const char* argv[] = {"/system/bin/echo", "hello", "world"};
+ const int argc = 3;
+ while (state.KeepRunning()) {
+ int rc = android_fork_execvp_ext(
+ argc, (char**)argv, NULL /* status */, false /* ignore_int_quit */, LOG_NONE,
+ false /* abbreviated */, NULL /* file_path */, NULL /* opts */, 0 /* opts_len */);
+ CHECK_EQ(0, rc);
+ }
+}
+BENCHMARK(BM_android_fork_execvp_ext);
+
+BENCHMARK_MAIN();
diff --git a/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h
index 89a8fdd..d3538b3 100644
--- a/logwrapper/include/logwrap/logwrap.h
+++ b/logwrapper/include/logwrap/logwrap.h
@@ -54,9 +54,8 @@
* the specified log until the child has exited.
* file_path: if log_target has the LOG_FILE bit set, then this parameter
* must be set to the pathname of the file to log to.
- * opts: set to non-NULL if you want to use one or more of the
- * FORK_EXECVP_OPTION_* features.
- * opts_len: the length of the opts array. When opts is NULL, pass 0.
+ * unused_opts: currently unused.
+ * unused_opts_len: currently unused.
*
* Return value:
* 0 when logwrap successfully run the child process and captured its status
@@ -72,30 +71,10 @@
#define LOG_KLOG 2
#define LOG_FILE 4
-/* Write data to child's stdin. */
-#define FORK_EXECVP_OPTION_INPUT 0
-/* Capture data from child's stdout and stderr. */
-#define FORK_EXECVP_OPTION_CAPTURE_OUTPUT 1
-
-struct AndroidForkExecvpOption {
- int opt_type;
- union {
- struct {
- const uint8_t* input;
- size_t input_len;
- } opt_input;
- struct {
- void (*on_output)(const uint8_t* /*output*/,
- size_t /*output_len*/,
- void* /* user_pointer */);
- void* user_pointer;
- } opt_capture_output;
- };
-};
-
+// TODO: Remove unused_opts / unused_opts_len in a followup change.
int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
- int log_target, bool abbreviated, char *file_path,
- const struct AndroidForkExecvpOption* opts, size_t opts_len);
+ int log_target, bool abbreviated, char *file_path, void* unused_opts,
+ int unused_opts_len);
/* Similar to above, except abbreviated logging is not available, and if logwrap
* is true, logging is to the Android system log, and if false, there is no
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 3ad0983..7076078 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -291,8 +291,7 @@
}
static int parent(const char *tag, int parent_read, pid_t pid,
- int *chld_sts, int log_target, bool abbreviated, char *file_path,
- const struct AndroidForkExecvpOption* opts, size_t opts_len) {
+ int *chld_sts, int log_target, bool abbreviated, char *file_path) {
int status = 0;
char buffer[4096];
struct pollfd poll_fds[] = {
@@ -359,13 +358,6 @@
sz = TEMP_FAILURE_RETRY(
read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
- for (size_t i = 0; sz > 0 && i < opts_len; ++i) {
- if (opts[i].opt_type == FORK_EXECVP_OPTION_CAPTURE_OUTPUT) {
- opts[i].opt_capture_output.on_output(
- (uint8_t*)&buffer[b], sz, opts[i].opt_capture_output.user_pointer);
- }
- }
-
sz += b;
// Log one line at a time
for (b = 0; b < sz; b++) {
@@ -483,7 +475,7 @@
int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
int log_target, bool abbreviated, char *file_path,
- const struct AndroidForkExecvpOption* opts, size_t opts_len) {
+ void *unused_opts, int unused_opts_len) {
pid_t pid;
int parent_ptty;
int child_ptty;
@@ -493,6 +485,9 @@
sigset_t oldset;
int rc = 0;
+ LOG_ALWAYS_FATAL_IF(unused_opts != NULL);
+ LOG_ALWAYS_FATAL_IF(unused_opts_len != 0);
+
rc = pthread_mutex_lock(&fd_mutex);
if (rc) {
ERROR("failed to lock signal_fd mutex\n");
@@ -538,13 +533,6 @@
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
close(parent_ptty);
- // redirect stdin, stdout and stderr
- for (size_t i = 0; i < opts_len; ++i) {
- if (opts[i].opt_type == FORK_EXECVP_OPTION_INPUT) {
- dup2(child_ptty, 0);
- break;
- }
- }
dup2(child_ptty, 1);
dup2(child_ptty, 2);
close(child_ptty);
@@ -561,24 +549,8 @@
sigaction(SIGQUIT, &ignact, &quitact);
}
- for (size_t i = 0; i < opts_len; ++i) {
- if (opts[i].opt_type == FORK_EXECVP_OPTION_INPUT) {
- size_t left = opts[i].opt_input.input_len;
- const uint8_t* input = opts[i].opt_input.input;
- while (left > 0) {
- ssize_t res =
- TEMP_FAILURE_RETRY(write(parent_ptty, input, left));
- if (res < 0) {
- break;
- }
- left -= res;
- input += res;
- }
- }
- }
-
rc = parent(argv[0], parent_ptty, pid, status, log_target,
- abbreviated, file_path, opts, opts_len);
+ abbreviated, file_path);
}
if (ignore_int_quit) {
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
new file mode 100644
index 0000000..e3468ca
--- /dev/null
+++ b/rootdir/etc/ld.config.txt
@@ -0,0 +1,47 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Bionic loader config file.
+#
+
+#dir.vendor=/vendor/bin/
+#dir.system=/system/bin/
+
+[vendor]
+
+# When this flag is set to true linker will
+# set target_sdk_version for this binary to
+# the version specified in <dirname>/.version
+# file, where <dirname> = dirname(executable_path)
+#
+# default value is false
+enable.target.sdk.version = true
+
+# There is always the default namespace no
+# need to mention it in this list
+additional.namespaces=system
+
+# Is the namespace isolated
+namespace.default.isolated = true
+namespace.default.search.paths = /vendor/${LIB}
+
+# TODO: property for asan search path?
+namespace.default.permitted.paths = /vendor/${LIB}
+namespace.default.asan.permitted.paths = /data/vendor/${LIB}
+namespace.default.links = system
+
+# Todo complete this list
+namespace.default.link.system.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so
+
+namespace.system.isolated = true
+namespace.system.search.paths = /system/${LIB}:/system/${LIB}/framework
+namespace.system.permitted.paths = /system/${LIB}
+
+[system]
+namespace.default.isolated = true
+namespace.default.search.paths = /system/${LIB}
+namespace.default.permitted.paths = /system/${LIB}
+
+# app_process will setup additional vendor namespace manually.
+# Note that zygote will need vendor namespace to setup list of public
+# libraries provided by vendors to apps.
+
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 25cea7f..d48b892 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -150,15 +150,6 @@
write /dev/cpuctl/cpu.rt_period_us 1000000
write /dev/cpuctl/cpu.rt_runtime_us 950000
- mkdir /dev/cpuctl/bg_non_interactive
- chown system system /dev/cpuctl/bg_non_interactive/tasks
- chmod 0666 /dev/cpuctl/bg_non_interactive/tasks
- # 5.0 %
- write /dev/cpuctl/bg_non_interactive/cpu.shares 52
- write /dev/cpuctl/bg_non_interactive/cpu.rt_period_us 1000000
- # active FIFO threads will never be in BG
- write /dev/cpuctl/bg_non_interactive/cpu.rt_runtime_us 10000
-
# sets up initial cpusets for ActivityManager
mkdir /dev/cpuset
mount cpuset none /dev/cpuset
@@ -308,6 +299,8 @@
on post-fs
start logd
+ start hwservicemanager
+
# once everything is setup, no need to modify /
mount rootfs rootfs / ro remount
# Mount shared so changes propagate into child namespaces
@@ -417,6 +410,9 @@
mkdir /data/misc/profman 0770 system shell
mkdir /data/misc/gcov 0770 root root
+ mkdir /data/vendor 0771 root root
+ mkdir /data/vendor/hardware 0771 root root
+
# For security reasons, /data/local/tmp should always be empty.
# Do not place files or directories in /data/local/tmp
mkdir /data/local/tmp 0771 shell shell
@@ -595,13 +591,14 @@
# Define default initial receive window size in segments.
setprop net.tcp.default_init_rwnd 60
- # Start all binderized HAL daemons
- start hwservicemanager
+ # Start standard binderized HAL daemons
+ class_start hal
+
class_start core
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
@@ -624,12 +621,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/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
index 381df5a..8fcd17f 100644
--- a/tzdatacheck/tzdatacheck.cpp
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -30,6 +30,19 @@
#include "android-base/logging.h"
+// The name of the directory that holds a staged time zone update distro. If this exists it should
+// replace the one in CURRENT_DIR_NAME.
+// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
+static const char* STAGED_DIR_NAME = "/staged";
+
+// The name of the directory that holds the (optional) installed time zone update distro.
+// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
+static const char* CURRENT_DIR_NAME = "/current";
+
+// The name of a file in the staged dir that indicates the staged operation is an "uninstall".
+// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
+static const char* UNINSTALL_TOMBSTONE_FILE_NAME = "/STAGED_UNINSTALL_TOMBSTONE";
+
// The name of the file containing the distro version information.
// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
static const char* DISTRO_VERSION_FILENAME = "/distro_version";
@@ -75,7 +88,6 @@
static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
-
static void usage() {
std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
"\n"
@@ -184,7 +196,7 @@
return 0;
}
-enum PathStatus { ERR, NONE, IS_DIR, NOT_DIR };
+enum PathStatus { ERR, NONE, IS_DIR, IS_REG, UNKNOWN };
static PathStatus checkPath(const std::string& path) {
struct stat buf;
@@ -195,7 +207,31 @@
}
return NONE;
}
- return S_ISDIR(buf.st_mode) ? IS_DIR : NOT_DIR;
+ return S_ISDIR(buf.st_mode) ? IS_DIR : S_ISREG(buf.st_mode) ? IS_REG : UNKNOWN;
+}
+
+/*
+ * Deletes fileToDelete and returns true if it is successful. If fileToDelete is not a file or
+ * cannot be accessed this method returns false.
+ */
+static bool deleteFile(const std::string& fileToDelete) {
+ // Check whether the file exists.
+ PathStatus pathStatus = checkPath(fileToDelete);
+ if (pathStatus == NONE) {
+ LOG(INFO) << "Path " << fileToDelete << " does not exist";
+ return true;
+ }
+ if (pathStatus != IS_REG) {
+ LOG(WARNING) << "Path " << fileToDelete << " failed to stat() or is not a file.";
+ return false;
+ }
+
+ // Attempt the deletion.
+ int rc = unlink(fileToDelete.c_str());
+ if (rc != 0) {
+ PLOG(WARNING) << "unlink() failed for " << fileToDelete;
+ }
+ return rc == 0;
}
/*
@@ -260,8 +296,7 @@
std::string dataUpdatesDirName(dataZoneInfoDir);
dataUpdatesDirName += "/updates";
LOG(INFO) << "Removing: " << dataUpdatesDirName;
- bool deleted = deleteDir(dataUpdatesDirName);
- if (!deleted) {
+ if (!deleteDir(dataUpdatesDirName)) {
LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
<< " was not successful";
}
@@ -270,14 +305,151 @@
/*
* Deletes the timezone update distro directory.
*/
-static void deleteUpdateDistroDir(std::string& distroDirName) {
+static void deleteUpdateDistroDir(const std::string& distroDirName) {
LOG(INFO) << "Removing: " << distroDirName;
- bool deleted = deleteDir(distroDirName);
- if (!deleted) {
+ if (!deleteDir(distroDirName)) {
LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful";
}
}
+static void handleStagedUninstall(const std::string& dataStagedDirName,
+ const std::string& dataCurrentDirName,
+ const PathStatus dataCurrentDirStatus) {
+ LOG(INFO) << "Staged operation is an uninstall.";
+
+ // Delete the current install directory.
+ switch (dataCurrentDirStatus) {
+ case NONE:
+ // This is unexpected: No uninstall should be staged if there is nothing to
+ // uninstall. Carry on anyway.
+ LOG(WARNING) << "No current install to delete.";
+ break;
+ case IS_DIR:
+ // This is normal. Delete the current install dir.
+ if (!deleteDir(dataCurrentDirName)) {
+ LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
+ << " was not successful";
+ // If this happens we don't know whether we were able to delete or not. We don't
+ // delete the staged operation so it will be retried next boot unless overridden.
+ return;
+ }
+ break;
+ case IS_REG:
+ default:
+ // This is unexpected: We can try to delete the unexpected file and carry on.
+ LOG(WARNING) << "Current distro dir " << dataCurrentDirName
+ << " is not actually a directory. Attempting deletion.";
+ if (!deleteFile(dataCurrentDirName)) {
+ LOG(WARNING) << "Could not delete " << dataCurrentDirName;
+ return;
+ }
+ break;
+ }
+
+ // Delete the staged uninstall dir.
+ if (!deleteDir(dataStagedDirName)) {
+ LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
+ << " was not successful";
+ // If this happens we don't know whether we were able to delete the staged operation
+ // or not.
+ return;
+ }
+ LOG(INFO) << "Staged uninstall complete.";
+}
+
+static void handleStagedInstall(const std::string& dataStagedDirName,
+ const std::string& dataCurrentDirName,
+ const PathStatus dataCurrentDirStatus) {
+ LOG(INFO) << "Staged operation is an install.";
+
+ switch (dataCurrentDirStatus) {
+ case NONE:
+ // This is expected: This is the first install.
+ LOG(INFO) << "No current install to replace.";
+ break;
+ case IS_DIR:
+ // This is expected: We are replacing an existing install.
+ // Delete the current dir so we can replace it.
+ if (!deleteDir(dataCurrentDirName)) {
+ LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
+ << " was not successful";
+ // If this happens, we cannot proceed.
+ return;
+ }
+ break;
+ case IS_REG:
+ default:
+ // This is unexpected: We can try to delete the unexpected file and carry on.
+ LOG(WARNING) << "Current distro dir " << dataCurrentDirName
+ << " is not actually a directory. Attempting deletion.";
+ if (!deleteFile(dataCurrentDirName)) {
+ LOG(WARNING) << "Could not delete " << dataCurrentDirName;
+ return;
+ }
+ break;
+ }
+
+ // Move the staged dir so it is the new current dir, completing the install.
+ LOG(INFO) << "Moving " << dataStagedDirName << " to " << dataCurrentDirName;
+ int rc = rename(dataStagedDirName.c_str(), dataCurrentDirName.c_str());
+ if (rc == -1) {
+ PLOG(WARNING) << "Unable to rename directory from " << dataStagedDirName << " to "
+ << &dataCurrentDirName[0];
+ return;
+ }
+
+ LOG(INFO) << "Staged install complete.";
+}
+/*
+ * Process a staged operation if there is one.
+ */
+static void processStagedOperation(const std::string& dataStagedDirName,
+ const std::string& dataCurrentDirName) {
+ PathStatus dataStagedDirStatus = checkPath(dataStagedDirName);
+
+ // Exit early for the common case.
+ if (dataStagedDirStatus == NONE) {
+ LOG(DEBUG) << "No staged time zone operation.";
+ return;
+ }
+
+ // Check known directory names are in a good starting state.
+ if (dataStagedDirStatus != IS_DIR) {
+ LOG(WARNING) << "Staged distro dir " << dataStagedDirName
+ << " could not be accessed or is not a directory."
+ << " stagedDirStatus=" << dataStagedDirStatus;
+ return;
+ }
+
+ // dataStagedDirStatus == IS_DIR.
+
+ // Work out whether there is anything currently installed.
+ PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
+ if (dataCurrentDirStatus == ERR) {
+ LOG(WARNING) << "Current install dir " << dataCurrentDirName << " could not be accessed"
+ << " dataCurrentDirStatus=" << dataCurrentDirStatus;
+ return;
+ }
+
+ // We must perform the staged operation.
+
+ // Check to see if the staged directory contains an uninstall or an install operation.
+ std::string uninstallTombStoneFile(dataStagedDirName);
+ uninstallTombStoneFile += UNINSTALL_TOMBSTONE_FILE_NAME;
+ int uninstallTombStoneFileStatus = checkPath(uninstallTombStoneFile);
+ if (uninstallTombStoneFileStatus != IS_REG && uninstallTombStoneFileStatus != NONE) {
+ // Error case.
+ LOG(WARNING) << "Unable to determine if the staged operation is an uninstall.";
+ return;
+ }
+ if (uninstallTombStoneFileStatus == IS_REG) {
+ handleStagedUninstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
+ } else {
+ // uninstallTombStoneFileStatus == NONE meaning this is a staged install.
+ handleStagedInstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
+ }
+}
+
/*
* After a platform update it is likely that timezone data found on the system partition will be
* newer than the version found in the data partition. This tool detects this case and removes the
@@ -300,15 +472,25 @@
const char* systemZoneInfoDir = argv[1];
const char* dataZoneInfoDir = argv[2];
- // Check the distro directory exists. If it does not, exit quickly: nothing to do.
+ std::string dataStagedDirName(dataZoneInfoDir);
+ dataStagedDirName += STAGED_DIR_NAME;
+
std::string dataCurrentDirName(dataZoneInfoDir);
- dataCurrentDirName += "/current";
- int dataCurrentDirStatus = checkPath(dataCurrentDirName);
+ dataCurrentDirName += CURRENT_DIR_NAME;
+
+ // Check for an process any staged operation.
+ // If the staged operation could not be handled we still have to validate the current installed
+ // directory so we do not check for errors and do not quit early.
+ processStagedOperation(dataStagedDirName, dataCurrentDirName);
+
+ // Check the distro directory exists. If it does not, exit quickly: nothing to do.
+ PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
if (dataCurrentDirStatus == NONE) {
LOG(INFO) << "timezone distro dir " << dataCurrentDirName
<< " does not exist. No action required.";
return 0;
}
+
// If the distro directory path is not a directory or we can't stat() the path, exit with a
// warning: either there's a problem accessing storage or the world is not as it should be;
// nothing to do.