Merge "libziparchive: Fix build flags for targets that build with GCC"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 1c1683e..1eb3a3c 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -245,7 +245,7 @@
if (pieces.size() > 2) {
const std::string& props = pieces[2];
- for (auto& prop : android::base::Split(props, ";")) {
+ for (const auto& prop : android::base::Split(props, ";")) {
// The list of properties was traditionally ;-terminated rather than ;-separated.
if (prop.empty()) continue;
@@ -904,7 +904,7 @@
}
std::string error_msg;
- atransport* transport = acquire_one_transport(kCsAny, type, serial, &error_msg);
+ atransport* transport = acquire_one_transport(type, serial, nullptr, &error_msg);
if (!transport) {
SendFail(reply_fd, error_msg);
return 1;
@@ -990,13 +990,13 @@
serial = service;
}
- std::string error_msg;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
if (t != nullptr) {
s->transport = t;
SendOkay(reply_fd);
} else {
- SendFail(reply_fd, error_msg);
+ SendFail(reply_fd, error);
}
return 1;
}
@@ -1014,12 +1014,12 @@
}
if (!strcmp(service, "features")) {
- std::string error_msg;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
if (t != nullptr) {
SendOkay(reply_fd, FeatureSetToString(t->features()));
} else {
- SendFail(reply_fd, error_msg);
+ SendFail(reply_fd, error);
}
return 0;
}
@@ -1049,29 +1049,41 @@
return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
}
- // returns our value for ADB_SERVER_VERSION
+ // Returns our value for ADB_SERVER_VERSION.
if (!strcmp(service, "version")) {
return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
}
// These always report "unknown" rather than the actual error, for scripts.
if (!strcmp(service, "get-serialno")) {
- std::string ignored;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
- return SendOkay(reply_fd, (t && t->serial) ? t->serial : "unknown");
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ if (t) {
+ return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
+ } else {
+ return SendFail(reply_fd, error);
+ }
}
if (!strcmp(service, "get-devpath")) {
- std::string ignored;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
- return SendOkay(reply_fd, (t && t->devpath) ? t->devpath : "unknown");
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ if (t) {
+ return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
+ } else {
+ return SendFail(reply_fd, error);
+ }
}
if (!strcmp(service, "get-state")) {
- std::string ignored;
- atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
- return SendOkay(reply_fd, t ? t->connection_state_name() : "unknown");
+ std::string error;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+ if (t) {
+ return SendOkay(reply_fd, t->connection_state_name());
+ } else {
+ return SendFail(reply_fd, error);
+ }
}
- // indicates a new emulator instance has started
+ // Indicates a new emulator instance has started.
if (!strncmp(service, "emulator:", 9)) {
int port = atoi(service+9);
local_connect(port);
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index 7b314c3..5309519 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -368,7 +368,7 @@
return;
}
- for (auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
+ for (const auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
if (!read_key(path.c_str(), key_list)) {
D("Failed to read '%s'", path.c_str());
}
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index 984910d..fa8ce9e 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -209,9 +209,8 @@
adb_close(fd);
if (sscanf(&version_string[0], "%04x", &version) != 1) {
- *error = android::base::StringPrintf(
- "cannot parse version string: %s",
- version_string.c_str());
+ *error = android::base::StringPrintf("cannot parse version string: %s",
+ version_string.c_str());
return -1;
}
} else {
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 7ff5c3d..f7b2e4e 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -229,3 +229,19 @@
std::string perror_str(const char* msg) {
return android::base::StringPrintf("%s: %s", msg, strerror(errno));
}
+
+#if !defined(_WIN32)
+bool set_file_block_mode(int fd, bool block) {
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1) {
+ PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd;
+ return false;
+ }
+ flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
+ if (fcntl(fd, F_SETFL, flags) != 0) {
+ PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd << ", flags " << flags;
+ return false;
+ }
+ return true;
+}
+#endif
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index b9da980..537d0e4 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -46,4 +46,6 @@
std::string perror_str(const char* msg);
+bool set_file_block_mode(int fd, bool block);
+
#endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 17c8d0a..4a2787a 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -202,3 +202,19 @@
ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
test_mkdirs(std::string("relative/subrel/file"));
}
+
+#if !defined(_WIN32)
+TEST(adb_utils, set_file_block_mode) {
+ int fd = adb_open("/dev/null", O_RDWR | O_APPEND);
+ ASSERT_GE(fd, 0);
+ int flags = fcntl(fd, F_GETFL, 0);
+ ASSERT_EQ(O_RDWR | O_APPEND, (flags & (O_RDWR | O_APPEND)));
+ ASSERT_TRUE(set_file_block_mode(fd, false));
+ int new_flags = fcntl(fd, F_GETFL, 0);
+ ASSERT_EQ(flags | O_NONBLOCK, new_flags);
+ ASSERT_TRUE(set_file_block_mode(fd, true));
+ new_flags = fcntl(fd, F_GETFL, 0);
+ ASSERT_EQ(flags, new_flags);
+ ASSERT_EQ(0, adb_close(fd));
+}
+#endif
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index f93fe05..d128df7 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -425,6 +425,7 @@
// Used to pass multiple values to the stdin read thread.
struct StdinReadArgs {
int stdin_fd, write_fd;
+ bool raw_stdin;
std::unique_ptr<ShellProtocol> protocol;
};
@@ -452,26 +453,42 @@
D("stdin_read_thread(): pre unix_read(fdi=%d,...)", args->stdin_fd);
int r = unix_read(args->stdin_fd, buffer_ptr, buffer_size);
D("stdin_read_thread(): post unix_read(fdi=%d,...)", args->stdin_fd);
- if (r <= 0) break;
- for (int n = 0; n < r; n++){
- switch(buffer_ptr[n]) {
- case '\n':
- state = 1;
- break;
- case '\r':
- state = 1;
- break;
- case '~':
- if(state == 1) state++;
- break;
- case '.':
- if(state == 2) {
- fprintf(stderr,"\n* disconnect *\n");
- stdin_raw_restore(args->stdin_fd);
- exit(0);
+ if (r <= 0) {
+ // Only devices using the shell protocol know to close subprocess
+ // stdin. For older devices we want to just leave the connection
+ // open, otherwise an unpredictable amount of return data could
+ // be lost due to the FD closing before all data has been received.
+ if (args->protocol) {
+ args->protocol->Write(ShellProtocol::kIdCloseStdin, 0);
+ }
+ break;
+ }
+ // If we made stdin raw, check input for the "~." escape sequence. In
+ // this situation signals like Ctrl+C are sent remotely rather than
+ // interpreted locally so this provides an emergency out if the remote
+ // process starts ignoring the signal. SSH also does this, see the
+ // "escape characters" section on the ssh man page for more info.
+ if (args->raw_stdin) {
+ for (int n = 0; n < r; n++){
+ switch(buffer_ptr[n]) {
+ case '\n':
+ state = 1;
+ break;
+ case '\r':
+ state = 1;
+ break;
+ case '~':
+ if(state == 1) state++;
+ break;
+ case '.':
+ if(state == 2) {
+ stdin_raw_restore(args->stdin_fd);
+ fprintf(stderr,"\n* disconnect *\n");
+ exit(0);
+ }
+ default:
+ state = 0;
}
- default:
- state = 0;
}
}
if (args->protocol) {
@@ -488,8 +505,44 @@
return nullptr;
}
-static int interactive_shell(const std::string& service_string,
- bool use_shell_protocol) {
+// Returns a shell service string with the indicated arguments and command.
+static std::string ShellServiceString(bool use_shell_protocol,
+ const std::string& type_arg,
+ const std::string& command) {
+ std::vector<std::string> args;
+ if (use_shell_protocol) {
+ args.push_back(kShellServiceArgShellProtocol);
+ }
+ if (!type_arg.empty()) {
+ args.push_back(type_arg);
+ }
+
+ // Shell service string can look like: shell[,arg1,arg2,...]:[command].
+ return android::base::StringPrintf("shell%s%s:%s",
+ args.empty() ? "" : ",",
+ android::base::Join(args, ',').c_str(),
+ command.c_str());
+}
+
+// Connects to a shell on the device and read/writes data.
+//
+// Note: currently this function doesn't properly clean up resources; the
+// FD connected to the adb server is never closed and the stdin read thread
+// may never exit.
+//
+// On success returns the remote exit code if |use_shell_protocol| is true,
+// 0 otherwise. On failure returns 1.
+static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
+ const std::string& command) {
+ std::string service_string = ShellServiceString(use_shell_protocol,
+ type_arg, command);
+
+ // Make local stdin raw if the device allocates a PTY, which happens if:
+ // 1. We are explicitly asking for a PTY shell, or
+ // 2. We don't specify shell type and are starting an interactive session.
+ bool raw_stdin = (type_arg == kShellServiceArgPty ||
+ (type_arg.empty() && command.empty()));
+
std::string error;
int fd = adb_connect(service_string, &error);
if (fd < 0) {
@@ -502,13 +555,16 @@
LOG(ERROR) << "couldn't allocate StdinReadArgs object";
return 1;
}
- args->stdin_fd = 0;
+ args->stdin_fd = STDIN_FILENO;
args->write_fd = fd;
+ args->raw_stdin = raw_stdin;
if (use_shell_protocol) {
args->protocol.reset(new ShellProtocol(args->write_fd));
}
- stdin_raw_init(args->stdin_fd);
+ if (raw_stdin) {
+ stdin_raw_init(STDIN_FILENO);
+ }
int exit_code = 0;
if (!adb_thread_create(stdin_read_thread, args)) {
@@ -519,7 +575,12 @@
exit_code = read_and_dump(fd, use_shell_protocol);
}
- stdin_raw_restore(args->stdin_fd);
+ if (raw_stdin) {
+ stdin_raw_restore(STDIN_FILENO);
+ }
+
+ // TODO(dpursell): properly exit stdin_read_thread and close |fd|.
+
return exit_code;
}
@@ -795,25 +856,6 @@
return adb_command(cmd);
}
-// Returns a shell service string with the indicated arguments and command.
-static std::string ShellServiceString(bool use_shell_protocol,
- const std::string& type_arg,
- const std::string& command) {
- std::vector<std::string> args;
- if (use_shell_protocol) {
- args.push_back(kShellServiceArgShellProtocol);
- }
- if (!type_arg.empty()) {
- args.push_back(type_arg);
- }
-
- // Shell service string can look like: shell[,arg1,arg2,...]:[command].
- return android::base::StringPrintf("shell%s%s:%s",
- args.empty() ? "" : ",",
- android::base::Join(args, ',').c_str(),
- command.c_str());
-}
-
// Connects to the device "shell" service with |command| and prints the
// resulting output.
static int send_shell_command(TransportType transport_type, const char* serial,
@@ -1093,8 +1135,6 @@
}
// TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
- const char* serial = getenv("ANDROID_SERIAL");
-
/* Validate and assign the server port */
const char* server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
int server_port = DEFAULT_ADB_PORT;
@@ -1108,7 +1148,9 @@
}
}
- /* modifiers and flags */
+ // We need to check for -d and -e before we look at $ANDROID_SERIAL.
+ const char* serial = nullptr;
+
while (argc > 0) {
if (!strcmp(argv[0],"server")) {
is_server = 1;
@@ -1199,6 +1241,11 @@
argv++;
}
+ // If none of -d, -e, or -s were specified, try $ANDROID_SERIAL.
+ if (transport_type == kTransportAny && serial == nullptr) {
+ serial = getenv("ANDROID_SERIAL");
+ }
+
adb_set_transport(transport_type, serial);
adb_set_tcp_specifics(server_port);
@@ -1315,51 +1362,26 @@
}
}
+ std::string command;
+ if (argc) {
+ // We don't escape here, just like ssh(1). http://b/20564385.
+ command = android::base::Join(
+ std::vector<const char*>(argv, argv + argc), ' ');
+ }
+
if (h) {
printf("\x1b[41;33m");
fflush(stdout);
}
- if (!argc) {
- D("starting interactive shell");
- std::string service_string =
- ShellServiceString(use_shell_protocol, shell_type_arg, "");
- r = interactive_shell(service_string, use_shell_protocol);
- if (h) {
- printf("\x1b[0m");
- fflush(stdout);
- }
- return r;
+ r = RemoteShell(use_shell_protocol, shell_type_arg, command);
+
+ if (h) {
+ printf("\x1b[0m");
+ fflush(stdout);
}
- // We don't escape here, just like ssh(1). http://b/20564385.
- std::string command = android::base::Join(
- std::vector<const char*>(argv, argv + argc), ' ');
- std::string service_string =
- ShellServiceString(use_shell_protocol, shell_type_arg, command);
-
- while (true) {
- D("non-interactive shell loop. cmd=%s", service_string.c_str());
- std::string error;
- int fd = adb_connect(service_string, &error);
- int r;
- if (fd >= 0) {
- D("about to read_and_dump(fd=%d)", fd);
- r = read_and_dump(fd, use_shell_protocol);
- D("read_and_dump() done.");
- adb_close(fd);
- } else {
- fprintf(stderr,"error: %s\n", error.c_str());
- r = -1;
- }
-
- if (h) {
- printf("\x1b[0m");
- fflush(stdout);
- }
- D("non-interactive shell loop. return r=%d", r);
- return r;
- }
+ return r;
}
else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
int exec_in = !strcmp(argv[0], "exec-in");
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 1ccee9b..ddd15a2 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -36,6 +36,7 @@
#include "adb_io.h"
#include "adb_trace.h"
+#include "adb_utils.h"
#if !ADB_HOST
// This socket is used when a subproc shell service exists.
@@ -124,11 +125,11 @@
fde->fd = fd;
fde->func = func;
fde->arg = arg;
- if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) {
+ if (!set_file_block_mode(fd, false)) {
// Here is not proper to handle the error. If it fails here, some error is
// likely to be detected by poll(), then we can let the callback function
// to handle it.
- LOG(ERROR) << "failed to fcntl(" << fd << ") to be nonblock";
+ LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
}
auto pair = g_poll_node_map.emplace(fde->fd, PollNode(fde));
CHECK(pair.second) << "install existing fd " << fd;
@@ -198,7 +199,7 @@
static std::string dump_pollfds(const std::vector<pollfd>& pollfds) {
std::string result;
- for (auto& pollfd : pollfds) {
+ for (const auto& pollfd : pollfds) {
std::string op;
if (pollfd.events & POLLIN) {
op += "R";
@@ -213,8 +214,8 @@
static void fdevent_process() {
std::vector<pollfd> pollfds;
- for (auto it = g_poll_node_map.begin(); it != g_poll_node_map.end(); ++it) {
- pollfds.push_back(it->second.pollfd);
+ for (const auto& pair : g_poll_node_map) {
+ pollfds.push_back(pair.second.pollfd);
}
CHECK_GT(pollfds.size(), 0u);
D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
@@ -223,7 +224,7 @@
PLOG(ERROR) << "poll(), ret = " << ret;
return;
}
- for (auto& pollfd : pollfds) {
+ for (const auto& pollfd : pollfds) {
if (pollfd.revents != 0) {
D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
}
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 7457712..7fe3d37 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -18,14 +18,14 @@
#include <gtest/gtest.h>
+#include <pthread.h>
+#include <signal.h>
+
#include <limits>
#include <queue>
#include <string>
#include <vector>
-#include <pthread.h>
-#include <signal.h>
-
#include "adb_io.h"
class FdHandler {
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 9bc634c..5513e8f 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -25,6 +25,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
+#include <unistd.h>
#include <utime.h>
#include <memory>
@@ -37,6 +38,7 @@
#include "adb_utils.h"
#include "file_sync_service.h"
+#include <base/file.h>
#include <base/strings.h>
#include <base/stringprintf.h>
@@ -46,8 +48,6 @@
char data[SYNC_DATA_MAX];
};
-static syncsendbuf send_buffer;
-
static long long NOW() {
struct timeval tv;
gettimeofday(&tv, 0);
@@ -69,26 +69,6 @@
fflush(stderr);
}
-static bool SendRequest(int fd, int id, const char* path) {
- size_t path_length = strlen(path);
- if (path_length > 1024) {
- fprintf(stderr, "SendRequest failed: path too long: %zu", path_length);
- errno = ENAMETOOLONG;
- return false;
- }
-
- // Sending header and payload in a single write makes a noticeable
- // difference to "adb sync" performance.
- char buf[sizeof(SyncRequest) + path_length] __attribute__((aligned(8)));
- SyncRequest* req = reinterpret_cast<SyncRequest*>(buf);
- req->id = id;
- req->path_length = path_length;
- char* data = reinterpret_cast<char*>(req + 1);
- memcpy(data, path, path_length);
-
- return WriteFdExactly(fd, buf, sizeof(buf));
-}
-
class SyncConnection {
public:
SyncConnection() : total_bytes(0), start_time_(NOW()) {
@@ -111,6 +91,93 @@
bool IsValid() { return fd >= 0; }
+ bool SendRequest(int id, const char* path_and_mode) {
+ size_t path_length = strlen(path_and_mode);
+ if (path_length > 1024) {
+ fprintf(stderr, "SendRequest failed: path too long: %zu", path_length);
+ errno = ENAMETOOLONG;
+ return false;
+ }
+
+ // Sending header and payload in a single write makes a noticeable
+ // difference to "adb sync" performance.
+ char buf[sizeof(SyncRequest) + path_length];
+ SyncRequest* req = reinterpret_cast<SyncRequest*>(buf);
+ req->id = id;
+ req->path_length = path_length;
+ char* data = reinterpret_cast<char*>(req + 1);
+ memcpy(data, path_and_mode, path_length);
+
+ return WriteFdExactly(fd, buf, sizeof(buf));
+ }
+
+ // Sending header, payload, and footer in a single write makes a huge
+ // difference to "adb sync" performance.
+ bool SendSmallFile(const char* path_and_mode,
+ const char* data, size_t data_length,
+ unsigned mtime) {
+ size_t path_length = strlen(path_and_mode);
+ if (path_length > 1024) {
+ fprintf(stderr, "SendSmallFile failed: path too long: %zu", path_length);
+ errno = ENAMETOOLONG;
+ return false;
+ }
+
+ char buf[sizeof(SyncRequest) + path_length +
+ sizeof(SyncRequest) + data_length +
+ sizeof(SyncRequest)];
+ char* p = buf;
+
+ SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
+ req_send->id = ID_SEND;
+ req_send->path_length = path_length;
+ p += sizeof(SyncRequest);
+ memcpy(p, path_and_mode, path_length);
+ p += path_length;
+
+ SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p);
+ req_data->id = ID_DATA;
+ req_data->path_length = data_length;
+ p += sizeof(SyncRequest);
+ memcpy(p, data, data_length);
+ p += data_length;
+
+ SyncRequest* req_done = reinterpret_cast<SyncRequest*>(p);
+ req_done->id = ID_DONE;
+ req_done->path_length = mtime;
+ p += sizeof(SyncRequest);
+
+ if (!WriteFdExactly(fd, buf, (p-buf))) return false;
+
+ total_bytes += data_length;
+ return true;
+ }
+
+ bool CopyDone(const char* from, const char* to) {
+ syncmsg msg;
+ if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
+ fprintf(stderr, "failed to copy '%s' to '%s': no ID_DONE: %s\n",
+ from, to, strerror(errno));
+ return false;
+ }
+ if (msg.status.id == ID_OKAY) {
+ return true;
+ }
+ if (msg.status.id != ID_FAIL) {
+ fprintf(stderr, "failed to copy '%s' to '%s': unknown reason\n", from, to);
+ return false;
+ }
+ char buffer[msg.status.msglen + 1];
+ if (!ReadFdExactly(fd, buffer, msg.status.msglen)) {
+ fprintf(stderr, "failed to copy '%s' to '%s'; failed to read reason (!): %s\n",
+ from, to, strerror(errno));
+ return false;
+ }
+ buffer[msg.status.msglen] = 0;
+ fprintf(stderr, "failed to copy '%s' to '%s': %s\n", from, to, buffer);
+ return false;
+ }
+
uint64_t total_bytes;
// TODO: add a char[max] buffer here, to replace syncsendbuf...
@@ -121,7 +188,7 @@
uint64_t start_time_;
void SendQuit() {
- SendRequest(fd, ID_QUIT, ""); // TODO: add a SendResponse?
+ SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
}
void ShowTransferRate() {
@@ -136,12 +203,12 @@
typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name, void* cookie);
-static bool sync_ls(int fd, const char* path, sync_ls_cb func, void* cookie) {
- if (!SendRequest(fd, ID_LIST, path)) return false;
+static bool sync_ls(SyncConnection& sc, const char* path, sync_ls_cb func, void* cookie) {
+ if (!sc.SendRequest(ID_LIST, path)) return false;
while (true) {
syncmsg msg;
- if (!ReadFdExactly(fd, &msg.dent, sizeof(msg.dent))) return false;
+ if (!ReadFdExactly(sc.fd, &msg.dent, sizeof(msg.dent))) return false;
if (msg.dent.id == ID_DONE) return true;
if (msg.dent.id != ID_DENT) return false;
@@ -150,17 +217,13 @@
if (len > 256) return false; // TODO: resize buffer? continue?
char buf[257];
- if (!ReadFdExactly(fd, buf, len)) return false;
+ if (!ReadFdExactly(sc.fd, buf, len)) return false;
buf[len] = 0;
func(msg.dent.mode, msg.dent.size, msg.dent.time, buf, cookie);
}
}
-static bool sync_start_stat(SyncConnection& sc, const char* path) {
- return SendRequest(sc.fd, ID_STAT, path);
-}
-
static bool sync_finish_stat(SyncConnection& sc, unsigned int* timestamp,
unsigned int* mode, unsigned int* size) {
syncmsg msg;
@@ -177,10 +240,17 @@
static bool sync_stat(SyncConnection& sc, const char* path,
unsigned int* timestamp, unsigned int* mode, unsigned int* size) {
- return sync_start_stat(sc, path) && sync_finish_stat(sc, timestamp, mode, size);
+ return sc.SendRequest(ID_STAT, path) && sync_finish_stat(sc, timestamp, mode, size);
}
-static bool write_data_file(SyncConnection& sc, const char* path, syncsendbuf* sbuf, bool show_progress) {
+static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const char* path,
+ unsigned mtime, bool show_progress) {
+ if (!sc.SendRequest(ID_SEND, path_and_mode)) {
+ fprintf(stderr, "failed to send ID_SEND message '%s': %s\n",
+ path_and_mode, strerror(errno));
+ return false;
+ }
+
unsigned long long size = 0;
if (show_progress) {
// Determine local file size.
@@ -199,9 +269,10 @@
return false;
}
- sbuf->id = ID_DATA;
+ syncsendbuf sbuf;
+ sbuf.id = ID_DATA;
while (true) {
- int ret = adb_read(lfd, sbuf->data, sc.max);
+ int ret = adb_read(lfd, sbuf.data, sc.max);
if (ret <= 0) {
if (ret < 0) {
fprintf(stderr, "cannot read '%s': %s\n", path, strerror(errno));
@@ -211,8 +282,8 @@
break;
}
- sbuf->size = ret;
- if (!WriteFdExactly(sc.fd, sbuf, sizeof(unsigned) * 2 + ret)) {
+ sbuf.size = ret;
+ if (!WriteFdExactly(sc.fd, &sbuf, sizeof(unsigned) * 2 + ret)) {
adb_close(lfd);
return false;
}
@@ -224,103 +295,77 @@
}
adb_close(lfd);
- return true;
-}
-
-#if defined(_WIN32)
-extern bool write_data_link(SyncConnection& sc, const char* path, syncsendbuf* sbuf) __attribute__((error("no symlinks on Windows")));
-#else
-static bool write_data_link(SyncConnection& sc, const char* path, syncsendbuf* sbuf) {
- ssize_t len = readlink(path, sbuf->data, sc.max - 1);
- if (len < 0) {
- fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
- return false;
- }
- sbuf->data[len] = '\0';
-
- sbuf->size = len + 1;
- sbuf->id = ID_DATA;
-
- if (!WriteFdExactly(sc.fd, sbuf, sizeof(unsigned) * 2 + len + 1)) {
- return false;
- }
-
- sc.total_bytes += len + 1;
-
- return true;
-}
-#endif
-
-static bool sync_send(SyncConnection& sc, const char *lpath, const char *rpath,
- unsigned mtime, mode_t mode, bool show_progress)
-{
- syncsendbuf* sbuf = &send_buffer;
-
- std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
- if (!SendRequest(sc.fd, ID_SEND, path_and_mode.c_str())) {
- fprintf(stderr, "failed to send ID_SEND message '%s': %s\n",
- path_and_mode.c_str(), strerror(errno));
- return false;
- }
-
- if (S_ISREG(mode)) {
- if (!write_data_file(sc, lpath, sbuf, show_progress)) return false;
- } else if (S_ISLNK(mode)) {
- if (!write_data_link(sc, lpath, sbuf)) return false;
- } else {
- fprintf(stderr, "local file '%s' has unsupported mode: 0o%o\n", lpath, mode);
- return false;
- }
syncmsg msg;
msg.data.id = ID_DONE;
msg.data.size = mtime;
if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
- fprintf(stderr, "failed to send ID_DONE message for '%s': %s\n", lpath, strerror(errno));
- return false;
- }
-
- if (!ReadFdExactly(sc.fd, &msg.status, sizeof(msg.status))) {
- fprintf(stderr, "failed to read ID_DONE response for '%s': %s\n", lpath, strerror(errno));
- return false;
- }
- if (msg.status.id != ID_OKAY) {
- if (msg.status.id == ID_FAIL) {
- size_t len = msg.status.msglen;
- if (len > 256) len = 256;
- if (!ReadFdExactly(sc.fd, sbuf->data, len)) {
- fprintf(stderr, "failed to read failure reason (!): %s\n", strerror(errno));
- return false;
- }
- sbuf->data[len] = 0;
- } else {
- strcpy(sbuf->data, "unknown reason");
- }
- fprintf(stderr, "failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
+ fprintf(stderr, "failed to send ID_DONE message for '%s': %s\n", path, strerror(errno));
return false;
}
return true;
}
-static int sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, bool show_progress) {
+static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
+ unsigned mtime, mode_t mode, bool show_progress)
+{
+ std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
+
+ if (S_ISLNK(mode)) {
+#if !defined(_WIN32)
+ char buf[PATH_MAX];
+ ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
+ if (data_length == -1) {
+ fprintf(stderr, "readlink '%s' failed: %s\n", lpath, strerror(errno));
+ return false;
+ }
+ buf[data_length++] = '\0';
+
+ if (!sc.SendSmallFile(path_and_mode.c_str(), buf, data_length, mtime)) return false;
+ return sc.CopyDone(lpath, rpath);
+#endif
+ }
+
+ if (!S_ISREG(mode)) {
+ fprintf(stderr, "local file '%s' has unsupported mode: 0o%o\n", lpath, mode);
+ return false;
+ }
+
+ struct stat st;
+ if (stat(lpath, &st) == -1) {
+ fprintf(stderr, "stat '%s' failed: %s\n", lpath, strerror(errno));
+ return false;
+ }
+ if (st.st_size < SYNC_DATA_MAX) {
+ std::string data;
+ if (!android::base::ReadFileToString(lpath, &data)) {
+ fprintf(stderr, "failed to read all of '%s': %s\n", lpath, strerror(errno));
+ return false;
+ }
+ if (!sc.SendSmallFile(path_and_mode.c_str(), data.data(), data.size(), mtime)) return false;
+ } else {
+ if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, mtime, show_progress)) return false;
+ }
+ return sc.CopyDone(lpath, rpath);
+}
+
+static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, bool show_progress) {
syncmsg msg;
int lfd = -1;
- char *buffer = send_buffer.data;
- unsigned id;
size_t len = strlen(rpath);
- if (len > 1024) return -1;
+ if (len > 1024) return false;
unsigned size = 0;
if (show_progress) {
- if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return -1;
+ if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
}
- if (!SendRequest(sc.fd, ID_RECV, rpath)) return -1;
- if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) return -1;
+ if (!sc.SendRequest(ID_RECV, rpath)) return false;
+ if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) return false;
- id = msg.data.id;
+ unsigned id = msg.data.id;
if (id == ID_DATA || id == ID_DONE) {
adb_unlink(lpath);
@@ -328,7 +373,7 @@
lfd = adb_creat(lpath, 0644);
if(lfd < 0) {
fprintf(stderr, "cannot create '%s': %s\n", lpath, strerror(errno));
- return -1;
+ return false;
}
goto handle_data;
} else {
@@ -336,9 +381,11 @@
}
while (true) {
- if(!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
+ char buffer[SYNC_DATA_MAX];
+
+ if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
adb_close(lfd);
- return -1;
+ return false;
}
id = msg.data.id;
@@ -349,18 +396,18 @@
if (len > sc.max) {
fprintf(stderr, "msg.data.size too large: %zu (max %zu)\n", len, sc.max);
adb_close(lfd);
- return -1;
+ return false;
}
- if(!ReadFdExactly(sc.fd, buffer, len)) {
+ if (!ReadFdExactly(sc.fd, buffer, len)) {
adb_close(lfd);
- return -1;
+ return false;
}
- if(!WriteFdExactly(lfd, buffer, len)) {
+ if (!WriteFdExactly(lfd, buffer, len)) {
fprintf(stderr, "cannot write '%s': %s\n", rpath, strerror(errno));
adb_close(lfd);
- return -1;
+ return false;
}
sc.total_bytes += len;
@@ -371,25 +418,13 @@
}
adb_close(lfd);
- return 0;
+ return true;
remote_error:
adb_close(lfd);
adb_unlink(lpath);
-
- if(id == ID_FAIL) {
- len = msg.data.size;
- if(len > 256) len = 256;
- if(!ReadFdExactly(sc.fd, buffer, len)) {
- return -1;
- }
- buffer[len] = 0;
- } else {
- memcpy(buffer, &id, 4);
- buffer[4] = 0;
- }
- fprintf(stderr, "failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer);
- return 0;
+ sc.CopyDone(rpath, lpath);
+ return false;
}
static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
@@ -401,7 +436,7 @@
SyncConnection sc;
if (!sc.IsValid()) return false;
- return sync_ls(sc.fd, path, do_sync_ls_cb, 0);
+ return sync_ls(sc, path, do_sync_ls_cb, 0);
}
struct copyinfo
@@ -411,7 +446,7 @@
const char *dst;
unsigned int time;
unsigned int mode;
- unsigned int size;
+ uint64_t size;
int flag;
};
@@ -531,7 +566,7 @@
if (check_timestamps) {
for (ci = filelist; ci != 0; ci = ci->next) {
- if (!sync_start_stat(sc, ci->dst)) return false;
+ if (!sc.SendRequest(ID_STAT, ci->dst)) return false;
}
for(ci = filelist; ci != 0; ci = ci->next) {
unsigned int timestamp, mode, size;
@@ -629,7 +664,7 @@
}
}
-static bool remote_build_list(int syncfd, copyinfo **filelist,
+static bool remote_build_list(SyncConnection& sc, copyinfo **filelist,
const char *rpath, const char *lpath) {
copyinfo *dirlist = NULL;
sync_ls_build_list_cb_args args;
@@ -640,14 +675,14 @@
args.lpath = lpath;
// Put the files/dirs in rpath on the lists.
- if (!sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) {
+ if (!sync_ls(sc, rpath, sync_ls_build_list_cb, (void *)&args)) {
return false;
}
// Recurse into each directory we found.
while (dirlist != NULL) {
copyinfo *next = dirlist->next;
- if (!remote_build_list(syncfd, filelist, dirlist->src, dirlist->dst)) {
+ if (!remote_build_list(sc, filelist, dirlist->src, dirlist->dst)) {
return false;
}
free(dirlist);
@@ -682,7 +717,7 @@
// Recursively build the list of files to copy.
fprintf(stderr, "pull: building file list...\n");
copyinfo* filelist = nullptr;
- if (!remote_build_list(sc.fd, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
+ if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
int pulled = 0;
int skipped = 0;
@@ -691,7 +726,7 @@
copyinfo* next = ci->next;
if (ci->flag == 0) {
fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
- if (sync_recv(sc, ci->src, ci->dst, false)) {
+ if (!sync_recv(sc, ci->src, ci->dst, false)) {
return false;
}
@@ -734,7 +769,7 @@
lpath = path_holder.c_str();
}
}
- if (sync_recv(sc, rpath, lpath, show_progress)) {
+ if (!sync_recv(sc, rpath, lpath, show_progress)) {
return false;
} else {
if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 9b56392..298ed82 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -56,7 +56,7 @@
path_components.pop_back(); // For "/system/bin/sh", only create "/system/bin".
std::string partial_path;
- for (auto& path_component : path_components) {
+ for (const auto& path_component : path_components) {
if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
partial_path += path_component;
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index 4f0f740..cc2d44e 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -27,6 +27,7 @@
#include <unistd.h>
#include "adb.h"
+#include "adb_utils.h"
/* here's how these things work.
@@ -343,7 +344,6 @@
struct iovec iov;
char dummy = '!';
char buffer[sizeof(struct cmsghdr) + sizeof(int)];
- int flags;
iov.iov_base = &dummy;
iov.iov_len = 1;
@@ -361,18 +361,8 @@
cmsg->cmsg_type = SCM_RIGHTS;
((int*)CMSG_DATA(cmsg))[0] = fd;
- flags = fcntl(proc->socket,F_GETFL,0);
-
- if (flags == -1) {
- D("failed to get cntl flags for socket %d: %s",
- proc->pid, strerror(errno));
- goto CloseProcess;
-
- }
-
- if (fcntl(proc->socket, F_SETFL, flags & ~O_NONBLOCK) == -1) {
- D("failed to remove O_NONBLOCK flag for socket %d: %s",
- proc->pid, strerror(errno));
+ if (!set_file_block_mode(proc->socket, true)) {
+ VLOG(JDWP) << "failed to set blocking mode for fd " << proc->socket;
goto CloseProcess;
}
@@ -395,9 +385,8 @@
for (n = 1; n < proc->out_count; n++)
proc->out_fds[n-1] = proc->out_fds[n];
- if (fcntl(proc->socket, F_SETFL, flags) == -1) {
- D("failed to set O_NONBLOCK flag for socket %d: %s",
- proc->pid, strerror(errno));
+ if (!set_file_block_mode(proc->socket, false)) {
+ VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
goto CloseProcess;
}
diff --git a/adb/services.cpp b/adb/services.cpp
index e832b1e..e24b470 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -363,23 +363,31 @@
ConnectionState state;
};
-static void wait_for_state(int fd, void* cookie)
-{
+static void wait_for_state(int fd, void* cookie) {
state_info* sinfo = reinterpret_cast<state_info*>(cookie);
D("wait_for_state %d", sinfo->state);
- std::string error_msg = "unknown error";
- atransport* t = acquire_one_transport(sinfo->state, sinfo->transport_type, sinfo->serial,
- &error_msg);
- if (t != nullptr) {
- SendOkay(fd);
- } else {
- SendFail(fd, error_msg);
+ while (true) {
+ bool is_ambiguous = false;
+ std::string error = "unknown error";
+ atransport* t = acquire_one_transport(sinfo->transport_type, sinfo->serial,
+ &is_ambiguous, &error);
+ if (t != nullptr && t->connection_state == sinfo->state) {
+ SendOkay(fd);
+ break;
+ } else if (!is_ambiguous) {
+ adb_sleep_ms(1000);
+ // Try again...
+ } else {
+ SendFail(fd, error);
+ break;
+ }
}
- if (sinfo->serial)
+ if (sinfo->serial) {
free(sinfo->serial);
+ }
free(sinfo);
adb_close(fd);
D("wait_for_state is done");
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 714a2d8..544afce 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -77,9 +77,9 @@
#define TRACE_TAG SHELL
-#include "shell_service.h"
+#include "sysdeps.h"
-#if !ADB_HOST
+#include "shell_service.h"
#include <errno.h>
#include <pty.h>
@@ -95,7 +95,7 @@
#include "adb.h"
#include "adb_io.h"
#include "adb_trace.h"
-#include "sysdeps.h"
+#include "adb_utils.h"
namespace {
@@ -327,9 +327,8 @@
// the pipe fills up.
for (int fd : {stdinout_sfd_.fd(), stderr_sfd_.fd()}) {
if (fd >= 0) {
- int flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
- PLOG(ERROR) << "error making FD " << fd << " non-blocking";
+ if (!set_file_block_mode(fd, false)) {
+ LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
return false;
}
}
@@ -383,7 +382,7 @@
subprocess->PassDataStreams();
subprocess->WaitForExit();
- D("deleting Subprocess");
+ D("deleting Subprocess for PID %d", subprocess->pid());
delete subprocess;
return nullptr;
@@ -502,11 +501,31 @@
return &protocol_sfd_;
}
- // We only care about stdin packets.
- if (stdinout_sfd_.valid() && input_->id() == ShellProtocol::kIdStdin) {
- input_bytes_left_ = input_->data_length();
- } else {
- input_bytes_left_ = 0;
+ if (stdinout_sfd_.valid()) {
+ switch (input_->id()) {
+ case ShellProtocol::kIdStdin:
+ input_bytes_left_ = input_->data_length();
+ break;
+ case ShellProtocol::kIdCloseStdin:
+ if (type_ == SubprocessType::kRaw) {
+ if (adb_shutdown(stdinout_sfd_.fd(), SHUT_WR) == 0) {
+ return nullptr;
+ }
+ PLOG(ERROR) << "failed to shutdown writes to FD "
+ << stdinout_sfd_.fd();
+ return &stdinout_sfd_;
+ } else {
+ // PTYs can't close just input, so rather than close the
+ // FD and risk losing subprocess output, leave it open.
+ // This only happens if the client starts a PTY shell
+ // non-interactively which is rare and unsupported.
+ // If necessary, the client can manually close the shell
+ // with `exit` or by killing the adb client process.
+ D("can't close input for PTY FD %d",
+ stdinout_sfd_.fd());
+ }
+ break;
+ }
}
}
@@ -533,7 +552,9 @@
ScopedFd* Subprocess::PassOutput(ScopedFd* sfd, ShellProtocol::Id id) {
int bytes = adb_read(sfd->fd(), output_->data(), output_->data_capacity());
if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
- if (bytes < 0) {
+ // read() returns EIO if a PTY closes; don't report this as an error,
+ // it just means the subprocess completed.
+ if (bytes < 0 && !(type_ == SubprocessType::kPty && errno == EIO)) {
PLOG(ERROR) << "error reading output FD " << sfd->fd();
}
return sfd;
@@ -624,5 +645,3 @@
subprocess->local_socket_fd(), subprocess->pid());
return subprocess->local_socket_fd();
}
-
-#endif // !ADB_HOST
diff --git a/adb/shell_service.h b/adb/shell_service.h
index 8868f10..01410a9 100644
--- a/adb/shell_service.h
+++ b/adb/shell_service.h
@@ -50,11 +50,12 @@
public:
// This is an unscoped enum to make it easier to compare against raw bytes.
enum Id : uint8_t {
- kIdStdin = 0,
+ kIdStdin = 0,
kIdStdout = 1,
kIdStderr = 2,
- kIdExit = 3,
- kIdInvalid = 255, // Indicates an invalid or unknown packet.
+ kIdExit = 3,
+ kIdCloseStdin = 4, // Close subprocess stdin if possible.
+ kIdInvalid = 255, // Indicates an invalid or unknown packet.
};
// ShellPackets will probably be too large to allocate on the stack so they
diff --git a/adb/shell_service_test.cpp b/adb/shell_service_test.cpp
index 20efd84..e18f905 100644
--- a/adb/shell_service_test.cpp
+++ b/adb/shell_service_test.cpp
@@ -245,6 +245,25 @@
EXPECT_FALSE(stdout.find("--abc123--") == std::string::npos);
}
+// Tests closing raw subprocess stdin.
+TEST_F(ShellServiceTest, CloseClientStdin) {
+ ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+ "cat; echo TEST_DONE",
+ SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+ std::string input = "foo\nbar";
+ ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+ memcpy(protocol->data(), input.data(), input.length());
+ ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length()));
+ ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0));
+ delete protocol;
+
+ std::string stdout, stderr;
+ EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+ ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"});
+ ExpectLinesEqual(stderr, {});
+}
+
// Tests that nothing breaks when the stdin/stdout pipe closes.
TEST_F(ShellServiceTest, CloseStdinStdoutSubprocess) {
ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 8562496..f8c2f64 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -671,8 +671,8 @@
{
unsigned len;
#if ADB_HOST
- char *service = NULL;
- char* serial = NULL;
+ char *service = nullptr;
+ char* serial = nullptr;
TransportType type = kTransportAny;
#endif
@@ -739,7 +739,7 @@
type = kTransportAny;
service += strlen("host:");
} else {
- service = NULL;
+ service = nullptr;
}
if (service) {
@@ -782,7 +782,7 @@
SendOkay(s->peer->fd);
s->peer->ready = local_socket_ready;
- s->peer->shutdown = NULL;
+ s->peer->shutdown = nullptr;
s->peer->close = local_socket_close;
s->peer->peer = s2;
s2->peer = s->peer;
@@ -795,12 +795,10 @@
return 0;
}
#else /* !ADB_HOST */
- if (s->transport == NULL) {
+ if (s->transport == nullptr) {
std::string error_msg = "unknown failure";
- s->transport =
- acquire_one_transport(kCsAny, kTransportAny, NULL, &error_msg);
-
- if (s->transport == NULL) {
+ s->transport = acquire_one_transport(kTransportAny, nullptr, nullptr, &error_msg);
+ if (s->transport == nullptr) {
SendFail(s->peer->fd, error_msg);
goto fail;
}
@@ -822,7 +820,7 @@
** tear down
*/
s->peer->ready = local_socket_ready_notify;
- s->peer->shutdown = NULL;
+ s->peer->shutdown = nullptr;
s->peer->close = local_socket_close_notify;
s->peer->peer = 0;
/* give him our transport and upref it */
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 5918a94..501a75a 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -488,6 +488,10 @@
{
return shutdown(fd, SHUT_RDWR);
}
+static __inline__ int adb_shutdown(int fd, int direction)
+{
+ return shutdown(fd, direction);
+}
#undef shutdown
#define shutdown ____xxx_shutdown
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 42f6d9b..994b851 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -3265,6 +3265,15 @@
// terminal.
return _console_read(_console_handle, buf, len);
} else {
+ // On older versions of Windows (definitely 7, definitely not 10),
+ // ReadConsole() with a size >= 31367 fails, so if |fd| is a console
+ // we need to limit the read size. This may also catch devices like NUL,
+ // but that is OK as we just want to avoid capping pipes and files which
+ // don't need size limiting. This isatty() test is very simple and quick
+ // and doesn't call the OS.
+ if (isatty(fd) && len > 4096) {
+ len = 4096;
+ }
// Just call into C Runtime which can read from pipes/files and which
// can do LF/CR translation (which is overridable with _setmode()).
// Undefine the macro that is set in sysdeps.h which bans calls to
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 501a39a..e9e774f 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -653,22 +653,28 @@
return !*to_test;
}
-atransport* acquire_one_transport(ConnectionState state, TransportType type,
- const char* serial, std::string* error_out) {
- atransport *result = NULL;
- int ambiguous = 0;
+atransport* acquire_one_transport(TransportType type, const char* serial,
+ bool* is_ambiguous, std::string* error_out) {
+ atransport* result = nullptr;
-retry:
- *error_out = serial ? android::base::StringPrintf("device '%s' not found", serial) : "no devices found";
+ if (serial) {
+ *error_out = android::base::StringPrintf("device '%s' not found", serial);
+ } else if (type == kTransportLocal) {
+ *error_out = "no emulators found";
+ } else if (type == kTransportAny) {
+ *error_out = "no devices/emulators found";
+ } else {
+ *error_out = "no devices found";
+ }
adb_mutex_lock(&transport_lock);
- for (auto t : transport_list) {
+ for (const auto& t : transport_list) {
if (t->connection_state == kCsNoPerm) {
*error_out = "insufficient permissions for device";
continue;
}
- /* check for matching serial number */
+ // Check for matching serial number.
if (serial) {
if ((t->serial && !strcmp(serial, t->serial)) ||
(t->devpath && !strcmp(serial, t->devpath)) ||
@@ -677,8 +683,8 @@
qual_match(serial, "device:", t->device, false)) {
if (result) {
*error_out = "more than one device";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
@@ -687,24 +693,24 @@
if (type == kTransportUsb && t->type == kTransportUsb) {
if (result) {
*error_out = "more than one device";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
} else if (type == kTransportLocal && t->type == kTransportLocal) {
if (result) {
*error_out = "more than one emulator";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
} else if (type == kTransportAny) {
if (result) {
*error_out = "more than one device/emulator";
- ambiguous = 1;
- result = NULL;
+ if (is_ambiguous) *is_ambiguous = true;
+ result = nullptr;
break;
}
result = t;
@@ -713,37 +719,26 @@
}
adb_mutex_unlock(&transport_lock);
- if (result) {
- if (result->connection_state == kCsUnauthorized) {
- *error_out = "device unauthorized.\n";
- char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
- *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
- *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
- *error_out += "\n";
- *error_out += "Try 'adb kill-server' if that seems wrong.\n";
- *error_out += "Otherwise check for a confirmation dialog on your device.";
- result = NULL;
- }
+ // Don't return unauthorized devices; the caller can't do anything with them.
+ if (result && result->connection_state == kCsUnauthorized) {
+ *error_out = "device unauthorized.\n";
+ char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
+ *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
+ *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+ *error_out += "\n";
+ *error_out += "Try 'adb kill-server' if that seems wrong.\n";
+ *error_out += "Otherwise check for a confirmation dialog on your device.";
+ result = nullptr;
+ }
- /* offline devices are ignored -- they are either being born or dying */
- if (result && result->connection_state == kCsOffline) {
- *error_out = "device offline";
- result = NULL;
- }
-
- /* check for required connection state */
- if (result && state != kCsAny && result->connection_state != state) {
- *error_out = "invalid device state";
- result = NULL;
- }
+ // Don't return offline devices; the caller can't do anything with them.
+ if (result && result->connection_state == kCsOffline) {
+ *error_out = "device offline";
+ result = nullptr;
}
if (result) {
- /* found one that we can take */
*error_out = "success";
- } else if (state != kCsAny && (serial || !ambiguous)) {
- adb_sleep_ms(1000);
- goto retry;
}
return result;
@@ -830,7 +825,7 @@
}
void atransport::RunDisconnects() {
- for (auto& disconnect : disconnects_) {
+ for (const auto& disconnect : disconnects_) {
disconnect->func(disconnect->opaque, this);
}
disconnects_.clear();
@@ -877,7 +872,7 @@
std::string list_transports(bool long_listing) {
std::string result;
adb_mutex_lock(&transport_lock);
- for (const auto t : transport_list) {
+ for (const auto& t : transport_list) {
append_transport(t, &result, long_listing);
}
adb_mutex_unlock(&transport_lock);
@@ -887,7 +882,7 @@
/* hack for osx */
void close_usb_devices() {
adb_mutex_lock(&transport_lock);
- for (auto t : transport_list) {
+ for (const auto& t : transport_list) {
if (!t->kicked) {
t->kicked = 1;
t->kick(t);
@@ -913,7 +908,7 @@
}
adb_mutex_lock(&transport_lock);
- for (auto transport : pending_list) {
+ for (const auto& transport : pending_list) {
if (transport->serial && strcmp(serial, transport->serial) == 0) {
adb_mutex_unlock(&transport_lock);
delete t;
@@ -921,7 +916,7 @@
}
}
- for (auto transport : transport_list) {
+ for (const auto& transport : transport_list) {
if (transport->serial && strcmp(serial, transport->serial) == 0) {
adb_mutex_unlock(&transport_lock);
delete t;
diff --git a/adb/transport.h b/adb/transport.h
index dfe8875..f41a8d4 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -122,12 +122,13 @@
/*
* Obtain a transport from the available transports.
- * If state is != kCsAny, only transports in that state are considered.
- * If serial is non-NULL then only the device with that serial will be chosen.
- * If no suitable transport is found, error is set.
+ * If serial is non-null then only the device with that serial will be chosen.
+ * If multiple devices/emulators would match, *is_ambiguous (if non-null)
+ * is set to true and nullptr returned.
+ * If no suitable transport is found, error is set and nullptr returned.
*/
-atransport* acquire_one_transport(ConnectionState state, TransportType type,
- const char* serial, std::string* error_out);
+atransport* acquire_one_transport(TransportType type, const char* serial,
+ bool* is_ambiguous, std::string* error_out);
void kick_transport(atransport* t);
void update_transports(void);
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index 817917e..3495a71 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -88,8 +88,11 @@
__le32 fs_count;
__le32 hs_count;
__le32 ss_count;
+ __le32 os_count;
struct func_desc fs_descs, hs_descs;
struct ss_func_desc ss_descs;
+ struct usb_os_desc_header os_header;
+ struct usb_ext_compat_desc os_desc;
} __attribute__((packed));
static struct func_desc fs_descriptors = {
@@ -181,6 +184,24 @@
},
};
+struct usb_ext_compat_desc os_desc_compat = {
+ .bFirstInterfaceNumber = 0,
+ .Reserved1 = cpu_to_le32(1),
+ .CompatibleID = {0},
+ .SubCompatibleID = {0},
+ .Reserved2 = {0},
+};
+
+static struct usb_os_desc_header os_desc_header = {
+ .interface = cpu_to_le32(1),
+ .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
+ .bcdVersion = cpu_to_le32(1),
+ .wIndex = cpu_to_le32(4),
+ .bCount = cpu_to_le32(1),
+ .Reserved = cpu_to_le32(0),
+};
+
+
#define STR_INTERFACE_ "ADB Interface"
static const struct {
@@ -332,13 +353,16 @@
v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
- FUNCTIONFS_HAS_SS_DESC;
+ FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
v2_descriptor.fs_count = 3;
v2_descriptor.hs_count = 3;
v2_descriptor.ss_count = 5;
+ v2_descriptor.os_count = 1;
v2_descriptor.fs_descs = fs_descriptors;
v2_descriptor.hs_descs = hs_descriptors;
v2_descriptor.ss_descs = ss_descriptors;
+ v2_descriptor.os_header = os_desc_header;
+ v2_descriptor.os_desc = os_desc_compat;
if (h->control < 0) { // might have already done this before
D("OPENING %s", USB_FFS_ADB_EP0);
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
index ae56b4c..2a9d1d3 100644
--- a/crash_reporter/crash_collector.cc
+++ b/crash_reporter/crash_collector.cc
@@ -35,8 +35,8 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/key_value_store.h>
-#include <chromeos/process.h>
+#include <brillo/key_value_store.h>
+#include <brillo/process.h>
namespace {
@@ -340,7 +340,7 @@
bool CrashCollector::GetLogContents(const FilePath &config_path,
const std::string &exec_name,
const FilePath &output_file) {
- chromeos::KeyValueStore store;
+ brillo::KeyValueStore store;
if (!store.Load(config_path)) {
LOG(INFO) << "Unable to read log configuration file "
<< config_path.value();
@@ -351,7 +351,7 @@
if (!store.GetString(exec_name, &command))
return false;
- chromeos::ProcessImpl diag_process;
+ brillo::ProcessImpl diag_process;
diag_process.AddArg(kShellPath);
diag_process.AddStringOption("-c", command);
diag_process.RedirectOutput(output_file.value());
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index 32cbe9f..d00a5b5 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -22,14 +22,14 @@
#include <base/files/file_util.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
#include <gtest/gtest.h>
#include "crash_collector.h"
using base::FilePath;
using base::StringPrintf;
-using chromeos::FindLog;
+using brillo::FindLog;
using ::testing::Invoke;
using ::testing::Return;
@@ -54,7 +54,7 @@
collector_.Initialize(CountCrash, IsMetrics);
test_dir_ = FilePath("test");
base::CreateDirectory(test_dir_);
- chromeos::ClearLog();
+ brillo::ClearLog();
}
void TearDown() {
@@ -208,7 +208,7 @@
symlink(kMetaFileBasename,
meta_symlink_path.value().c_str()));
ASSERT_TRUE(base::PathExists(meta_symlink_path));
- chromeos::ClearLog();
+ brillo::ClearLog();
collector_.WriteCrashMetaData(meta_symlink_path,
"kernel",
payload_file.value());
@@ -221,7 +221,7 @@
// Test target of dangling symlink is not created.
base::DeleteFile(meta_file, false);
ASSERT_FALSE(base::PathExists(meta_file));
- chromeos::ClearLog();
+ brillo::ClearLog();
collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
payload_file.value());
EXPECT_FALSE(base::PathExists(meta_file));
diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc
index 7872f7b..3955fe5 100644
--- a/crash_reporter/crash_reporter.cc
+++ b/crash_reporter/crash_reporter.cc
@@ -25,9 +25,9 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/flag_helper.h>
-#include <chromeos/process.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/flag_helper.h>
+#include <brillo/process.h>
+#include <brillo/syslog_logging.h>
#include <metrics/metrics_library.h>
#include "kernel_collector.h"
@@ -102,7 +102,7 @@
// Note: This will mean that the dbus-send process will become a zombie and
// reparent to init for reaping, but that's OK -- see above.
- chromeos::ProcessImpl dbus_send;
+ brillo::ProcessImpl dbus_send;
dbus_send.AddArg("/system/bin/dbus-send");
dbus_send.AddArg("--type=signal");
dbus_send.AddArg("--system");
@@ -183,10 +183,10 @@
}
// Accumulate logs to help in diagnosing failures during user collection.
- chromeos::LogToString(true);
+ brillo::LogToString(true);
// Handle the crash, get the name of the process from procfs.
bool handled = user_collector->HandleCrash(user, nullptr);
- chromeos::LogToString(false);
+ brillo::LogToString(false);
if (!handled)
return 1;
return 0;
@@ -198,9 +198,9 @@
CHECK(!udev_event.empty()) << "--udev= must be set";
// Accumulate logs to help in diagnosing failures during user collection.
- chromeos::LogToString(true);
+ brillo::LogToString(true);
bool handled = udev_collector->HandleCrash(udev_event);
- chromeos::LogToString(false);
+ brillo::LogToString(false);
if (!handled)
return 1;
return 0;
@@ -209,9 +209,9 @@
static int HandleKernelWarning(KernelWarningCollector
*kernel_warning_collector) {
// Accumulate logs to help in diagnosing failures during collection.
- chromeos::LogToString(true);
+ brillo::LogToString(true);
bool handled = kernel_warning_collector->Collect();
- chromeos::LogToString(false);
+ brillo::LogToString(false);
if (!handled)
return 1;
return 0;
@@ -278,9 +278,9 @@
OpenStandardFileDescriptors();
FilePath my_path = base::MakeAbsoluteFilePath(FilePath(argv[0]));
s_metrics_lib.Init();
- chromeos::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter");
- chromeos::OpenLog(my_path.BaseName().value().c_str(), true);
- chromeos::InitLog(chromeos::kLogToSyslog);
+ brillo::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter");
+ brillo::OpenLog(my_path.BaseName().value().c_str(), true);
+ brillo::InitLog(brillo::kLogToSyslog);
KernelCollector kernel_collector;
kernel_collector.Initialize(CountKernelCrash, IsFeedbackAllowed);
diff --git a/crash_reporter/crash_reporter.rc b/crash_reporter/crash_reporter.rc
index 30e87f5..57c1d40 100644
--- a/crash_reporter/crash_reporter.rc
+++ b/crash_reporter/crash_reporter.rc
@@ -1,11 +1,11 @@
on property:crash_reporter.coredump.enabled=1
write /proc/sys/kernel/core_pattern \
- "|/system/bin/crash_reporter --user=%P:%s:%u:%e"
+ "|/system/bin/crash_reporter --user=%P:%s:%u:%g:%e"
on property:crash_reporter.coredump.enabled=0
write /proc/sys/kernel/core_pattern "core"
-on boot
+on post-fs-data
# Allow catching multiple unrelated concurrent crashes, but use a finite
# number to prevent infinitely recursing on crash handling.
write /proc/sys/kernel/core_pipe_limit 4
@@ -14,11 +14,14 @@
rmdir /data/misc/crash_reporter/lock/crash_sender
# Create crash directories.
- mkdir /data/misc/crash_reporter 0700 root root
+ # These directories are group-writable by root so that crash_reporter can
+ # still access them when it switches users.
+ mkdir /data/misc/crash_reporter 0770 root root
+ mkdir /data/misc/crash_reporter/crash 0770 root root
mkdir /data/misc/crash_reporter/lock 0700 root root
mkdir /data/misc/crash_reporter/log 0700 root root
mkdir /data/misc/crash_reporter/run 0700 root root
- mkdir /data/misc/crash_reporter/tmp 0700 root root
+ mkdir /data/misc/crash_reporter/tmp 0770 root root
service crash_reporter /system/bin/crash_reporter --init
class late_start
diff --git a/crash_reporter/crash_reporter_logs_test.cc b/crash_reporter/crash_reporter_logs_test.cc
index c9ca02d..e778002 100644
--- a/crash_reporter/crash_reporter_logs_test.cc
+++ b/crash_reporter/crash_reporter_logs_test.cc
@@ -17,7 +17,7 @@
#include <string>
#include <base/files/file_path.h>
-#include <chromeos/key_value_store.h>
+#include <brillo/key_value_store.h>
#include <gtest/gtest.h>
namespace {
@@ -32,7 +32,7 @@
// Tests that the config file is parsable and that Chrome is listed.
TEST(CrashReporterLogsTest, ReadConfig) {
- chromeos::KeyValueStore store;
+ brillo::KeyValueStore store;
ASSERT_TRUE(store.Load(base::FilePath(kConfigFile)));
std::string command;
EXPECT_TRUE(store.GetString(kChromeExecName, &command));
diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender
index 8a422dd..1f58792 100755
--- a/crash_reporter/crash_sender
+++ b/crash_reporter/crash_sender
@@ -292,14 +292,14 @@
local kind="$(get_kind "${meta_path}")"
local exec_name="$(get_key_value "${meta_path}" "exec_name")"
local url="$(getprop crash_reporter.server)"
- local brillo_version="$(get_key_value "${meta_path}" "ver")"
+ local bdk_version="$(get_key_value "${meta_path}" "bdk_version")"
local hwclass="$(get_hardware_class)"
local write_payload_size="$(get_key_value "${meta_path}" "payload_size")"
local log="$(get_key_value "${meta_path}" "log")"
local sig="$(get_key_value "${meta_path}" "sig")"
local send_payload_size="$(stat -c "%s" "${report_payload}" 2>/dev/null)"
- local product="$(get_key_value "${meta_path}" "upload_var_prod")"
- local version="$(get_key_value "${meta_path}" "upload_var_ver")"
+ local product="$(get_key_value "${meta_path}" "product_id")"
+ local version="$(get_key_value "${meta_path}" "product_version")"
local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")"
local guid
local model_manifest_id="$(get_key_value "${WEAVE_CONF_FILE}" "model_id")"
@@ -350,32 +350,13 @@
esac
done
- # When uploading Chrome reports we need to report the right product and
- # version. If the meta file does not specify it, use GOOGLE_CRASH_ID
- # as the product and GOOGLE_CRASH_VERSION_ID as the version.
- if [ "${product}" = "undefined" ]; then
- product="$(get_key_value /etc/os-release 'GOOGLE_CRASH_ID')"
- fi
- if [ "${version}" = "undefined" ]; then
- version="$(get_key_value /etc/os-release 'GOOGLE_CRASH_VERSION_ID')"
- fi
-
- # If GOOGLE_CRASH_* is undefined, we look for ID and VERSION_ID in
- # /etc/os-release.
- if [ "${product}" = "undefined" ]; then
- product="$(get_key_value /etc/os-release 'ID')"
- fi
- if [ "${version}" = "undefined" ]; then
- version="$(get_key_value /etc/os-release 'VERSION_ID')"
- fi
-
# If ID or VERSION_ID is undefined, we use the default product name
- # and CHROMEOS_RELEASE_VERSION from /etc/lsb-release.
+ # and bdk_version from /etc/os-release.d.
if [ "${product}" = "undefined" ]; then
product="${BRILLO_PRODUCT}"
fi
if [ "${version}" = "undefined" ]; then
- version="${brillo_version}"
+ version="${bdk_version}"
fi
local image_type
@@ -408,6 +389,7 @@
lecho " Metadata: ${meta_path} (${kind})"
lecho " Payload: ${report_payload}"
lecho " Version: ${version}"
+ lecho " Bdk Version: ${bdk_version}"
[ -n "${image_type}" ] && lecho " Image type: ${image_type}"
[ -n "${boot_mode}" ] && lecho " Boot mode: ${boot_mode}"
if is_mock; then
@@ -460,6 +442,7 @@
--capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \
-F "prod=${product}" \
-F "ver=${version}" \
+ -F "bdk_version=${bdk_version}" \
-F "hwclass=${hwclass}" \
-F "exec_name=${exec_name}" \
-F "model_manifest_id=${model_manifest_id}" \
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
index e690b77..cdb0ae7 100644
--- a/crash_reporter/kernel_collector_test.cc
+++ b/crash_reporter/kernel_collector_test.cc
@@ -22,13 +22,13 @@
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
#include <gtest/gtest.h>
using base::FilePath;
using base::StringPrintf;
-using chromeos::FindLog;
-using chromeos::GetLog;
+using brillo::FindLog;
+using brillo::GetLog;
namespace {
@@ -78,7 +78,7 @@
test_crash_directory_ = scoped_temp_dir_.path().Append("crash_directory");
ASSERT_TRUE(base::CreateDirectory(test_crash_directory_));
- chromeos::ClearLog();
+ brillo::ClearLog();
}
FilePath test_kcrash_;
@@ -282,7 +282,7 @@
ASSERT_EQ(1, s_crashes);
ASSERT_TRUE(FindLog("(handling)"));
static const char kNamePrefix[] = "Stored kcrash to ";
- std::string log = chromeos::GetLog();
+ std::string log = brillo::GetLog();
size_t pos = log.find(kNamePrefix);
ASSERT_NE(std::string::npos, pos)
<< "Did not find string \"" << kNamePrefix << "\" in log: {\n"
diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc
index a39441d..d445557 100644
--- a/crash_reporter/list_proxies.cc
+++ b/crash_reporter/list_proxies.cc
@@ -28,8 +28,8 @@
#include <base/strings/string_tokenizer.h>
#include <base/strings/string_util.h>
#include <base/values.h>
-#include <chromeos/daemons/dbus_daemon.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/daemons/dbus_daemon.h>
+#include <brillo/syslog_logging.h>
#include "libcrosservice/dbus-proxies.h"
@@ -111,7 +111,7 @@
// must be called, which blocks on the D-Bus call to Chrome. The call returns
// after either the timeout or the proxy has been resolved. The resolved
// proxies can then be accessed through the proxies() function.
-class ProxyResolver : public chromeos::DBusDaemon {
+class ProxyResolver : public brillo::DBusDaemon {
public:
ProxyResolver(const std::string& source_url,
const std::string& signal_interface,
@@ -138,7 +138,7 @@
timeout_callback_.callback(),
timeout_);
- return chromeos::DBusDaemon::Run();
+ return brillo::DBusDaemon::Run();
}
protected:
@@ -162,7 +162,7 @@
return;
}
- chromeos::ErrorPtr error;
+ brillo::ErrorPtr error;
call_proxy_->ResolveNetworkProxy(source_url_,
signal_interface_,
signal_name_,
@@ -189,7 +189,7 @@
}
int OnInit() override {
- int return_code = chromeos::DBusDaemon::OnInit();
+ int return_code = brillo::DBusDaemon::OnInit();
if (return_code != EX_OK)
return return_code;
@@ -276,13 +276,13 @@
}
// Default to logging to syslog.
- int init_flags = chromeos::kLogToSyslog;
+ int init_flags = brillo::kLogToSyslog;
// Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose"
// was passed.
if ((!quiet && isatty(STDERR_FILENO)) || verbose)
- init_flags |= chromeos::kLogToStderr;
- chromeos::InitLog(init_flags);
+ init_flags |= brillo::kLogToStderr;
+ brillo::InitLog(init_flags);
std::string url;
base::CommandLine::StringVector urls = cl->GetArgs();
diff --git a/crash_reporter/testrunner.cc b/crash_reporter/testrunner.cc
index a8c717e..744cf10 100644
--- a/crash_reporter/testrunner.cc
+++ b/crash_reporter/testrunner.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <chromeos/test_helpers.h>
+#include <brillo/test_helpers.h>
#include <gtest/gtest.h>
int main(int argc, char** argv) {
diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc
index 576fdbd..1e018db 100644
--- a/crash_reporter/udev_collector.cc
+++ b/crash_reporter/udev_collector.cc
@@ -27,7 +27,7 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/process.h>
+#include <brillo/process.h>
using base::FilePath;
@@ -120,7 +120,7 @@
}
// Compress the output using gzip.
- chromeos::ProcessImpl gzip_process;
+ brillo::ProcessImpl gzip_process;
gzip_process.AddArg(kGzipPath);
gzip_process.AddArg(crash_path.value());
int process_result = gzip_process.Run();
diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc
index a6643fb..5474f48 100644
--- a/crash_reporter/udev_collector_test.cc
+++ b/crash_reporter/udev_collector_test.cc
@@ -18,7 +18,7 @@
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -141,7 +141,7 @@
kLogConfigFileContents,
strlen(kLogConfigFileContents)));
- chromeos::ClearLog();
+ brillo::ClearLog();
}
UdevCollectorMock collector_;
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
index c5c0662..3bdeca1 100644
--- a/crash_reporter/unclean_shutdown_collector_test.cc
+++ b/crash_reporter/unclean_shutdown_collector_test.cc
@@ -20,12 +20,12 @@
#include <base/files/file_util.h>
#include <base/strings/string_util.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using base::FilePath;
-using ::chromeos::FindLog;
+using ::brillo::FindLog;
namespace {
@@ -65,7 +65,7 @@
base::DeleteFile(test_unclean_, true);
// Set up an alternate power manager state file as well
collector_.powerd_suspended_file_ = FilePath(kTestSuspended);
- chromeos::ClearLog();
+ brillo::ClearLog();
}
protected:
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
index a9522cc..56e7bb9 100644
--- a/crash_reporter/user_collector.cc
+++ b/crash_reporter/user_collector.cc
@@ -23,9 +23,11 @@
#include <pwd.h> // For struct passwd.
#include <stdint.h>
#include <sys/cdefs.h> // For __WORDSIZE
+#include <sys/fsuid.h>
#include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
#include <unistd.h> // For setgroups
+#include <iostream> // For std::oct
#include <string>
#include <vector>
@@ -35,8 +37,9 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <chromeos/process.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/osrelease_reader.h>
+#include <brillo/process.h>
+#include <brillo/syslog_logging.h>
#include <cutils/properties.h>
#include <private/android_filesystem_config.h>
@@ -49,17 +52,17 @@
static const char kCoreTempFolder[] = "/data/misc/crash_reporter/tmp";
-// Define an otherwise invalid value that represents an unknown UID.
+// Define an otherwise invalid value that represents an unknown UID and GID.
static const uid_t kUnknownUid = -1;
+static const gid_t kUnknownGid = -1;
const char *UserCollector::kUserId = "Uid:\t";
const char *UserCollector::kGroupId = "Gid:\t";
-// The property containing the OS version.
-const char kVersionProperty[] = "ro.build.id";
-
-// The property containing the product id.
-const char kProductIDProperty[] = "ro.product.product_id";
+// Product information keys in the /etc/os-release.d folder.
+static const char kBdkVersionKey[] = "bdk_version";
+static const char kProductIDKey[] = "product_id";
+static const char kProductVersionKey[] = "product_version";
using base::FilePath;
@@ -87,9 +90,9 @@
directory_failure_ = directory_failure;
filter_in_ = filter_in;
- gid_t groups[] = { AID_SYSTEM, AID_DBUS };
+ gid_t groups[] = { AID_ROOT, AID_SYSTEM, AID_DBUS };
if (setgroups(arraysize(groups), groups) != 0) {
- PLOG(FATAL) << "Unable to set groups to system and dbus";
+ PLOG(FATAL) << "Unable to set groups to root, system, and dbus";
}
}
@@ -115,22 +118,6 @@
}
}
-// Return the string that should be used for the kernel's core_pattern file.
-// Note that if you change the format of the enabled pattern, you'll probably
-// also need to change the ParseCrashAttributes() function below, the
-// user_collector_test.cc unittest, and the logging_UserCrash.py autotest.
-std::string UserCollector::GetPattern(bool enabled) const {
- if (enabled) {
- // Combine the four crash attributes into one parameter to try to reduce
- // the size of the invocation line for crash_reporter, since the kernel
- // has a fixed-sized (128B) buffer for it (before parameter expansion).
- // Note that the kernel does not support quoted arguments in core_pattern.
- return StringPrintf("|%s --user=%%P:%%s:%%u:%%e", our_path_.c_str());
- } else {
- return "core";
- }
-}
-
bool UserCollector::SetUpInternal(bool enabled) {
CHECK(initialized_);
LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
@@ -200,7 +187,7 @@
AddCrashMetaData("sig", kCollectionErrorSignature);
AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
- std::string error_log = chromeos::GetLog();
+ std::string error_log = brillo::GetLog();
FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature,
diag_log_path)) {
@@ -227,7 +214,19 @@
bool UserCollector::CopyOffProcFiles(pid_t pid,
const FilePath &container_dir) {
if (!base::CreateDirectory(container_dir)) {
- PLOG(ERROR) << "Could not create " << container_dir.value().c_str();
+ PLOG(ERROR) << "Could not create " << container_dir.value();
+ return false;
+ }
+ int dir_mask = base::FILE_PERMISSION_READ_BY_USER
+ | base::FILE_PERMISSION_WRITE_BY_USER
+ | base::FILE_PERMISSION_EXECUTE_BY_USER
+ | base::FILE_PERMISSION_READ_BY_GROUP
+ | base::FILE_PERMISSION_WRITE_BY_GROUP;
+ if (!base::SetPosixFilePermissions(container_dir,
+ base::FILE_PERMISSION_MASK & dir_mask)) {
+ PLOG(ERROR) << "Could not set permissions for " << container_dir.value()
+ << " to " << std::oct
+ << (base::FILE_PERMISSION_MASK & dir_mask);
return false;
}
FilePath process_path = GetProcessPath(pid);
@@ -368,7 +367,7 @@
const FilePath &minidump_path,
const FilePath &temp_directory) {
FilePath output_path = temp_directory.Append("output");
- chromeos::ProcessImpl core2md;
+ brillo::ProcessImpl core2md;
core2md.RedirectOutput(output_path.value());
core2md.AddArg(kCoreToMinidumpConverterPath);
core2md.AddArg(core_path.value());
@@ -410,6 +409,43 @@
bool proc_files_usable =
CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
+ // Switch back to the original UID/GID.
+ gid_t rgid, egid, sgid;
+ if (getresgid(&rgid, &egid, &sgid) != 0) {
+ PLOG(FATAL) << "Unable to read saved gid";
+ }
+ if (setresgid(sgid, sgid, -1) != 0) {
+ PLOG(FATAL) << "Unable to set real group ID back to saved gid";
+ } else {
+ if (getresgid(&rgid, &egid, &sgid) != 0) {
+ // If the groups cannot be read at this point, the rgid variable will
+ // contain the previously read group ID from before changing it. This
+ // will cause the chown call below to set the incorrect group for
+ // non-root crashes. But do not treat this as a fatal error, so that
+ // the rest of the collection will continue for potential manual
+ // collection by a developer.
+ PLOG(ERROR) << "Unable to read real group ID after setting it";
+ }
+ }
+
+ uid_t ruid, euid, suid;
+ if (getresuid(&ruid, &euid, &suid) != 0) {
+ PLOG(FATAL) << "Unable to read saved uid";
+ }
+ if (setresuid(suid, suid, -1) != 0) {
+ PLOG(FATAL) << "Unable to set real user ID back to saved uid";
+ } else {
+ if (getresuid(&ruid, &euid, &suid) != 0) {
+ // If the user ID cannot be read at this point, the ruid variable will
+ // contain the previously read user ID from before changing it. This
+ // will cause the chown call below to set the incorrect user for
+ // non-root crashes. But do not treat this as a fatal error, so that
+ // the rest of the collection will continue for potential manual
+ // collection by a developer.
+ PLOG(ERROR) << "Unable to read real user ID after setting it";
+ }
+ }
+
if (!CopyStdinToCoreFile(core_path)) {
return kErrorReadCoreData;
}
@@ -425,6 +461,13 @@
return error;
}
+ // Chown the temp container directory back to the original user/group that
+ // crash_reporter is run as, so that additional files can be written to
+ // the temp folder.
+ if (chown(container_dir.value().c_str(), ruid, rgid) < 0) {
+ PLOG(ERROR) << "Could not set owner for " << container_dir.value();
+ }
+
if (!RunCoreToMinidump(core_path,
container_dir, // procfs directory
minidump_path,
@@ -462,11 +505,28 @@
if (GetLogContents(FilePath(log_config_path_), exec, log_path))
AddCrashMetaData("log", log_path.value());
- char value[PROPERTY_VALUE_MAX];
- property_get(kVersionProperty, value, "undefined");
- AddCrashMetaUploadData("ver", value);
- property_get(kProductIDProperty, value, "undefined");
- AddCrashMetaUploadData("prod", value);
+ brillo::OsReleaseReader reader;
+ reader.Load();
+ std::string value = "undefined";
+ if (!reader.GetString(kBdkVersionKey, &value)) {
+ LOG(ERROR) << "Could not read " << kBdkVersionKey
+ << " from /etc/os-release.d/";
+ }
+ AddCrashMetaData(kBdkVersionKey, value);
+
+ value = "undefined";
+ if (!reader.GetString(kProductIDKey, &value)) {
+ LOG(ERROR) << "Could not read " << kProductIDKey
+ << " from /etc/os-release.d/";
+ }
+ AddCrashMetaData(kProductIDKey, value);
+
+ value = "undefined";
+ if (!reader.GetString(kProductVersionKey, &value)) {
+ LOG(ERROR) << "Could not read " << kProductVersionKey
+ << " from /etc/os-release.d/";
+ }
+ AddCrashMetaData(kProductVersionKey, value);
ErrorType error_type =
ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
@@ -496,15 +556,18 @@
bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
pid_t *pid, int *signal, uid_t *uid,
+ gid_t *gid,
std::string *kernel_supplied_name) {
- pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(.*)");
- if (re.FullMatch(crash_attributes, pid, signal, uid, kernel_supplied_name))
+ pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(\\d+):(.*)");
+ if (re.FullMatch(crash_attributes, pid, signal, uid, gid,
+ kernel_supplied_name))
return true;
LOG(INFO) << "Falling back to parsing crash attributes '"
- << crash_attributes << "' without UID";
+ << crash_attributes << "' without UID and GID";
pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)");
*uid = kUnknownUid;
+ *gid = kUnknownGid;
return re_without_uid.FullMatch(crash_attributes, pid, signal,
kernel_supplied_name);
}
@@ -537,14 +600,25 @@
pid_t pid = 0;
int signal = 0;
uid_t supplied_ruid = kUnknownUid;
+ gid_t supplied_rgid = kUnknownGid;
std::string kernel_supplied_name;
if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid,
- &kernel_supplied_name)) {
+ &supplied_rgid, &kernel_supplied_name)) {
LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes;
return false;
}
+ // Switch to the group and user that ran the crashing binary in order to
+ // access their /proc files. Do not set suid/sgid, so that we can switch
+ // back after copying the necessary files.
+ if (setresgid(supplied_rgid, supplied_rgid, -1) != 0) {
+ PLOG(FATAL) << "Unable to set real group ID to access process files";
+ }
+ if (setresuid(supplied_ruid, supplied_ruid, -1) != 0) {
+ PLOG(FATAL) << "Unable to set real user ID to access process files";
+ }
+
std::string exec;
if (force_exec) {
exec.assign(force_exec);
diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h
index 8c38aa2..7261ed4 100644
--- a/crash_reporter/user_collector.h
+++ b/crash_reporter/user_collector.h
@@ -105,7 +105,6 @@
// crash_reporter-user-collection signature.
std::string GetErrorTypeSignature(ErrorType error_type) const;
- std::string GetPattern(bool enabled) const;
bool SetUpInternal(bool enabled);
// Returns, via |line|, the first line in |lines| that starts with |prefix|.
@@ -166,7 +165,7 @@
ErrorType ConvertAndEnqueueCrash(pid_t pid, const std::string &exec_name,
uid_t supplied_ruid, bool *out_of_capacity);
bool ParseCrashAttributes(const std::string &crash_attributes,
- pid_t *pid, int *signal, uid_t *uid,
+ pid_t *pid, int *signal, uid_t *uid, gid_t *gid,
std::string *kernel_supplied_name);
bool ShouldDump(bool has_owner_consent,
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index 4419e7c..72e61e6 100644
--- a/crash_reporter/user_collector_test.cc
+++ b/crash_reporter/user_collector_test.cc
@@ -23,12 +23,12 @@
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_split.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/syslog_logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using base::FilePath;
-using chromeos::FindLog;
+using brillo::FindLog;
namespace {
@@ -73,7 +73,7 @@
base::DeleteFile(FilePath("test"), true);
mkdir("test", 0777);
pid_ = getpid();
- chromeos::ClearLog();
+ brillo::ClearLog();
}
protected:
@@ -98,35 +98,38 @@
pid_t pid;
int signal;
uid_t uid;
+ gid_t gid;
std::string exec_name;
- EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:foobar",
- &pid, &signal, &uid, &exec_name));
+ EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:2000:foobar",
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_EQ(123456, pid);
EXPECT_EQ(11, signal);
EXPECT_EQ(1000, uid);
+ EXPECT_EQ(2000, gid);
EXPECT_EQ("foobar", exec_name);
EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_EQ(4321, pid);
EXPECT_EQ(6, signal);
EXPECT_EQ(-1, uid);
+ EXPECT_EQ(-1, gid);
EXPECT_EQ("barfoo", exec_name);
EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:exec:extra",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_EQ("exec:extra", exec_name);
EXPECT_FALSE(collector_.ParseCrashAttributes("12345p:11:foobar",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_FALSE(collector_.ParseCrashAttributes("123456:1 :foobar",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
EXPECT_FALSE(collector_.ParseCrashAttributes("123456::foobar",
- &pid, &signal, &uid, &exec_name));
+ &pid, &signal, &uid, &gid, &exec_name));
}
TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) {
@@ -219,7 +222,7 @@
"GetSymlinkTarget failed - Path /proc/0 DirectoryExists: 0"));
EXPECT_TRUE(FindLog("stat /proc/0/exe failed: -1 2"));
- chromeos::ClearLog();
+ brillo::ClearLog();
pid_t my_pid = getpid();
EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name));
EXPECT_FALSE(FindLog("Readlink failed"));
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 599995c..713638d 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -130,31 +130,44 @@
"dump_backtrace"
};
-static bool selinux_action_allowed(int s, pid_t tid, debugger_action_t action)
+static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len)
+{
+ struct debugger_request_t* req = reinterpret_cast<debugger_request_t*>(data);
+
+ if (!req) {
+ ALOGE("No debuggerd request audit data");
+ return 0;
+ }
+
+ snprintf(buf, len, "pid=%d uid=%d gid=%d", req->pid, req->uid, req->gid);
+ return 0;
+}
+
+static bool selinux_action_allowed(int s, debugger_request_t* request)
{
char *scon = NULL, *tcon = NULL;
const char *tclass = "debuggerd";
const char *perm;
bool allowed = false;
- if (action <= 0 || action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
- ALOGE("SELinux: No permission defined for debugger action %d", action);
+ if (request->action <= 0 || request->action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
+ ALOGE("SELinux: No permission defined for debugger action %d", request->action);
return false;
}
- perm = debuggerd_perms[action];
+ perm = debuggerd_perms[request->action];
if (getpeercon(s, &scon) < 0) {
ALOGE("Cannot get peer context from socket\n");
goto out;
}
- if (getpidcon(tid, &tcon) < 0) {
- ALOGE("Cannot get context for tid %d\n", tid);
+ if (getpidcon(request->tid, &tcon) < 0) {
+ ALOGE("Cannot get context for tid %d\n", request->tid);
goto out;
}
- allowed = (selinux_check_access(scon, tcon, tclass, perm, NULL) == 0);
+ allowed = (selinux_check_access(scon, tcon, tclass, perm, reinterpret_cast<void*>(request)) == 0);
out:
freecon(scon);
@@ -225,7 +238,7 @@
return -1;
}
- if (!selinux_action_allowed(fd, out_request->tid, out_request->action))
+ if (!selinux_action_allowed(fd, out_request))
return -1;
} else {
// No one else is allowed to dump arbitrary processes.
@@ -566,6 +579,8 @@
int main(int argc, char** argv) {
union selinux_callback cb;
if (argc == 1) {
+ cb.func_audit = audit_callback;
+ selinux_set_callback(SELINUX_CB_AUDIT, cb);
cb.func_log = selinux_log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
return do_server();
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 5e6c6f9..b47199f 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -3,7 +3,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c fs_mgr_fstab.c
+LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.cpp fs_mgr_fstab.c
LOCAL_SRC_FILES += fs_mgr_format.c fs_mgr_slotselect.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
@@ -12,7 +12,7 @@
external/openssl/include
LOCAL_MODULE:= libfs_mgr
-LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils
+LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils libbase
LOCAL_C_INCLUDES += system/extras/ext4_utils system/extras/squashfs_utils \
bootable/recovery
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
@@ -39,7 +39,7 @@
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils
+LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils libbase
LOCAL_STATIC_LIBRARIES += libsparse_static libz libselinux
LOCAL_CXX_STL := libc++_static
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 367ab6e..ba0e097 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -20,6 +20,8 @@
#include <cutils/klog.h>
#include <fs_mgr.h>
+__BEGIN_DECLS
+
#define INFO(x...) KLOG_INFO("fs_mgr", x)
#define WARNING(x...) KLOG_WARNING("fs_mgr", x)
#define ERROR(x...) KLOG_ERROR("fs_mgr", x)
@@ -86,4 +88,6 @@
int fs_mgr_set_blk_ro(const char *blockdev);
int fs_mgr_update_for_slotselect(struct fstab *fstab);
+__END_DECLS
+
#endif /* __CORE_FS_MGR_PRIV_H */
diff --git a/fs_mgr/fs_mgr_priv_verity.h b/fs_mgr/fs_mgr_priv_verity.h
index f90e596..cd673f3 100644
--- a/fs_mgr/fs_mgr_priv_verity.h
+++ b/fs_mgr/fs_mgr_priv_verity.h
@@ -14,7 +14,14 @@
* limitations under the License.
*/
+#include <sys/cdefs.h>
+
#define FS_MGR_SETUP_VERITY_DISABLED -2
#define FS_MGR_SETUP_VERITY_FAIL -1
#define FS_MGR_SETUP_VERITY_SUCCESS 0
+
+__BEGIN_DECLS
+
int fs_mgr_setup_verity(struct fstab_rec *fstab);
+
+__END_DECLS
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.cpp
similarity index 95%
rename from fs_mgr/fs_mgr_verity.c
rename to fs_mgr/fs_mgr_verity.cpp
index eddc3e4..8f7c6a2 100644
--- a/fs_mgr/fs_mgr_verity.c
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -29,6 +29,7 @@
#include <libgen.h>
#include <time.h>
+#include <base/file.h>
#include <private/android_filesystem_config.h>
#include <cutils/properties.h>
#include <logwrap/logwrap.h>
@@ -40,6 +41,7 @@
#include "ext4_sb.h"
#include "squashfs_utils.h"
+#include "fs_mgr.h"
#include "fs_mgr_priv.h"
#include "fs_mgr_priv_verity.h"
@@ -74,18 +76,15 @@
extern struct fs_info info;
-static RSAPublicKey *load_key(char *path)
+static RSAPublicKey *load_key(const char *path)
{
- FILE *f;
- RSAPublicKey *key;
-
- key = malloc(sizeof(RSAPublicKey));
+ RSAPublicKey* key = static_cast<RSAPublicKey*>(malloc(sizeof(RSAPublicKey)));
if (!key) {
ERROR("Can't malloc key\n");
return NULL;
}
- f = fopen(path, "r");
+ FILE* f = fopen(path, "r");
if (!f) {
ERROR("Can't open '%s'\n", path);
free(key);
@@ -202,7 +201,7 @@
return -1;
}
- if (TEMP_FAILURE_RETRY(read(data_device, &sb, sizeof(sb))) != sizeof(sb)) {
+ if (!android::base::ReadFully(data_device, &sb, sizeof(sb))) {
ERROR("Error reading superblock");
close(data_device);
return -1;
@@ -258,10 +257,8 @@
ERROR("Could not seek to start of verity metadata block.\n");
goto out;
}
-
// check the magic number
- if (TEMP_FAILURE_RETRY(read(device, &magic_number, sizeof(magic_number))) !=
- sizeof(magic_number)) {
+ if (!android::base::ReadFully(device, &magic_number, sizeof(magic_number))) {
ERROR("Couldn't read magic number!\n");
goto out;
}
@@ -275,13 +272,13 @@
#endif
if (magic_number != VERITY_METADATA_MAGIC_NUMBER) {
- ERROR("Couldn't find verity metadata at offset %"PRIu64"!\n", device_size);
+ ERROR("Couldn't find verity metadata at offset %" PRIu64 "!\n", device_size);
goto out;
}
// check the protocol version
- if (TEMP_FAILURE_RETRY(read(device, &protocol_version,
- sizeof(protocol_version))) != sizeof(protocol_version)) {
+ if (!android::base::ReadFully(device, &protocol_version,
+ sizeof(protocol_version))) {
ERROR("Couldn't read verity metadata protocol version!\n");
goto out;
}
@@ -296,7 +293,7 @@
ERROR("Couldn't allocate memory for signature!\n");
goto out;
}
- if (TEMP_FAILURE_RETRY(read(device, *signature, RSANUMBYTES)) != RSANUMBYTES) {
+ if (!android::base::ReadFully(device, *signature, RSANUMBYTES)) {
ERROR("Couldn't read signature from verity metadata!\n");
goto out;
}
@@ -307,20 +304,18 @@
}
// get the size of the table
- if (TEMP_FAILURE_RETRY(read(device, &table_length, sizeof(table_length))) !=
- sizeof(table_length)) {
+ if (!android::base::ReadFully(device, &table_length, sizeof(table_length))) {
ERROR("Couldn't get the size of the verity table from metadata!\n");
goto out;
}
// get the table + null terminator
- *table = malloc(table_length + 1);
+ *table = static_cast<char*>(malloc(table_length + 1));
if (!*table) {
ERROR("Couldn't allocate memory for verity table!\n");
goto out;
}
- if (TEMP_FAILURE_RETRY(read(device, *table, table_length)) !=
- (ssize_t)table_length) {
+ if (!android::base::ReadFully(device, *table, table_length)) {
ERROR("Couldn't read the verity table from metadata!\n");
goto out;
}
@@ -485,7 +480,7 @@
goto out;
}
- if (TEMP_FAILURE_RETRY(read(fd, buffer, size)) != size) {
+ if (!android::base::ReadFully(fd, buffer, size)) {
ERROR("Failed to read %zd bytes from %s (%s)\n", size, fname,
strerror(errno));
goto out;
@@ -887,7 +882,7 @@
int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
{
- _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
+ alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
bool use_state = true;
char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
char *mount_point;
@@ -989,7 +984,7 @@
int verity_table_length = 0;
uint64_t device_size = 0;
- _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
+ alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
struct dm_ioctl *io = (struct dm_ioctl *) buffer;
char *mount_point = basename(fstab->mount_point);
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index cb77a8e..ddfdd88 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -39,6 +39,7 @@
#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
#define FAKE_BATTERY_CAPACITY 42
#define FAKE_BATTERY_TEMPERATURE 424
+#define ALWAYS_PLUGGED_CAPACITY 100
namespace android {
@@ -208,6 +209,15 @@
mBatteryFixedTemperature :
getIntField(mHealthdConfig->batteryTemperaturePath);
+ // For devices which do not have battery and are always plugged
+ // into power souce.
+ if (mAlwaysPluggedDevice) {
+ props.chargerAcOnline = true;
+ props.batteryPresent = true;
+ props.batteryStatus = BATTERY_STATUS_CHARGING;
+ props.batteryHealth = BATTERY_HEALTH_GOOD;
+ }
+
const int SIZE = 128;
char buf[SIZE];
String8 btech;
@@ -590,8 +600,15 @@
closedir(dir);
}
- if (!mChargerNames.size())
+ // This indicates that there is no charger driver registered.
+ // Typically the case for devices which do not have a battery and
+ // and are always plugged into AC mains.
+ if (!mChargerNames.size()) {
KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
+ mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
+ mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
+ mAlwaysPluggedDevice = true;
+ }
if (!mBatteryDevicePresent) {
KLOG_WARNING(LOG_TAG, "No battery devices found\n");
hc->periodic_chores_interval_fast = -1;
@@ -611,7 +628,7 @@
KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
if (mHealthdConfig->batteryTechnologyPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
- if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+ if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
if (mHealthdConfig->batteryFullChargePath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h
index 3425f27..a61171f 100644
--- a/healthd/BatteryMonitor.h
+++ b/healthd/BatteryMonitor.h
@@ -46,6 +46,7 @@
struct healthd_config *mHealthdConfig;
Vector<String8> mChargerNames;
bool mBatteryDevicePresent;
+ bool mAlwaysPluggedDevice;
int mBatteryFixedCapacity;
int mBatteryFixedTemperature;
struct BatteryProperties props;
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
index 7818b4e..20a379e 100644
--- a/include/utils/LruCache.h
+++ b/include/utils/LruCache.h
@@ -99,17 +99,29 @@
TValue mNullValue;
public:
+ // To be used like:
+ // while (it.next()) {
+ // it.value(); it.key();
+ // }
class Iterator {
public:
- Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIterator(mCache.mSet->begin()) {
+ Iterator(const LruCache<TKey, TValue>& cache):
+ mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {
}
bool next() {
if (mIterator == mCache.mSet->end()) {
return false;
}
- std::advance(mIterator, 1);
- return mIterator != mCache.mSet->end();
+ if (!mBeginReturned) {
+ // mIterator has been initialized to the beginning and
+ // hasn't been returned. Do not advance:
+ mBeginReturned = true;
+ } else {
+ std::advance(mIterator, 1);
+ }
+ bool ret = (mIterator != mCache.mSet->end());
+ return ret;
}
const TValue& value() const {
@@ -122,6 +134,7 @@
private:
const LruCache<TKey, TValue>& mCache;
typename LruCacheSet::iterator mIterator;
+ bool mBeginReturned;
};
};
diff --git a/init/init.cpp b/init/init.cpp
index ee1351d..a898b03 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -467,7 +467,16 @@
}
static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
- snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data);
+
+ property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
+
+ if (!d || !d->name || !d->cr) {
+ ERROR("audit_callback invoked with null data arguments!");
+ return 0;
+ }
+
+ snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
+ d->cr->pid, d->cr->uid, d->cr->gid);
return 0;
}
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 02b3985..5b8e27b 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -105,7 +105,10 @@
for (const auto& sp : section_parsers_) {
sp.second->EndFile(path);
}
- DumpState();
+
+ // Turning this on and letting the INFO logging be discarded adds 0.2s to
+ // Nexus 9 boot time, so it's disabled by default.
+ if (false) DumpState();
NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
return true;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index a37d6f6..96b4a37 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -90,10 +90,11 @@
}
}
-static int check_mac_perms(const char *name, char *sctx)
+static int check_mac_perms(const char *name, char *sctx, struct ucred *cr)
{
char *tctx = NULL;
int result = 0;
+ property_audit_data audit_data;
if (!sctx)
goto err;
@@ -104,7 +105,10 @@
if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
goto err;
- if (selinux_check_access(sctx, tctx, "property_service", "set", (void*) name) == 0)
+ audit_data.name = name;
+ audit_data.cr = cr;
+
+ if (selinux_check_access(sctx, tctx, "property_service", "set", reinterpret_cast<void*>(&audit_data)) == 0)
result = 1;
freecon(tctx);
@@ -112,7 +116,7 @@
return result;
}
-static int check_control_mac_perms(const char *name, char *sctx)
+static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
{
/*
* Create a name prefix out of ctl.<service name>
@@ -126,19 +130,19 @@
if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
return 0;
- return check_mac_perms(ctl_name, sctx);
+ return check_mac_perms(ctl_name, sctx, cr);
}
/*
* Checks permissions for setting system properties.
* Returns 1 if uid allowed, 0 otherwise.
*/
-static int check_perms(const char *name, char *sctx)
+static int check_perms(const char *name, char *sctx, struct ucred *cr)
{
if(!strncmp(name, "ro.", 3))
name +=3;
- return check_mac_perms(name, sctx);
+ return check_mac_perms(name, sctx, cr);
}
std::string property_get(const char* name) {
@@ -321,14 +325,14 @@
// Keep the old close-socket-early behavior when handling
// ctl.* properties.
close(s);
- if (check_control_mac_perms(msg.value, source_ctx)) {
+ if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
handle_control_message((char*) msg.name + 4, (char*) msg.value);
} else {
ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
}
} else {
- if (check_perms(msg.name, source_ctx)) {
+ if (check_perms(msg.name, source_ctx, &cr)) {
property_set((char*) msg.name, (char*) msg.value);
} else {
ERROR("sys_prop: permission denied uid:%d name:%s\n",
diff --git a/init/property_service.h b/init/property_service.h
index f30577b..8b76d2b 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,9 +18,15 @@
#define _INIT_PROPERTY_H
#include <stddef.h>
+#include <sys/socket.h>
#include <sys/system_properties.h>
#include <string>
+struct property_audit_data {
+ ucred *cr;
+ const char* name;
+};
+
extern void property_init(void);
extern void property_load_boot_defaults(void);
extern void load_persist_props(void);
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 681c57f..970b386 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -126,11 +126,10 @@
{ 00644, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private/*" },
{ 00644, AID_APP, AID_APP, 0, "data/data/*" },
- /* the following five files are INTENTIONALLY set-uid, but they
+ /* the following four files are INTENTIONALLY set-uid, but they
* are NOT included on user builds. */
{ 04750, AID_ROOT, AID_SHELL, 0, "system/xbin/su" },
{ 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/librank" },
- { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procrank" },
{ 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" },
{ 04770, AID_ROOT, AID_RADIO, 0, "system/bin/pppd-ril" },
diff --git a/libcutils/tests/MemsetTest.cpp b/libcutils/tests/MemsetTest.cpp
index 45efc51..a98485f 100644
--- a/libcutils/tests/MemsetTest.cpp
+++ b/libcutils/tests/MemsetTest.cpp
@@ -20,6 +20,8 @@
#include <sys/mman.h>
#include <sys/types.h>
+#include <memory>
+
#include <cutils/memory.h>
#include <gtest/gtest.h>
@@ -127,14 +129,14 @@
min_incr = 2;
value |= value << 16;
}
- uint32_t* expected_buf = new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)];
+ std::unique_ptr<uint32_t[]> expected_buf(new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)]);
for (size_t i = 0; i < MAX_TEST_SIZE/sizeof(uint32_t); i++) {
expected_buf[i] = value;
}
// Allocate one large buffer with lots of extra space so that we can
// guarantee that all possible alignments will fit.
- uint8_t *buf = new uint8_t[3*MAX_TEST_SIZE];
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[3*MAX_TEST_SIZE]);
uint8_t *buf_align;
for (size_t i = 0; i < num_aligns; i++) {
size_t incr = min_incr;
@@ -142,7 +144,7 @@
incr = GetIncrement(len, min_incr);
buf_align = reinterpret_cast<uint8_t*>(GetAlignedPtr(
- buf+FENCEPOST_LENGTH, align[i][0], align[i][1]));
+ buf.get()+FENCEPOST_LENGTH, align[i][0], align[i][1]));
SetFencepost(&buf_align[-FENCEPOST_LENGTH]);
SetFencepost(&buf_align[len]);
@@ -153,15 +155,13 @@
} else {
android_memset32(reinterpret_cast<uint32_t*>(buf_align), value, len);
}
- ASSERT_EQ(0, memcmp(expected_buf, buf_align, len))
+ ASSERT_EQ(0, memcmp(expected_buf.get(), buf_align, len))
<< "Failed size " << len << " align " << align[i][0] << " " << align[i][1] << "\n";
VerifyFencepost(&buf_align[-FENCEPOST_LENGTH]);
VerifyFencepost(&buf_align[len]);
}
}
- delete expected_buf;
- delete buf;
}
TEST(libcutils, android_memset16_non_zero) {
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
index 2ed84d7..580b980 100644
--- a/libutils/tests/LruCache_test.cpp
+++ b/libutils/tests/LruCache_test.cpp
@@ -293,4 +293,110 @@
EXPECT_EQ(3, callback.callbackCount);
}
+TEST_F(LruCacheTest, IteratorCheck) {
+ LruCache<int, int> cache(100);
+
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+ EXPECT_EQ(3U, cache.size());
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ int v = it.value();
+ // Check we haven't seen the value before.
+ EXPECT_TRUE(returnedValues.find(v) == returnedValues.end());
+ returnedValues.insert(v);
+ }
+ EXPECT_EQ(std::unordered_set<int>({4, 5, 6}), returnedValues);
+}
+
+TEST_F(LruCacheTest, EmptyCacheIterator) {
+ // Check that nothing crashes...
+ LruCache<int, int> cache(100);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>(), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheIterator) {
+ // Check that nothing crashes...
+ LruCache<int, int> cache(100);
+ cache.put(1, 2);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 2 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheRemove) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 2);
+
+ cache.remove(1);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ }), returnedValues);
+}
+
+TEST_F(LruCacheTest, Remove) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+
+ cache.remove(2);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 4, 6 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveYoungest) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+
+ cache.remove(3);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 4, 5 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveNonMember) {
+ LruCache<int, int> cache(100);
+ cache.put(1, 4);
+ cache.put(2, 5);
+ cache.put(3, 6);
+
+ cache.remove(7);
+
+ LruCache<int, int>::Iterator it(cache);
+ std::unordered_set<int> returnedValues;
+ while (it.next()) {
+ returnedValues.insert(it.value());
+ }
+ EXPECT_EQ(std::unordered_set<int>({ 4, 5, 6 }), returnedValues);
+}
+
}
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 9440b68..ddbfb3e 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -656,7 +656,7 @@
break;
case 'f':
- if ((tail_time == log_time::EPOCH) && (tail_lines != 0)) {
+ if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
tail_time = lastLogTime(optarg);
}
// redirect output to a file
@@ -832,49 +832,66 @@
} else {
logger_list = android_logger_list_alloc(mode, tail_lines, 0);
}
+ const char *openDeviceFail = NULL;
+ const char *clearFail = NULL;
+ const char *setSizeFail = NULL;
+ const char *getSizeFail = NULL;
+ // We have three orthogonal actions below to clear, set log size and
+ // get log size. All sharing the same iteration loop.
while (dev) {
dev->logger_list = logger_list;
dev->logger = android_logger_open(logger_list,
android_name_to_log_id(dev->device));
if (!dev->logger) {
- logcat_panic(false, "Unable to open log device '%s'\n",
- dev->device);
+ openDeviceFail = openDeviceFail ?: dev->device;
+ dev = dev->next;
+ continue;
}
if (clearLog) {
- int ret;
- ret = android_logger_clear(dev->logger);
- if (ret) {
- logcat_panic(false, "failed to clear the log");
+ if (android_logger_clear(dev->logger)) {
+ clearFail = clearFail ?: dev->device;
}
}
- if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
- logcat_panic(false, "failed to set the log size");
+ if (setLogSize) {
+ if (android_logger_set_log_size(dev->logger, setLogSize)) {
+ setSizeFail = setSizeFail ?: dev->device;
+ }
}
if (getLogSize) {
- long size, readable;
+ long size = android_logger_get_log_size(dev->logger);
+ long readable = android_logger_get_log_readable_size(dev->logger);
- size = android_logger_get_log_size(dev->logger);
- if (size < 0) {
- logcat_panic(false, "failed to get the log size");
+ if ((size < 0) || (readable < 0)) {
+ getSizeFail = getSizeFail ?: dev->device;
+ } else {
+ printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
+ "max entry is %db, max payload is %db\n", dev->device,
+ value_of_size(size), multiplier_of_size(size),
+ value_of_size(readable), multiplier_of_size(readable),
+ (int) LOGGER_ENTRY_MAX_LEN,
+ (int) LOGGER_ENTRY_MAX_PAYLOAD);
}
-
- readable = android_logger_get_log_readable_size(dev->logger);
- if (readable < 0) {
- logcat_panic(false, "failed to get the readable log size");
- }
-
- printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
- "max entry is %db, max payload is %db\n", dev->device,
- value_of_size(size), multiplier_of_size(size),
- value_of_size(readable), multiplier_of_size(readable),
- (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
}
dev = dev->next;
}
+ // report any errors in the above loop and exit
+ if (openDeviceFail) {
+ logcat_panic(false, "Unable to open log device '%s'\n", openDeviceFail);
+ }
+ if (clearFail) {
+ logcat_panic(false, "failed to clear the '%s' log\n", clearFail);
+ }
+ if (setSizeFail) {
+ logcat_panic(false, "failed to set the '%s' log size\n", setSizeFail);
+ }
+ if (getSizeFail) {
+ logcat_panic(false, "failed to get the readable '%s' log size",
+ getSizeFail);
+ }
if (setPruneList) {
size_t len = strlen(setPruneList);
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index bcf8d82..610a6ec 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -15,10 +15,12 @@
*/
#include <ctype.h>
+#include <dirent.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/types.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -368,7 +370,7 @@
while (fgets(buffer, sizeof(buffer), fp)) {
int size, consumed, max, payload;
- char size_mult[2], consumed_mult[2];
+ char size_mult[3], consumed_mult[3];
long full_size, full_consumed;
size = consumed = max = payload = 0;
@@ -573,12 +575,12 @@
static const char comm[] = "logcat -b radio -b events -b system -b main"
" -d -f %s/log.txt -n 7 -r 1";
char command[sizeof(buf) + sizeof(comm)];
- sprintf(command, comm, buf);
+ snprintf(command, sizeof(command), comm, buf);
int ret;
EXPECT_FALSE((ret = system(command)));
if (!ret) {
- sprintf(command, "ls -s %s 2>/dev/null", buf);
+ snprintf(command, sizeof(command), "ls -s %s 2>/dev/null", buf);
FILE *fp;
EXPECT_TRUE(NULL != (fp = popen(command, "r")));
@@ -587,16 +589,12 @@
int count = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
- static const char match_1[] = "4 log.txt";
- static const char match_2[] = "8 log.txt";
- static const char match_3[] = "12 log.txt";
- static const char match_4[] = "16 log.txt";
static const char total[] = "total ";
+ int num;
+ char c;
- if (!strncmp(buffer, match_1, sizeof(match_1) - 1)
- || !strncmp(buffer, match_2, sizeof(match_2) - 1)
- || !strncmp(buffer, match_3, sizeof(match_3) - 1)
- || !strncmp(buffer, match_4, sizeof(match_4) - 1)) {
+ if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
+ (num <= 24)) {
++count;
} else if (strncmp(buffer, total, sizeof(total) - 1)) {
fprintf(stderr, "WARNING: Parse error: %s", buffer);
@@ -606,7 +604,7 @@
EXPECT_TRUE(count == 7 || count == 8);
}
}
- sprintf(command, "rm -rf %s", buf);
+ snprintf(command, sizeof(command), "rm -rf %s", buf);
EXPECT_FALSE(system(command));
}
@@ -618,12 +616,12 @@
static const char logcat_cmd[] = "logcat -b radio -b events -b system -b main"
" -d -f %s/log.txt -n 10 -r 1";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
- sprintf(command, logcat_cmd, tmp_out_dir);
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
int ret;
EXPECT_FALSE((ret = system(command)));
if (!ret) {
- sprintf(command, "ls %s 2>/dev/null", tmp_out_dir);
+ snprintf(command, sizeof(command), "ls %s 2>/dev/null", tmp_out_dir);
FILE *fp;
EXPECT_TRUE(NULL != (fp = popen(command, "r")));
@@ -659,7 +657,113 @@
pclose(fp);
EXPECT_EQ(11, log_file_count);
}
- sprintf(command, "rm -rf %s", tmp_out_dir);
+ snprintf(command, sizeof(command), "rm -rf %s", tmp_out_dir);
+ EXPECT_FALSE(system(command));
+}
+
+TEST(logcat, logrotate_continue) {
+ static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX";
+ char tmp_out_dir[sizeof(tmp_out_dir_form)];
+ ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
+
+ static const char log_filename[] = "log.txt";
+ static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 256 -r 1024";
+ static const char cleanup_cmd[] = "rm -rf %s";
+ char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+
+ int ret;
+ EXPECT_FALSE((ret = system(command)));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ return;
+ }
+ FILE *fp;
+ snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, log_filename);
+ EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+ if (!fp) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ return;
+ }
+ char *line = NULL;
+ char *last_line = NULL; // this line is allowed to stutter, one-line overlap
+ char *second_last_line = NULL;
+ size_t len = 0;
+ while (getline(&line, &len, fp) != -1) {
+ free(second_last_line);
+ second_last_line = last_line;
+ last_line = line;
+ line = NULL;
+ }
+ fclose(fp);
+ free(line);
+ if (second_last_line == NULL) {
+ fprintf(stderr, "No second to last line, using last, test may fail\n");
+ second_last_line = last_line;
+ last_line = NULL;
+ }
+ free(last_line);
+ EXPECT_TRUE(NULL != second_last_line);
+ if (!second_last_line) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ return;
+ }
+ // re-run the command, it should only add a few lines more content if it
+ // continues where it left off.
+ snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
+ EXPECT_FALSE((ret = system(command)));
+ if (ret) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ return;
+ }
+ DIR *dir;
+ EXPECT_TRUE(NULL != (dir = opendir(tmp_out_dir)));
+ if (!dir) {
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
+ EXPECT_FALSE(system(command));
+ return;
+ }
+ struct dirent *entry;
+ unsigned count = 0;
+ while ((entry = readdir(dir))) {
+ if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) {
+ continue;
+ }
+ snprintf(command, sizeof(command), "%s/%s", tmp_out_dir, entry->d_name);
+ EXPECT_TRUE(NULL != ((fp = fopen(command, "r"))));
+ if (!fp) {
+ fprintf(stderr, "%s ?\n", command);
+ continue;
+ }
+ line = NULL;
+ size_t number = 0;
+ while (getline(&line, &len, fp) != -1) {
+ ++number;
+ if (!strcmp(line, second_last_line)) {
+ EXPECT_TRUE(++count <= 1);
+ fprintf(stderr, "%s(%zu):\n", entry->d_name, number);
+ }
+ }
+ fclose(fp);
+ free(line);
+ unlink(command);
+ }
+ closedir(dir);
+ if (count > 1) {
+ char *brk = strpbrk(second_last_line, "\r\n");
+ if (!brk) {
+ brk = second_last_line + strlen(second_last_line);
+ }
+ fprintf(stderr, "\"%.*s\" occured %u times\n",
+ (int)(brk - second_last_line), second_last_line, count);
+ }
+ free(second_last_line);
+
+ snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir);
EXPECT_FALSE(system(command));
}
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 031c740..eafa28f 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -96,8 +96,7 @@
return 0;
}
- mBuf.clear((log_id_t) id, uid);
- cli->sendMsg("success");
+ cli->sendMsg(mBuf.clear((log_id_t) id, uid) ? "busy" : "success");
return 0;
}
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 808d17a..2f7cbfb 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -183,7 +183,7 @@
static const char comm_str[] = " comm=\"";
const char *comm = strstr(str, comm_str);
const char *estr = str + strlen(str);
- char *commfree = NULL;
+ const char *commfree = NULL;
if (comm) {
estr = comm;
comm += sizeof(comm_str) - 1;
@@ -231,7 +231,7 @@
// end scope for main buffer
}
- free(commfree);
+ free(const_cast<char *>(commfree));
free(str);
if (notify) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index c2f846e..1de8e64 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -217,7 +217,7 @@
return len;
}
-// Prune at most 10% of the log entries or 256, whichever is less.
+// Prune at most 10% of the log entries or maxPrune, whichever is less.
//
// mLogElementsLock must be held when this function is called.
void LogBuffer::maybePrune(log_id_t id) {
@@ -225,21 +225,24 @@
unsigned long maxSize = log_buffer_size(id);
if (sizes > maxSize) {
size_t sizeOver = sizes - ((maxSize * 9) / 10);
- size_t elements = stats.elements(id);
- size_t minElements = elements / 10;
+ size_t elements = stats.realElements(id);
+ size_t minElements = elements / 100;
+ if (minElements < minPrune) {
+ minElements = minPrune;
+ }
unsigned long pruneRows = elements * sizeOver / sizes;
- if (pruneRows <= minElements) {
+ if (pruneRows < minElements) {
pruneRows = minElements;
}
- if (pruneRows > 256) {
- pruneRows = 256;
+ if (pruneRows > maxPrune) {
+ pruneRows = maxPrune;
}
prune(id, pruneRows);
}
}
LogBufferElementCollection::iterator LogBuffer::erase(
- LogBufferElementCollection::iterator it, bool engageStats) {
+ LogBufferElementCollection::iterator it, bool coalesce) {
LogBufferElement *e = *it;
log_id_t id = e->getLogId();
@@ -248,10 +251,10 @@
mLastWorstUid[id].erase(f);
}
it = mLogElements.erase(it);
- if (engageStats) {
- stats.subtract(e);
- } else {
+ if (coalesce) {
stats.erase(e);
+ } else {
+ stats.subtract(e);
}
delete e;
@@ -286,7 +289,7 @@
public:
- bool merge(LogBufferElement *e, unsigned short dropped) {
+ bool coalesce(LogBufferElement *e, unsigned short dropped) {
LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
LogBufferElementMap::iterator it = map.find(key.getKey());
if (it != map.end()) {
@@ -374,8 +377,10 @@
//
// mLogElementsLock must be held when this function is called.
//
-void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
+bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
LogTimeEntry *oldest = NULL;
+ bool busy = false;
+ bool clearAll = pruneRows == ULONG_MAX;
LogTimeEntry::lock();
@@ -393,35 +398,31 @@
LogBufferElementCollection::iterator it;
if (caller_uid != AID_ROOT) {
+ // Only here if clearAll condition (pruneRows == ULONG_MAX)
for(it = mLogElements.begin(); it != mLogElements.end();) {
LogBufferElement *e = *it;
- if (oldest && (oldest->mStart <= e->getSequence())) {
- break;
- }
-
- if (e->getLogId() != id) {
+ if ((e->getLogId() != id) || (e->getUid() != caller_uid)) {
++it;
continue;
}
- if (e->getUid() == caller_uid) {
- it = erase(it);
- pruneRows--;
- if (pruneRows == 0) {
- break;
- }
- } else {
- ++it;
+ if (oldest && (oldest->mStart <= e->getSequence())) {
+ oldest->triggerSkip_Locked(id, pruneRows);
+ busy = true;
+ break;
}
+
+ it = erase(it);
+ pruneRows--;
}
LogTimeEntry::unlock();
- return;
+ return busy;
}
// prune by worst offender by uid
bool hasBlacklist = mPrune.naughty();
- while (pruneRows > 0) {
+ while (!clearAll && (pruneRows > 0)) {
// recalculate the worst offender on every batched pass
uid_t worst = (uid_t) -1;
size_t worst_sizes = 0;
@@ -456,7 +457,7 @@
it = mLogElements.begin();
// Perform at least one mandatory garbage collection cycle in following
// - clear leading chatty tags
- // - merge chatty tags
+ // - coalesce chatty tags
// - check age-out of preserved logs
bool gc = pruneRows <= 1;
if (!gc && (worst != (uid_t) -1)) {
@@ -478,6 +479,7 @@
LogBufferElement *e = *it;
if (oldest && (oldest->mStart <= e->getSequence())) {
+ busy = true;
break;
}
@@ -494,9 +496,8 @@
continue;
}
- // merge any drops
- if (dropped && last.merge(e, dropped)) {
- it = erase(it, false);
+ if (dropped && last.coalesce(e, dropped)) {
+ it = erase(it, true);
continue;
}
@@ -527,7 +528,6 @@
break;
}
- // unmerged drop message
if (dropped) {
last.add(e);
if ((!gc && (e->getUid() == worst))
@@ -561,8 +561,8 @@
} else {
stats.drop(e);
e->setDropped(1);
- if (last.merge(e, 1)) {
- it = erase(it, false);
+ if (last.coalesce(e, 1)) {
+ it = erase(it, true);
} else {
last.add(e);
if (!gc || (mLastWorstUid[id].find(worst)
@@ -585,7 +585,7 @@
}
bool whitelist = false;
- bool hasWhitelist = mPrune.nice();
+ bool hasWhitelist = mPrune.nice() && !clearAll;
it = mLogElements.begin();
while((pruneRows > 0) && (it != mLogElements.end())) {
LogBufferElement *e = *it;
@@ -596,6 +596,8 @@
}
if (oldest && (oldest->mStart <= e->getSequence())) {
+ busy = true;
+
if (whitelist) {
break;
}
@@ -631,6 +633,7 @@
}
if (oldest && (oldest->mStart <= e->getSequence())) {
+ busy = true;
if (stats.sizes(id) > (2 * log_buffer_size(id))) {
// kick a misbehaving log reader client off the island
oldest->release_Locked();
@@ -646,13 +649,50 @@
}
LogTimeEntry::unlock();
+
+ return (pruneRows > 0) && busy;
}
// clear all rows of type "id" from the buffer.
-void LogBuffer::clear(log_id_t id, uid_t uid) {
- pthread_mutex_lock(&mLogElementsLock);
- prune(id, ULONG_MAX, uid);
- pthread_mutex_unlock(&mLogElementsLock);
+bool LogBuffer::clear(log_id_t id, uid_t uid) {
+ bool busy = true;
+ // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
+ for (int retry = 4;;) {
+ if (retry == 1) { // last pass
+ // Check if it is still busy after the sleep, we say prune
+ // one entry, not another clear run, so we are looking for
+ // the quick side effect of the return value to tell us if
+ // we have a _blocked_ reader.
+ pthread_mutex_lock(&mLogElementsLock);
+ busy = prune(id, 1, uid);
+ pthread_mutex_unlock(&mLogElementsLock);
+ // It is still busy, blocked reader(s), lets kill them all!
+ // otherwise, lets be a good citizen and preserve the slow
+ // readers and let the clear run (below) deal with determining
+ // if we are still blocked and return an error code to caller.
+ if (busy) {
+ LogTimeEntry::lock();
+ LastLogTimes::iterator times = mTimes.begin();
+ while (times != mTimes.end()) {
+ LogTimeEntry *entry = (*times);
+ // Killer punch
+ if (entry->owned_Locked() && entry->isWatching(id)) {
+ entry->release_Locked();
+ }
+ times++;
+ }
+ LogTimeEntry::unlock();
+ }
+ }
+ pthread_mutex_lock(&mLogElementsLock);
+ busy = prune(id, ULONG_MAX, uid);
+ pthread_mutex_unlock(&mLogElementsLock);
+ if (!busy || !--retry) {
+ break;
+ }
+ sleep (1); // Let reader(s) catch up after notification
+ }
+ return busy;
}
// get the used space associated with "id".
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index de76693..7ed92e9 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -63,7 +63,7 @@
int (*filter)(const LogBufferElement *element, void *arg) = NULL,
void *arg = NULL);
- void clear(log_id_t id, uid_t uid = AID_ROOT);
+ bool clear(log_id_t id, uid_t uid = AID_ROOT);
unsigned long getSize(log_id_t id);
int setSize(log_id_t id, unsigned long size);
unsigned long getSizeUsed(log_id_t id);
@@ -78,17 +78,21 @@
std::string formatPrune() { return mPrune.format(); }
// helper must be protected directly or implicitly by lock()/unlock()
- char *pidToName(pid_t pid) { return stats.pidToName(pid); }
+ const char *pidToName(pid_t pid) { return stats.pidToName(pid); }
uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
- char *uidToName(uid_t uid) { return stats.uidToName(uid); }
+ const char *uidToName(uid_t uid) { return stats.uidToName(uid); }
void lock() { pthread_mutex_lock(&mLogElementsLock); }
void unlock() { pthread_mutex_unlock(&mLogElementsLock); }
private:
+
+ static constexpr size_t minPrune = 4;
+ static constexpr size_t maxPrune = 256;
+
void maybePrune(log_id_t id);
- void prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
+ bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
LogBufferElementCollection::iterator erase(
- LogBufferElementCollection::iterator it, bool engageStats = true);
+ LogBufferElementCollection::iterator it, bool coalesce = false);
};
#endif // _LOGD_LOG_BUFFER_H__
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 150ce22..c4c302b 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -113,9 +113,9 @@
static const char format_uid[] = "uid=%u%s%s expire %u line%s";
parent->lock();
- char *name = parent->uidToName(mUid);
+ const char *name = parent->uidToName(mUid);
parent->unlock();
- char *commName = android::tidToName(mTid);
+ const char *commName = android::tidToName(mTid);
if (!commName && (mTid != mPid)) {
commName = android::tidToName(mPid);
}
@@ -128,28 +128,28 @@
size_t len = strlen(name + 1);
if (!strncmp(name + 1, commName + 1, len)) {
if (commName[len + 1] == '\0') {
- free(commName);
+ free(const_cast<char *>(commName));
commName = NULL;
} else {
- free(name);
+ free(const_cast<char *>(name));
name = NULL;
}
}
}
if (name) {
- char *p = NULL;
- asprintf(&p, "(%s)", name);
- if (p) {
- free(name);
- name = p;
+ char *buf = NULL;
+ asprintf(&buf, "(%s)", name);
+ if (buf) {
+ free(const_cast<char *>(name));
+ name = buf;
}
}
if (commName) {
- char *p = NULL;
- asprintf(&p, " %s", commName);
- if (p) {
- free(commName);
- commName = p;
+ char *buf = NULL;
+ asprintf(&buf, " %s", commName);
+ if (buf) {
+ free(const_cast<char *>(commName));
+ commName = buf;
}
}
// identical to below to calculate the buffer size required
@@ -166,18 +166,19 @@
buffer = static_cast<char *>(calloc(1, hdrLen + len + 1));
if (!buffer) {
- free(name);
- free(commName);
+ free(const_cast<char *>(name));
+ free(const_cast<char *>(commName));
return 0;
}
size_t retval = hdrLen + len;
if (mLogId == LOG_ID_EVENTS) {
- android_log_event_string_t *e = reinterpret_cast<android_log_event_string_t *>(buffer);
+ android_log_event_string_t *event =
+ reinterpret_cast<android_log_event_string_t *>(buffer);
- e->header.tag = htole32(LOGD_LOG_TAG);
- e->type = EVENT_TYPE_STRING;
- e->length = htole32(len);
+ event->header.tag = htole32(LOGD_LOG_TAG);
+ event->type = EVENT_TYPE_STRING;
+ event->length = htole32(len);
} else {
++retval;
buffer[0] = ANDROID_LOG_INFO;
@@ -187,8 +188,8 @@
snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
commName ? commName : "",
mDropped, (mDropped > 1) ? "s" : "");
- free(name);
- free(commName);
+ free(const_cast<char *>(name));
+ free(const_cast<char *>(commName));
return retval;
}
diff --git a/logd/LogCommand.cpp b/logd/LogCommand.cpp
index 06d865c..6d0e92e 100644
--- a/logd/LogCommand.cpp
+++ b/logd/LogCommand.cpp
@@ -42,7 +42,6 @@
static bool groupIsLog(char *buf) {
char *ptr;
static const char ws[] = " \n";
- bool ret = false;
for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) {
errno = 0;
@@ -51,10 +50,10 @@
return false;
}
if (Gid == AID_LOG) {
- ret = true;
+ return true;
}
}
- return ret;
+ return false;
}
bool clientHasLogCredentials(SocketClient * cli) {
@@ -69,61 +68,79 @@
}
// FYI We will typically be here for 'adb logcat'
- bool ret = false;
+ char filename[256];
+ snprintf(filename, sizeof(filename), "/proc/%u/status", cli->getPid());
- char filename[1024];
- snprintf(filename, sizeof(filename), "/proc/%d/status", cli->getPid());
-
- FILE *file = fopen(filename, "r");
- if (!file) {
- return ret;
- }
-
+ bool ret;
+ bool foundLog = false;
bool foundGid = false;
bool foundUid = false;
- char line[1024];
- while (fgets(line, sizeof(line), file)) {
- static const char groups_string[] = "Groups:\t";
- static const char uid_string[] = "Uid:\t";
- static const char gid_string[] = "Gid:\t";
-
- if (strncmp(groups_string, line, strlen(groups_string)) == 0) {
- ret = groupIsLog(line + strlen(groups_string));
- if (!ret) {
- break;
- }
- } else if (strncmp(uid_string, line, strlen(uid_string)) == 0) {
- uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
-
- sscanf(line + strlen(uid_string), "%u\t%u\t%u\t%u",
- &u[0], &u[1], &u[2], &u[3]);
-
- // Protect against PID reuse by checking that the UID is the same
- if ((uid != u[0]) || (uid != u[1]) || (uid != u[2]) || (uid != u[3])) {
- ret = false;
- break;
- }
- foundUid = true;
- } else if (strncmp(gid_string, line, strlen(gid_string)) == 0) {
- gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
-
- sscanf(line + strlen(gid_string), "%u\t%u\t%u\t%u",
- &g[0], &g[1], &g[2], &g[3]);
-
- // Protect against PID reuse by checking that the GID is the same
- if ((gid != g[0]) || (gid != g[1]) || (gid != g[2]) || (gid != g[3])) {
- ret = false;
- break;
- }
- foundGid = true;
+ //
+ // Reading /proc/<pid>/status is rife with race conditions. All of /proc
+ // suffers from this and its use should be minimized. However, we have no
+ // choice.
+ //
+ // Notably the content from one 4KB page to the next 4KB page can be from a
+ // change in shape even if we are gracious enough to attempt to read
+ // atomically. getline can not even guarantee a page read is not split up
+ // and in effect can read from different vintages of the content.
+ //
+ // We are finding out in the field that a 'logcat -c' via adb occasionally
+ // is returned with permission denied when we did only one pass and thus
+ // breaking scripts. For security we still err on denying access if in
+ // doubt, but we expect the falses should be reduced significantly as
+ // three times is a charm.
+ //
+ for (int retry = 3;
+ !(ret = foundGid && foundUid && foundLog) && retry;
+ --retry) {
+ FILE *file = fopen(filename, "r");
+ if (!file) {
+ continue;
}
- }
- fclose(file);
+ char *line = NULL;
+ size_t len = 0;
+ while (getline(&line, &len, file) > 0) {
+ static const char groups_string[] = "Groups:\t";
+ static const char uid_string[] = "Uid:\t";
+ static const char gid_string[] = "Gid:\t";
- if (!foundGid || !foundUid) {
- ret = false;
+ if (strncmp(groups_string, line, sizeof(groups_string) - 1) == 0) {
+ if (groupIsLog(line + sizeof(groups_string) - 1)) {
+ foundLog = true;
+ }
+ } else if (strncmp(uid_string, line, sizeof(uid_string) - 1) == 0) {
+ uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
+
+ sscanf(line + sizeof(uid_string) - 1, "%u\t%u\t%u\t%u",
+ &u[0], &u[1], &u[2], &u[3]);
+
+ // Protect against PID reuse by checking that UID is the same
+ if ((uid == u[0])
+ && (uid == u[1])
+ && (uid == u[2])
+ && (uid == u[3])) {
+ foundUid = true;
+ }
+ } else if (strncmp(gid_string, line, sizeof(gid_string) - 1) == 0) {
+ gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
+
+ sscanf(line + sizeof(gid_string) - 1, "%u\t%u\t%u\t%u",
+ &g[0], &g[1], &g[2], &g[3]);
+
+ // Protect against PID reuse by checking that GID is the same
+ if ((gid == g[0])
+ && (gid == g[1])
+ && (gid == g[2])
+ && (gid == g[3])) {
+ foundGid = true;
+ }
+ }
+ }
+ free(line);
+ fclose(file);
}
return ret;
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index a9c3086..fdb2576 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,15 +14,12 @@
* limitations under the License.
*/
-#include <algorithm> // std::max
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include <base/stringprintf.h>
#include <log/logger.h>
-#include <private/android_filesystem_config.h>
#include "LogStatistics.h"
@@ -30,6 +27,7 @@
log_id_for_each(id) {
mSizes[id] = 0;
mElements[id] = 0;
+ mDroppedElements[id] = 0;
mSizesTotal[id] = 0;
mElementsTotal[id] = 0;
}
@@ -63,9 +61,9 @@
}
-void LogStatistics::add(LogBufferElement *e) {
- log_id_t log_id = e->getLogId();
- unsigned short size = e->getMsgLen();
+void LogStatistics::add(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
mSizes[log_id] += size;
++mElements[log_id];
@@ -76,65 +74,69 @@
return;
}
- uidTable[log_id].add(e->getUid(), e);
+ uidTable[log_id].add(element->getUid(), element);
if (!enable) {
return;
}
- pidTable.add(e->getPid(), e);
- tidTable.add(e->getTid(), e);
+ pidTable.add(element->getPid(), element);
+ tidTable.add(element->getTid(), element);
- uint32_t tag = e->getTag();
+ uint32_t tag = element->getTag();
if (tag) {
- tagTable.add(tag, e);
+ tagTable.add(tag, element);
}
}
-void LogStatistics::subtract(LogBufferElement *e) {
- log_id_t log_id = e->getLogId();
- unsigned short size = e->getMsgLen();
+void LogStatistics::subtract(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
mSizes[log_id] -= size;
--mElements[log_id];
+ if (element->getDropped()) {
+ --mDroppedElements[log_id];
+ }
if (log_id == LOG_ID_KERNEL) {
return;
}
- uidTable[log_id].subtract(e->getUid(), e);
+ uidTable[log_id].subtract(element->getUid(), element);
if (!enable) {
return;
}
- pidTable.subtract(e->getPid(), e);
- tidTable.subtract(e->getTid(), e);
+ pidTable.subtract(element->getPid(), element);
+ tidTable.subtract(element->getTid(), element);
- uint32_t tag = e->getTag();
+ uint32_t tag = element->getTag();
if (tag) {
- tagTable.subtract(tag, e);
+ tagTable.subtract(tag, element);
}
}
// Atomically set an entry to drop
// entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::drop(LogBufferElement *e) {
- log_id_t log_id = e->getLogId();
- unsigned short size = e->getMsgLen();
+void LogStatistics::drop(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
mSizes[log_id] -= size;
+ ++mDroppedElements[log_id];
- uidTable[log_id].drop(e->getUid(), e);
+ uidTable[log_id].drop(element->getUid(), element);
if (!enable) {
return;
}
- pidTable.drop(e->getPid(), e);
- tidTable.drop(e->getTid(), e);
+ pidTable.drop(element->getPid(), element);
+ tidTable.drop(element->getTid(), element);
}
// caller must own and free character string
-char *LogStatistics::uidToName(uid_t uid) {
+const char *LogStatistics::uidToName(uid_t uid) const {
// Local hard coded favourites
if (uid == AID_LOGD) {
return strdup("auditd");
@@ -152,7 +154,7 @@
// Parse /data/system/packages.list
uid_t userId = uid % AID_USER;
- char *name = android::uidToName(userId);
+ const char *name = android::uidToName(userId);
if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
}
@@ -161,17 +163,17 @@
}
// report uid -> pid(s) -> pidToName if unique
- for(pidTable_t::iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
+ for(pidTable_t::const_iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
const PidEntry &entry = it->second;
if (entry.getUid() == uid) {
- const char *n = entry.getName();
+ const char *nameTmp = entry.getName();
- if (n) {
+ if (nameTmp) {
if (!name) {
- name = strdup(n);
- } else if (fast<strcmp>(name, n)) {
- free(name);
+ name = strdup(nameTmp);
+ } else if (fast<strcmp>(name, nameTmp)) {
+ free(const_cast<char *>(name));
name = NULL;
break;
}
@@ -183,28 +185,152 @@
return name;
}
-static std::string format_line(
- const std::string &name,
- const std::string &size,
- const std::string &pruned) {
- static const size_t pruned_len = 6;
- static const size_t total_len = 70 + pruned_len;
-
- ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
- ssize_t size_len = std::max(size.length() + 1,
- total_len - name.length() - drop_len - 1);
-
- if (pruned.length()) {
- return android::base::StringPrintf("%s%*s%*s\n", name.c_str(),
- (int)size_len, size.c_str(),
- (int)drop_len, pruned.c_str());
- } else {
- return android::base::StringPrintf("%s%*s\n", name.c_str(),
- (int)size_len, size.c_str());
- }
+std::string UidEntry::formatHeader(const std::string &name, log_id_t id) const {
+ bool isprune = worstUidEnabledForLogid(id);
+ return formatLine(android::base::StringPrintf(
+ name.c_str(), android_log_id_to_name(id)),
+ std::string("Size"),
+ std::string(isprune ? "Pruned" : ""))
+ + formatLine(std::string("UID PACKAGE"),
+ std::string("BYTES"),
+ std::string(isprune ? "NUM" : ""));
}
-std::string LogStatistics::format(uid_t uid, unsigned int logMask) {
+std::string UidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+ uid_t uid = getKey();
+ std::string name = android::base::StringPrintf("%u", uid);
+ const char *nameTmp = stat.uidToName(uid);
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(6 - name.length(), (size_t)1),
+ "", nameTmp);
+ free(const_cast<char *>(nameTmp));
+ }
+
+ std::string size = android::base::StringPrintf("%zu", getSizes());
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string PidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
+ return formatLine(name,
+ std::string("Size"),
+ std::string("Pruned"))
+ + formatLine(std::string(" PID/UID COMMAND LINE"),
+ std::string("BYTES"),
+ std::string("NUM"));
+}
+
+std::string PidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+ uid_t uid = getUid();
+ std::string name = android::base::StringPrintf("%5u/%u",
+ getKey(), uid);
+ const char *nameTmp = getName();
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ } else if ((nameTmp = stat.uidToName(uid))) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ free(const_cast<char *>(nameTmp));
+ }
+
+ std::string size = android::base::StringPrintf("%zu",
+ getSizes());
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string TidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
+ return formatLine(name,
+ std::string("Size"),
+ std::string("Pruned"))
+ + formatLine(std::string(" TID/UID COMM"),
+ std::string("BYTES"),
+ std::string("NUM"));
+}
+
+std::string TidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+ uid_t uid = getUid();
+ std::string name = android::base::StringPrintf("%5u/%u",
+ getKey(), uid);
+ const char *nameTmp = getName();
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ } else if ((nameTmp = stat.uidToName(uid))) {
+ // if we do not have a PID name, lets punt to try UID name?
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+ "", nameTmp);
+ free(const_cast<char *>(nameTmp));
+ // We tried, better to not have a name at all, we still
+ // have TID/UID by number to report in any case.
+ }
+
+ std::string size = android::base::StringPrintf("%zu",
+ getSizes());
+
+ std::string pruned = "";
+ size_t dropped = getDropped();
+ if (dropped) {
+ pruned = android::base::StringPrintf("%zu", dropped);
+ }
+
+ return formatLine(name, size, pruned);
+}
+
+std::string TagEntry::formatHeader(const std::string &name, log_id_t id) const {
+ bool isprune = worstUidEnabledForLogid(id);
+ return formatLine(name,
+ std::string("Size"),
+ std::string(isprune ? "Prune" : ""))
+ + formatLine(std::string(" TAG/UID TAGNAME"),
+ std::string("BYTES"),
+ std::string(isprune ? "NUM" : ""));
+}
+
+std::string TagEntry::format(const LogStatistics & /* stat */, log_id_t /* id */) const {
+ std::string name;
+ uid_t uid = getUid();
+ if (uid == (uid_t)-1) {
+ name = android::base::StringPrintf("%7u",
+ getKey());
+ } else {
+ name = android::base::StringPrintf("%7u/%u",
+ getKey(), uid);
+ }
+ const char *nameTmp = getName();
+ if (nameTmp) {
+ name += android::base::StringPrintf(
+ "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
+ "", nameTmp);
+ }
+
+ std::string size = android::base::StringPrintf("%zu",
+ getSizes());
+
+ std::string pruned = "";
+
+ return formatLine(name, size, pruned);
+}
+
+std::string LogStatistics::format(uid_t uid, unsigned int logMask) const {
static const unsigned short spaces_total = 19;
// Report on total logging, current and for all time
@@ -227,7 +353,7 @@
}
spaces = 4;
- output += android::base::StringPrintf("\nTotal");
+ output += "\nTotal";
log_id_for_each(id) {
if (!(logMask & (1 << id))) {
@@ -244,7 +370,7 @@
}
spaces = 6;
- output += android::base::StringPrintf("\nNow");
+ output += "\nNow";
log_id_for_each(id) {
if (!(logMask & (1 << id))) {
@@ -266,245 +392,30 @@
// Report on Chattiest
+ std::string name;
+
// Chattiest by application (UID)
- static const size_t maximum_sorted_entries = 32;
log_id_for_each(id) {
if (!(logMask & (1 << id))) {
continue;
}
- bool headerPrinted = false;
- std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
- ssize_t index = -1;
- while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
- const UidEntry *entry = sorted[index];
- uid_t u = entry->getKey();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- if (!headerPrinted) {
- output += android::base::StringPrintf("\n\n");
- std::string name;
- if (uid == AID_ROOT) {
- name = android::base::StringPrintf(
- "Chattiest UIDs in %s log buffer:",
- android_log_id_to_name(id));
- } else {
- name = android::base::StringPrintf(
- "Logging for your UID in %s log buffer:",
- android_log_id_to_name(id));
- }
- std::string size = "Size";
- std::string pruned = "Pruned";
- if (!worstUidEnabledForLogid(id)) {
- pruned = "";
- }
- output += format_line(name, size, pruned);
-
- name = "UID PACKAGE";
- size = "BYTES";
- pruned = "LINES";
- if (!worstUidEnabledForLogid(id)) {
- pruned = "";
- }
- output += format_line(name, size, pruned);
-
- headerPrinted = true;
- }
-
- std::string name = android::base::StringPrintf("%u", u);
- char *n = uidToName(u);
- if (n) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(6 - name.length(), (size_t)1),
- "", n);
- free(n);
- }
-
- std::string size = android::base::StringPrintf("%zu",
- entry->getSizes());
-
- std::string pruned = "";
- size_t dropped = entry->getDropped();
- if (dropped) {
- pruned = android::base::StringPrintf("%zu", dropped);
- }
-
- output += format_line(name, size, pruned);
- }
+ name = (uid == AID_ROOT)
+ ? "Chattiest UIDs in %s log buffer:"
+ : "Logging for your UID in %s log buffer:";
+ output += uidTable[id].format(*this, uid, name, id);
}
if (enable) {
- // Pid table
- bool headerPrinted = false;
- std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
- ssize_t index = -1;
- while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
- const PidEntry *entry = sorted[index];
- uid_t u = entry->getUid();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- if (!headerPrinted) {
- output += android::base::StringPrintf("\n\n");
- std::string name;
- if (uid == AID_ROOT) {
- name = android::base::StringPrintf("Chattiest PIDs:");
- } else {
- name = android::base::StringPrintf("Logging for this PID:");
- }
- std::string size = "Size";
- std::string pruned = "Pruned";
- output += format_line(name, size, pruned);
-
- name = " PID/UID COMMAND LINE";
- size = "BYTES";
- pruned = "LINES";
- output += format_line(name, size, pruned);
-
- headerPrinted = true;
- }
-
- std::string name = android::base::StringPrintf("%5u/%u",
- entry->getKey(), u);
- const char *n = entry->getName();
- if (n) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
- "", n);
- } else {
- char *un = uidToName(u);
- if (un) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
- "", un);
- free(un);
- }
- }
-
- std::string size = android::base::StringPrintf("%zu",
- entry->getSizes());
-
- std::string pruned = "";
- size_t dropped = entry->getDropped();
- if (dropped) {
- pruned = android::base::StringPrintf("%zu", dropped);
- }
-
- output += format_line(name, size, pruned);
- }
- }
-
- if (enable) {
- // Tid table
- bool headerPrinted = false;
- // sort() returns list of references, unique_ptr makes sure self-delete
- std::unique_ptr<const TidEntry *[]> sorted = tidTable.sort(maximum_sorted_entries);
- ssize_t index = -1;
- while ((index = tidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
- const TidEntry *entry = sorted[index];
- uid_t u = entry->getUid();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- if (!headerPrinted) { // Only print header if we have table to print
- output += android::base::StringPrintf("\n\n");
- std::string name = "Chattiest TIDs:";
- std::string size = "Size";
- std::string pruned = "Pruned";
- output += format_line(name, size, pruned);
-
- name = " TID/UID COMM";
- size = "BYTES";
- pruned = "LINES";
- output += format_line(name, size, pruned);
-
- headerPrinted = true;
- }
-
- std::string name = android::base::StringPrintf("%5u/%u",
- entry->getKey(), u);
- const char *n = entry->getName();
- if (n) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
- "", n);
- } else {
- // if we do not have a PID name, lets punt to try UID name?
- char *un = uidToName(u);
- if (un) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
- "", un);
- free(un);
- }
- // We tried, better to not have a name at all, we still
- // have TID/UID by number to report in any case.
- }
-
- std::string size = android::base::StringPrintf("%zu",
- entry->getSizes());
-
- std::string pruned = "";
- size_t dropped = entry->getDropped();
- if (dropped) {
- pruned = android::base::StringPrintf("%zu", dropped);
- }
-
- output += format_line(name, size, pruned);
- }
+ name = (uid == AID_ROOT) ? "Chattiest PIDs:" : "Logging for this PID:";
+ output += pidTable.format(*this, uid, name);
+ name = "Chattiest TIDs:";
+ output += tidTable.format(*this, uid, name);
}
if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
- // Tag table
- bool headerPrinted = false;
- std::unique_ptr<const TagEntry *[]> sorted = tagTable.sort(maximum_sorted_entries);
- ssize_t index = -1;
- while ((index = tagTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
- const TagEntry *entry = sorted[index];
- uid_t u = entry->getUid();
- if ((uid != AID_ROOT) && (u != uid)) {
- continue;
- }
-
- std::string pruned = "";
-
- if (!headerPrinted) {
- output += android::base::StringPrintf("\n\n");
- std::string name = "Chattiest events log buffer TAGs:";
- std::string size = "Size";
- output += format_line(name, size, pruned);
-
- name = " TAG/UID TAGNAME";
- size = "BYTES";
- output += format_line(name, size, pruned);
-
- headerPrinted = true;
- }
-
- std::string name;
- if (u == (uid_t)-1) {
- name = android::base::StringPrintf("%7u",
- entry->getKey());
- } else {
- name = android::base::StringPrintf("%7u/%u",
- entry->getKey(), u);
- }
- const char *n = entry->getName();
- if (n) {
- name += android::base::StringPrintf(
- "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
- "", n);
- }
-
- std::string size = android::base::StringPrintf("%zu",
- entry->getSizes());
-
- output += format_line(name, size, pruned);
- }
+ name = "Chattiest events log buffer TAGs:";
+ output += tagTable.format(*this, uid, name, LOG_ID_EVENTS);
}
return output;
@@ -536,8 +447,10 @@
}
// caller must free character string
-char *LogStatistics::pidToName(pid_t pid) {
- const char *name = pidTable.add(pid)->second.getName();
+const char *LogStatistics::pidToName(pid_t pid) const {
+ // An inconvenient truth ... getName() can alter the object
+ pidTable_t &writablePidTable = const_cast<pidTable_t &>(pidTable);
+ const char *name = writablePidTable.add(pid)->second.getName();
if (!name) {
return NULL;
}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 5a44d59..6943820 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -21,9 +21,13 @@
#include <stdlib.h>
#include <sys/types.h>
+#include <algorithm> // std::max
+#include <string> // std::string
#include <unordered_map>
+#include <base/stringprintf.h>
#include <log/log.h>
+#include <private/android_filesystem_config.h>
#include "LogBufferElement.h"
#include "LogUtils.h"
@@ -31,6 +35,8 @@
#define log_id_for_each(i) \
for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1))
+class LogStatistics;
+
template <typename TKey, typename TEntry>
class LogHashtable {
@@ -39,50 +45,43 @@
public:
typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
+ typedef typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
- std::unique_ptr<const TEntry *[]> sort(size_t n) {
- if (!n) {
+ std::unique_ptr<const TEntry *[]> sort(size_t len) const {
+ if (!len) {
std::unique_ptr<const TEntry *[]> sorted(NULL);
return sorted;
}
- const TEntry **retval = new const TEntry* [n];
- memset(retval, 0, sizeof(*retval) * n);
+ const TEntry **retval = new const TEntry* [len];
+ memset(retval, 0, sizeof(*retval) * len);
- for(iterator it = map.begin(); it != map.end(); ++it) {
+ for(const_iterator it = map.begin(); it != map.end(); ++it) {
const TEntry &entry = it->second;
- size_t s = entry.getSizes();
- ssize_t i = n - 1;
- while ((!retval[i] || (s > retval[i]->getSizes())) && (--i >= 0))
+ size_t sizes = entry.getSizes();
+ ssize_t index = len - 1;
+ while ((!retval[index] || (sizes > retval[index]->getSizes()))
+ && (--index >= 0))
;
- if (++i < (ssize_t)n) {
- size_t b = n - i - 1;
- if (b) {
- memmove(&retval[i+1], &retval[i], b * sizeof(retval[0]));
+ if (++index < (ssize_t)len) {
+ size_t num = len - index - 1;
+ if (num) {
+ memmove(&retval[index + 1], &retval[index],
+ num * sizeof(retval[0]));
}
- retval[i] = &entry;
+ retval[index] = &entry;
}
}
std::unique_ptr<const TEntry *[]> sorted(retval);
return sorted;
}
- // Iteration handler for the sort method output
- static ssize_t next(ssize_t index, std::unique_ptr<const TEntry *[]> &sorted, size_t n) {
- ++index;
- if (!sorted.get() || (index < 0) || (n <= (size_t)index) || !sorted[index]
- || (sorted[index]->getSizes() <= (sorted[0]->getSizes() / 100))) {
- return -1;
- }
- return index;
- }
-
- inline iterator add(TKey key, LogBufferElement *e) {
+ inline iterator add(TKey key, LogBufferElement *element) {
iterator it = map.find(key);
if (it == map.end()) {
- it = map.insert(std::make_pair(key, TEntry(e))).first;
+ it = map.insert(std::make_pair(key, TEntry(element))).first;
} else {
- it->second.add(e);
+ it->second.add(element);
}
return it;
}
@@ -97,65 +96,138 @@
return it;
}
- void subtract(TKey key, LogBufferElement *e) {
+ void subtract(TKey key, LogBufferElement *element) {
iterator it = map.find(key);
- if ((it != map.end()) && it->second.subtract(e)) {
+ if ((it != map.end()) && it->second.subtract(element)) {
map.erase(it);
}
}
- inline void drop(TKey key, LogBufferElement *e) {
+ inline void drop(TKey key, LogBufferElement *element) {
iterator it = map.find(key);
if (it != map.end()) {
- it->second.drop(e);
+ it->second.drop(element);
}
}
inline iterator begin() { return map.begin(); }
+ inline const_iterator begin() const { return map.begin(); }
inline iterator end() { return map.end(); }
+ inline const_iterator end() const { return map.end(); }
+ std::string format(
+ const LogStatistics &stat,
+ uid_t uid,
+ const std::string &name = std::string(""),
+ log_id_t id = LOG_ID_MAX) const {
+ static const size_t maximum_sorted_entries = 32;
+ std::string output;
+ std::unique_ptr<const TEntry *[]> sorted = sort(maximum_sorted_entries);
+
+ if (!sorted.get()) {
+ return output;
+ }
+ bool headerPrinted = false;
+ for (size_t index = 0; index < maximum_sorted_entries; ++index) {
+ const TEntry *entry = sorted[index];
+ if (!entry) {
+ break;
+ }
+ if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
+ break;
+ }
+ if ((uid != AID_ROOT) && (uid != entry->getUid())) {
+ continue;
+ }
+ if (!headerPrinted) {
+ output += "\n\n";
+ output += entry->formatHeader(name, id);
+ headerPrinted = true;
+ }
+ output += entry->format(stat, id);
+ }
+ return output;
+ }
};
+namespace EntryBaseConstants {
+ static constexpr size_t pruned_len = 14;
+ static constexpr size_t total_len = 80;
+}
+
struct EntryBase {
size_t size;
EntryBase():size(0) { }
- EntryBase(LogBufferElement *e):size(e->getMsgLen()) { }
+ EntryBase(LogBufferElement *element):size(element->getMsgLen()) { }
size_t getSizes() const { return size; }
- inline void add(LogBufferElement *e) { size += e->getMsgLen(); }
- inline bool subtract(LogBufferElement *e) { size -= e->getMsgLen(); return !size; }
+ inline void add(LogBufferElement *element) { size += element->getMsgLen(); }
+ inline bool subtract(LogBufferElement *element) {
+ size -= element->getMsgLen();
+ return !size;
+ }
+
+ static std::string formatLine(
+ const std::string &name,
+ const std::string &size,
+ const std::string &pruned) {
+ ssize_t drop_len = std::max(pruned.length() + 1,
+ EntryBaseConstants::pruned_len);
+ ssize_t size_len = std::max(size.length() + 1,
+ EntryBaseConstants::total_len
+ - name.length() - drop_len - 1);
+
+ if (pruned.length()) {
+ return android::base::StringPrintf("%s%*s%*s\n", name.c_str(),
+ (int)size_len, size.c_str(),
+ (int)drop_len, pruned.c_str());
+ } else {
+ return android::base::StringPrintf("%s%*s\n", name.c_str(),
+ (int)size_len, size.c_str());
+ }
+ }
};
struct EntryBaseDropped : public EntryBase {
size_t dropped;
EntryBaseDropped():dropped(0) { }
- EntryBaseDropped(LogBufferElement *e):EntryBase(e),dropped(e->getDropped()){ }
+ EntryBaseDropped(LogBufferElement *element):
+ EntryBase(element),
+ dropped(element->getDropped()){
+ }
size_t getDropped() const { return dropped; }
- inline void add(LogBufferElement *e) {
- dropped += e->getDropped();
- EntryBase::add(e);
+ inline void add(LogBufferElement *element) {
+ dropped += element->getDropped();
+ EntryBase::add(element);
}
- inline bool subtract(LogBufferElement *e) {
- dropped -= e->getDropped();
- return EntryBase::subtract(e) && !dropped;
+ inline bool subtract(LogBufferElement *element) {
+ dropped -= element->getDropped();
+ return EntryBase::subtract(element) && !dropped;
}
- inline void drop(LogBufferElement *e) {
+ inline void drop(LogBufferElement *element) {
dropped += 1;
- EntryBase::subtract(e);
+ EntryBase::subtract(element);
}
};
struct UidEntry : public EntryBaseDropped {
const uid_t uid;
- UidEntry(LogBufferElement *e):EntryBaseDropped(e),uid(e->getUid()) { }
+ UidEntry(LogBufferElement *element):
+ EntryBaseDropped(element),
+ uid(element->getUid()) {
+ }
inline const uid_t&getKey() const { return uid; }
+ inline const uid_t&getUid() const { return uid; }
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
};
namespace android {
@@ -167,51 +239,54 @@
uid_t uid;
char *name;
- PidEntry(pid_t p):
- EntryBaseDropped(),
- pid(p),
- uid(android::pidToUid(p)),
- name(android::pidToName(pid)) { }
- PidEntry(LogBufferElement *e):
- EntryBaseDropped(e),
- pid(e->getPid()),
- uid(e->getUid()),
- name(android::pidToName(e->getPid())) { }
- PidEntry(const PidEntry &c):
- EntryBaseDropped(c),
- pid(c.pid),
- uid(c.uid),
- name(c.name ? strdup(c.name) : NULL) { }
+ PidEntry(pid_t pid):
+ EntryBaseDropped(),
+ pid(pid),
+ uid(android::pidToUid(pid)),
+ name(android::pidToName(pid)) {
+ }
+ PidEntry(LogBufferElement *element):
+ EntryBaseDropped(element),
+ pid(element->getPid()),
+ uid(element->getUid()),
+ name(android::pidToName(pid)) {
+ }
+ PidEntry(const PidEntry &element):
+ EntryBaseDropped(element),
+ pid(element.pid),
+ uid(element.uid),
+ name(element.name ? strdup(element.name) : NULL) {
+ }
~PidEntry() { free(name); }
const pid_t&getKey() const { return pid; }
const uid_t&getUid() const { return uid; }
const char*getName() const { return name; }
- inline void add(pid_t p) {
+ inline void add(pid_t newPid) {
if (name && !fast<strncmp>(name, "zygote", 6)) {
free(name);
name = NULL;
}
if (!name) {
- char *n = android::pidToName(p);
- if (n) {
- name = n;
- }
+ name = android::pidToName(newPid);
}
}
- inline void add(LogBufferElement *e) {
- uid_t u = e->getUid();
- if (getUid() != u) {
- uid = u;
+ inline void add(LogBufferElement *element) {
+ uid_t incomingUid = element->getUid();
+ if (getUid() != incomingUid) {
+ uid = incomingUid;
free(name);
- name = android::pidToName(e->getPid());
+ name = android::pidToName(element->getPid());
} else {
- add(e->getPid());
+ add(element->getPid());
}
- EntryBaseDropped::add(e);
+ EntryBaseDropped::add(element);
}
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
};
struct TidEntry : public EntryBaseDropped {
@@ -219,79 +294,87 @@
uid_t uid;
char *name;
- TidEntry(pid_t t):
- EntryBaseDropped(),
- tid(t),
- uid(android::pidToUid(t)),
- name(android::tidToName(tid)) { }
- TidEntry(LogBufferElement *e):
- EntryBaseDropped(e),
- tid(e->getTid()),
- uid(e->getUid()),
- name(android::tidToName(e->getTid())) { }
- TidEntry(const TidEntry &c):
- EntryBaseDropped(c),
- tid(c.tid),
- uid(c.uid),
- name(c.name ? strdup(c.name) : NULL) { }
+ TidEntry(pid_t tid):
+ EntryBaseDropped(),
+ tid(tid),
+ uid(android::pidToUid(tid)),
+ name(android::tidToName(tid)) {
+ }
+ TidEntry(LogBufferElement *element):
+ EntryBaseDropped(element),
+ tid(element->getTid()),
+ uid(element->getUid()),
+ name(android::tidToName(tid)) {
+ }
+ TidEntry(const TidEntry &element):
+ EntryBaseDropped(element),
+ tid(element.tid),
+ uid(element.uid),
+ name(element.name ? strdup(element.name) : NULL) {
+ }
~TidEntry() { free(name); }
const pid_t&getKey() const { return tid; }
const uid_t&getUid() const { return uid; }
const char*getName() const { return name; }
- inline void add(pid_t t) {
+ inline void add(pid_t incomingTid) {
if (name && !fast<strncmp>(name, "zygote", 6)) {
free(name);
name = NULL;
}
if (!name) {
- char *n = android::tidToName(t);
- if (n) {
- name = n;
- }
+ name = android::tidToName(incomingTid);
}
}
- inline void add(LogBufferElement *e) {
- uid_t u = e->getUid();
- if (getUid() != u) {
- uid = u;
+ inline void add(LogBufferElement *element) {
+ uid_t incomingUid = element->getUid();
+ if (getUid() != incomingUid) {
+ uid = incomingUid;
free(name);
- name = android::tidToName(e->getTid());
+ name = android::tidToName(element->getTid());
} else {
- add(e->getTid());
+ add(element->getTid());
}
- EntryBaseDropped::add(e);
+ EntryBaseDropped::add(element);
}
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
};
struct TagEntry : public EntryBase {
const uint32_t tag;
uid_t uid;
- TagEntry(LogBufferElement *e):
- EntryBase(e),
- tag(e->getTag()),
- uid(e->getUid()) { }
+ TagEntry(LogBufferElement *element):
+ EntryBase(element),
+ tag(element->getTag()),
+ uid(element->getUid()) {
+ }
const uint32_t&getKey() const { return tag; }
const uid_t&getUid() const { return uid; }
const char*getName() const { return android::tagToName(tag); }
- inline void add(LogBufferElement *e) {
- uid_t u = e->getUid();
- if (uid != u) {
+ inline void add(LogBufferElement *element) {
+ uid_t incomingUid = element->getUid();
+ if (uid != incomingUid) {
uid = -1;
}
- EntryBase::add(e);
+ EntryBase::add(element);
}
+
+ std::string formatHeader(const std::string &name, log_id_t id) const;
+ std::string format(const LogStatistics &stat, log_id_t id) const;
};
// Log Statistics
class LogStatistics {
size_t mSizes[LOG_ID_MAX];
size_t mElements[LOG_ID_MAX];
+ size_t mDroppedElements[LOG_ID_MAX];
size_t mSizesTotal[LOG_ID_MAX];
size_t mElementsTotal[LOG_ID_MAX];
bool enable;
@@ -321,23 +404,32 @@
void subtract(LogBufferElement *entry);
// entry->setDropped(1) must follow this call
void drop(LogBufferElement *entry);
- // Correct for merging two entries referencing dropped content
- void erase(LogBufferElement *e) { --mElements[e->getLogId()]; }
+ // Correct for coalescing two entries referencing dropped content
+ void erase(LogBufferElement *element) {
+ log_id_t log_id = element->getLogId();
+ --mElements[log_id];
+ --mDroppedElements[log_id];
+ }
- std::unique_ptr<const UidEntry *[]> sort(size_t n, log_id i) { return uidTable[i].sort(n); }
+ std::unique_ptr<const UidEntry *[]> sort(size_t len, log_id id) {
+ return uidTable[id].sort(len);
+ }
// fast track current value by id only
size_t sizes(log_id_t id) const { return mSizes[id]; }
size_t elements(log_id_t id) const { return mElements[id]; }
+ size_t realElements(log_id_t id) const {
+ return mElements[id] - mDroppedElements[id];
+ }
size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
- std::string format(uid_t uid, unsigned int logMask);
+ std::string format(uid_t uid, unsigned int logMask) const;
// helper (must be locked directly or implicitly by mLogElementsLock)
- char *pidToName(pid_t pid);
+ const char *pidToName(pid_t pid) const;
uid_t pidToUid(pid_t pid);
- char *uidToName(uid_t uid);
+ const char *uidToName(uid_t uid) const;
};
#endif // _LOGD_LOG_STATISTICS_H__
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 68a0680..229be3c 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -128,9 +128,9 @@
lock();
- while (me->threadRunning && !me->isError_Locked()) {
- uint64_t start = me->mStart;
+ uint64_t start = me->mStart;
+ while (me->threadRunning && !me->isError_Locked()) {
unlock();
if (me->mTail) {
@@ -143,8 +143,11 @@
if (start == LogBufferElement::FLUSH_ERROR) {
me->error_Locked();
+ break;
}
+ me->mStart = start + 1;
+
if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
break;
}
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 9fd27f5..8dbaad9 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -25,44 +25,30 @@
metrics_client_sources := \
metrics_client.cc
-metrics_daemon_sources := \
+metrics_daemon_common := \
collectors/averaged_statistics_collector.cc \
collectors/disk_usage_collector.cc \
metrics_daemon.cc \
- metrics_daemon_main.cc \
persistent_integer.cc \
- uploader/metrics_hashes.cc \
- uploader/metrics_log_base.cc \
- uploader/metrics_log.cc \
- uploader/sender_http.cc \
- uploader/system_profile_cache.cc \
- uploader/upload_service.cc \
- serialization/metric_sample.cc \
- serialization/serialization_utils.cc
-
-metrics_tests_sources := \
- collectors/averaged_statistics_collector.cc \
- collectors/averaged_statistics_collector_test.cc \
- collectors/disk_usage_collector.cc \
- metrics_daemon.cc \
- metrics_daemon_test.cc \
- metrics_library_test.cc \
- persistent_integer.cc \
- persistent_integer_test.cc \
serialization/metric_sample.cc \
serialization/serialization_utils.cc \
- serialization/serialization_utils_unittest.cc \
- timer.cc \
- timer_test.cc \
uploader/metrics_hashes.cc \
- uploader/metrics_hashes_unittest.cc \
uploader/metrics_log_base.cc \
- uploader/metrics_log_base_unittest.cc \
uploader/metrics_log.cc \
- uploader/mock/sender_mock.cc \
uploader/sender_http.cc \
uploader/system_profile_cache.cc \
uploader/upload_service.cc \
+
+metrics_tests_sources := \
+ collectors/averaged_statistics_collector_test.cc \
+ metrics_daemon_test.cc \
+ metrics_library_test.cc \
+ persistent_integer_test.cc \
+ serialization/serialization_utils_unittest.cc \
+ timer_test.cc \
+ uploader/metrics_hashes_unittest.cc \
+ uploader/metrics_log_base_unittest.cc \
+ uploader/mock/sender_mock.cc \
uploader/upload_service_test.cc \
metrics_CFLAGS := -Wall \
@@ -78,7 +64,17 @@
-fvisibility=default
metrics_includes := external/gtest/include \
$(LOCAL_PATH)/include
-metrics_shared_libraries := libchrome libchromeos
+libmetrics_shared_libraries := libchrome libchromeos
+metrics_daemon_shared_libraries := $(libmetrics_shared_libraries) \
+ libchrome-dbus \
+ libchromeos-http \
+ libchromeos-dbus \
+ libdbus \
+ libmetrics \
+ libprotobuf-cpp-lite \
+ librootdev \
+ libupdate_engine_client \
+ libweaved \
# Shared library for metrics.
# ========================================================
@@ -91,7 +87,7 @@
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
LOCAL_RTTI_FLAG := -frtti
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries)
+LOCAL_SHARED_LIBRARIES := $(libmetrics_shared_libraries)
LOCAL_SRC_FILES := $(libmetrics_sources)
include $(BUILD_SHARED_LIBRARY)
@@ -104,7 +100,7 @@
LOCAL_CLANG := true
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
-LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
+LOCAL_SHARED_LIBRARIES := $(libmetrics_shared_libraries) \
libmetrics
LOCAL_SRC_FILES := $(metrics_client_sources)
include $(BUILD_EXECUTABLE)
@@ -130,18 +126,15 @@
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
LOCAL_INIT_RC := metrics_daemon.rc
-LOCAL_RTTI_FLAG := -frtti
-LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
- libmetrics \
- libprotobuf-cpp-lite \
- libchromeos-http \
- libchromeos-dbus \
- libcutils \
- libdbus \
- librootdev
+LOCAL_REQUIRED_MODULES := \
+ metrics.json \
+ metrics.schema.json \
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := $(metrics_daemon_shared_libraries)
LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(metrics_daemon_sources)
+LOCAL_SRC_FILES := $(metrics_daemon_common) \
+ metrics_daemon_main.cc
LOCAL_STATIC_LIBRARIES := metrics_daemon_protos
include $(BUILD_EXECUTABLE)
@@ -154,15 +147,24 @@
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
LOCAL_RTTI_FLAG := -frtti
-LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
- libmetrics \
- libprotobuf-cpp-lite \
- libchromeos-http \
- libchromeos-dbus \
- libcutils \
- libdbus \
-
-LOCAL_SRC_FILES := $(metrics_tests_sources)
+LOCAL_SHARED_LIBRARIES := $(metrics_daemon_shared_libraries)
+LOCAL_SRC_FILES := $(metrics_tests_sources) $(metrics_daemon_common)
LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metrics_daemon_protos
include $(BUILD_NATIVE_TEST)
+
+# Weave schema files
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics.json
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/weaved/commands
+LOCAL_SRC_FILES := etc/weaved/commands/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics.schema.json
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/weaved/states
+LOCAL_SRC_FILES := etc/weaved/states/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/metricsd/constants.h b/metricsd/constants.h
index 8a00fc4..7e1e116 100644
--- a/metricsd/constants.h
+++ b/metricsd/constants.h
@@ -27,10 +27,9 @@
static const char kFailedUploadCountName[] = "failed_upload_count";
static const char kDefaultVersion[] = "0.0.0.0";
-// System properties used.
-static const char kProductIdProperty[] = "ro.product.product_id";
-static const char kChannelProperty[] = "ro.product.channel";
-static const char kProductVersionProperty[] = "ro.product.version";
+// Build time properties name.
+static const char kProductId[] = "product_id";
+static const char kProductVersion[] = "product_version";
} // namespace metrics
#endif // METRICS_CONSTANTS_H_
diff --git a/metricsd/etc/weaved/commands/metrics.json b/metricsd/etc/weaved/commands/metrics.json
new file mode 100644
index 0000000..b7f32d5
--- /dev/null
+++ b/metricsd/etc/weaved/commands/metrics.json
@@ -0,0 +1,10 @@
+{
+ "_metrics": {
+ "_enableAnalyticsReporting": {
+ "minimalRole": "manager"
+ },
+ "_disableAnalyticsReporting": {
+ "minimalRole": "manager"
+ }
+ }
+}
diff --git a/metricsd/etc/weaved/states/metrics.schema.json b/metricsd/etc/weaved/states/metrics.schema.json
new file mode 100644
index 0000000..130ac46
--- /dev/null
+++ b/metricsd/etc/weaved/states/metrics.schema.json
@@ -0,0 +1,8 @@
+{
+ "_metrics": {
+ "_AnalyticsReportingState": {
+ "enum": ["enabled", "disabled"],
+ "default": "disabled"
+ }
+ }
+}
diff --git a/metricsd/include/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h
index 5556e57..b766194 100644
--- a/metricsd/include/metrics/metrics_library.h
+++ b/metricsd/include/metrics/metrics_library.h
@@ -48,6 +48,12 @@
// Initializes the library.
void Init() override;
+ // Initializes the library and disables the cache of whether or not the
+ // metrics collection is enabled.
+ // By disabling this, we may have to check for the metrics status more often
+ // but the result will never be stale.
+ void InitWithNoCaching();
+
// Returns whether or not the machine is running in guest mode.
bool IsGuestMode();
@@ -125,6 +131,7 @@
friend class MetricsLibraryTest;
friend class UploadServiceTest;
FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabled);
+ FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabledNoCaching);
FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage);
FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong);
FRIEND_TEST(MetricsLibraryTest, IsDeviceMounted);
@@ -147,6 +154,9 @@
// Cached state of whether or not metrics were enabled.
bool cached_enabled_;
+ // True iff we should cache the enabled/disabled status.
+ bool use_caching_;
+
base::FilePath uma_events_file_;
base::FilePath consent_file_;
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
index 9eb6802..ed786e1 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -28,7 +28,7 @@
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
-#include <cutils/properties.h>
+#include <brillo/osrelease_reader.h>
#include <dbus/dbus.h>
#include <dbus/message.h>
@@ -41,6 +41,8 @@
using base::TimeDelta;
using base::TimeTicks;
using chromeos_metrics::PersistentInteger;
+using com::android::Weave::CommandProxy;
+using com::android::Weave::ManagerProxy;
using std::map;
using std::string;
using std::vector;
@@ -138,7 +140,7 @@
version_cumulative_cpu_use_->Set(0);
}
- return chromeos::DBusDaemon::Run();
+ return brillo::DBusDaemon::Run();
}
void MetricsDaemon::RunUploaderTest() {
@@ -151,23 +153,19 @@
}
uint32_t MetricsDaemon::GetOsVersionHash() {
- static uint32_t cached_version_hash = 0;
- static bool version_hash_is_cached = false;
- if (version_hash_is_cached)
- return cached_version_hash;
- version_hash_is_cached = true;
-
- char version[PROPERTY_VALUE_MAX];
- // The version might not be set for development devices. In this case, use the
- // zero version.
- property_get(metrics::kProductVersionProperty, version,
- metrics::kDefaultVersion);
-
- cached_version_hash = base::Hash(version);
- if (testing_) {
- cached_version_hash = 42; // return any plausible value for the hash
+ brillo::OsReleaseReader reader;
+ reader.Load();
+ string version;
+ if (!reader.GetString(metrics::kProductVersion, &version)) {
+ LOG(ERROR) << "failed to read the product version.";
+ version = metrics::kDefaultVersion;
}
- return cached_version_hash;
+
+ uint32_t version_hash = base::Hash(version);
+ if (testing_) {
+ version_hash = 42; // return any plausible value for the hash
+ }
+ return version_hash;
}
void MetricsDaemon::Init(bool testing,
@@ -240,8 +238,8 @@
}
int MetricsDaemon::OnInit() {
- int return_code = dbus_enabled_ ? chromeos::DBusDaemon::OnInit() :
- chromeos::Daemon::OnInit();
+ int return_code = dbus_enabled_ ? brillo::DBusDaemon::OnInit() :
+ brillo::Daemon::OnInit();
if (return_code != EX_OK)
return return_code;
@@ -280,6 +278,16 @@
LOG(ERROR) << "DBus isn't connected.";
return EX_UNAVAILABLE;
}
+
+ device_ = weaved::Device::CreateInstance(
+ bus_,
+ base::Bind(&MetricsDaemon::UpdateWeaveState, base::Unretained(this)));
+ device_->AddCommandHandler(
+ "_metrics._enableAnalyticsReporting",
+ base::Bind(&MetricsDaemon::OnEnableMetrics, base::Unretained(this)));
+ device_->AddCommandHandler(
+ "_metrics._disableAnalyticsReporting",
+ base::Bind(&MetricsDaemon::OnDisableMetrics, base::Unretained(this)));
}
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
@@ -314,7 +322,56 @@
<< error.name << ": " << error.message;
}
}
- chromeos::DBusDaemon::OnShutdown(return_code);
+ brillo::DBusDaemon::OnShutdown(return_code);
+}
+
+void MetricsDaemon::OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd) {
+ auto command = cmd.lock();
+ if (!command)
+ return;
+
+ if (base::WriteFile(metrics_directory_.Append(metrics::kConsentFileName),
+ "", 0) != 0) {
+ PLOG(ERROR) << "Could not create the consent file.";
+ command->Abort("metrics_error", "Could not create the consent file",
+ nullptr);
+ return;
+ }
+
+ UpdateWeaveState();
+ command->Complete({}, nullptr);
+}
+
+void MetricsDaemon::OnDisableMetrics(
+ const std::weak_ptr<weaved::Command>& cmd) {
+ auto command = cmd.lock();
+ if (!command)
+ return;
+
+ if (!base::DeleteFile(metrics_directory_.Append(metrics::kConsentFileName),
+ false)) {
+ PLOG(ERROR) << "Could not delete the consent file.";
+ command->Abort("metrics_error", "Could not delete the consent file",
+ nullptr);
+ return;
+ }
+
+ UpdateWeaveState();
+ command->Complete({}, nullptr);
+}
+
+void MetricsDaemon::UpdateWeaveState() {
+ if (!device_)
+ return;
+
+ brillo::VariantDictionary state_change{
+ { "_metrics._AnalyticsReportingState",
+ metrics_lib_->AreMetricsEnabled() ? "enabled" : "disabled" }
+ };
+
+ if (!device_->SetStateProperties(state_change, nullptr)) {
+ LOG(ERROR) << "failed to update weave's state";
+ }
}
// static
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
index 612dfe2..3d691c5 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_daemon.h
@@ -26,7 +26,9 @@
#include <base/files/file_path.h>
#include <base/memory/scoped_ptr.h>
#include <base/time/time.h>
-#include <chromeos/daemons/dbus_daemon.h>
+#include <brillo/daemons/dbus_daemon.h>
+#include <libweaved/command.h>
+#include <libweaved/device.h>
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include "collectors/averaged_statistics_collector.h"
@@ -37,7 +39,7 @@
using chromeos_metrics::PersistentInteger;
-class MetricsDaemon : public chromeos::DBusDaemon {
+class MetricsDaemon : public brillo::DBusDaemon {
public:
MetricsDaemon();
~MetricsDaemon();
@@ -121,6 +123,15 @@
DBusMessage* message,
void* user_data);
+ // Enables metrics reporting.
+ void OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd);
+
+ // Disables metrics reporting.
+ void OnDisableMetrics(const std::weak_ptr<weaved::Command>& cmd);
+
+ // Updates the weave device state.
+ void UpdateWeaveState();
+
// Updates the active use time and logs time between user-space
// process crashes.
void ProcessUserCrash();
@@ -301,6 +312,7 @@
std::string server_;
scoped_ptr<UploadService> upload_service_;
+ std::unique_ptr<weaved::Device> device_;
};
#endif // METRICS_METRICS_DAEMON_H_
diff --git a/metricsd/metrics_daemon.rc b/metricsd/metrics_daemon.rc
index 0e1fcd5..0ee577e 100644
--- a/metricsd/metrics_daemon.rc
+++ b/metricsd/metrics_daemon.rc
@@ -1,4 +1,4 @@
-on boot
+on post-fs-data
mkdir /data/misc/metrics 0770 system system
service metrics_daemon /system/bin/metrics_daemon --uploader -nodaemon
diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc
index c2e794e..50c279d 100644
--- a/metricsd/metrics_daemon_main.cc
+++ b/metricsd/metrics_daemon_main.cc
@@ -18,8 +18,8 @@
#include <base/command_line.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
-#include <chromeos/flag_helper.h>
-#include <chromeos/syslog_logging.h>
+#include <brillo/flag_helper.h>
+#include <brillo/syslog_logging.h>
#include <rootdev.h>
#include "constants.h"
@@ -79,18 +79,18 @@
metrics::kMetricsDirectory,
"Root of the configuration files (testing only)");
- chromeos::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon");
+ brillo::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon");
// Also log to stderr when not running as daemon.
- chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogHeader |
- (FLAGS_daemon ? 0 : chromeos::kLogToStderr));
+ brillo::InitLog(brillo::kLogToSyslog | brillo::kLogHeader |
+ (FLAGS_daemon ? 0 : brillo::kLogToStderr));
if (FLAGS_daemon && daemon(0, 0) != 0) {
return errno;
}
MetricsLibrary metrics_lib;
- metrics_lib.Init();
+ metrics_lib.InitWithNoCaching();
MetricsDaemon daemon;
daemon.Init(FLAGS_uploader_test,
FLAGS_uploader | FLAGS_uploader_test,
diff --git a/metricsd/metrics_daemon_test.cc b/metricsd/metrics_daemon_test.cc
index 3a8fc3a..d3c9a23 100644
--- a/metricsd/metrics_daemon_test.cc
+++ b/metricsd/metrics_daemon_test.cc
@@ -20,7 +20,7 @@
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_number_conversions.h>
-#include <chromeos/flag_helper.h>
+#include <brillo/flag_helper.h>
#include <gtest/gtest.h>
#include "constants.h"
@@ -43,7 +43,7 @@
class MetricsDaemonTest : public testing::Test {
protected:
virtual void SetUp() {
- chromeos::FlagHelper::Init(0, nullptr, "");
+ brillo::FlagHelper::Init(0, nullptr, "");
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
scaling_max_freq_path_ = temp_dir_.path().Append("scaling_max");
cpu_max_freq_path_ = temp_dir_.path().Append("cpu_freq_max");
diff --git a/metricsd/metrics_library.cc b/metricsd/metrics_library.cc
index 3109704..a651b76 100644
--- a/metricsd/metrics_library.cc
+++ b/metricsd/metrics_library.cc
@@ -126,7 +126,7 @@
bool MetricsLibrary::AreMetricsEnabled() {
static struct stat stat_buffer;
time_t this_check_time = time(nullptr);
- if (this_check_time != cached_enabled_time_) {
+ if (!use_caching_ || this_check_time != cached_enabled_time_) {
cached_enabled_time_ = this_check_time;
cached_enabled_ = stat(consent_file_.value().data(), &stat_buffer) >= 0;
}
@@ -139,6 +139,12 @@
consent_file_ = dir.Append(metrics::kConsentFileName);
cached_enabled_ = false;
cached_enabled_time_ = 0;
+ use_caching_ = true;
+}
+
+void MetricsLibrary::InitWithNoCaching() {
+ Init();
+ use_caching_ = false;
}
void MetricsLibrary::InitForTest(const base::FilePath& metrics_directory) {
@@ -146,6 +152,7 @@
consent_file_ = metrics_directory.Append(metrics::kConsentFileName);
cached_enabled_ = false;
cached_enabled_time_ = 0;
+ use_caching_ = true;
}
bool MetricsLibrary::SendToUMA(const std::string& name,
diff --git a/metricsd/metrics_library_test.cc b/metricsd/metrics_library_test.cc
index f300d17..be8a4bb 100644
--- a/metricsd/metrics_library_test.cc
+++ b/metricsd/metrics_library_test.cc
@@ -93,3 +93,16 @@
VerifyEnabledCacheEviction(false);
VerifyEnabledCacheEviction(true);
}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledNoCaching) {
+ // disable caching.
+ lib_.use_caching_ = false;
+
+ // Checking the consent repeatedly should return the right result.
+ for (int i=0; i<100; ++i) {
+ SetMetricsConsent(true);
+ ASSERT_EQ(true, lib_.AreMetricsEnabled());
+ SetMetricsConsent(false);
+ ASSERT_EQ(false, lib_.AreMetricsEnabled());
+ }
+}
diff --git a/metricsd/uploader/proto/system_profile.proto b/metricsd/uploader/proto/system_profile.proto
index 4cab0d9..bac828b 100644
--- a/metricsd/uploader/proto/system_profile.proto
+++ b/metricsd/uploader/proto/system_profile.proto
@@ -88,7 +88,7 @@
optional string application_locale = 4;
message BrilloDeviceData {
- optional string build_target_id = 1;
+ optional string product_id = 1;
}
optional BrilloDeviceData brillo = 21;
diff --git a/metricsd/uploader/sender_http.cc b/metricsd/uploader/sender_http.cc
index 953afc1..4b572a6 100644
--- a/metricsd/uploader/sender_http.cc
+++ b/metricsd/uploader/sender_http.cc
@@ -20,8 +20,8 @@
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
-#include <chromeos/http/http_utils.h>
-#include <chromeos/mime_utils.h>
+#include <brillo/http/http_utils.h>
+#include <brillo/mime_utils.h>
HttpSender::HttpSender(const std::string server_url)
: server_url_(server_url) {}
@@ -31,14 +31,14 @@
const std::string hash =
base::HexEncode(content_hash.data(), content_hash.size());
- chromeos::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}};
- chromeos::ErrorPtr error;
- auto response = chromeos::http::PostTextAndBlock(
+ brillo::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}};
+ brillo::ErrorPtr error;
+ auto response = brillo::http::PostTextAndBlock(
server_url_,
content,
- chromeos::mime::application::kWwwFormUrlEncoded,
+ brillo::mime::application::kWwwFormUrlEncoded,
headers,
- chromeos::http::Transport::CreateDefault(),
+ brillo::http::Transport::CreateDefault(),
&error);
if (!response || response->ExtractDataAsString() != "OK") {
if (error) {
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index 2437b56..1995510 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -21,8 +21,9 @@
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
-#include <cutils/properties.h>
+#include <brillo/osrelease_reader.h>
#include <string>
+#include <update_engine/client.h>
#include <vector>
#include "constants.h"
@@ -73,16 +74,27 @@
CHECK(!initialized_)
<< "this should be called only once in the metrics_daemon lifetime.";
- profile_.product_id = GetProperty(metrics::kProductIdProperty);
+ brillo::OsReleaseReader reader;
+ std::string channel;
+ if (testing_) {
+ reader.LoadTestingOnly(metrics_directory_);
+ channel = "unknown";
+ } else {
+ reader.Load();
+ auto client = update_engine::UpdateEngineClient::CreateInstance();
+ if (!client->GetChannel(&channel)) {
+ LOG(ERROR) << "failed to read the current channel from update engine.";
+ }
+ }
- if (profile_.product_id.empty()) {
- LOG(ERROR) << "System property " << metrics::kProductIdProperty
- << " is not set.";
+ if (!reader.GetString(metrics::kProductId, &profile_.product_id)) {
+ LOG(ERROR) << "product_id is not set.";
return false;
}
- std::string channel = GetProperty(metrics::kChannelProperty);
- profile_.version = GetProperty(metrics::kProductVersionProperty);
+ if (!reader.GetString(metrics::kProductVersion, &profile_.version)) {
+ LOG(ERROR) << "failed to read the product version";
+ }
if (channel.empty() || profile_.version.empty()) {
// If the channel or version is missing, the image is not official.
@@ -136,7 +148,7 @@
profile_proto->set_channel(profile_.channel);
metrics::SystemProfileProto_BrilloDeviceData* device_data =
profile_proto->mutable_brillo();
- device_data->set_build_target_id(profile_.product_id);
+ device_data->set_product_id(profile_.product_id);
return true;
}
@@ -154,18 +166,6 @@
return guid;
}
-std::string SystemProfileCache::GetProperty(const std::string& name) {
- if (testing_) {
- std::string content;
- base::ReadFileToString(metrics_directory_.Append(name), &content);
- return content;
- } else {
- char value[PROPERTY_VALUE_MAX];
- property_get(name.data(), value, "");
- return std::string(value);
- }
-}
-
metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
const std::string& channel) {
if (channel == "stable") {
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
index 3410157..97fb33a 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -76,11 +76,6 @@
// Initializes |profile_| only if it has not been yet initialized.
bool InitializeOrCheck();
- // Gets a system property as a string.
- // When |testing_| is true, reads the value from |metrics_directory_|/|name|
- // instead.
- std::string GetProperty(const std::string& name);
-
bool initialized_;
bool testing_;
base::FilePath metrics_directory_;
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index 305fd0c..236376a 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -58,9 +58,11 @@
}
void SetTestingProperty(const std::string& name, const std::string& value) {
+ base::FilePath filepath = dir_.path().Append("etc/os-release.d").Append(name);
+ ASSERT_TRUE(base::CreateDirectory(filepath.DirName()));
ASSERT_EQ(
value.size(),
- base::WriteFile(dir_.path().Append(name), value.data(), value.size()));
+ base::WriteFile(filepath, value.data(), value.size()));
}
base::ScopedTempDir dir_;
@@ -225,9 +227,8 @@
SenderMock* sender = new SenderMock();
upload_service_->sender_.reset(sender);
- SetTestingProperty(metrics::kChannelProperty, "beta");
- SetTestingProperty(metrics::kProductIdProperty, "hello");
- SetTestingProperty(metrics::kProductVersionProperty, "1.2.3.4");
+ SetTestingProperty(metrics::kProductId, "hello");
+ SetTestingProperty(metrics::kProductVersion, "1.2.3.4");
scoped_ptr<metrics::MetricSample> histogram =
metrics::MetricSample::SparseHistogramSample("myhistogram", 1);
@@ -242,8 +243,6 @@
EXPECT_TRUE(sender->is_good_proto());
EXPECT_EQ(1, sender->last_message_proto().histogram_event().size());
- EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_BETA,
- sender->last_message_proto().system_profile().channel());
EXPECT_NE(0, sender->last_message_proto().client_id());
EXPECT_NE(0, sender->last_message_proto().system_profile().build_timestamp());
EXPECT_NE(0, sender->last_message_proto().session_id());
@@ -269,7 +268,7 @@
}
TEST_F(UploadServiceTest, SessionIdIncrementedAtInitialization) {
- SetTestingProperty(metrics::kProductIdProperty, "hello");
+ SetTestingProperty(metrics::kProductId, "hello");
SystemProfileCache cache(true, dir_.path());
cache.Initialize();
int session_id = cache.profile_.session_id;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a2844e7..a204eab 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -14,6 +14,9 @@
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
+ # Disable sysrq from keyboard
+ write /proc/sys/kernel/sysrq 0
+
# Set the security context of /adb_keys if present.
restorecon /adb_keys
@@ -325,6 +328,7 @@
mkdir /data/misc/media 0700 media media
mkdir /data/misc/vold 0700 root root
mkdir /data/misc/boottrace 0771 system shell
+ mkdir /data/misc/update_engine 0700 root root
# For security reasons, /data/local/tmp should always be empty.
# Do not place files or directories in /data/local/tmp